Neon816: PS/2 keyboard working

This commit is contained in:
mgcaret 2020-03-23 20:25:28 -07:00
parent 8078f8cf21
commit 367ec0d98d
2 changed files with 275 additions and 137 deletions

View File

@ -0,0 +1,111 @@
# -
Updated: 2020-03-23 20:21:18 -0700
## $KBDRESET
_( -- )_ send reset command to PS/2 keyboard
## DUMPEDID
_( -- )_ dump display EDID data, first 256 bytes.
## GETRTC
_( -- day hour minutes seconds ms us )_ get RTC
## I2C2!
_( byte -- )_ write byte to I2C2.
## I2C2@
_( -- byte )_ receive byte from I2C2, do not send ack.
## I2C2@+
_( -- byte )_ receive byte from I2C2, send ack.
## I2C2START
_( -- )_ start I2C2 communication.
## I2C2STOP
_( -- )_ stop I2C2 communication.
## PS2K!
_( byte -- )_ write byte to PS/2 keyboard port.
## PS2K?
_( -- f )_ f is true if data waiting at PS/2 keyboard port.
## PS2K@
_( -- byte )_ read byte from PS/2 keyboard port.
## PS2KEY
_( -- c )_ wait for keypress on PS/2 port, c is the character typed.
## PS2M!
_( byte -- )_ write byte to PS/2 mouse port.
## PS2M?
_( -- f )_ f is true if data waiting at PS/2 mouse port.
## PS2M@
_( -- byte )_ read byte from PS/2 keyboard port.
## PS2RAW
_( -- code f )_ read raw keycode from PS/2 port.
code is keycode, either xx or E0xx, f is true if break.
## SETRTC
_( day hour minutes seconds ms us -- )_ set RTC
## SPI2!
_( byte -- )_ write byte to SPI2.
## SPI2@
_( -- byte )_ fetch byte from SPI2.
## SPI2INIT
_( -- )_ initialize SPI2.
## SPI2START
_( -- )_ start SPI2 communication.
## SPI2STOP
_( -- )_ stop SPI2 communication.
## VDC!
_( offset word -- )_ write word to VDC at offset
## VDCC!
_( offset byte -- )_ write byte to VDC at offset
## VDCC@
_( offset -- byte )_ read byte from VDC at offset
## VIDSTART
## VIDSTOP
## VMODELINE

View File

@ -24,55 +24,53 @@ KEYMODS = PLATF_DP ; keyboard modifiers, 16 bits
dstart "neon816"
dchain H_FORTH ; Make branch off the word FORTH
; H: ( byte -- ) write byte to PS/2 keyboard port.
dword PS2K_STORE,"PS2K!"
jsr _popay
tya
sep #SHORT_A
.a8
sta f:PS2Kio
: lda f:PS2Kstat
bit #$08
bne :-
rep #SHORT_A
.a16
jsr ps2k_write
NEXT
eword
; H: ( -- f ) f is true if data waiting at PS/2 keyboard port.
dword PS2K_QUERY,"PS2K?"
jsr ps2k_ready
ldy #$0000
sep #SHORT_A
.a8
lda f:PS2Kstat
ror
rep #SHORT_A
.a16
bcc :+
dey
: tya
PUSHNEXT
eword
; H: ( -- byte ) read byte from PS/2 keyboard port.
dword PS2K_FETCH,"PS2K@"
sep #SHORT_A
.a8
: lda f:PS2Kstat
ror
bcc :-
lda f:PS2Kio
rep #SHORT_A
.a16
and #$00FF
jsr ps2k_read
jsr _pusha
NEXT
eword
; H: ( -- c ) wait for keypress on PS/2 port, c is the character typed.
dword PS2KEY,"PS2KEY"
: jsr ps2_keyin
bcc :-
jsr ps2_keyin
jsr _pusha
NEXT
eword
; H: ( -- code f ) read raw keycode from PS/2 port.
; H: code is keycode, either xx or E0xx, f is true if break.
dword PS2RAW,"PS2RAW"
jsr ps2_readcode
php
jsr _pusha
ldy #$00
plp
bcc :+
dey
: tya
PUSHNEXT
eword
; H: ( byte -- ) write byte to PS/2 mouse port.
dword PS2M_STORE,"PS2M!"
jsr _popay
tya
@ -87,6 +85,7 @@ dword PS2M_STORE,"PS2M!"
NEXT
eword
; H: ( -- f ) f is true if data waiting at PS/2 mouse port.
dword PS2M_QUERY,"PS2M?"
ldy #$0000
sep #SHORT_A
@ -101,6 +100,7 @@ dword PS2M_QUERY,"PS2M?"
PUSHNEXT
eword
; H: ( -- byte ) read byte from PS/2 keyboard port.
dword PS2M_FETCH,"PS2M@"
sep #SHORT_A
.a8
@ -115,6 +115,7 @@ dword PS2M_FETCH,"PS2M@"
NEXT
eword
; H: ( -- ) send reset command to PS/2 keyboard
dword dKBDRESET,"$KBDRESET"
ENTER
ONLIT $FF
@ -122,6 +123,7 @@ dword dKBDRESET,"$KBDRESET"
EXIT
eword
; H: ( day hour minutes seconds ms us -- ) set RTC
; this probably isn't fast enough to reliably set micro and milliseconds
dword SETRTC,"SETRTC"
ENTER
@ -140,6 +142,7 @@ dword SETRTC,"SETRTC"
EXIT
eword
; H: ( -- day hour minutes seconds ms us ) get RTC
dword GETRTC,"GETRTC"
ENTER
ONLIT RTCday
@ -169,6 +172,7 @@ nosep:
.a16
.endproc
; H: ( -- ) start I2C2 communication.
dword I2C2START,"I2C2START"
jsr I2C2_busy_wait
.a8
@ -179,6 +183,7 @@ dword I2C2START,"I2C2START"
NEXT
eword
; H: ( -- ) stop I2C2 communication.
dword I2C2STOP,"I2C2STOP"
jsr I2C2_busy_wait
.a8
@ -189,6 +194,7 @@ dword I2C2STOP,"I2C2STOP"
NEXT
eword
; H: ( byte -- ) write byte to I2C2.
dword I2C2_STORE,"I2C2!"
jsr _popay
jsr I2C2_busy_wait
@ -202,6 +208,7 @@ dword I2C2_STORE,"I2C2!"
NEXT
eword
; H: ( -- byte ) receive byte from I2C2, send ack.
dword I2C2_FETCH_ACK,"I2C2@+"
jsr I2C2_busy_wait
.a8
@ -216,7 +223,8 @@ dofetch: sta f:I2C2ctrl
NEXT
eword
dword I2C2_FETCH,"I2C2@+"
; H: ( -- byte ) receive byte from I2C2, do not send ack.
dword I2C2_FETCH,"I2C2@"
jsr I2C2_busy_wait
.a8
lda #$04
@ -224,6 +232,7 @@ dword I2C2_FETCH,"I2C2@+"
.a16
eword
; H: ( offset byte -- ) write byte to VDC at offset
dword VDC_C_STORE,"VDCC!"
jsr _popay ; pop offset
phy ; save low word
@ -241,6 +250,7 @@ dword VDC_C_STORE,"VDCC!"
NEXT
eword
; H: ( offset -- byte ) read byte from VDC at offset
dword VDC_C_FETCH,"VDCC@"
jsr _popay ; pop offet
phx ; save SP
@ -257,6 +267,7 @@ dword VDC_C_FETCH,"VDCC@"
NEXT
eword
; H: ( offset word -- ) write word to VDC at offset
dword VDC_STORE,"VDC!"
jsr _popay ; again! again! again!
phy
@ -363,6 +374,7 @@ dword VIDSTOP,"VIDSTOP"
EXIT
eword
; H: ( -- ) dump display EDID data, first 256 bytes.
dword DUMPEDID,"DUMPEDID"
dump_size = $0100
ENTER
@ -398,6 +410,7 @@ dump_size = $0100
EXIT
eword
; H: ( -- ) initialize SPI2.
dword SPI2INIT,"SPI2INIT"
sep #SHORT_A
.a8
@ -411,6 +424,7 @@ dword SPI2INIT,"SPI2INIT"
NEXT
eword
; H: ( -- ) start SPI2 communication.
dword SPI2START,"SPI2START"
sep #SHORT_A
.a8
@ -421,6 +435,7 @@ dword SPI2START,"SPI2START"
NEXT
eword
; H: ( -- ) stop SPI2 communication.
dword SPI2STOP,"SPI2STOP"
sep #SHORT_A
.a8
@ -445,6 +460,7 @@ nosep:
.a16
.endproc
; H: ( byte -- ) write byte to SPI2.
dword SPI2_STORE,"SPI2!"
jsr _popay
jsr SPI2_busy_wait
@ -456,6 +472,7 @@ dword SPI2_STORE,"SPI2!"
NEXT
eword
; H: ( -- byte ) fetch byte from SPI2.
dword SPI2_FETCH,"SPI2@"
jsr SPI2_busy_wait
.a8
@ -527,6 +544,7 @@ table: .addr _sf_pre_init
.proc _sf_post_init
plx
stz KEYMODS
; Here we make a vocabulary definition for the neon816 dictionary
; that we defined at the beginning of this file.
ENTER
@ -662,6 +680,13 @@ list:
rts
.endproc
.proc ps2k_command
jsr ps2k_write
jsr ps2k_read
cmp #$00FA
rts
.endproc
; Keyboard translate tables
; unshifted and shifted codes
; if high bit set, jump to special handler routine
@ -767,7 +792,6 @@ list:
.byte $00,$00 ; none
.byte $00,$00 ; none
; $58
.byte $00,$00 ; none
.byte $84,$84 ; caps lock
.byte $82,$82 ; right shift
.byte $0D,$0D ; enter
@ -775,6 +799,7 @@ list:
.byte $00,$00 ; none
.byte '\','|'
.byte $00,$00 ; none
.byte $00,$00 ; none
; $60
.byte $00,$00 ; none
.byte $00,$00 ; none
@ -977,7 +1002,7 @@ list:
.addr bk_lshift-1 ; $80
.addr bk_rshift-1 ; $82
.addr bk_caps-1 ; $84
.addr bk_lctrl-1 ; $88
.addr bk_lctrl-1 ; $86
.addr bk_rctrl-1 ; $88
.endproc
@ -992,17 +1017,19 @@ list:
.endproc
.proc mk_caps
lda #%0000000000000001
jsr makemod
lda KEYMODS
eor #%1
sta KEYMODS
; fall-through to ps2_setLEDs
.endproc
.proc ps2_setleds
lda #$ED ; set LEDs command
jsr ps2k_write
jsr ps2k_command
lda KEYMODS
jsr ps2k_write
bra nokey
and #%111
jsr ps2k_command
rts
.endproc
.proc mk_lctrl
@ -1017,12 +1044,6 @@ list:
.proc makemod
tsb KEYMODS
; fall through to nokey
.endproc
.proc nokey
lda #$0000
clc
rts
.endproc
@ -1037,9 +1058,7 @@ list:
.endproc
.proc bk_caps
lda #%0000000000000001
jsr breakmod
bra ps2_setleds
rts
.endproc
.proc bk_lctrl
@ -1054,116 +1073,124 @@ list:
.proc breakmod
trb KEYMODS
bra nokey
rts
.endproc
; expects a 'make' code, either 00xx or E0xx
.proc ps2_keydn
lda #%1100000000000000 ; shift keys
bit KEYMODS
php ; save result (Z=1 if no shift)
ora #$0000 ; if high bit is set we will treat as $E0
php
; Read a code from the keyboard, return carry set if it was 'break' and clear
; if it was 'make'
.proc ps2_readcode
phy ; save Y
ldy #$0000 ; flag for break
phy ; space for extended code byte
: jsr ps2k_read
cmp #$E1 ; pause/break annoyance
beq pausebrk
cmp #$E0 ; extended code?
bne :+
xba
sta 1,s ; yes, put in upper byte on stack
bra :- ; back to read
: cmp #$F0 ; break code?
bne :+
iny ; yes, flag
bra :-- ; back to read
: ora 1,s ; if none of those, put in lower byte
sta 1,s ; inefficient but it works
pla ; and get it off the stack
done: cpy #$01 ; set carry if break
ply ; restore Y
rts
pausebrk: pla
ldy #7
: jsr ps2k_read ; drop 7 bytes
dey
bne :-
lda #$E000 ; bogus scan code
bra done
.endproc
; get code from translate table for key code in A, return in A
; destroys y, returns 0000 if there is no translation
.proc ps2_keytran
pha
and #$00FF
asl
tay
lda #$0000 ; anticipate failure of cpy
plp
bmi :+
cpy .sizeof(mktab)/2
bcs :++ ; bad value
lda mktab,y
bra :++
: cpy .sizeof(ektab)/2
bcs :+ ; bad value
lda ektab,y
: plp
beq :+ ; unshifted
xba
: and #$00FF
cmp #$0080
bcs special
pha
lda KEYMODS
ror ; caps lock into carry
pla
bcc nocaps ; if no caps lock
cmp #'a'
bcc nocaps
cmp #'z'+1
bcs nocaps
and #$DF ; make caps
nocaps: pha ; save on stack
lda #%0000000011000000 ; ctrl keys
bit KEYMODS
beq :+ ; if no ctrl
and #$FF00
cmp #$E000
beq ext
cpy #.sizeof(mktab)
bcs bad
lda mktab,y
bra done
ext: cpy #.sizeof(ektab)
bcs bad
lda ektab,y
bra done
bad: lda #$0000
done: cmp #$0001
rts
.endproc
; take translated code in A, apply keyboard modifiers, and return result in A
; if special, return carry set
.proc ps2_keymod
pha ; work with it on stack
lda KEYMODS
and #%1100000000000000 ; shift keys
beq :+
lda 1,s
and #%0000000000011111 ; make ctrl
xba
sta 1,s
: lda 1,s
and #$0080
bne :++ ; shortcut special
lda KEYMODS
ror ; caps into carry
bcc :+
lda 1,s
and #$00FF
cmp #'a'
bcc :+
cmp #'z'+1
bcs :+
and #$DF
sta 1,s
: lda KEYMODS
and #%0000000011000000 ; ctrl keys
beq :+
lda 1,s
and #%11111 ; make ctrl char
sta 1,s
: pla
cmp #$01 ; set carry if >= 1
rts
special: and #$7f
tay
lda kmktbl,y
pha
rts
.endproc
; for the break we only care about modifiers, we aren't keeping track of anything else
; so we also ignore shifted values
; expects a 'make'-like code, either 00xx or E0xx
.proc ps2_keyup
ora #$0000
php
and #$00FF
asl
tay
plp
bmi :+
cpy .sizeof(mktab)/2
bcs :++
lda mktab,y
bra :++
: cpy .sizeof(ektab)/2
bcs :+
lda ektab,y
: and #$00FF
cmp #$0080
bcs special
lda #$0000
clc
rts
special: and #$7f
tay
lda kbktbl,y
pha
rts
.endproc
; Wait for a valid key, when we get one, decode and return
.proc ps2_keyin
: jsr ps2k_read
cmp #$F0
beq break
cmp #$FE
beq extended
cmp #$AA ; diags passed
beq :-
cmp #$FC ; diags failed
beq :- ; prob shouldn't do this
cmp #$FA ; ACK
beq :-
jmp ps2_keydn ; see if we can decode it
break: jsr ps2k_read
bra ps2_keyup
extended: jsr ps2k_read
cmp #$F0
beq :+ ; extended break
ora #$E000
jmp ps2_keydn
: jsr ps2k_read
ora #$E000
bra ps2_keyup
: php
: plp
jsr ps2_readcode
php
jsr ps2_keytran
bcc :- ; carry is clear if no value for key
jsr ps2_keymod
bcs special
plp
bcs :-- ; break code, wait for another
rts
special: and #$007F
tay
lda kmktbl,y
plp
bcc :+ ; if make
lda kbktbl,y
: jsr :+
bra :--- ; for special, we go back to waiting
: pha ; do RTS trick
rts
.endproc