mirror of https://github.com/wwarthen/RomWBW.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
699 lines
15 KiB
699 lines
15 KiB
;
|
|
;=======================================================================
|
|
; Keyboard Information Utility (KBDINFO)
|
|
;=======================================================================
|
|
;
|
|
; Simple utility that attempts to determine the type of keyboard you
|
|
; have attached to an 8242 keyboard controller.
|
|
;
|
|
;=======================================================================
|
|
;
|
|
; Keyboard controller port addresses (adjust as needed)
|
|
;
|
|
iocmd .equ $E3 ; keyboard controller command port address
|
|
iodat .equ $E2 ; keyboard controller data port address
|
|
;
|
|
; General operational equates (should not requre adjustment)
|
|
;
|
|
stksiz .equ $40 ; Working stack size
|
|
;
|
|
timeout .equ $00 ; Controller timeout constant
|
|
;
|
|
restart .equ $0000 ; CP/M restart vector
|
|
bdos .equ $0005 ; BDOS invocation vector
|
|
;
|
|
;=======================================================================
|
|
;
|
|
.org $100 ; standard CP/M executable
|
|
;
|
|
;
|
|
; setup stack (save old value)
|
|
ld (stksav),sp ; save stack
|
|
ld sp,stack ; set new stack
|
|
;
|
|
call crlf
|
|
ld de,str_banner ; banner
|
|
call prtstr
|
|
;
|
|
call main ; do the real work
|
|
;
|
|
exit:
|
|
call crlf2
|
|
ld de,str_exit
|
|
call prtstr
|
|
;call crlf
|
|
|
|
; clean up and return to command processor
|
|
call crlf ; formatting
|
|
ld sp,(stksav) ; restore stack
|
|
jp restart ; return to CP/M via restart
|
|
;
|
|
;
|
|
;=======================================================================
|
|
; Main Program
|
|
;=======================================================================
|
|
;
|
|
main:
|
|
;
|
|
; Display active keyboard controller port addresses
|
|
;
|
|
call crlf2
|
|
ld de,str_cmdport
|
|
call prtstr
|
|
ld a,iocmd
|
|
call prthex
|
|
call crlf
|
|
ld de,str_dataport
|
|
call prtstr
|
|
ld a,iodat
|
|
call prthex
|
|
;
|
|
; Attempt self-test command on keyboard controller
|
|
;
|
|
; Keyboard controller should respond with an 0x55 on data port
|
|
; after being sent a 0xAA on the command port.
|
|
;
|
|
call crlf2
|
|
ld de,str_ctrl_test
|
|
call prtstr
|
|
ld a,$aa ; self-test command
|
|
call put_cmd_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
call get_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
cp $55 ; expected value?
|
|
jp nz,err_ctlr_test ; handle self-test error
|
|
call crlf
|
|
ld de,str_ctrl_test_ok
|
|
call prtstr
|
|
;
|
|
; Disable translation on keyboard controller to get raw scan codes!
|
|
;
|
|
call crlf2
|
|
ld de,str_trans_off
|
|
call prtstr
|
|
ld a,$60 ; write to command register 0
|
|
call put_cmd_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
ld a,$20 ; xlat disabled, mouse disabled, no ints
|
|
call put_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
;
|
|
;
|
|
;
|
|
call test2
|
|
;
|
|
; Enable translation on keyboard controller
|
|
;
|
|
call crlf2
|
|
ld de,str_trans_on
|
|
call prtstr
|
|
ld a,$60 ; write to command register 0
|
|
call put_cmd_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
ld a,$60 ; xlat disabled, mouse disabled, no ints
|
|
call put_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
;
|
|
; fall thru
|
|
;
|
|
test2:
|
|
;
|
|
; Perform a keyboard reset
|
|
;
|
|
call crlf2
|
|
ld de,str_kbd_reset
|
|
call prtstr
|
|
ld a,$ff ; Keyboard reset
|
|
call put_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
call get_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
cp $FA ; Is it an ack as expected?
|
|
jp nz,err_kbd_reset
|
|
call get_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
cp $AA ; Success?
|
|
jp nz,err_kbd_reset
|
|
call crlf
|
|
ld de,str_kbd_reset_ok
|
|
call prtstr
|
|
;
|
|
; Identify keyboard
|
|
;
|
|
call crlf2
|
|
ld de,str_kbd_ident
|
|
call prtstr
|
|
ld a,$f2 ; Identify keyboard command
|
|
call put_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
call get_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
cp $FA ; Is it an ack as expected?
|
|
jp nz,err_kbd_ident
|
|
; Now we need to receive 0-2 bytes. There is no way to know
|
|
; how many are coming, so we receive bytes until there is a
|
|
; timeout error.
|
|
ld ix,workbuf
|
|
ld iy,workbuf_len
|
|
xor a
|
|
ld (iy),a
|
|
ident_loop:
|
|
call get_data_dbg
|
|
jr c,ident_done
|
|
ld (ix),a
|
|
inc ix
|
|
inc (iy)
|
|
jr ident_loop
|
|
ident_done:
|
|
call crlf
|
|
ld de,str_kbd_ident_disp
|
|
call prtstr
|
|
ld a,'['
|
|
call prtchr
|
|
ld ix,workbuf
|
|
ld b,(iy)
|
|
xor a
|
|
cp b
|
|
jr z,ident_done2
|
|
ident_done1:
|
|
ld a,(ix)
|
|
call prthex
|
|
inc ix
|
|
djnz ident_done1
|
|
ident_done2:
|
|
ld a,']'
|
|
call prtchr
|
|
;
|
|
; Get active scan code set being used
|
|
;
|
|
call crlf2
|
|
ld de,str_kbd_getsc
|
|
call prtstr
|
|
ld a,$f0 ; Keyboard get/set scan code
|
|
call put_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
call get_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
cp $FA ; Is it an ack as expected?
|
|
jp nz,err_kbd_getsc
|
|
ld a,$00 ; Get active scan code set
|
|
call put_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
call get_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
cp $FA ; Is it an ack as expected?
|
|
jp nz,err_kbd_getsc
|
|
call get_data_dbg
|
|
jp c,err_ctlr_io ; handle controller error
|
|
push af
|
|
call crlf
|
|
ld de,str_kbd_dispsc
|
|
call prtstr
|
|
pop af
|
|
call prtdecb
|
|
;;;;
|
|
;;;; Set active scan code set to 2
|
|
;;;;
|
|
;;; call crlf2
|
|
;;; ld de,str_kbd_setsc
|
|
;;; call prtstr
|
|
;;; ld a,$f0 ; Keyboard get/set scan code
|
|
;;; call put_data_dbg
|
|
;;; jp c,err_ctlr_io ; handle controller error
|
|
;;; call get_data_dbg
|
|
;;; jp c,err_ctlr_io ; handle controller error
|
|
;;; cp $FA ; Is it an ack as expected?
|
|
;;; jp nz,err_kbd_getsc
|
|
;;; ld a,$02 ; Set scan code set to 2
|
|
;;; call put_data_dbg
|
|
;;; jp c,err_ctlr_io ; handle controller error
|
|
;;; call get_data_dbg
|
|
;;; jp c,err_ctlr_io ; handle controller error
|
|
;;; cp $FA ; Is it an ack as expected?
|
|
;;; jp nz,err_kbd_getsc
|
|
;;;;
|
|
;;;; Get active scan code set being used
|
|
;;;;
|
|
;;; call crlf2
|
|
;;; ld de,str_kbd_getsc
|
|
;;; call prtstr
|
|
;;; ld a,$f0 ; Keyboard get/set scan code
|
|
;;; call put_data_dbg
|
|
;;; jp c,err_ctlr_io ; handle controller error
|
|
;;; call get_data_dbg
|
|
;;; jp c,err_ctlr_io ; handle controller error
|
|
;;; cp $FA ; Is it an ack as expected?
|
|
;;; jp nz,err_kbd_getsc
|
|
;;; ld a,$00 ; Get active scan code set
|
|
;;; call put_data_dbg
|
|
;;; jp c,err_ctlr_io ; handle controller error
|
|
;;; call get_data_dbg
|
|
;;; jp c,err_ctlr_io ; handle controller error
|
|
;;; cp $FA ; Is it an ack as expected?
|
|
;;; jp nz,err_kbd_getsc
|
|
;;; call get_data_dbg
|
|
;;; jp c,err_ctlr_io ; handle controller error
|
|
;;; push af
|
|
;;; call crlf
|
|
;;; ld de,str_kbd_dispsc
|
|
;;; call prtstr
|
|
;;; pop af
|
|
;;; and $0f
|
|
;;; call prtdecb
|
|
;
|
|
; Read and display raw scan codes
|
|
;
|
|
call crlf2
|
|
ld de,str_disp_scan_codes
|
|
call prtstr
|
|
read_loop:
|
|
ld c,$06 ; BDOS direct console I/O
|
|
ld e,$FF ; Subfunction = read
|
|
call bdos
|
|
cp $1B ; Escape key?
|
|
jp z,done
|
|
call check_read
|
|
jr nz,read_loop
|
|
call get_data
|
|
jp c,err_ctlr_io ; handle controller error
|
|
push af
|
|
ld a,' '
|
|
call prtchr
|
|
ld a,'['
|
|
call prtchr
|
|
pop af
|
|
call prthex
|
|
ld a,']'
|
|
call prtchr
|
|
jr read_loop
|
|
|
|
done:
|
|
ret
|
|
;
|
|
;=======================================================================
|
|
; Keyboard Controller I/O Routines
|
|
;=======================================================================
|
|
;
|
|
wait_write:
|
|
;
|
|
; Wait for keyboard controller to be ready for a write
|
|
; A=0 indicates success (ZF set)
|
|
;
|
|
ld b,timeout ; setup timeout constant
|
|
wait_write1:
|
|
in a,(iocmd) ; get status
|
|
ld c,a ; save status
|
|
and $02 ; isolate input buf status bit
|
|
ret z ; 0 means ready, all done
|
|
call delay ; wait a bit
|
|
djnz wait_write1 ; loop until counter exhausted
|
|
ld de,str_timeout_write ; write timeout message
|
|
call crlf
|
|
call prtstr
|
|
ld a,c ; recover last status value
|
|
call prthex
|
|
or $ff ; signal error
|
|
ret
|
|
;
|
|
wait_read:
|
|
;
|
|
; Wait for keyboard controller to be ready to read a byte
|
|
; A=0 indicates success (ZF set)
|
|
;
|
|
ld b,timeout ; setup timeout constant
|
|
wait_read1:
|
|
in a,(iocmd) ; get status
|
|
ld c,a ; save status
|
|
and $01 ; isolate input buf status bit
|
|
xor $01 ; invert so 0 means ready
|
|
ret z ; if 0, all done
|
|
call delay ; wait a bit
|
|
djnz wait_read1 ; loop until counter exhausted
|
|
ld de,str_timeout_read ; write timeout message
|
|
call crlf
|
|
call prtstr
|
|
ld a,c ; recover last status value
|
|
call prthex
|
|
or $ff ; signal error
|
|
ret
|
|
;
|
|
check_read:
|
|
;
|
|
; Check for data ready to read
|
|
; A=0 indicates data available (ZF set)
|
|
;
|
|
in a,(iocmd) ; get status
|
|
and $01 ; isolate input buf status bit
|
|
xor $01 ; invert so 0 means ready
|
|
ret
|
|
;
|
|
put_cmd:
|
|
;
|
|
; Put a cmd byte from A to the keyboard interface with timeout
|
|
; CF set indicates timeout error
|
|
;
|
|
ld e,a ; save incoming value
|
|
call wait_write ; wait for controller ready
|
|
jr z,put_cmd1 ; if ready, move on
|
|
scf ; else, signal timeout error
|
|
ret ; and bail out
|
|
put_cmd1:
|
|
ld a,e ; recover value to write
|
|
out (iocmd),a ; write it
|
|
or a ; clear CF for success
|
|
ret
|
|
;
|
|
put_cmd_dbg:
|
|
call put_cmd
|
|
ret c
|
|
push af
|
|
call crlf
|
|
ld de,str_put_cmd
|
|
call prtstr
|
|
call prthex
|
|
pop af
|
|
ret
|
|
;
|
|
put_data:
|
|
;
|
|
; Put a data byte from A to the keyboard interface with timeout
|
|
; CF set indicates timeout error
|
|
;
|
|
ld e,a ; save incoming value
|
|
call wait_write ; wait for controller ready
|
|
jr z,put_data1 ; if ready, move on
|
|
scf ; else, signal timeout error
|
|
ret ; and bail out
|
|
put_data1:
|
|
ld a,e ; recover value to write
|
|
out (iodat),a ; write it
|
|
or a ; clear CF for success
|
|
ret
|
|
;
|
|
put_data_dbg:
|
|
call put_data
|
|
ret c
|
|
push af
|
|
call crlf
|
|
ld de,str_put_data
|
|
call prtstr
|
|
call prthex
|
|
pop af
|
|
ret
|
|
|
|
;
|
|
; Get a data byte from the keyboard interface to A with timeout
|
|
; CF set indicates timeout error
|
|
;
|
|
get_data:
|
|
call wait_read ; wait for byte to be ready
|
|
jr z,get_data1 ; if readym, move on
|
|
scf ; else signal timeout error
|
|
ret ; and bail out
|
|
get_data1:
|
|
in a,(iodat) ; get data byte
|
|
or a ; clear CF for success
|
|
ret
|
|
;
|
|
get_data_dbg:
|
|
call get_data
|
|
ret c
|
|
push af
|
|
call crlf
|
|
ld de,str_get_data
|
|
call prtstr
|
|
call prthex
|
|
pop af
|
|
ret
|
|
;
|
|
; Error Handlers
|
|
;
|
|
err_ctlr_io:
|
|
ld de,str_err_ctrl_io
|
|
jr err_ret
|
|
;
|
|
err_ctlr_test:
|
|
ld de,str_err_ctrl_test
|
|
jr err_ret
|
|
;
|
|
err_kbd_reset:
|
|
ld de,str_err_kbd_reset
|
|
jr err_ret
|
|
;
|
|
err_kbd_getsc:
|
|
ld de,str_err_kbd_getsc
|
|
jr err_ret
|
|
;
|
|
err_kbd_setsc:
|
|
ld de,str_err_kbd_setsc
|
|
jr err_ret
|
|
;
|
|
err_kbd_ident:
|
|
ld de,str_err_kbd_ident
|
|
jr err_ret
|
|
;
|
|
err_ret:
|
|
call crlf2
|
|
call prtstr
|
|
or $ff ; signal error
|
|
ret
|
|
;
|
|
;=======================================================================
|
|
; Utility Routines
|
|
;=======================================================================
|
|
;
|
|
;
|
|
; Print character in A without destroying any registers
|
|
;
|
|
prtchr:
|
|
push bc ; save registers
|
|
push de
|
|
push hl
|
|
ld e,a ; character to print in E
|
|
ld c,$02 ; BDOS function to output a character
|
|
call bdos ; do it
|
|
pop hl ; restore registers
|
|
pop de
|
|
pop bc
|
|
ret
|
|
;
|
|
prtdot:
|
|
;
|
|
; shortcut to print a dot preserving all regs
|
|
push af ; save af
|
|
ld a,'.' ; load dot char
|
|
call prtchr ; print it
|
|
pop af ; restore af
|
|
ret ; done
|
|
;
|
|
; Print a zero terminated string at (de) without destroying any registers
|
|
;
|
|
prtstr:
|
|
push af
|
|
push de
|
|
;
|
|
prtstr1:
|
|
ld a,(de) ; get next char
|
|
or a
|
|
jr z,prtstr2
|
|
call prtchr
|
|
inc de
|
|
jr prtstr1
|
|
;
|
|
prtstr2:
|
|
pop de ; restore registers
|
|
pop af
|
|
ret
|
|
;
|
|
; Print the value in A in hex without destroying any registers
|
|
;
|
|
prthex:
|
|
push af ; save AF
|
|
push de ; save DE
|
|
call hexascii ; convert value in A to hex chars in DE
|
|
ld a,d ; get the high order hex char
|
|
call prtchr ; print it
|
|
ld a,e ; get the low order hex char
|
|
call prtchr ; print it
|
|
pop de ; restore DE
|
|
pop af ; restore AF
|
|
ret ; done
|
|
;
|
|
; print the hex word value in hl
|
|
;
|
|
prthexword:
|
|
push af
|
|
ld a,h
|
|
call prthex
|
|
ld a,l
|
|
call prthex
|
|
pop af
|
|
ret
|
|
;
|
|
; print the hex dword value in de:hl
|
|
;
|
|
prthex32:
|
|
push bc
|
|
push de
|
|
pop bc
|
|
call prthexword
|
|
push hl
|
|
pop bc
|
|
call prthexword
|
|
pop bc
|
|
ret
|
|
;
|
|
; Convert binary value in A to ascii hex characters in DE
|
|
;
|
|
hexascii:
|
|
ld d,a ; save A in D
|
|
call hexconv ; convert low nibble of A to hex
|
|
ld e,a ; save it in E
|
|
ld a,d ; get original value back
|
|
rlca ; rotate high order nibble to low bits
|
|
rlca
|
|
rlca
|
|
rlca
|
|
call hexconv ; convert nibble
|
|
ld d,a ; save it in D
|
|
ret ; done
|
|
;
|
|
; Convert low nibble of A to ascii hex
|
|
;
|
|
hexconv:
|
|
and $0F ; low nibble only
|
|
add a,$90
|
|
daa
|
|
adc a,$40
|
|
daa
|
|
ret
|
|
;
|
|
; Print value of A or HL in decimal with leading zero suppression
|
|
; Use prtdecb for A or prtdecw for HL
|
|
;
|
|
prtdecb:
|
|
push hl
|
|
ld h,0
|
|
ld l,a
|
|
call prtdecw ; print it
|
|
pop hl
|
|
ret
|
|
;
|
|
prtdecw:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
call prtdec0
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
;
|
|
prtdec0:
|
|
ld e,'0'
|
|
ld bc,-10000
|
|
call prtdec1
|
|
ld bc,-1000
|
|
call prtdec1
|
|
ld bc,-100
|
|
call prtdec1
|
|
ld c,-10
|
|
call prtdec1
|
|
ld e,0
|
|
ld c,-1
|
|
prtdec1:
|
|
ld a,'0' - 1
|
|
prtdec2:
|
|
inc a
|
|
add hl,bc
|
|
jr c,prtdec2
|
|
sbc hl,bc
|
|
cp e
|
|
ret z
|
|
ld e,0
|
|
call prtchr
|
|
ret
|
|
;
|
|
; Start a new line
|
|
;
|
|
crlf2:
|
|
call crlf ; two of them
|
|
crlf:
|
|
push af ; preserve AF
|
|
ld a,13 ; <CR>
|
|
call prtchr ; print it
|
|
ld a,10 ; <LF>
|
|
call prtchr ; print it
|
|
pop af ; restore AF
|
|
ret
|
|
;
|
|
; Brief delay
|
|
;
|
|
delay:
|
|
push bc
|
|
ld b,0
|
|
delay1:
|
|
ex (sp),hl
|
|
ex (sp),hl
|
|
ex (sp),hl
|
|
ex (sp),hl
|
|
ex (sp),hl
|
|
ex (sp),hl
|
|
ex (sp),hl
|
|
ex (sp),hl
|
|
djnz delay1
|
|
pop bc
|
|
ret
|
|
;
|
|
;=======================================================================
|
|
; Constants
|
|
;=======================================================================
|
|
;
|
|
str_banner .db "Keyboard Information, v0.1",0
|
|
str_exit .db "Done, Thank you for using KBDINFO!",0
|
|
str_cmdport .db "Keyboard Controller Command Port: 0x",0
|
|
str_dataport .db "Keyboard Controller Data Port: 0x",0
|
|
str_timeout_write .db "Keyboard Controller Write Timeout, Status: 0x",0
|
|
str_timeout_read .db "Keyboard Controller Read Timeout, Status: 0x",0
|
|
str_err_ctrl_io .db "Keyboard Controller I/O Failure",0
|
|
str_err_ctrl_test .db "Keyboard Controller Self-Test Failed",0
|
|
str_put_cmd .db "Sent Command 0x",0
|
|
str_put_data .db "Sent Data 0x",0
|
|
str_get_data .db "Got Data 0x",0
|
|
str_ctrl_test .db "Attempting Controller Self-Test",0
|
|
str_ctrl_test_ok .db "Controller Self-Test OK",0
|
|
str_trans_off .db "Disabling Controller Translation",0
|
|
str_trans_on .db "Enabling Controller Translation",0
|
|
str_kbd_reset .db "Attempting Keyboard Reset",0
|
|
str_kbd_reset_ok .db "Keyboard Reset OK",0
|
|
str_err_kbd_reset .db "Keyboard Reset Failed",0
|
|
|
|
str_kbd_getsc .db "Requesting Active Scan Code Set from Keyboard",0
|
|
str_kbd_dispsc .db "Active Keyboard Scan Code Set is ",0
|
|
str_err_kbd_getsc .db "Error getting active keyboard scan code set",0
|
|
str_kbd_setsc .db "Setting Active Keyboard Scan Code Set",0
|
|
str_err_kbd_setsc .db "Error setting keyboard scan code set",0
|
|
str_kbd_ident .db "Keyboard Identification",0
|
|
str_kbd_ident_disp .db "Keyboard Identify: ",0
|
|
str_err_kbd_ident .db "Error performing Keyboard Identification",0
|
|
str_disp_scan_codes .db "Displaying Raw Scan Codes",13,10
|
|
.db " Press keys on keyboard to display scan codes",13,10
|
|
.db " Press <esc> on CP/M console to end",13,10,13,10,0
|
|
;
|
|
;=======================================================================
|
|
; Working data
|
|
;=======================================================================
|
|
;
|
|
stksav .dw 0 ; stack pointer saved at start
|
|
.fill stksiz,0 ; stack
|
|
stack .equ $ ; stack top
|
|
;
|
|
workbuf .fill 8
|
|
workbuf_len .db 0
|
|
;
|
|
;=======================================================================
|
|
;
|
|
.end
|