在接下来的程序输入工作确定,但是当我问到显示输出,DOS不显示在所有的东西! 这怎么可能?
ORG 256
mov dx, msg1
mov ah, 09h ;DOS.WriteString
int 21h
mov dx, buf
mov ah, 0Ah ;DOS.BufferedInput
int 21h
mov dx, msg2
mov ah, 09h ;DOS.WriteString
int 21h
mov dx, buf
mov ah, 09h ;DOS.WriteString
int 21h
mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
msg1: db 'Input : ', '$'
buf: db 20 dup ('$')
msg2: db 13, 10, 'Output : ', '$'
; --------------------------------------
看你如何定义你的输入缓冲区( buf: db 20 dup ('$')
我明白了,你想抄近路,有输入-封端已经准备好重新显示它$。 可悲的是这打乱了所需的设置为DOS输入功能0AH和你的程序是严重的问题与潜在的缓冲区溢出。
使用另外$ -termination是不是,你可以做,因为$字符可能已经出现跻身输入的字符中最亮的选择。 所有我下面给出的例子程序将使用零终止代替。
使用输入文本int 21h AH=0Ah
这种缓冲STDIN输入功能从键盘获得字符,并继续这样做,直到用户按下Enter键。 所有字符和最终回车被放置在开始于经由在指针由调用程序提供的输入缓冲器的第3字节的存储空间DS:DX
。
字符计数,不包括最后的回车,被存储在输入缓冲器的第二字节。
这是调用程序告诉DOS存储空间有多大的责任。 因此,你必须把它的输入缓冲区的第一个字节长度调用此函数之前。 为了允许1个字符的输入你2.设置的存储大小,以允许你在255设置的存储大小的254个字符的输入。
如果你不希望能够从模板回忆以往任何输入,那么最好是也为零的第二个字节。 基本上,模板是在调用程序提供的输入缓存器中的预先存在的(合法的)内容。 如果预先存在的内容是无效的,那么该模板不可用。
令人惊讶的这一功能限制了编辑功能。
- 逃生从当前输入的所有字符。
电流输入被放弃,但停留在屏幕上光标放在下一行,下方其中,输入第一次开始。 - Backspace键从当前输入的最后一个字符。
按预期工作,如果输入的单个行内停留在屏幕上。 如果在另一方面,输入跨越几行,然后该退格将停止在屏幕的左边缘。 从此会有逻辑输入和视觉输入之间严重脱节,因为已达到逻辑上退格将继续,直到存储空间第一的位置! - F6插入在当前输入的档案结尾字符(为1Ah)。
屏幕上会显示“^ Z”。 - F7插入当前输入一个零字节。
屏幕上会显示“^ @”。 - CTRL + ENTER转换到下一行(执行回车和换行),不添加到当前输入,你不能回去。
还有更多的编辑键可用。 他们都是让人想起Edlin.exe将 ,古老的DOS行编辑器,这是每个前行成为其上构建下一行的模板文本编辑器。
- F1从复制的模板新行一个字符。
- F2 + ...... +复制模板中的所有字符为新的线路,到指定的字符。
- F3复制模板中的新行的所有剩余的字符。
- F4 + ...跳过的人物模板,到指定的字符。
- F5使新线新模板。
- 逃生清除当前输入和离开模板不变。
- 删除模板跳过一个字符。
- 插入进入或退出插入模式。
- 退格键删除新行的最后一个字符,并在模板中的光标置于后面一个字符。
- 左同退格键。
- 右与F1。
标签是由该功能扩展。 Tab扩展是由一系列的一个或多个空间(ASCII 32),直到光标到达的列位置是8的倍数的替换ASCII 9的过程。
此选项卡扩展只发生在屏幕上。 存储空间将于9 ASCII。
这个函数CTRL C / CTRL 歇检查。
当此功能完成后,光标会在当前行最左边的列。
例1中,缓冲STDIN输入。
ORG 256 ;Create .COM program
cld
mov si, msg1
call WriteStringDOS
mov dx, buf
mov ah, 0Ah ;DOS.BufferedInput
int 21h
mov si, msg2
call WriteStringDOS
mov si, buf+2
movzx bx, [si-1] ;Get character count
mov word [si+bx+1], 10 ;Keep CR, append LF and 0
call WriteStringDOS
mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
; IN (ds:si) OUT ()
WriteStringDOS:
pusha
jmps .b
.a: mov dl, al
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
.b: lodsb
test al, al
jnz .a
popa
ret
; --------------------------------------
buf: db 255, 16, "I'm the template", 13, 255-16-1+2 dup (0)
msg1: db 'Choose color ? ', 0
msg2: db 10, 'You chose ', 0
; --------------------------------------
使用输入文本int 21h AH=3Fh
当与预定义的手柄0(在使用BX
)这个从文件中读取或设备功能从键盘获得字符,并继续这样做,直到用户按回车 。 所有字符(从未超过127个),并最终回车加上额外的换行符放置在DOS内核中的专用缓冲区。 现在,这将成为新的模板。
此后该函数将在所提供的缓冲器写DS:DX
,要求在该字节数CX
参数。 如果CX
指定一个数字,是小于由该输入产生的字节数,需要一个或多个额外的调用该函数检索完整输入。 只要有剩余的字符被拾起,使用键盘此功能不会启动另一个输入会话! 这是不同的程序或者同一个程序的会话之间,即使真。
在上一节中描述的所有编辑键可用。
标签只,而不是在模板上的屏幕扩大。
这个函数CTRL C / CTRL 歇检查。
当此功能完成后,光标会在在最左边的列
- 当前行如果终止换行符不返回字节中。
- 下一行,如果终止换行符是返回的字节之间。
例2a,从文件读取或设备,拿起所有的一次。
ORG 256 ;Create .COM program
cld
mov si, msg1
call WriteStringDOS
mov dx, buf
mov cx, 127+2 ;Max input is 127 chars + CR + LF
xor bx, bx ;STDIN=0
mov ah, 3Fh ;DOS.ReadFileOrDevice
int 21h ; -> AX CF
jc Exit
mov bx, ax ;Bytes count is less than CX
mov si, msg2
call WriteStringDOS
mov si, buf
mov [si+bx], bh ;Keep CR and LF, append 0 (BH=0)
call WriteStringDOS
Exit: mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
; IN (ds:si) OUT ()
WriteStringDOS:
pusha
jmps .b
.a: mov dl, al
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
.b: lodsb
test al, al
jnz .a
popa
ret
; --------------------------------------
buf: db 127+2+1 dup (0)
msg1: db 'Choose color ? ', 0
msg2: db 'You chose ', 0
; --------------------------------------
例2b,从文件读取或设备,在同一时间拿起一个字节。
ORG 256 ;Create .COM program
cld
mov si, msg1
call WriteStringDOS
mov dx, buf
mov cx, 1
xor bx, bx ;STDIN=0
mov ah, 3Fh ;DOS.ReadFileOrDevice
int 21h ; -> AX CF
jc Exit
mov si, msg2
call WriteStringDOS
mov si, dx ;DX=buf, CX=1, BX=0
Next: mov ah, 3Fh ;DOS.ReadFileOrDevice
int 21h ; -> AX CF
jc Exit
call WriteStringDOS ;Display a single byte
cmp byte [si], 10
jne Next
Exit: mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
; IN (ds:si) OUT ()
WriteStringDOS:
pusha
jmps .b
.a: mov dl, al
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
.b: lodsb
test al, al
jnz .a
popa
ret
; --------------------------------------
msg1: db 'Choose color ? ', 0
msg2: db 10, 'You chose '
buf: db 0, 0
; --------------------------------------
使用输入文本int 2Fh AX=4810h
这DOSKEY缓冲STDIN输入功能只能调用如果安装了DOSKEY.COM TSR 。 它操作起来非常象常规缓冲STDIN输入功能0AH(见上文),但所有相同的编辑可能性,在DOS命令行,其中包括使用所有的DOSKEY特殊键的能力。
- 最多从历史中获取一个项目。
- 退出历史获取下一个项目。
- F7显示在历史中的所有项目的列表。
- ALT + F7清除历史记录。
- ... F8查找以启动项目(S)...
- F9按编号选择从历史的项目。
- Alt键 F10删除所有macrodefinitions。
上DOS 6.2存储空间总是被限制为128个字节,从而允许的127个字符为强制性回车输入和房间。 这是不可能预先加载的模板,所以总是输入缓冲区的第2个字节设置为零。
在DOS Win95的,如果你用以下命令安装DOSKEY.COM TSR的存储空间可以大到255个字节doskey /line:255
。 这是可能的预加载的模板的存储空间。 这使得Win95的版本非常接近的是feasable与输入功能0AH。
这个函数CTRL C / CTRL 歇检查。
当此功能完成后,光标会在当前行最左边的列。 如果字符计数为零,这意味着用户在DOSKEY宏,尚未扩展的名称输入。 你没有能看到未膨胀的线! 需要此功能的第二次调用,并在返回这个时候,光标会扩展的文本的最后一个字符后面。
一个特点是,当多宏命令( $T
)被扩大,你只能得到1号指令的展开文本。 需要的功能的其他调用,以获得其他扩展文本。 虽然这一切是从像COMMAND.COM命令shell中非常有用,从用户应用程序中它真的很烦人,你可以不知道什么时候出现这种情况。
由于输入的文本添加到命令历史记录,这是不可避免的历史与无关的项目填满。 当然不是你想看到在DOS提示符下什么!
实施例3,调用DOSKEY.COM。
ORG 256 ;Create .COM program
cld
mov ax, 4800h ;DOSKEY.CheckInstalled
int 2Fh ; -> AL
test al, al
mov si, err1
jz Exit_
Again: mov si, msg1
call WriteStringDOS
mov dx, buf
mov ax, 4810h ;DOSKEY.BufferedInput
int 2Fh ; -> AX
test ax, ax
mov si, err2
jnz Exit_
cmp [buf+1], al ;AL=0
je Again ;Macro expansion needed
mov si, msg2
call WriteStringDOS
mov si, buf+2
movzx bx, [si-1] ;Get character count (is GT 0)
mov word [si+bx+1], 10 ;Keep CR, append LF and 0
Exit_: call WriteStringDOS
Exit: mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
; IN (ds:si) OUT ()
WriteStringDOS:
pusha
jmps .b
.a: mov dl, al
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
.b: lodsb
test al, al
jnz .a
popa
ret
; --------------------------------------
buf: db 128, 0, 128+2 dup (0)
msg1: db 'Choose color ? ', 0
msg2: db 13, 10, 'You chose ', 0
err1: db 'N/A', 13, 10, 0
err2: db 'Failed', 13, 10, 0
; --------------------------------------
使用输入文本int 21h AH=08h
因为堆栈溢出强加文本30000字节限制的继续下面的答案...
问题认识的来源? 我用汇编:
- considers labels that start with a dot ( . ) as 1st level local labels
- considers labels that start with a colon ( : ) as 2nd level local labels
- is Single Instruction Multiple Operands (SIMO), so
push cx si
translates to push cx
push si
.
使用输入文本int 21h AH=08h
描述到现在为止(在上面的答案!)所有三种输入方式显然是量身定做,以适应微软工具,如Edlin.exe将和COMMAND.COM。
如果您正在编写自己的应用程序,然后更好的结果可以通过制作自己的输入程序来实现。 在这样的心脏的过程将是DOS单个字符输入功能中的一个。 我选择了STDIN输入功能08H,因为我想允许CTRL C / CTRL 歇检查,我打算通过BIOS来呼应人物自己Int 10h AH=09h
写入字符和属性在光标位置 。 这样我就可以避免搞乱了任何重定向输出。
编程有一个在使用这种BufferedInput程序或DOS.BufferedInput系统调用没有区别。 然而,对于在键盘上输入用户因为所有与旧的和困难的模板编辑相关的密钥已被驳回,由通常的编辑键,使您可以自由移动光标周围取而代之的将是更加容易。
- 向左移动光标离开了。
- 向右移动光标。
- 首页将光标移到最左边。
- 最终将光标移到最右侧。
- 按Ctrl 首页删除所有字符到左边。
- 按Ctrl 最终删除所有字符的权利。
- 删除删除当前字符。
- 退格键删除字符光标的左侧。
- 逃亡删除所有的字符。
- 返回结束输入。
如果输入缓冲器的第二字节保存一个非零值,则所述存储空间被认为(从先前输入也许)包含旧字符串。 DOS会叫这个模板。 从DOS不同的是:
- 旧的字符串不要求回车结束。
- 旧的字符串会立即显示在屏幕上。
在输入过程中,标签不扩大,输入被限制在当前行内停留。 较长的文本会水平滚动。
当输入终于完成了,完成的文本与选项卡扩展编写一次(在屏幕上,存储空间将始终保持ASCII 9),并不再限制为单行。
这个过程确实CTRL C / CTRL 歇检查。
当这个过程完成时,光标会在当前行最左边的列。
这个过程与写输入重定向和输出重定向记,因而非常适合控制台应用程序。
输入重定向的一个作用是,它是无用的呼应任何临时输出到屏幕上。 任一用户是不存在在屏幕凝视或临时输出将在眨眼消失。
实施例4,改进的缓冲STDIN输入。
ORG 256 ;Create .COM program
cld
mov si, msg1
call WriteStringDOS
mov dx, buf
call BufferedInput ;Replaces 'mov ah, 0Ah : int 21h'
mov si, msg2
call WriteStringDOS
mov si, buf+2
movzx bx, [si-1] ;Get character count
mov word [si+bx+1], 10 ;Keep CR, append LF and 0
call WriteStringDOS
mov ax, 4C00h ;DOS.TerminateWithExitcode
int 21h
; --------------------------------------
; IN (ds:si) OUT ()
WriteStringDOS:
pusha
jmps .b
.a: mov dl, al
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
.b: lodsb
test al, al
jnz .a
popa
ret
; --------------------------------------
; IN (ds:dx) OUT ()
BufferedInput:
; Entry DS:DX Buffer of max 1+1+255 bytes
; 1st byte is size of storage space starting at 3rd byte
; 2nd byte is size of old (CR-terminated) string, 0 if none
; Storage space can contain old (CR-terminated) string
; Exit DS:DX Nothing changed if header bytes were invalid
; 1st byte unchanged
; 2nd byte is size of new CR-terminated string
; Storage space contains new CR-terminated string
; Local [bp-1] PAGE Display page
; [bp-2] STORE Size of storage space
; [bp-3] ROW Row of input box
; [bp-4] COL Column of input box
; [bp-5] SHIFT Number of characters shifted out on the leftside
; [bp-6] INBOX Size of input box
; [bp-7] LIX Number of characters in current input string
; [bp-8] CIX Position of cursor in current input string
; [bp-10] FLAGS Bit[0] is ON for normal keyboard input
pusha
mov si, dx
lodsw ; -> SI points at storage space
test al, al ;AL is size of storage space
jz .Quit ;No storage space!
cmp ah, al ;AH is size of old string
jnb .Quit ;Old string too long!
mov bl, al
sub sp, 256 ;Local edit buffer (max size)
mov bp, sp
mov ah, 0Fh ;BIOS.GetVideoMode
int 10h ; -> AL=Mode AH=Cols BH=Page
push bx ;STORE and PAGE
mov bl, ah
mov ah, 03h ;BIOS.GetCursor
int 10h ; -> CX=Shape DL=Col DH=Row
push dx ;COL and ROW
sub bl, dl ;Size of the widest inbox
xor bh, bh
push bx ;INBOX and SHIFT
push bx ;CIX and LIX (replaces 'sub sp, 2')
call .ESC ;Clear edit buffer, reset some vars
mov cl, [si-1] ;Size of old string (starts at SI)
jmps .b
.a: lodsb ;Storage space gives old string
push cx si
call .Asc ;Input old string
pop si cx
.b: sub cl, 1
jnb .a
xor bx, bx ;STDIN
mov ax, 4400h ;DOS.GetDeviceInformation
int 21h ; -> AX DX CF
jc .c ;Go default to keyboard
test dl, dl
jns .d ;Block device, not keyboard
shr dl, 1
.c: adc bx, bx ; -> BX=1 if Keyboard
.d: push bx ;FLAGS
.Main: call .Show ;Refresh input box on screen
call .Key ;Get key from DOS -> AX
mov bx, .Scans
test ah, ah
jz .f ;Not an extended ASCII
mov [cs:.Fail], ah ;Sentinel
.e: lea bx, [bx+3]
cmp ah, [cs:bx-1]
jne .e
.f: call [cs:bx]
jmps .Main
.Quit: popa ;Silently quiting just like DOS
ret
; - - - - - - - - - - - - - - - - - - -
.Scans: db .Asc
db 4Bh, .s4B ;<LEFT>
db 4Dh, .s4D ;<RIGHT>
db 47h, .s47 ;<HOME>
db 4Fh, .s4F ;<END>
db 77h, .s77 ;<CTRL-HOME>
db 75h, .s75 ;<CTRL-END>
db 53h, .s53 ;<DELETE>
.Fail: db ?, .Beep
; - - - - - - - - - - - - - - - - - - -
.Beep: mov ax, 0E07h ;BIOS.TeletypeBell
int 10h
ret
; - - - - - - - - - - - - - - - - - - -
.Key: call :1
test ah, ah ;Extended ASCII requires 2 calls
jnz :2
:1: mov ah, 08h ;DOS.STDINInput
int 21h ; -> AL
mov ah, 0
:2: xchg al, ah
ret
; - - - - - - - - - - - - - - - - - - -
.Show: test word [bp-10], 1 ;FLAGS.Keyboard ?
jz :Ready ;No, input is redirected
movzx di, [bp-6] ;INBOX
movzx si, [bp-5] ;SHIFT
mov dx, [bp-4] ;COL and ROW
mov cx, 1 ;Replication count
mov bh, [bp-1] ;PAGE
mov bl, 07h ;WhiteOnBlack
:Next: mov ah, 02h ;BIOS.SetCursor
int 10h
mov al, [bp+si]
mov ah, 09h ;BIOS.WriteCharacterAndAttribute
int 10h
inc dl ;Next column
inc si ;Next character
dec di
jnz :Next ;Process all of the input box
mov dx, [bp-4] ;COL and ROW
add dl, [bp-8] ;CIX
sub dl, [bp-5] ;SHIFT
mov ah, 02h ;BIOS.SetCursor
int 10h
:Ready: ret
; - - - - - - - - - - - - - - - - - - -
.BS: cmp byte [bp-8], 0 ;CIX
jne :1
ret
:1: call .s4B ;<LEFT>
; --- --- --- --- --- --- --
; <DELETE>
.s53: movzx di, [bp-8] ;CIX
movzx cx, [bp-7] ;LIX
sub cx, di
je :2 ;Cursor behind the current input
:1: mov dl, [bp+di+1] ;Move down in edit buffer
mov [bp+di], dl
inc di
dec cx
jnz :1
dec byte [bp-7] ;LIX
:2: ret
; - - - - - - - - - - - - - - - - - - -
.RET: xor si, si
mov bx, [bp+256+10] ;pusha.DX -> DS:BX
mov al, [bp-7] ;LIX
inc bx
mov [bx], al ;2nd byte is size of new string
inc bx
jmps :2
:1: mov dl, [bp+si]
mov [bx+si], dl ;Storage space receives new string
inc si
:2: sub al, 1
jnb :1
mov byte [bx+si], 13 ;Terminating CR
push bx ;(1)
call .ESC ;Wipe clean the input box
call .Show ; and reset cursor
pop si ;(1) -> DS:SI
:3: lodsb ;Final unrestricted display,
mov dl, al ; expanding tabs
mov ah, 02h ;DOS.DisplayCharacter
int 21h ; -> AL
cmp dl, 13 ;Cursor ends in far left column
jne :3
lea sp, [bp+256] ;Free locals and edit buffer
popa
ret
; - - - - - - - - - - - - - - - - - - -
.ESC: mov di, 256 ;Fill edit buffer with spaces
:1: sub di, 2
mov word [bp+di], " "
jnz :1
mov [bp-8], di ;DI=0 -> CIX=0 LIX=0
mov byte [bp-5], 0 ;SHIFT=0
ret
; - - - - - - - - - - - - - - - - - - -
.Asc: cmp al, 8 ;<BACKSPACE>
je .BS
cmp al, 13 ;<RETURN>
je .RET
cmp al, 27 ;<ESCAPE>
je .ESC
cmp al, 10 ;Silently ignoring linefeed
jne :1 ; in favor of input redirection
ret
:1: movzx di, [bp-8] ;CIX
movzx si, [bp-7] ;LIX
lea dx, [si+1]
cmp dl, [bp-2] ;STORE
jb :3
jmp .Beep ;Storage capacity reached
:2: mov dl, [bp+si-1] ;Move up in edit buffer
mov [bp+si], dl
dec si
:3: cmp si, di
ja :2
mov [bp+si], al ;Add newest character
inc byte [bp-7] ;LIX
; --- --- --- --- --- --- --
; <RIGHT>
.s4D: inc byte [bp-8] ;CIX
mov al, [bp-7] ;LIX
cmp [bp-8], al ;CIX
jbe .Shift
mov [bp-8], al ;CIX
ret
; - - - - - - - - - - - - - - - - - - -
; <LEFT>
.s4B: sub byte [bp-8], 1 ;CIX
jnb .Shift
; --- --- --- --- --- --- --
; <HOME>
.s47: mov byte [bp-8], 0 ;CIX
jmps .Shift
; - - - - - - - - - - - - - - - - - - -
; <END>
.s4F: mov al, [bp-7] ;LIX
mov [bp-8], al ;CIX
; --- --- --- --- --- --- --
.Shift: mov dl, [bp-5] ;SHIFT
mov al, [bp-8] ;CIX
cmp al, dl
jb :1
add dl, [bp-6] ;INBOX
sub al, dl
jb :2
inc al
add al, [bp-5] ;SHIFT
:1: mov [bp-5], al ;SHIFT
:2: ret
; - - - - - - - - - - - - - - - - - - -
; <CTRL-HOME>
.s77: call .BS
cmp byte [bp-8], 0 ;CIX
ja .s77
ret
; - - - - - - - - - - - - - - - - - - -
; <CTRL-END>
.s75: call .s53 ;<DELETE>
mov al, [bp-8] ;CIX
cmp al, [bp-7] ;LIX
jb .s75
ret
; --------------------------------------
buf: db 255, 16, "I'm an OldString", 13, 255-16-1+2 dup (0)
msg1: db 'Choose color ? ', 0
msg2: db 10, 'You chose ', 0
; --------------------------------------
问题认识的来源? 我用汇编:
- 认为以点开始的标签(。)为第一级局部标签
- 认为以冒号开始的标签(:)为2级局部标签
- 是单指令多操作数(SIMO),所以
push cx si
转化为push cx
push si
。
对于一个真正高性能的输入过程,看看丰富的编辑表单输入 ,代码审查的贡献。