DOS借检查键盘的状态来判断,是否有字符输入,ROM BIOS上的键盘输入功能在没有输入字符时就把ZF(Zero Flag)设定为1,否则就把ZF设定为0.如果可以控制这个功能,反复地欺骗DOS目前有字符要输入,然后把预的字符串传回给DOS,那么就可以让DOS接受任何数量的字符.
5.1 基本的扩充程序
可以把上面的空的New_Keyboard_IO程序,改用以下的程序来代替.
New_Keyboard_IO proc far
sti
cmp ah,0 ;A read request?
je ksread
cmp ah,1 ;A status request?
je ksstat
assume ds:nothing ;Let original routine
jmp Old_Keyboard_IO ;Do remaining subfunction
ksRead:
call keyRead ;Get next char to return
iret
ksstat:
call keyStat ;GetStatus
ret 2 ;It''s important!!
New_Keyboard_IO endp
上面的New_Keyboard_IO程序中,把0H(读取字符)和1H(取得键盘状态)这两项功能自行处理.这个程序很简单,但是其中有一个关键点.当我们处理取得键盘状态的功能时,因为原先的键盘中断处理程序是利用ZF返回键盘状态,因此程序包中也必须保有这种特性,如果使用IRET返回的话,那么设定好ZF就会因为CPU状态标志从堆栈中取出,而恢复成未中断前的状态.
为了解决这个问题可以使用RET的参数来设置.这个参数是用来指示从堆栈中取出多少个字节.通常这是用在高级语言的子程序返回时,用来从堆栈中除去一些参数或是变数.在这里我们希望用来移去原先中断时堆栈的CPU状态,这样才有办法把改变的ZF传回,因此在这里使用了RET 2这个指令.
上面的程序码中调用到Keyread和KeyStat这两个子程序,其内容如下所示:
assume ds:nothing
;If expansion is in progress,return a fake status
;of ZF=0,indicatin gthat a character is ready to be
;read,If expansion is not in progress,then return
;the actual status from the keyboard
KeyStat proc
cmp cs:current,0
jne FakeStat
pushf ;Let original routine
call Old_Keyboard_IO ;get keyboard status
ret
FakeStat:
mov bx,1 ;Fake a "char ready"
cmp bx,0 ;by clearing ZF
KeyStat endp
;Read a character from the keyboard input queue,
;if not expanding or the expansion string.
;if expansion is in progress
KeyRead proc
cmp cs:current,0
jne ExpandChar
ReadChar:
mov cs:current,0 ;Slightly peculiar
pushf ;Let original routine
call Old_Keyboard_IO ;Get keyboard status
cmp al,0
je Extended
ReadDone:
ret
Expanded:
cmp ah,59 ;Is this character to expand?
jne ReadDone ;If not,then return it normally
;If so,then start expanding
mov cs:current,offset string
ExpandChar:
push si
mov si,cs:current
mov al,cs:[si]
inc cs:current
pop si
cmp al,0 ;Is this end of string?
je ReadChar