How can I determine whether the ROM-BIOS supports the circular keypress buffer start and end offsets at 40h:80h and 40h:82h?

Viewed 55

For a new feature in my application I want to detect keypresses in the ROM-BIOS's circular buffer in the BIOS Data Area at segment 40h. I need to detect keypresses anywhere in the buffer, not just at the current head, therefore using interrupt 16h function 01h is not sufficient. The circular buffer defaults to extending from 40h:1Eh up to below 40h:3Eh. The "head" offset into the buffer is stored in the word [40h:1Ah] and the "tail" offset is in word [40h:1Ch].

However, some ROM-BIOSes allow relocating the buffer away from its default location. Those that do use the word [40h:80h] as the start offset of the buffer and word [40h:82h] as the end offset of the buffer. References such as this one often contain notes like this though:

Note: this variable is not supported on many systems, be careful if you use it.

Another reference appears to indicate that some specific ROM-BIOS revisions support these fields, presumably implying that others don't:

BIOS 10-27-82

I put this code into my application's initialisation. It checks whether the values in the extension fields appear valid. This means they must be nonzero both, the end must be above the start, the delta end minus start must be even, and both the head and the tail offsets must each be above-or-equal the start, below the end, and an even amount of bytes from the start.

        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

This code is meant to check the keypress buffer for Ctrl-C presses. It is where I use the variables:

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

Before the initialisation the variables default to zero both. A user can force the variables to any nonzero value (usually 1Eh and 3Eh then to use the default buffer area) or to zero (to use the extension's fields).

Is there any more reliable way to detect whether the extension is supported by the BIOS?

Note: I do not want to disassemble or trace the interrupt 09h (IRQ #1) or interrupt 16h handlers. I also do not want to wait for an actual keypress to arrive, so I cannot experimentally change/set the extension fields to find out whether they are supported.

1 Answers

One way to find out whether BIOS uses the 0080h and 0082h variables is to see how BIOS deals with the necessary buffer wraparound when it has to return a key that is stored at the high end of the keyboard buffer.

Next .COM program sets up for a small (8-key) keyboard buffer, stores the record for the Enter key in the highest position, and then fetches that key using the regular BIOS keyboard api.
If BIOS ignores the variables under scrutiny, then the new Head pointer will be at the usual value of 001Eh.
If the keyboard buffer is indeed relocatable then the new Head pointer will be where we redefined it to be at 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, '$'
Related