缓冲输入的工作原理 [英] How buffered input works

查看:43
本文介绍了缓冲输入的工作原理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下一个程序中的输入工作正常,但是当我要求显示输出时,DOS根本不显示任何东西!这怎么可能?

 ORG 256mov dx, msg1mov ah, 09h ;DOS.WriteString21 小时mov dx, bufmov ah, 0Ah ;DOS.BufferedInput21 小时mov dx, msg2mov ah, 09h ;DOS.WriteString21 小时mov dx, bufmov ah, 09h ;DOS.WriteString21 小时mov ax, 4C00h ;DOS.TerminateWithExitcode21 小时;-------------------------------msg1: db '输入:', '$'buf: db 20 dup ('$')msg2: db 13, 10, '输出: ', '$';-------------------------------

解决方案

看看你是如何定义输入缓冲区的 (buf: db 20 dup ('$')),我明白了你想偷工减料并让输入已经 $-terminated 准备好重新显示它.可悲的是,这弄乱了 DOS 输入所需的设置函数 0Ah 并且您的程序存在潜在缓冲区的严重问题超限.
此外,使用 $-termination 并不是您能做出的最明智的选择因为 $ 字符可能已经出现在输入的字符中.我在下面展示的所有示例程序都将使用零终止相反.

使用int 21h AH=0Ah

输入文本

缓冲标准输入函数从键盘获取字符并继续这样做,直到用户按下 Enter 键.全部字符和最后的回车符放在存储空间中从调用程序提供的输入缓冲区的第 3 个字节开始通过DS:DX中的指针.
字符数,不包括最后的回车符,存储在输入缓冲区的第二个字节.
调用程序有责任告诉 DOS 有多大存储空间是.因此,您必须将其长度放在调用此函数之前的输入缓冲区.允许输入 1字符,您将存储大小设置为 2.要允许输入 254您将存储大小设置为 255 个字符.
如果您不想从模板中调用任何以前的输入,那么最好也将第二个字节归零.基本上模板是调用程序在输入缓冲区中预先存在的(和有效的)内容假如.如果预先存在的内容无效,则模板不是可用的.

令人惊讶的是,此功能的编辑功能有限.

  • Escape 从当前输入中删除所有字符.
    当前输入被放弃但保留在屏幕上并且光标位于下一行,在第一次输入的下方.
  • Backspace 从当前输入中删除最后一个字符.
    如果输入保持在屏幕上的单行内,则按预期工作.另一方面,如果输入跨越几行,那么这种退格将停在屏幕的左边缘.以后会很严重逻辑输入和视觉输入之间的差异,因为逻辑上退格将一直进行,直到到达存储空间中的第 1 个位置!
  • F6 在当前输入中插入文件结束符 (1Ah).
    屏幕将显示^Z".
  • F7 在当前输入中插入一个零字节.
    屏幕将显示^@".
  • ctrlEnter 转换到下一行(执行一​​个回车和换行),不会在当前输入中添加任何内容,并且您回不去了.

还有更多编辑键可用.它们都让人想起 EDLIN.EXE,古老的 DOS 行编辑器,这是一个文本编辑器,其中每行成为您构建下一行的模板.

  • F1 将模板中的一个字符复制到新行.
  • F2 + ... 将模板中的所有字符复制到新行,直到指定的字符.
  • F3 将模板中所有剩余的字符复制到新行.
  • F4 + ... 跳过模板中的字符,向上到指定的字符.
  • F5 使新行成为新模板.
  • Escape 清除当前输入并保持模板不变.
  • 删除 跳过模板中的一个字符.
  • Insert 进入或退出插入模式.
  • Backspace 删除新行的最后一个字符,并将光标放回模板中的一个字符.
  • 与 Backspace 相同.
  • 与 F1 相同.

标签被这个函数扩展.标签扩展是替换的过程ASCII 9 由一系列一个或多个空格 (ASCII 32) 组成,直到光标到达8 的倍数的列位置.
此选项卡扩展仅发生在屏幕上.存储空间将容纳 ASCII 9.

这个函数执行ctrlC/ctrlBreak检查.

此功能完成后,光标将位于最左侧的列中当前行.

示例 1,缓冲的 STDIN 输入.

 ORG 256 ;创建.COM程序CLDmov si, msg1调用 WriteStringDOSmov dx, bufmov ah, 0Ah ;DOS.BufferedInput21 小时mov si, msg2调用 WriteStringDOSmov si, buf+2movzx bx, [si-1] ;获取字符数mov word [si+bx+1], 10 ;保留CR,附加LF和0调用 WriteStringDOSmov ax, 4C00h ;DOS.TerminateWithExitcode21 小时;-------------------------------;输入 (ds:si) 输出 ()写字符串DOS:普萨jmps.b.a: mov dl, almov ah, 02h ;DOS.DisplayCharacter整数 21 小时;->AL.b: lodsb测试 al, aljnz.a波帕退;-------------------------------buf: db 255, 16, "我是模板", 13, 255-16-1+2 dup (0)msg1: db '选择颜色?', 0msg2: db 10, '你选择了 ', 0;-------------------------------

使用int 21h AH=3Fh

输入文本

与预定义句柄 0(在 BX 中)一起使用时,此 从文件或设备读取函数从键盘获取字符并继续这样做,直到用户按 Enter.所有字符(不超过 127 个)和最后的回车加一个额外的换行被放置在一个私有的DOS 内核中的缓冲区.这现在成为新模板.
此后该函数将在 DS:DX 提供的缓冲区中写入数量在 CX 参数中请求的字节数.如果 CX 指定了一个数字小于此输入生成的字节数,一个或多个需要额外调用此函数来检索完整的输入.只要还有剩余的字符要拾取,这个功能就会不要使用键盘启动另一个输入会话!这甚至是真实的不同的程序或同一程序的会话.

上一节中描述的所有编辑键都可用.

选项卡仅在屏幕上展开,不在模板中展开.

这个函数执行ctrlC/ctrlBreak检查.

当这个函数完成时,光标会在最左边的列中

  • 如果终止换行不在返回的字节中,则为当前行.
  • 如果终止换行符在返回的字节中,则为下一行.

示例 2a,从文件或设备读取,一次全部提取.

 ORG 256 ;创建.COM程序CLDmov si, msg1调用 WriteStringDOSmov dx, bufmov cx, 127+2 ;最大输入为 127 个字符 + CR + LF异或 bx, bx ;STDIN=0mov ah, 3Fh ;DOS.ReadFileOrDevice整数 21 小时;->轴心CF退出mov bx, ax ;字节数小于CXmov si, msg2调用 WriteStringDOSmov si, bufmov [si+bx], bh ;保留CR和LF,追加0(BH=0)调用 WriteStringDOS退出: mov ax, 4C00h ;DOS.TerminateWithExitcode21 小时;-------------------------------;输入 (ds:si) 输出 ()写字符串DOS:普萨jmps.b.a: mov dl, almov ah, 02h ;DOS.DisplayCharacter整数 21 小时;->AL.b: lodsb测试 al, aljnz.a波帕退;-------------------------------buf: db 127+2+1 重复 (0)msg1: db '选择颜色?', 0msg2: db '你选择了 ', 0;-------------------------------

示例 2b,从文件或设备读取,一次取一个字节.

 ORG 256 ;创建.COM程序CLDmov si, msg1调用 WriteStringDOSmov dx, bufmov cx, 1异或 bx, bx ;STDIN=0mov ah, 3Fh ;DOS.ReadFileOrDevice整数 21 小时;->轴心CF退出mov si, msg2调用 WriteStringDOSmov si, dx ;DX=buf, CX=1, BX=0下一个: mov ah, 3Fh ;DOS.ReadFileOrDevice整数 21 小时;->轴心CF退出call WriteStringDOS ;显示单个字节cmp 字节 [si], 10下一个退出: mov ax, 4C00h ;DOS.TerminateWithExitcode21 小时;-------------------------------;输入 (ds:si) 输出 ()写字符串DOS:普萨jmps.b.a: mov dl, almov ah, 02h ;DOS.DisplayCharacter整数 21 小时;->AL.b: lodsb测试 al, aljnz.a波帕退;-------------------------------msg1: db '选择颜色?', 0msg2: db 10, '你选择了 '缓冲区:数据库 0, 0;-------------------------------

使用int 2Fh AX=4810h

输入文本

这个DOSKEY Buffered STDIN Input 函数只能被调用如果DOSKEY.COM TSR 已安装.它的操作很像常规的 BufferedSTDIN 输入函数 0Ah(见上文),但具有所有相同的编辑作为 DOS 命令行的可能性,包括使用所有DOSKEY 特殊键.

  • Up 从历史中获取上一项.
  • Down 从历史中获取下一项.
  • F7 显示历史记录中所有项目的列表.
  • AltF7 清除历史记录.
  • ...F8 查找以...开头的项目
  • F9 按编号从历史记录中选择一个项目.
  • AltF10 删除所有宏定义.

在 DOS 6.2 上,存储空间总是限制在 128 字节,允许输入127 个字符和强制回车的空间.不是可以预加载模板,所以总是设置输入的第二个字节缓冲为零.
在 DOS Win95 上,如果安装了 255 字节的存储空间DOSKEY.COM TSR 带有类似 doskey/line:255 的命令.有可能使用模板预加载存储空间.这带来了Win95版本非常接近输入函数 0Ah 的可行性.

这个函数执行ctrlC/ctrlBreak检查.

此功能完成后,光标将位于最左侧的列中当前行.如果字符数为零,则表示用户输入了尚未展开的 DOSKEY 宏的名称.你不去看看未扩展的线!需要第二次调用该函数这次返回时,光标会在最后一个字符的后面扩展文本.
一个特点是当一个多命令宏 ($T) 被扩展时,你只会获取第一个命令的扩展文本.的额外调用需要函数来获取其他扩展文本.虽然这一切都是在像 COMMAND.COM 这样的命令 shell 中,在用户中非常有用应用程序您不知道何时发生这种情况真的很烦人.

由于输入的文本被添加到命令历史中,因此不可避免地历史充满了不相关的项目.当然不是你想看到的在 DOS 提示符下!

示例 3,调用 DOSKEY.COM.

 ORG 256 ;创建.COM程序CLDmov ax, 4800h ;DOSKEY.CheckInstalled整数 2Fh ;->AL测试 al, almov si, err1jz退出_再次:mov si, msg1调用 WriteStringDOSmov dx, bufmov ax, 4810h ;DOSKEY.BufferedInput整数 2Fh ;->斧头测试斧,斧头mov si, err2jnz 退出_cmp [buf+1], al ;AL=0je 再次;需要宏扩展mov si, msg2调用 WriteStringDOSmov si, buf+2movzx bx, [si-1] ;获取字符数(为GT 0)mov word [si+bx+1], 10 ;保留CR,附加LF和0Exit_:调用WriteStringDOS退出: mov ax, 4C00h ;DOS.TerminateWithExitcode21 小时;-------------------------------;输入 (ds:si) 输出 ()写字符串DOS:普萨jmps.b.a: mov dl, almov ah, 02h ;DOS.DisplayCharacter整数 21 小时;->AL.b: lodsb测试 al, aljnz.a波帕退;-------------------------------buf: db 128, 0, 128+2 dup (0)msg1: db '选择颜色?', 0msg2: db 13, 10, '你选择了 ', 0err1: db 'N/A', 13, 10, 0err2: db '失败', 13, 10, 0;-------------------------------

使用int 21h AH=08h

输入文本<块引用>

由于 Stack Overflow 的 30000 字节限制,文本在下面的答案中继续......

理解来源有问题?我使用的汇编程序:

  • 将以点 ( . ) 开头的标签视为第一级本地标签
  • 将以冒号 ( : ) 开头的标签视为二级本地标签
  • 是单指令多操作数(SIMO),所以push cx si转换为 push cx push si.

The input in next program works OK, but when I ask to display the output, DOS doesn't display anything at all! How is this possible?

        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 : ', '$'
; --------------------------------------

解决方案

Looking at how you defined your input buffer (buf: db 20 dup ('$')), I get it that you want to cut corners and have the input already $-terminated ready for re-displaying it. Sadly this messes up the required settings for the DOS input function 0Ah and your program is in serious problems with a potential buffer overrun.
Moreover using $-termination is not the brightest choice that you can make since the $ character could already appear amongst the inputted characters. All the example programs that I present below will use zero-termination instead.

Inputting text using int 21h AH=0Ah

This Buffered STDIN Input function gets characters from the keyboard and continues doing so until the user presses the Enter key. All characters and the final carriage return are placed in the storage space that starts at the 3rd byte of the input buffer supplied by the calling program via the pointer in DS:DX.
The character count, not including the final carriage return, is stored in the 2nd byte of the input buffer.
It's the responsibility of the calling program to tell DOS how large the storage space is. Therefore you must put its length in the 1st byte of the input buffer before calling this function. To allow for an input of 1 character you set the storage size at 2. To allow for an input of 254 characters you set the storage size at 255.
If you don't want to be able to recall from the template any previous input, then it is best to also zero the 2nd byte. Basically the template is the pre-existing (and valid) content in the input buffer that the calling program provided. If pre-existing content is invalid then the template is not available.

Surprisingly this function has limited editing facilities.

  • Escape Removes all characters from the current input.
    The current input is abandoned but stays on screen and the cursor is placed on the next row, beneath where the input first started.
  • Backspace Removes the last character from the current input.
    Works as expected if the input stays within a single row on screen. If on the other hand the input spans several rows then this backspacing will stop at the left edge of the screen. From then on there will be a serious discrepancy between the logical input and the visual input because logically backspacing will go on until the 1st position in the storage space is reached!
  • F6 Inserts an end-of-file character (1Ah) in the current input.
    The screen will show "^Z".
  • F7 Inserts a zero byte in the current input.
    The screen will show "^@".
  • ctrlEnter Transitions to the next row (executing a carriage return and linefeed), nothing is added to the current input, and you can't go back.

Many more editing keys are available. They are all reminiscent of EDLIN.EXE, the ancient DOS line editor, which is a text editor where each previous line becomes the template on which you build the next line.

  • F1 Copies one character from the template to the new line.
  • F2 + ... Copies all characters from the template to the new line, up to the character specified.
  • F3 Copies all remaining characters in the template to the new line.
  • F4 + ... Skips over the characters in the template, up to the character specified.
  • F5 Makes the new line the new template.
  • Escape Clears the current input and leaves the template unchanged.
  • Delete Skips one character in the template.
  • Insert Enters or exits insert mode.
  • Backspace Deletes the last character of the new line and places the cursor back one character in the template.
  • Left Same as Backspace.
  • Right Same as F1.

Tabs are expanded by this function. Tab expansion is the process of replacing ASCII 9 by a series of one or more spaces (ASCII 32) until the cursor reaches a column position that is a multiple of 8.
This tab expansion only happens on screen. The storage space will hold ASCII 9.

This function does ctrlC/ctrlBreak checking.

When this function finishes, the cursor will be in the far left column on the current row.

Example 1, Buffered STDIN input.

        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
; --------------------------------------

Inputting text using int 21h AH=3Fh

When used with predefined handle 0 (in BX) this Read From File Or Device function gets characters from the keyboard and continues doing so until the user presses Enter. All characters (never more than 127) and the final carriage return plus an additional linefeed are placed in a private buffer within the DOS kernel. This now becomes the new template.
Hereafter the function will write in the buffer provided at DS:DX, the amount of bytes that were requested in the CX parameter. If CX specified a number that is less than the number of bytes generated by this input, one or more additional calls to this function are required to retrieve the complete input. As long as there are characters remaining to be picked up, this function will not launch another input session using the keyboard! This is even true between different programs or sessions of the same program.

All the editing keys described in the previous section are available.

Tabs are expanded on screen only, not in the template.

This function does ctrlC/ctrlBreak checking.

When this function finishes, the cursor will be in the far left column on the

  • current row if the terminating linefeed was not among the returned bytes.
  • next row if the terminating linefeed was among the returned bytes.

Example 2a, Read From File Or Device, pick up all at once.

        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
; --------------------------------------

Example 2b, Read From File Or Device, pick up one byte at a time.

        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
; --------------------------------------

Inputting text using int 2Fh AX=4810h

This DOSKEY Buffered STDIN Input function can only be invoked if the DOSKEY.COM TSR was installed. It operates much like the regular Buffered STDIN Input function 0Ah (see above), but has all the same editing possibilities as the DOS command line, including the ability to use all of the DOSKEY special keys.

  • Up Gets previous item from history.
  • Down Gets next item from history.
  • F7 Shows a list of all the items in the history.
  • AltF7 Clears the history.
  • ...F8 Finds item(s) that start with ...
  • F9 Selects an item from the history by number.
  • AltF10 Removes all macrodefinitions.

On DOS 6.2 the storage space is always limited to 128 bytes, allowing an input of 127 characters and room for the mandatory carriage return. It's not possible to pre-load a template, so always set the 2nd byte of the input buffer to zero.
On DOS Win95 the storage space can be as big as 255 bytes if you installed the DOSKEY.COM TSR with a command like doskey /line:255. It's possible to pre-load the storage space with a template. This brings the Win95 version very close to what is feasable with input function 0Ah.

This function does ctrlC/ctrlBreak checking.

When this function finishes, the cursor will be in the far left column on the current row. If the character count is zero, it means that the user typed in the name of a DOSKEY macro that was not yet expanded. You don't get to see the un-expanded line! A second invocation of the function is needed and upon returning this time, the cursor will be behind the last character of the expanded text.
A peculiarity is that when a multi-command macro ($T) gets expanded, you only get the expanded text of the 1st command. Additional invocations of the function are needed to get the other expanded texts. Although all of this is very useful from within a command shell like COMMAND.COM, from within a user application it's really annoying that you can't know when this happens.

Since the inputted text is added to the command history, it is unavoidable that the history fills up with unrelated items. Certainly not what you want to see at the DOS prompt!

Example 3, Invoking 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
; --------------------------------------

Inputting text using int 21h AH=08h

Because of the 30000 byte limit that Stack Overflow imposes the text continues in the below answer...

Problem understanding the source? The assembler I used:

  • 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.

这篇关于缓冲输入的工作原理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆