;//--------------------------------------------------------------- ;// minikybd.s ;// ;// This program installs its own Interrupt Service Routine ;// for the keyboard interrupt (INT-0x09), then it echos to ;// the screen each keystroke that it finds in the keyboard ;// queue located in the ROM-BIOS DATA AREA. It terminates ;// when the user presses the -key (after restoring ;// the original keyboard interrupt-vector). ;// ;// assemble with: $ as86 minikybd.s -b minikybd.b ;// install using: $ dd if=minikybd.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// date begun: 12 FEB 2004 ;// completion: 10 MAR 2004 ;// revised on: 06 APR 2004 -- fix error noticed by Wei Chen ;//--------------------------------------------------------------- .SECT .TEXT ;----------------------------------------------------------------- .WORD 0xABCD ; programming signature ;----------------------------------------------------------------- main: mov ax, cs ; address this segment mov ds, ax ; with DS register pop dword return_address ; store return-address mov ss, ax ; adjust SS register lea esp, tos0 ; and set new stacktop call install_mini_handler call execute_kb_echo_demo call restore_bios_handler push dword return_address ; recover our exit-address retf ; exit to program launcher ;----------------------------------------------------------------- return_address: .LONG 0 ; to store exit-address ;----------------------------------------------------------------- ;================================================================= ;----------------------------------------------------------------- isrKBD: push ax ; must preserve registers push bx push ds call query_kb_controller ; find out why we're here call process_keybd_input ; interpret keyboard data call transmit_EOI_to_PIC ; signal end-of-interrupt pop ds ; restore used registers pop bx pop ax iret ;----------------------------------------------------------------- ;----------------------------------------------------------------- KBFLAGS EQU 0x0017 ; offset to KBFLAGS word KBHEAD EQU 0x001A ; offset to KBHEAD word KBTAIL EQU 0x001C ; offset to KBTAIL word KBBASE EQU 0x0080 ; offset to KBBASE word KBEDGE EQU 0x0082 ; offset to KBEDGE word LSHIFT_MK EQU 0x2A ; LEFT-SHIFT 'make' LSHIFT_BK EQU 0xAA ; LEFT-SHIFT 'break' RSHIFT_MK EQU 0x36 ; RIGHT-SHIFT 'make' RSHIFT_BK EQU 0xB6 ; RIGHT-SHIFT 'break' ;----------------------------------------------------------------- kbstat: .BYTE 0 ; kb-controller's status kbdata: .BYTE 0 ; stores recent scancode ;----------------------------------------------------------------- query_kb_controller: mov ax, cs ; address this segment mov ds, ax ; with DS register in al, #0x64 ; read controller status mov kbstat, al ; save controller status test al, #0x01 ; output-buffer empty? jz ignore ; yes, nothing to read in al, #0x60 ; read the new scancode mov kbdata, al ; save the new scancode test byte kbstat, #0xC0 ; parity/timeout errors? jnz resend ; yes, a retry is needed jmp queryx ; else ready to process resend: ; NOTE: our error-recovery routine isn't implemented yet ignore: mov byte kbdata, #0x00 ; substitute null byte queryx: ret ;----------------------------------------------------------------- process_keybd_input: test byte kbstat, #1 ; was new data received? jz keyxx ; no, bypass processing call is_special_key ; handle shifts/locks jz keyxx ; done? ready to exit call is_normal_case ; else to translation keyxx: ret ;----------------------------------------------------------------- transmit_EOI_to_PIC: ; Note: a correction was made here (on 06 APR 2004), to ; revoved a superflous instruction, noticed by Wei Chen mov al, #0x20 ; non-specific EOI out #0x20, al ; sent to the Master PIC ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; SCANCODE TRANSLATION-TABLES uppercase: .BYTE 0, 27 .ASCII "!@#$%^&*()_+" .BYTE 8, 9 .ASCII "QWERTYUIOP{}" .BYTE 13, 0 .ASCII "ASDFGHJKL:""~" .BYTE 0 .ASCII "|ZXCVBNM<>?" .BYTE 0, 0, 0, 32 .SPACE 70 lowercase: .BYTE 0, 27 .ASCII "1234567890-=" .BYTE 8, 9 .ASCII "qwertyuiop[]" .BYTE 13, 0 .ASCII "asdfghjkl;'`" .BYTE 0 .ASCII "\\zxcvbnm,./" .BYTE 0, 0, 0, 32 .SPACE 70 ;----------------------------------------------------------------- is_special_key: mov bx, #0x40 ; address bios segment mov ds, bx ; with DS register CSEG mov al, kbdata ; get scancode into AL cmp al, #LSHIFT_MK ; was LEFT-SHIFT depressed? je ls_mk ; yes, adjust kb_flags cmp al, #LSHIFT_BK ; was LEFT-SHIFT released? je ls_bk ; yes, adjust kb_flags cmp al, #RSHIFT_MK ; was RIGHT-SHIFT depressed? je rs_mk ; yes, adjust kb_flags cmp al, #RSHIFT_BK ; was RIGHT-SHIFT released? je rs_bk ; yes, adjust kb_flags jmp notsp ; exit w/ZF-flag clear ls_mk: bts [KBFLAGS], #0 ; set bit 0 in KBFLAGS jmp wassp ls_bk: btr [KBFLAGS], #0 ; reset bit 0 in KBFLAGS jmp wassp rs_mk: bts [KBFLAGS], #1 ; set bit 1 in KBFLAGS jmp wassp rs_bk: bts [KBFLAGS], #1 ; reset bit 1 in KBFLAGS jmp wassp wassp: xor al, al ; set ZF-bit for return notsp: ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- is_normal_case: mov bx, #0x40 ; address bios segment mov ds, bx ; with DS register CSEG mov al, kbdata ; copy scancode to AL mov ah, al ; and also copy to AH test al, #0x80 ; was keypress break? jnz discard ; yes, then disregard lea bx, lowercase ; provisional translate test [KBFLAGS], #0x03 ; is a shift-key down? jz xok ; no, retain xlat-table lea bx, uppercase ; else change translate xok: CSEG xlat ; translate the scancode mov bx, [KBTAIL] ; next storage location mov [bx], ax ; save ascii/scan codes call advbx ; advance tail-pointer cmp bx, [KBHEAD] ; kb-queue is full? je discard ; yes, discard new data mov [KBTAIL], bx ; commit the new pointer jmp normx discard: ; NOTE: some form of user-alert ought to be activated here normx: ret ;----------------------------------------------------------------- advbx: ; increments the array-index for circular keyboard-queue push ax ; preserve registers push ds mov ax, #0x40 ; address ROM-BIOS data mov ds, ax ; using DS register add bx, #2 ; advance the array-index cmp bx, [KBEDGE] ; is index beyond bounds? jne advok ; no, the new index is ok mov bx, [KBBASE] ; else reset to beginning advok: pop ds ; restore registers pop ax ret ;----------------------------------------------------------------- ;================================================================= ;----------------------------------------------------------------- old_vector: .WORD 0x0000, 0x0000 ; holds interrupt vector new_vector: .WORD isrKBD, 0x1000 ; holds interrupt vector ;----------------------------------------------------------------- ;----------------------------------------------------------------- install_mini_handler: xor ax, ax ; address vector table mov es, ax ; with ES register mov edi, #9 ; keyboard interrupt ID ESEG mov eax, [edi*4] ; get the current vector mov old_vector, eax ; save to restore later mov eax, new_vector ; get replacement vector ESEG mov [edi*4], eax ; install in vector table ret ;----------------------------------------------------------------- execute_kb_echo_demo: ; ; Echos keyboard input to the screen until -key is hit ; mov ax, #0x0040 ; address ROM-BIOS data mov ds, ax ; using DS register await: mov bx, [KBHEAD] ; load index to front cmp bx, [KBTAIL] ; is it same as rear? je await ; yes, queue is empty mov ax, [bx] ; else dequeue datum call advbx ; queue-index advances mov [KBHEAD], bx ; new index is saved cmp al, #0x1B ; was it -key? je isesc ; yes, exit from loop mov ah, #0x0E ; write_TTY function mov bx, #0x0007 ; BH=page, BL=colors int 0x10 ; request BIOS service cmp al, #0x0D ; was it =key? jne await ; no, get next datum mov al, #0x0A ; else do a LINEFEED int 0x10 ; request BIOS service jmp await ; then get next datum isesc: mov bx, #0x0007 ; BH=page, BL=colors mov ax, #0x0E0D ; write_TTY int 0x10 ; request BIOS service mov ax, #0x0E0A ; write_TTY int 0x10 ; request BIOS service ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- restore_bios_handler: mov ax, #0x1000 ; address this segment mov ds, ax ; with DS register xor ax, ax ; address vector table mov es, ax ; with ES register mov edi, #9 ; keyboard interrupt ID mov eax, old_vector ; get the original vector ESEG mov [edi*4], eax ; install in vector table ret ;----------------------------------------------------------------- .ALIGN 16 ; alignment at paragraph .SPACE 512 ; reserved for stack use tos0: ; label fop top-of-stack ;----------------------------------------------------------------- END