对于我的应用程序中的一个新功能,我想在段40h
的BIOS数据区中检测ROM-BIOS的循环缓冲区中的按键。我需要检测缓冲区中任何位置的按键,而不仅仅是当前头的按键,因此使用中断16h功能01h是不够的。循环缓冲区默认从40h:1Eh
扩展到40h:3Eh
以下。";头部;偏移到缓冲器中被存储在CCD_ 4中;尾部;偏移在CCD_ 5中。
然而,一些ROM BIOS允许将缓冲区从其默认位置移开。那些确实使用word [40h:80h]
作为缓冲器的开始偏移并且使用word [40h:82h]
作为缓冲器的结束偏移的。像这样的参考文献通常包含这样的注释:
注意:许多系统不支持此变量,如果使用它,请小心。
另一个参考似乎表明一些特定的ROM-BIOS修订版支持这些字段,可能意味着其他版本不支持:
BIOS 10-27-82
我将此代码放入应用程序的初始化中。它检查扩展字段中的值是否有效。这意味着它们必须都是非零的,结束必须在开始之上,delta结束减去开始必须是偶数,并且头和尾偏移必须都在开始之上或等于开始,在结束之下,从开始算起的字节数必须是偶数。
push ds
mov ax, 40h
mov ds, ax
mov ax, word [82h] ; end of circular keypress buffer
mov dx, word [80h] ; start of circular buffer
test ax, ax
jz .forcekeybuffer
test dx, dx
jz .forcekeybuffer
mov bx, ax
sub bx, dx ; cmp end, start
jbe .forcekeybuffer ; below or equal is invalid -->
test bl, 1 ; even amount of bytes ?
jnz .forcekeybuffer ; no, invalid -->
mov bx, word [1Ah] ; current head of circular buffer
cmp bx, ax
jae .forcekeybuffer
sub bx, dx
jb .forcekeybuffer
test bl, 1
jnz .forcekeybuffer ; invalid -->
mov bx, word [1Ch] ; current tail of circular buffer
cmp bx, ax
jae .forcekeybuffer
sub bx, dx
jb .forcekeybuffer
test bl, 1
jz @F ; valid -->
.forcekeybuffer:
pop ds
mov word [io_end_buffer], 3Eh
mov word [io_start_buffer], 1Eh
db __TEST_IMM8 ; (skip pop)
@@:
pop ds
此代码用于检查Ctrl-C按键的按键缓冲区。这是我使用变量的地方:
handle_serial_flags_ctrl_c:
[...]
.check_rombios_buffer:
push bx
push dx
mov ax, 40h ; dual mode segment/selector
push ax
mov ax, word [io_end_buffer]
mov dx, word [io_start_buffer]
pop ds
test ax, ax
jnz @F
mov ax, word [82h] ; end of circular keypress buffer
@@:
test dx, dx
jnz @F
mov dx, word [80h] ; start of circular buffer
@@:
mov bx, ax
sub bx, dx ; cmp end, start
jbe .ret_dx_bx ; invalid -->
test bl, 1 ; even amount of bytes ?
jnz .ret_dx_bx ; invalid -->
mov bx, word [1Ah] ; current head of circular buffer
.loop:
cmp bx, word [1Ch] ; equal to current tail ?
je .ret_dx_bx ; yes, all entries checked -->
cmp byte [bx], 3 ; is it Ctrl-C ?
je handle_ctrl_c ; yes, handle -->
inc bx
inc bx ; -> next entry
cmp bx, ax ; at end of buffer ?
jb .loop ; no, loop -->
ja .ret_dx_bx ; invalid -->
mov bx, dx ; reset to start of buffer
jmp .loop ; then loop -->
.ret_dx_bx:
pop dx
pop bx
在初始化之前,变量都默认为零。用户可以强制变量为任何非零值(通常为1Eh
和3Eh
,然后使用默认缓冲区(或为零(使用扩展的字段(。
有没有更可靠的方法来检测BIOS是否支持扩展?
注意:我不想反汇编或跟踪中断09h(IRQ#1(或中断16h处理程序。我也不想等待实际的按键到达,所以我无法通过实验更改/设置扩展字段来确定它们是否受支持。
了解BIOS是否使用0080h和0082h变量的一种方法是查看BIOS在必须返回存储在键盘缓冲区高端的键时如何处理必要的缓冲区环绕。
接下来,COM程序设置一个小(8键(键盘缓冲区,将Enter键的记录存储在最高位置,然后使用常规BIOS键盘api获取该键
如果BIOS忽略了正在检查的变量,则新的Head指针将处于常用值001Eh
如果键盘缓冲区确实是可重新定位的,那么新的Head指针将位于我们将其重新定义为002Eh的位置。
ORG 256
push ds ; (1)
mov ax, 0040h
mov ds, ax
; Preserve buffer variables and setup for an 8-key buffer
cli
mov bx, [0080h]
mov cx, [0082h]
mov dx, [001Ah]
mov word [0080h], 002Eh ; LowBorder
mov word [0082h], 003Eh ; HighBorder
; Fill with a single key near the end of the 8-key buffer
mov word [001Ah], 003Ch ; Head = HighBorder-2
mov word [003Ch], 1C0Dh ; [Head] = <ENTER>
mov word [001Ch], 002Eh ; Tail = LowBorder (at expected wraparound)
sti
; Fetch the single key
mov ah, 00h ; BIOS.WaitKey
int 16h ; -> AX=1C0Dh
; Restore buffer variables and inspect buffer wraparound
cmp word [001Ah], 002Eh ; Head = {001Eh=Hard, 002Eh=Soft}
mov [001Ch], dx
mov [001Ah], dx ; Make sure buffer is empty
mov [0082h], cx
mov [0080h], bx
pop ds ; (1)
mov dx, S
je SoftcodedBorders
mov dx, H
SoftcodedBorders:
mov ah, 09h ; DOS.PrintString
int 21h
ret
; - - - - - - - - - - - - - - -
S db 'BIOS uses the 0080h and 0082h variables', 13, 10, '$'
H db 'BIOS uses the hardcoded values 001Eh and 003Eh', 13, 10, '$'