; Platform support library for Neon816 ; .include "./Neon816-hw.inc" PLATF_DP = DP_END KEYMODS = PLATF_DP ; keyboard modifiers, 16 bits ; b15 = left shift ; b14 = right shift ; b7 = left ctrl ; b6 = right ctrl ; these 3 are same position as set LED command: ; b2 = scroll lock (reserved) ; b1 = num lock (reserved) ; b0 = caps lock ; Neon816 dictionary, a bit of a different approach than the other ports ; This will get set up by the post init function of the system interface ; The system interface functions are after this dictionary. ; Note that most of the words are based on words found in NeonFORTH and ; are not subject to the OF816 license terms, but rather any terms that ; Lenore Byron places on them. 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 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 bcc :+ dey : tya PUSHNEXT eword ; H: ( -- byte ) read byte from PS/2 keyboard port. dword PS2K_FETCH,"PS2K@" 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 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 sep #SHORT_A .a8 sta f:PS2Mio : lda f:PS2Mstat bit #$08 bne :- rep #SHORT_A .a16 NEXT eword ; H: ( -- f ) f is true if data waiting at PS/2 mouse port. dword PS2M_QUERY,"PS2M?" ldy #$0000 sep #SHORT_A .a8 lda f:PS2Mstat ror rep #SHORT_A .a16 bcc :+ dey : tya PUSHNEXT eword ; H: ( -- byte ) read byte from PS/2 keyboard port. dword PS2M_FETCH,"PS2M@" sep #SHORT_A .a8 : lda f:PS2Mstat ror bcc :- lda f:PS2Mio rep #SHORT_A .a16 and #$00FF jsr _pusha NEXT eword ; H: ( -- ) send reset command to PS/2 keyboard dword dKBDRESET,"$KBDRESET" ENTER ONLIT $FF .dword PS2K_STORE 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 ONLIT RTCus .dword WSTORE ONLIT RTCms .dword WSTORE ONLIT RTCsec .dword CSTORE ONLIT RTCmin .dword CSTORE ONLIT RTChour .dword CSTORE ONLIT RTCday .dword WSTORE EXIT eword ; H: ( -- day hour minutes seconds ms us ) get RTC dword GETRTC,"GETRTC" ENTER ONLIT RTCday .dword WFETCH ONLIT RTChour .dword CFETCH ONLIT RTCmin .dword CFETCH ONLIT RTCsec .dword CFETCH ONLIT RTCms .dword WFETCH ONLIT RTCus .dword WFETCH EXIT eword ; NOTE: sets short accumulator and leaves it that way on exit! .proc I2C2_busy_wait sep #SHORT_A nosep: .a8 : lda f:I2C2ctrl rol bcs :- rts .a16 .endproc ; H: ( -- ) start I2C2 communication. dword I2C2START,"I2C2START" jsr I2C2_busy_wait .a8 lda #$01 sta f:I2C2ctrl rep #SHORT_A .a16 NEXT eword ; H: ( -- ) stop I2C2 communication. dword I2C2STOP,"I2C2STOP" jsr I2C2_busy_wait .a8 lda #$02 sta f:I2C2ctrl rep #SHORT_A .a16 NEXT eword ; H: ( byte -- ) write byte to I2C2. dword I2C2_STORE,"I2C2!" jsr _popay jsr I2C2_busy_wait .a8 tya sta f:I2C2io lda #$08 sta f:I2C2ctrl rep #SHORT_A .a16 NEXT eword ; H: ( -- byte ) receive byte from I2C2, send ack. dword I2C2_FETCH_ACK,"I2C2@+" jsr I2C2_busy_wait .a8 lda #$44 dofetch: sta f:I2C2ctrl jsr I2C2_busy_wait::nosep lda f:I2C2io rep #SHORT_A .a16 and #$00FF jsr _pusha NEXT eword ; H: ( -- byte ) receive byte from I2C2, do not send ack. dword I2C2_FETCH,"I2C2@" jsr I2C2_busy_wait .a8 lda #$04 bra I2C2_FETCH_ACK::dofetch .a16 eword ; H: ( offset byte -- ) write byte to VDC at offset dword VDC_C_STORE,"VDCC!" jsr _popay ; pop offset phy ; save low word jsr _popay ; pop value to write pla ; get offset back phx ; save SP tax ; offset to x reg sep #SHORT_A ; whew! that was fun! .a8 tya ; value to A sta f:VDCbase,x rep #SHORT_A .a16 plx ; restore SP NEXT eword ; H: ( offset -- byte ) read byte from VDC at offset dword VDC_C_FETCH,"VDCC@" jsr _popay ; pop offet phx ; save SP tya tax sep #SHORT_A ; whew! that was fun! .a8 lda f:VDCbase,x rep #SHORT_A .a16 plx ; restore SP and #$00FF jsr _pusha NEXT eword ; H: ( offset word -- ) write word to VDC at offset dword VDC_STORE,"VDC!" jsr _popay ; again! again! again! phy jsr _popay pla phx tax tya sta f:VDCbase,x rep #SHORT_A .a16 plx ; restore SP NEXT eword dword VIDSTART,"VIDSTART" ENTER ONLIT $0799 ONLIT $10 .dword VDC_STORE ONLIT $0839 ONLIT $12 .dword VDC_STORE ONLIT $03C7 ONLIT $14 .dword VDC_STORE ONLIT $041E ONLIT $16 .dword VDC_STORE ONLIT $0257 ONLIT $18 .dword VDC_STORE ONLIT $0258 ONLIT $1A .dword VDC_STORE ONLIT $025C ONLIT $1C .dword VDC_STORE ONLIT $0272 ONLIT $1E .dword VDC_STORE vid_on: ONLIT $92 : .dword ZERO .dword VDC_C_STORE .dword ZERO .dword VDC_C_FETCH .dword IF .dword :- ; branch if false .dword I2C2START ONLIT $70 .dword I2C2_STORE ONLIT $08 .dword I2C2_STORE ONLIT $B9 .dword I2C2_STORE .dword I2C2STOP EXIT eword dword VMODELINE,"VMODELINE" ENTER .dword TWO .dword MINUS ONLIT $1E .dword VDC_STORE .dword DECR ONLIT $1C .dword VDC_STORE .dword DECR ONLIT $1A .dword VDC_STORE .dword DECR ONLIT $18 .dword VDC_STORE .dword TWO .dword MINUS ONLIT $16 .dword VDC_STORE .dword DECR ONLIT $14 .dword VDC_STORE .dword DECR ONLIT $12 .dword VDC_STORE .dword DECR ONLIT $10 .dword VDC_STORE JUMP VIDSTART::vid_on eword dword VIDSTOP,"VIDSTOP" ENTER .dword I2C2START ONLIT $70 .dword I2C2_STORE ONLIT $08 .dword I2C2_STORE ONLIT $FE .dword I2C2_STORE .dword I2C2STOP .dword ZERO .dword ZERO .dword VDC_C_STORE EXIT eword ; H: ( -- ) dump display EDID data, first 256 bytes. dword DUMPEDID,"DUMPEDID" dump_size = $0100 ENTER ONLIT dump_size .dword ALLOC ; buffer for downloaded EDID data .dword I2C2START ONLIT $A0 .dword I2C2_STORE .dword ZERO .dword I2C2_STORE .dword I2C2START ONLIT $A1 .dword I2C2_STORE ONLIT dump_size .dword ZERO .dword _DO : .dword I2C2_FETCH_ACK .dword OVER .dword IX .dword PLUS .dword CSTORE .dword ONE .dword _PLOOP .dword :- .dword UNLOOP .dword I2C2_FETCH ; NeonFORTH displays this .dword I2C2STOP .dword DUP ONLIT dump_size .dword DUMP ONLIT dump_size .dword FREE EXIT eword ; H: ( -- ) initialize SPI2. dword SPI2INIT,"SPI2INIT" sep #SHORT_A .a8 lda #$00 sta f:SPI2ctrl sta f:SPI2ctrl2 lda #$05 sta f:SPI2ctrl3 rep #SHORT_A .a16 NEXT eword ; H: ( -- ) start SPI2 communication. dword SPI2START,"SPI2START" sep #SHORT_A .a8 lda #$01 sta f:SPI2ctrl rep #SHORT_A .a16 NEXT eword ; H: ( -- ) stop SPI2 communication. dword SPI2STOP,"SPI2STOP" sep #SHORT_A .a8 : lda f:SPI2ctrl and #$40 bne :- sta f:SPI2ctrl ; note A=0 rep #SHORT_A .a16 NEXT eword ; NOTE: sets short accumulator and leaves it that way on exit! .proc SPI2_busy_wait sep #SHORT_A nosep: .a8 : lda f:SPI2ctrl rol bcs :- rts .a16 .endproc ; H: ( byte -- ) write byte to SPI2. dword SPI2_STORE,"SPI2!" jsr _popay jsr SPI2_busy_wait .a8 tya sta f:SPI2io rep #SHORT_A .a16 NEXT eword ; H: ( -- byte ) fetch byte from SPI2. dword SPI2_FETCH,"SPI2@" jsr SPI2_busy_wait .a8 lda #$00 sta f:SPI2io : lda f:SPI2ctrl bit #$40 bne :- lda f:SPI2io rep #SHORT_A .a16 and #$00FF jsr _pusha NEXT eword dend ; and now for the system interface .proc _system_interface ;wdm 3 phx asl tax jmp (.loword(table),x) table: .addr _sf_pre_init .addr _sf_post_init .addr _sf_emit .addr _sf_keyq .addr _sf_key .addr _sf_fcode .addr _sf_reset_all .endproc .export _system_interface .proc _sf_success lda #$0000 tay clc rtl .endproc .proc _sf_fail ldy #.loword(-21) lda #.hiword(-21) sec rtl .endproc .proc _sf_pre_init ; NeonFORTH does this, presumably to initialize the serial port ; The code from here to the EOC commment was adapted from code written by Lenore Byron sep #SHORT_A .a8 lda #$8D sta f:SERctrlA lda #$06 sta f:SERctrlB lda #$00 sta f:SERctrlC rep #SHORT_A .a16 ; EOC plx jmp _sf_success .endproc .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 ONLIT LAST_neon816 SLIT "NEON816" .dword dVOCAB .dword LAST ; now set the head of the vocabulary to the .dword drXT ; last word defined in the neon816 dictionary .dword rBODY .dword STORE .dword GETRTC ; start the clock .dword CLEAR CODE jmp _sf_success .endproc .proc _sf_emit plx ; get forth SP jsr _popay ; grab the top item phx ; and save new SP ; The code from here to the EOC commment was adapted from code written by Lenore Byron sep #SHORT_A .a8 tya sta f:SERio : lda f:SERstat bit #$08 bne :- rep #SHORT_A .a16 ; EOC plx jmp _sf_success .endproc .proc _sf_keyq ldy #$0000 ; anticipate false ; The code from here to the EOC commment was adapted from code written by Lenore Byron sep #SHORT_A .a8 lda f:SERstat ; b0=1 if data ready ror bcc :+ iny : rep #SHORT_A .a16 ; EOC tya plx jsr _pushay jmp _sf_success .endproc .proc _sf_key ; The code from here to the EOC commment was adapted from code written by Lenore Byron sep #SHORT_A .a8 : lda f:SERstat ror bcc :- lda f:SERio rep #SHORT_A .a16 ; EOC and #$00FF tay lda #$0000 plx jsr _pushay jmp _sf_success .endproc .proc _sf_fcode .if include_fcode ldy #.loword(list) lda #.hiword(list) .else lda #$0000 tay .endif plx jsr _pushay jmp _sf_success .if include_fcode list: .dword 0 .endif .endproc ; TODO.... .proc _sf_reset_all plx jmp _sf_fail .endproc ; return carry set if data waiting at PS/2 keyboard port, clear otherwise ; destroys A .proc ps2k_ready sep #SHORT_A .a8 lda f:PS2Kstat ror rep #SHORT_A .a16 rts .endproc ; read data from PS/2 keyboard port, blocking ; returns byte in A .proc ps2k_read sep #SHORT_A .a8 : lda f:PS2Kstat ror bcc :- lda f:PS2Kio rep #SHORT_A .a16 and #$00FF rts .endproc ; write data byte in A to PS/2 keyboard port .proc ps2k_write sep #SHORT_A .a8 sta f:PS2Kio : lda f:PS2Kstat bit #$08 bne :- rep #SHORT_A .a16 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 .proc mktab ; 'main' scan codes' ; $00 .byte $00,$00 ; none .byte $00,$00 ; F9 .byte $00,$00 ; none .byte $00,$00 ; F5 .byte $00,$00 ; F3 .byte $00,$00 ; F1 .byte $00,$00 ; F2 .byte $00,$00 ; F12 ; $08 .byte $00,$00 ; none .byte $00,$00 ; F10 .byte $00,$00 ; F8 .byte $00,$00 ; F6 .byte $00,$00 ; F4 .byte $09,$09 ; Tab .byte '`','~' .byte $00,$00 ; none ; $10 .byte $00,$00 ; none .byte $00,$00 ; left alt .byte $80,$80 ; left shift .byte $00,$00 ; none .byte $86,$86 ; left control .byte 'q','Q' .byte '1','!' .byte $00,$00 ; none ; $18 .byte $00,$00 ; none .byte $00,$00 ; none .byte 'z','Z' .byte 's','S' .byte 'a','A' .byte 'w','W' .byte '2','@' .byte $00,$00 ; none ; $20 .byte $00,$00 ; none .byte 'c','C' .byte 'x','X' .byte 'd','D' .byte 'e','E' .byte '4','$' .byte '3','#' .byte $00,$00 ; none ; $28 .byte $00,$00 ; none .byte ' ',' ' .byte 'v','V' .byte 'f','F' .byte 't','T' .byte 'r','R' .byte '5','S' .byte $00,$00 ; none ; $30 .byte $00,$00 ; none .byte 'n','N' .byte 'b','B' .byte 'h','H' .byte 'g','G' .byte 'y','Y' .byte '6','^' .byte $00,$00 ; none ; $38 .byte $00,$00 ; none .byte $00,$00 ; none .byte 'm','M' .byte 'j','J' .byte 'u','U' .byte '7','&' .byte '8','*' .byte $00,$00 ; none ; $40 .byte $00,$00 ; none .byte ',','<' .byte 'k','K' .byte 'i','I' .byte 'o','O' .byte '0',')' .byte '9','(' .byte $00,$00 ; none ; $48 .byte $00,$00 ; none .byte '.','>' .byte '/','?' .byte 'l','L' .byte ';',':' .byte 'p','P' .byte '-','_' .byte $00,$00 ; none ; $50 .byte $00,$00 ; none .byte $00,$00 ; none .byte $27,'"' .byte $00,$00 ; none .byte '[','{' .byte '=','+' .byte $00,$00 ; none .byte $00,$00 ; none ; $58 .byte $84,$84 ; caps lock .byte $82,$82 ; right shift .byte $0D,$0D ; enter .byte ']','}' .byte $00,$00 ; none .byte '\','|' .byte $00,$00 ; none .byte $00,$00 ; none ; $60 .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $08,$7F ; backspace .byte $00,$00 ; none ; $68 .byte $00,$00 ; none .byte '1','1' ; keypad .byte $00,$00 ; none .byte '4','4' ; keypad .byte '7','7' ; keypad .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none ; $70 .byte '0','0' ; keypad .byte '.','.' ; keypad .byte '2','2' ; keypad .byte '5','5' ; keypad .byte '6','6' ; keypad .byte '8','8' ; keypad .byte $1B,$1B ; escape .byte $00,$00 ; num lock ; $78 .byte $00,$00 ; F11 .byte '+','+' ; keypad .byte '3','3' ; keypad .byte '-','-' ; keypad .byte '*','*' ; keypad .byte '9','9' ; keypad .byte $00,$00 ; scroll lock .byte $00,$00 ; none ; $80 .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; F7 .endproc .proc ektab ; E0 scan codes ; $00 .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none ; $08 .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none ; $10 .byte $00,$00 ; MM WWW search .byte $00,$00 ; right alt .byte $00,$00 ; none .byte $00,$00 ; none .byte $88,$88 ; right control .byte $00,$00 ; MM prev track .byte $00,$00 ; none .byte $00,$00 ; none ; $18 .byte $00,$00 ; MM WWW favorites .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; left GUI ; $20 .byte $00,$00 ; MM WWW refresh .byte $00,$00 ; MM vol down .byte $00,$00 ; MM mute .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; right GUI ; $28 .byte $00,$00 ; MM WWW stop .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; MM calculator .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; 'apps' ; $30 .byte $00,$00 ; MM WWW forward .byte $00,$00 ; none .byte $00,$00 ; MM vol up .byte $00,$00 ; none .byte $00,$00 ; MM play/pause .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; ACPI power ; $38 .byte $00,$00 ; MM WWW back .byte $00,$00 ; none .byte $00,$00 ; MM WWW home .byte $00,$00 ; MM stop .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; ACPI sleep ; $40 .byte $00,$00 ; MM my computer .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none ; $48 .byte $00,$00 ; MM email .byte $00,$00 ; none .byte '/','/' ; keypad .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; MM next track .byte $00,$00 ; none .byte $00,$00 ; none ; $50 .byte $00,$00 ; MM select .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none ; $58 .byte $00,$00 ; none .byte $00,$00 ; none .byte $0D,$0D ; keypad 'enter' .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; ACPI wake .byte $00,$00 ; none ; $60 .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none ; $68 .byte $00,$00 ; none .byte $00,$00 ; end .byte $00,$00 ; none .byte $08,$08 ; cursor left .byte $00,$00 ; home .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; none ; $70 .byte $00,$00 ; insert .byte $7F,$7F ; delete .byte $0A,$0A ; cursor down .byte $00,$00 ; none .byte $15,$15 ; cursor right .byte $0B,$0B ; cursor up .byte $00,$00 ; none .byte $00,$00 ; none ; $78 .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; page down .byte $00,$00 ; none .byte $00,$00 ; none .byte $00,$00 ; page up .endproc ; Tables of routines for special make/break of keys (values > $80) ; kmktbl and kbktbl must match up. .proc kmktbl .addr mk_lshift-1 ; $80 .addr mk_rshift-1 ; $82 .addr mk_caps-1 ; $84 .addr mk_lctrl-1 ; $86 .addr mk_rctrl-1 ; $88 .endproc .proc kbktbl .addr bk_lshift-1 ; $80 .addr bk_rshift-1 ; $82 .addr bk_caps-1 ; $84 .addr bk_lctrl-1 ; $86 .addr bk_rctrl-1 ; $88 .endproc .proc mk_lshift lda #%1000000000000000 bra makemod .endproc .proc mk_rshift lda #%0100000000000000 bra makemod .endproc .proc mk_caps lda KEYMODS eor #%1 sta KEYMODS ; fall-through to ps2_setLEDs .endproc .proc ps2_setleds lda #$ED ; set LEDs command jsr ps2k_command lda KEYMODS and #%111 jsr ps2k_command rts .endproc .proc mk_lctrl lda #%0000000010000000 bra makemod .endproc .proc mk_rctrl lda #%0000000001000000 ;bra makemod .endproc .proc makemod tsb KEYMODS rts .endproc .proc bk_lshift lda #%1000000000000000 bra breakmod .endproc .proc bk_rshift lda #%0100000000000000 bra breakmod .endproc .proc bk_caps rts .endproc .proc bk_lctrl lda #%0000000010000000 bra breakmod .endproc .proc bk_rctrl lda #%0000000001000000 ;bra breakmod .endproc .proc breakmod trb KEYMODS rts .endproc ; 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 pla 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 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 and #$00FF cmp #$0080 rts .endproc ; Wait for a valid key, when we get one, decode and return .proc ps2_keyin : 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