Compare commits

..

No commits in common. "5c02ff65858dc5d348c666e9731b34d77f40dc99" and "326b2f624794582b39625b7241ae02f0e8c9da6b" have entirely different histories.

11 changed files with 603 additions and 849 deletions

View File

@ -1,33 +0,0 @@
.include "lcd.h65"
.include "ps2_keyboard_text_handler.h65"
.export ps2_keyboard_printer
.import home:absolute
.code
.proc ps2_keyboard_printer
jsr lcd::clear
jsr kb::init
loop:
lda kb::char
beq @no_char
stz kb::char
jsr lcd::print_char
bra loop
@no_char:
lda kb::keycode
beq loop
cmp #kb::K::ESCAPE
beq @esacpe
cmp #kb::K::PRINT
beq @clear_display
bra loop
@esacpe:
jmp home
@clear_display:
jsr lcd::clear
bra loop
.endproc

View File

@ -1,33 +0,0 @@
;;********************************************************************************
;; @module ps2_keyboard_simple_handler
;; @type system
;; @details:
;; This module processes keyboard scancodes from the ps2_keyboard driver.
;; It requires a PS/2 keyboard that supports scancode set 3.
;;
;; @section What it does
;; - swallow break (release) scancodes
;;
;; This handler is designed for debug purposes.
;;
;; @section How to use it
;; Call `kb::init` and check if it was successful (Z = 1).
;; In your program loop, check if kb::scancode is 0. If it is not, a key has been pressed.
;;
;; @subsection Changing the keyboard behaviour
;; You can change the typematic rate and delay (see PS/2 keyboard command 0xF3)
;; and the typematic behavior (make, make/release, typematic) for all keys.
;;
;; You may also send the echo (0xEE) and identify (0xF2) commands to the keyboard.
;;********************************************************************************
.ifndef INCLUDE_KEYBOARD_SIMPLE
INCLUDE_KEYBOARD_SIMPLE = 1
.include "ps2_keyboard.h65"
.scope skb
Import skb, init, scancode
K_BREAK = $F0
.endscope
.endif ; guard

View File

@ -1,77 +0,0 @@
.include "ps2_keyboard_simple_handler.h65"
Export skb, init, scancode
.bss
previous_scancode: .res 1 ; all 1 if previous_scancode is break
scancode: .res 1
.code
;;********************************************************************************
;; @function Initialize the keyboard handler
;; @returns Z: Z = 1 => success, Z = 0 => failure
;; @details:
;; Initializes the PS/2 keyboard driver and the handler.
;; If any PS/2 command fails, init returns with Z = 0. The failed command (and parameter) can be loaded from `ps2kb::send_cmd` and `ps2kb::send_data`
;; After init, the keyboard will be able to send scancodes.
;;********************************************************************************
.proc init
stz scancode
stz previous_scancode
jsr ps2kb::init
; init & reset
ps2kb_CmdSelfTest
ps2kb_WaitFinishCmd
lda ps2kb::cmd_response+1
cmp #$aa
bne fail
; set set 3
ps2kb_CmdSetScancodeSet 3
ps2kb_WaitFinishCmd
lda ps2kb::cmd_response+1
cmp #ps2kb::ACK
bne fail
ps2kb_CmdEnable
ps2kb_WaitFinishCmd
lda ps2kb::cmd_response
cmp #ps2kb::ACK
bne fail
StoreDByte process_scancode, ps2kb::scancode_handler
jsr ps2kb::begin_receive
lda #0 ; set Z = success
fail:
rts
.endproc
;;********************************************************************************
;; @function Process a scancode
;; @details
;; If it is a 'make' scancode, store it in skb::scancode
;; If the key is not a mod key, the keycode is stored in kb::keycode
;; If the key is an ascii character, the corresponding ascii character is stored in kb::char
;; @param X: The scancode
;; @modifies A, X, Y
;;********************************************************************************
.proc process_scancode
; check how this scancode needs to be interpreted
bit previous_scancode
jmi break_key ; bit 7 set = BREAK
lda ps2kb::scancode
cmp #skb::K_BREAK
beq @break_scancode
sta scancode
rts
@break_scancode:
lda #$ff
sta previous_scancode
rts
break_key:
stz previous_scancode
rts
.endproc

View File

@ -1,189 +0,0 @@
;;********************************************************************************
;; @module ps2_keyboard_text_handler
;; @type system
;; @details:
;; This module processes keyboard scancodes from the ps2_keyboard driver.
;; It requires a PS/2 keyboard that supports scancode set 3.
;;
;; @section What it does
;; - keeps track of modifier keys: shift, left/right alt, left right meta (super),
;; control, capslock, numlock, scrollock
;; - convert valid keycodes to chars, dependening on the state of shift and right alt
;; - update the keyboard LEDs (numlock, capslock,, scrolllock)
;; This handler is designed for efficient text input.
;; It does not track the status of the keys (except modifiers) and is therefore not suitable for games.
;;
;; @section How it works
;; In the init function, the module disables the typematic behaviour for the modifier keys by
;; sending commands to the keyboard. The 3 lock keys will be set to make only mode and
;; the others to make release.
;; This makes tracking the modifier keys much easier and is the reason why scancode set 3
;; is required, as these commands are only available with that.
;;
;; Using scancode set 3 also adds the benefit that all scancodes are only 1 byte large,
;; making the recognition of scancodes trivial.
;;
;; Scancodes from the keyboard are processed immediately, as the handling functions is called
;; from the keyboard's interrupt handler.
;;
;; @subsection Mapping keycodes to chars
;; The mapping of keycodes to chars is done using an array of chars for each state:
;; default, shift and right alt. The tables contains the ascii characters, 0 where the keycode does not
;; generate a character and 1 where the keycode is modifier key.
;; This approach consumes a relatively large amount of ROM (3 * 144 bytes), but is super fast,
;; as loading a character is merely one indexed load instruction.
;; Checking for mod keys is now also fast, beceause we only have to do 1 comparison to see IF it is a modifier key.
;;
;; @section How to use it
;; Call `kb::init` and check if it was successful (Z = 1).
;; In your program loop, check if kb::keycode is 0. If it is not, a key has been pressed.
;;
;; If the keypress generated a character `kb::char` will be non-zero and contain the character.
;;
;; Note that if you will probably want to write a 0 to `kb::keycode` and `kb::char`
;; after processing the keycode. This will not be done by the handler.
;;
;; Conditional branches based on the status of a modifier (SHIFT, RALT, LALT, CTRL, META, NUMLOCK, SCROLLLOCK) can be done
;; using the `bbs` and `bbr` instructions, as each modifier is represented by a bit of the `kb::modifier` variable
;; in the zeropage.
;;
;; @subsection Changing the keyboard behaviour
;; You can change the typematic rate and delay (see PS/2 keyboard command 0xF3)
;; and the typematic behavior (make, make/release, typematic) for all keys except the modifier keys.
;;
;; You may also send the echo (0xEE) and identify (0xF2) commands to the keyboard.
;;********************************************************************************
.ifndef INCLUDE_KEYBOARD_TEXT
INCLUDE_KEYBOARD_TEXT = 1
.include "ps2_keyboard.h65"
.scope kb
Import kb, init, char, keycode
Import kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_NUMLOCK_ON
ImportZp kb, modifier
.enum MOD
SHIFT = %10000000 ; set if either capslock is on or shift is pressed
RALT = %01000000 ; set while rightalt is pressed
LALT = %00100000 ; set while leftalt is pressed
META = %00010000 ; set while left or right meta is pressed
CTRL = %00001000 ; set while left or right ctrl is pressed
CAPSLOCK = %00000100 ; ps/2 LED
NUMLOCK = %00000010 ; ps/2 LED
SCROLLLOCK = %00000001 ; ps/2 LED
.endenum
K_BREAK = $F0
.enum K
F1 = $07
ESCAPE = $08
TAB = $0D
GRAVE = $0E
F2 = $0F
LEFTCTRL = $11
LEFTSHIFT = $12
CAPSLOCK = $14
K_Q = $15
K_1 = $16
F3 = $17
LEFTALT = $19
K_Y = $1A
K_S = $1B
K_A = $1C
K_W = $1D
K_2 = $1E
F4 = $1F
K_C = $21
K_X = $22
K_D = $23
K_E = $24
K_4 = $25
K_3 = $26
F5 = $27
SPACE = $29
K_V = $2A
K_F = $2B
K_T = $2C
K_R = $2D
K_5 = $2E
F6 = $2F
K_N = $31
K_B = $32
K_H = $33
K_G = $34
K_Z = $35
K_6 = $36
F7 = $37
RIGHTALT = $39
K_M = $3A
K_J = $3B
K_U = $3C
K_7 = $3D
K_8 = $3E
F8 = $3F
COMMA = $41
K_K = $42
K_I = $43
K_O = $44
K_0 = $45
K_9 = $46
F9 = $47
DOT = $49
DASH = $4A
KPDIVIDE = $4A
K_L = $4B
OUML = $4C
K_P = $4D
SSHARP = $4E
KPMINUS = $4E
F10 = $4F
AUML = $52
UUML = $54
BACKTICK = $55
F11 = $56
PRINT = $57
RIGHTCTRL = $58
RIGHTSHIFT = $59
ENTER = $5A
PLUS = $5B
LESSTHAN = $5C
HASH = $5D
F12 = $5E
SCROLLLOCK = $5F
RIGHT = $60
LEFT = $61
PAUSE = $62
UP = $63
DELETE = $64
END = $65
BACKSPACE = $66
INSERT = $67
KP1 = $69
DOWN = $6A
KP4 = $6B
KP7 = $6C
PAGEDOWN = $6D
POS1 = $6E
PAGEUP = $6F
KP0 = $70
KPDOT = $71
KP2 = $72
KP5 = $73
KP6 = $74
KP8 = $75
NUMLOCK = $76
KPENTER = $79
KP3 = $7A
KPPLUS = $7C
KP9 = $7D
KPASTERISK = $7E
LEFTMETA = $8B
RIGHTMETA = $8C
MENU = $8D
.endenum
.endscope
.endif ; guard

View File

@ -1,306 +0,0 @@
.include "ps2_keyboard_text_handler.h65"
Export kb, init, char, keycode
Export kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_NUMLOCK_ON
ExportZp kb, modifier
.zeropage
modifier: .res 1
.bss
previous_scancode: .res 1 ; all 1 if previous_scancode is break
char: .res 1
keycode: .res 1
.code
;;********************************************************************************
;; @function Initialize the keyboard handler
;; @returns Z: Z = 1 => success, Z = 0 => failure
;; @details:
;; Initializes the PS/2 keyboard driver and the handler.
;; If any PS/2 command fails, init returns with Z = 0. The failed command (and parameter) can be loaded from `ps2kb::send_cmd` and `ps2kb::send_data`
;; After init, the keyboard will be able to send scancodes.
;; @modifies A, X, Y
;;********************************************************************************
.proc init
stz modifier
stz char
stz keycode
stz previous_scancode
jsr ps2kb::init
; init & reset
ps2kb_CmdSelfTest
ps2kb_WaitFinishCmd
lda ps2kb::cmd_response+1
cmp #$aa
bne fail
; set set 3
ps2kb_CmdSetScancodeSet 3
ps2kb_WaitFinishCmd
lda ps2kb::cmd_response+1
cmp #ps2kb::ACK
bne fail
; toggle-behaving keys
ldx #kb::K::CAPSLOCK
jsr set_make
ldx #kb::K::NUMLOCK
jsr set_make
ldx #kb::K::SCROLLLOCK
jsr set_make
; modifiers
ldx #kb::K::LEFTSHIFT
jsr set_make_release
ldx #kb::K::RIGHTSHIFT
jsr set_make_release
ldx #kb::K::LEFTCTRL
jsr set_make_release
ldx #kb::K::RIGHTCTRL
jsr set_make_release
ldx #kb::K::LEFTALT
jsr set_make_release
ldx #kb::K::RIGHTALT
jsr set_make_release
ldx #kb::K::LEFTMETA
jsr set_make_release
ldx #kb::K::RIGHTMETA
jsr set_make_release
ps2kb_CmdEnable
ps2kb_WaitFinishCmd
lda ps2kb::cmd_response
cmp #ps2kb::ACK
bne fail
StoreDByte process_scancode, ps2kb::scancode_handler
jsr ps2kb::begin_receive
lda #0 ; set Z = success
fail:
rts
set_make:
; x must be set to scancode
lda #$fd
bra set
set_make_release:
; x must be set to scancode
lda #$fc
set:
ldy #0
jsr ps2kb::send_command
ps2kb_WaitFinishCmd
lda ps2kb::cmd_response+1
cmp #ps2kb::ACK
bne fail
rts
.endproc
;;********************************************************************************
;; @function Process a scancode
;; @details
;; Update modifier bits if a mod key is pressed.
;; If the key is not a mod key, the keycode is stored in kb::keycode
;; If the key is an ascii character, the corresponding ascii character is stored in kb::char
;; @modifies A, X
;;********************************************************************************
.proc process_scancode
; check how this scancode needs to be interpreted
ldx ps2kb::scancode
bit previous_scancode
jmi break_key ; bit 7 set = BREAK
cmp #$90 ; only BREAK is higher than 8F, (also code table only has 8F bytes)
bcs check_release
cmp #$60 ; codes >= 60 in separate table to reduce memory consumption
bcs upper_range
bbs kb::MOD::SHIFT, modifier, modshift ; if shift active
bbs kb::MOD::RALT, modifier, modralt ; if ralt active
; load from the correct table, if 0 => non-char, if 1 => non-char but mod, else char
nomod:
lda CHARS_NOMOD,x
bra check_char
upper_range:
bbs kb::MOD::NUMLOCK, modifier, modnumlock ; if numlock active
; numlock off
lda CHARS_NUMLOCK_OFF,x
bra check_char
modnumlock:
lda CHARS_NUMLOCK_ON,x
bra check_char
modshift:
lda CHARS_MODSHIFT,x
bra check_char
modralt:
lda CHARS_MODRALT,x
check_char:
beq not_mod ; 0 => not mod and not char
cmp #1 ; 1 => mod
beq is_mod
@char: ; store char
sta char
not_mod: ; store keycode if not mod
stx keycode
_rts:
rts
check_release:
cmp #kb::K_BREAK
bne _rts
lda #$ff
sta previous_scancode
rts
is_mod:
; check which mod
cpx #kb::K::LEFTSHIFT
beq set_shift
cpx #kb::K::RIGHTSHIFT
beq set_shift
cpx #kb::K::RIGHTALT
beq set_rightalt
cpx #kb::K::LEFTALT
beq set_leftalt
cpx #kb::K::RIGHTMETA
beq set_meta
cpx #kb::K::LEFTMETA
beq set_meta
cpx #kb::K::RIGHTCTRL
beq set_ctrl
cpx #kb::K::LEFTCTRL
beq set_ctrl
cpx #kb::K::CAPSLOCK
beq toggle_capslock
cpx #kb::K::NUMLOCK
beq toggle_numlock
cpx #kb::K::SCROLLLOCK
beq toggle_scrolllock
; a real keycode and not a released keycode, twobyte or release scancode
set_ctrl:
smb kb::MOD::CTRL, modifier
rts
set_meta:
smb kb::MOD::META, modifier
rts
set_leftalt:
smb kb::MOD::LALT, modifier
rts
set_rightalt:
smb kb::MOD::RALT, modifier
rts
set_shift: ; process shift
bbs kb::MOD::CAPSLOCK, modifier, @unset_shift ; CAPSLOCK unset shift when capslock is on
@set_shift:
smb kb::MOD::SHIFT, modifier
rts
@unset_shift:
rmb kb::MOD::SHIFT, modifier
rts
toggle_numlock: ; toggle numlock
lda modifier
eor #kb::MOD::NUMLOCK
sta modifier
bra update_leds
toggle_scrolllock: ; toggle numlock
lda modifier
eor #kb::MOD::SCROLLLOCK
sta modifier
bra update_leds
toggle_capslock: ; toggle capslock & shift
lda modifier
eor #(kb::MOD::CAPSLOCK | kb::MOD::SHIFT)
sta modifier
; fallthrough
load_update_leds:
lda modifier
update_leds:
and #%00000111 ; only bottom 3 bits allowed - otherwise command fails
tax
ps2kb_CmdSetLeds
rts
.endproc
;;********************************************************************************
;; @details
;; Unset modifier bits if a mod key is released.
;; @param X: The scancode
;; @modifies:
;;********************************************************************************
.proc break_key
; rmb kb::PREVIOUS::BREAK, kb::previous_scancode
stz previous_scancode
; check mod keys
cpx #kb::K::LEFTSHIFT
beq unset_shift
cpx #kb::K::RIGHTSHIFT
beq unset_shift
cpx #kb::K::RIGHTALT
beq unset_rightalt
cpx #kb::K::LEFTALT
beq unset_leftalt
cpx #kb::K::RIGHTMETA
beq unset_meta
cpx #kb::K::LEFTMETA
beq unset_meta
cpx #kb::K::RIGHTCTRL
beq unset_ctrl
cpx #kb::K::LEFTCTRL
rts
unset_shift: ; process shift
bbs kb::MOD::CAPSLOCK, modifier, @set_shift ; set shift when capslock is on
rmb kb::MOD::SHIFT, modifier ; unset if capslock is off
rts
@set_shift:
smb kb::MOD::SHIFT, modifier
rts
unset_ctrl:
rmb kb::MOD::CTRL, modifier
rts
unset_meta:
rmb kb::MOD::META, modifier
rts
unset_leftalt:
rmb kb::MOD::LALT, modifier
rts
unset_rightalt:
rmb kb::MOD::RALT, modifier
rts
.endproc
.rodata
.align 256
CHARS_NOMOD:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00"
.byte "\x00\x01\x01<\x01q1\x00\x00\x01ysaw2\x00"
.byte "\x00cxde43\x00\x00 vftr5\x00"
.byte "\x00nbhgz6\x00\x00\x01mju78\x00"
.byte "\x00,kio09\x00\x00.-l\xEFp\xE2\x00"
.byte "\x00\x00\xE1#\xF5`\x00\x00\x01\x01\x00\x00\x00\x00\x00\x01"
CHARS_MODSHIFT:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDF\x00"
.byte "\x00\x01\x01>\x01Q!\x00\x00\x01YSAW\"\x00"
.byte "\x00CXDE$\xED\x00\x00 VFTR%\x00"
.byte "\x00NBHGZ&\x00\x00\x01MJU/(\x00"
.byte "\x00;KIO=)\x00\x00:_L\xEFP\xE2\x00"
.byte "\x00\x00\xE1'\xF5`\x00\x00\x01\x01\x00\x00\x00\x00\x00\x01"
CHARS_MODRALT:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00"
.byte "\x00\x01\x01|\x01\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00{[\x00"
.byte "\x00\x00\x00\x00\x00}]\x00\x00\xA5\xB0\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x01"
CHARS_NUMLOCK_OFF:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x01/\x00\x00\x00\x00+\x00*\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00-\x00"
CHARS_NUMLOCK_ON:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x001\x0047\x00\x00\x00"
.byte "0.2568\x01\x00\x00\x003\x00\x009\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00"

View File

@ -1,135 +0,0 @@
.include "system.h65"
.include "string.h65"
.include "keypad.h65"
.include "lcd.h65"
.include "ps2_keyboard_simple_handler.h65"
.include "ps2_keyboard_text_handler.h65"
.export ps2_keyboard_util
.import home:absolute
.code
.proc ps2_keyboard_util
stz kp::_DEBUG_VAL
jsr skb::init
beq print_menu
ps2kb_PrintCmdFailed
bra loop
print_menu:
Print MENU
loop:
wai
lda skb::scancode
beq @read_keypad
Printf FMT_CODE,skb::scancode
stz skb::scancode
@read_keypad:
lda kp::_DEBUG_VAL
jeq loop
stz kp::_DEBUG_VAL
cmp #'*'
jeq home
pha
jsr lcd::clear
pla
ldy #0 ; arg for commands
ldx #ps2kb::NO_DATA
cmp #'0'
beq @l0
cmp #'1'
beq @l1
cmp #'2'
beq @l2
cmp #'3'
beq @l3
cmp #'4'
beq @l4
cmp #'5'
beq @l5
cmp #'6'
beq @l6
cmp #'7'
beq @l7
cmp #'8'
beq @l8
cmp #'9'
beq @l9
cmp #'#'
beq @lhash
cmp #'A'
beq @lA
cmp #'B'
beq @lB
cmp #'C'
beq @lC
cmp #'D'
beq @lD
jsr lcd::print_char
jmp loop
@l0: ; get scancode set
lda #$f0
ldx #0
ldy #1
bra @send_cmd
@l1: ; echo
lda #$ee
bra @send_cmd
@l2: ; identify
lda #$f2
ldy #2
bra @send_cmd
@l3: ; set typematic min speed and delay
lda #$f3
ldx #%01111111
bra @send_cmd
@l4: ; enable
lda #$f4
bra @send_cmd
@l5: ; disable
lda #$f5
bra @send_cmd
@l6: ; set typematic max speed and min delay
lda #$f3
ldx #0
bra @send_cmd
@l7: ; set typematic all
lda #$f7
bra @send_cmd
@l8: ; set make/release all
lda #$f8
bra @send_cmd
@l9: ; set CAPSLOCK
lda #$ed
ldx #kb::MOD::CAPSLOCK
bra @send_cmd
@lhash: ; set SCROLLLOCK led
lda #$ed
ldx #kb::MOD::SCROLLLOCK
bra @send_cmd
@lC: ; set NUMLOCK led
lda #$ed
ldx #kb::MOD::NUMLOCK
bra @send_cmd
@lD: ; turn all leds off
lda #$ed
ldx #0
bra @send_cmd
@lA:
; TODO
@lB: ; reprint menu
jmp print_menu
@send_cmd:
jsr ps2kb::send_command
ps2kb_WaitFinishCmd
Printf FMT_CMD_DATA, ps2kb::send_cmd, ps2kb::send_data, ps2kb::cmd_response, ps2kb::cmd_response+1, ps2kb::cmd_response+2
jmp loop
.endproc
.rodata
FMT_CODE: .asciiz "K %x "
FMT_CMD_DATA: .asciiz "C %x-%x > %x%x%x"
MENU: .byte " EE F2 F3 "
.byte " F4 F5 F3' MEN"
.byte " T1 T0 LC LN "
.asciiz " RET F0 LS L0 "

View File

@ -1,5 +1,5 @@
# 8-bit Breadboard Computer with W65C02S Processor # 8-bit Breadboard Computer with W65C02S Processor
This repo contains the assembly code for my [6502-project](https://quintern.xyz/de/posts/2021_6502). This repo contains the assembly code for my [6502-project](https://quintern.xyz/de/6502.html).
The assembler used for this project is the excellent [ca65](https://github.com/cc65/cc65). The assembler used for this project is the excellent [ca65](https://github.com/cc65/cc65).
After assembling it, the binary is loaded onto the EEPROM using [my *eeprom.py* script](https://git.quintern.xyz/MatthiasQuintern/AT28C256-rpi-util) on a Raspberry Pi 4B. After assembling it, the binary is loaded onto the EEPROM using [my *eeprom.py* script](https://git.quintern.xyz/MatthiasQuintern/AT28C256-rpi-util) on a Raspberry Pi 4B.

View File

@ -3,14 +3,12 @@
.include "lcd.h65" .include "lcd.h65"
.include "math.h65" .include "math.h65"
.include "keypad.h65" .include "keypad.h65"
.include "ps2_keyboard_text_handler.h65" .include "keyboard_handler.h65"
.include "chars.h65" .include "chars.h65"
.include "parity.h65" .include "parity.h65"
.include "sleep.h65" .include "sleep.h65"
.import homeloop:absolute .import homeloop:absolute
.import home:absolute .import home:absolute
.import ps2_keyboard_util:absolute
.import ps2_keyboard_printer:absolute
.segment "SPI" .segment "SPI"
.export CODE_START .export CODE_START
@ -22,43 +20,88 @@
CODE_START: CODE_START:
.assert * = $5000, error, "SPI Code not at $5000" .assert * = $5000, error, "SPI Code not at $5000"
jsr lcd::clear jsr lcd::clear
lda #'$'
jsr lcd::print_char
; jmp homeloop
DEBUG_LED_OFF 0 DEBUG_LED_OFF 0
DEBUG_LED_OFF 1 DEBUG_LED_OFF 1
DEBUG_LED_OFF 2 DEBUG_LED_OFF 2
stz kp::_DEBUG_VAL StoreDByte _process_cmd_answer, $3200
lda #'$'
jsr ps2kb::init
stz kb::status
StoreDByte process_scancode,ps2kb::scancode_handler
; jsr ps2kb::begin_receive
JsrIndirect @a1, @a2
stp
stp
stp
stp
stp
@a1:
lda #'1'
jsr lcd::print_char jsr lcd::print_char
rts
stp
stp
stp
stp
@a2:
lda #'2'
jsr lcd::print_char
lda #'%'
jsr lcd::print_char
stz kp::_DEBUG_VAL
ldy #0
@loop: @loop:
wai wai
lda ps2kb::scancode
beq @noscancode
jsr process_scancode
stz ps2kb::scancode
lda char
beq @onlykeycode
Strf fmt_codechar,out_str,keycode,kb::status,char
PrintNC out_str
stz char
stz keycode
bra @noscancode
@onlykeycode:
lda keycode
beq @noscancode
Strf fmt_code,out_str,keycode,keycode_flags
PrintNC out_str
stz keycode
@noscancode:
bbr5 kb::status, @shift_off
@shift_on:
; DEBUG_LED_ON 0
bra @check_caps
@shift_off:
; DEBUG_LED_OFF 0
@check_caps:
bbr4 kb::status, @caps_off
@caps_on:
; DEBUG_LED_ON 2
bra @read_keypad
@caps_off:
; DEBUG_LED_OFF 2
@read_keypad: @read_keypad:
lda kp::_DEBUG_VAL lda kp::_DEBUG_VAL
jeq @loop jeq @loop
stz kp::_DEBUG_VAL
cmp #'*' cmp #'*'
jeq homeloop jeq homeloop
cmp #'0'
beq @l0
cmp #'1' cmp #'1'
beq @l1 beq @l1
cmp #'2' cmp #'2'
beq @l2 beq @l2
cmp #'3' cmp #'3'
beq @l3 beq @l3
cmp #'4'
beq @l4
cmp #'5'
beq @l5
cmp #'6'
beq @l6
cmp #'7'
beq @l7
cmp #'8'
beq @l8
cmp #'9'
beq @l9
cmp #'#'
beq @lhash
cmp #'A' cmp #'A'
beq @lA beq @lA
cmp #'B' cmp #'B'
@ -69,61 +112,378 @@ CODE_START:
beq @lD beq @lD
jsr lcd::print_char jsr lcd::print_char
jmp @loop jmp @loop
@l0:
jmp ps2_keyboard_util
@l1: @l1:
jmp _ps2_keyboard_printer ; jsr irq_on_shift_reg
jsr $3000
lda #'*'
jsr lcd::print_char
jmp @loop
@l2: @l2:
; jsr irq_on_timer
jsr $3100
lda #'#'
jsr lcd::print_char
jmp @loop
@l3: @l3:
@l4: lda $3000,y
@l5: jsr lcd::print_char
@l6: iny
@l7: jmp @loop
@l8:
@l9:
@lhash:
@lA: @lA:
jsr ps2kb::begin_receive
; StoreDByte _receive_irq_timer_handler, $3100
jmp @loop
@lB: @lB:
; Printf "kc%x;", out_str, kb::scancode
jsr lcd::clear
ldx out_idx
lda #0
sta out_str,x
Print out_str
stz out_idx
jmp @loop
@lC: @lC:
jsr lcd::clear
jmp @loop
@lD: @lD:
lda ps2kb::STATUS::SEND_CMD
sta ps2kb::status
; lda #$ed
; ldx #%00000111
; lda #$f6
; lda #$ff
lda #$ee
; ldx #$ee
; ldx #0
ldx #ps2kb::NO_DATA
sta ps2kb::send_cmd
stx ps2kb::send_data
jsr _send_byte
; jsr ps2kb::send_command;
jmp @loop jmp @loop
.proc _ps2_keyboard_printer .byte '@'
jsr lcd::clear
jsr kb::init
jeq loop
ps2kb_PrintCmdFailed
; jmp homeloop
ps2kb_CmdEnable
jsr ps2kb::begin_receive
loop: .proc process_scancode
lda kb::char ; DEBUG_LED_OFF 1
beq @no_char ; check how this scancode needs to be interpreted
stz kb::char ldx ps2kb::scancode
bit kb::status
jmi release_key ; bit 7 set
bvs @twobytecode ; bit 6 set
; DEBUG_LED_ON 1
; check mods
cpx #kb::K_RELEASE
beq @code_release
cpx #kb::K::LEFTSHIFT
beq @code_shift
cpx #kb::K::RIGHTSHIFT
beq @code_shift
cpx #kb::K_TWOBYTE
beq @code_twobytecode
cpx #kb::K::CAPSLOCK
beq @code_capslock
; a real keycode and not a released keycode, twobyte or release scancode
stz keycode_flags
stx keycode
cmp #$62 ; $61 is the largest ascii scancode
bcs @special
bbs5 kb::status,@modshift ; if shift active
@nomod:
lda kb::CHARS_NOMOD,x
beq @special
@char: ; order which is fastet most of the time (lowercase characters)
sta char
rts
@modshift:
lda kb::CHARS_MODSHIFT,x
bne @char
@special:
rts
@code_release: ; set release
smb7 kb::status
rts
@code_twobytecode: ; set twobytecode
smb6 kb::status
rts
@code_shift: ; process shift
bbs4 kb::status, @unset_shift ; unset shift when capslock is on
@set_shift:
smb5 kb::status
rts
@unset_shift:
rmb5 kb::status
rts
@code_capslock: ; toggle capslock & shift
bbs1 kb::status, @rts ; ignore if held
lda kb::status
ora #(kb::STATUS::CAPSLOCK_HELD)
; toggle and capslock shift
eor #(kb::STATUS::CAPSLOCK | kb::STATUS::SHIFT)
sta kb::status
rts
@code_rightalt: ; set rightalt
smb3 kb::status
rts
@code_numlock: ; set numlock
smb2 kb::status
rts
@twobytecode:
rmb6 kb::status ; unset twobytecode
; check mods
cpx #kb::K2::RIGHTALT
beq @code_rightalt
lda #kb::STATUS::TWOBYTE
sta keycode_flags
stx keycode
@rts:
rts
.endproc
;; @param S: The status register must be set through `bit kb::status`
;; @param X: The scancode
.proc release_key
bvs @twobytecode
rmb7 kb::status
; check mods
cpx #kb::K_TWOBYTE
beq @code_twobytecode
cpx #kb::K::LEFTSHIFT
beq @code_shift
cpx #kb::K::RIGHTSHIFT
beq @code_shift
cpx #kb::K::CAPSLOCK
beq @code_capslock
; no need to process capslock - it gets toggled when pressed
rts
@code_twobytecode: ; unset special
rmb6 kb::status
rts
@code_shift: ; process shift
bbs4 kb::status, @code_shift_caps ; set shift when capslock is on
rmb5 kb::status ; unset if capslock is off
rts
@code_shift_caps:
smb5 kb::status
rts
@code_capslock: ; unset CAPSLOCK_HELD
rmb1 kb::status
rts
@code_rightalt: ; unset rightalt
rmb3 kb::status
rts
@code_numlock: ; unset numlock
rmb2 kb::status
rts
@twobytecode:
rmb6 kb::status ; unset twobytecode
; check mods
cpx #kb::K2::RIGHTALT
beq @code_rightalt
rts
.endproc
;;********************************************************************************
;; @macro Enable the clock signal from the keyboard
;; @modifies: A
;; @details
;; Stop pulling the keyboards clock low
;;********************************************************************************
.macro _EnableClock
; set pin to input
lda ps2kb::VIA + ps2kb::PULL_DDR
and #<~ps2kb::PULL_MASK_CLK
sta ps2kb::VIA + ps2kb::PULL_DDR
.endmacro
;;********************************************************************************
;; @macro Disable the clock signal from the keyboard
;; @modifies: A
;; @details
;; Pulls the keyboards clock low
;;********************************************************************************
.macro _DisableClock
; set pin to output
lda ps2kb::VIA + ps2kb::PULL_DDR
ora #ps2kb::PULL_MASK_CLK
sta ps2kb::VIA + ps2kb::PULL_DDR
; set pin low
lda ps2kb::VIA + ps2kb::PULL_REG
and #<~ps2kb::PULL_MASK_CLK
sta ps2kb::VIA + ps2kb::PULL_REG
.endmacro
.proc _send_byte
pha
IO_DisableIRQ ps2kb::VIA, (IO::IRQ::T2 | IO::IRQ::SR)
_DisableClock
lda #'X'
jsr lcd::print_char
; set shift register to output
lda ps2kb::VIA + IO::ACR
and #<~IO::ACR_MASK::SR
ora #IO::ACR::SR_SOUT_PHIE
sta ps2kb::VIA + IO::ACR
stz ps2kb::VIA + IO::SR
; shift out the startbit = data low
_EnableClock
_DisableClock
; reset shift register to start the count at 0 again
lda #IO::ACR::SR_SOUT_PHIE
trb ps2kb::VIA + IO::ACR
tsb ps2kb::VIA + IO::ACR
; setup the low timer byte
lda #<ps2kb::TIMER_SEND
sta ps2kb::VIA + IO::T2CL
; setup interrupt handlers
StoreDByte ps2kb::_send_irq_shift_reg_handler, $3000
; StoreDByte _send_irq_cb2_handler, $3020
; StoreDByte _send_irq_cb1_handler, $3040
StoreDByte _send_irq_timer_handler, $3060
pla
Reverse A
pha
CalculateOddParity
ror ; Parity -> C
lda #$ff
ror ; Parity -> bit 7, rest = 1
sta ps2kb::send_last_bits ; loaded into SR by irq handler
pla
sta ps2kb::VIA + IO::SR
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
_EnableClock
rts
.endproc
; .proc _send_irq_shift_reg_handler
; lda ps2kb::send_last_bits
; sta ps2kb::VIA + IO::SR
; IO_DisableIRQ ps2kb::VIA, IO::IRQ::SR
; IO_EnableIRQ ps2kb::VIA, IO::IRQ::T2
; ; start timer, low order count already in latch after init
; lda #>ps2kb::TIMER_SEND
; sta ps2kb::VIA + IO::T2CH
; rts
; .endproc
.proc _send_irq_timer_handler
lda ps2kb::VIA + IO::T2CL ; clear interrupt flag
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
; disable shift register
lda ps2kb::VIA + IO::ACR
and #<~IO::ACR_MASK::SR
sta ps2kb::VIA + IO::ACR
bra _send_done
; lda ps2kb::VIA + IO::IFR
; jsr str::uint8_to_hex_str
; jsr lcd::print_char
; txa
; jsr lcd::print_char
rts
.endproc
; @details
; Set the SEND_CMD_WAIT status bit the first time
; and jump to _send_done the second time it is called
; .proc _send_irq_cb1_handler
; ; clear the flag and disable interrupts
; ; DEBUG_LED_ON 1
; lda #'i'
; jsr lcd::print_char
; stp
; lda #IO::IRQ::CB1
; sta ps2kb::VIA + IO::IFR
; sta ps2kb::VIA + IO::IER
; ; DEBUG_LED_OFF 1
; bra _check_done
; .endproc
; .proc _send_irq_cb2_handler
; ; DEBUG_LED_ON 0
; ; lda #'I'
; ; jsr lcd::print_char
; lda #IO::IRQ::CB2
; sta ps2kb::VIA + IO::IFR
; sta ps2kb::VIA + IO::IER
; ; DEBUG_LED_OFF 0
; bra _send_done
; bra _check_done
; .endproc
; .proc _check_done
; ; check if done, by checking if SEND_CMD_WAIT bit is set
; lda #ps2kb::STATUS::SEND_CMD_WAIT
; tsb ps2kb::status
; bne _send_done ; SEND_CMD_WAIT was already set
; rts
; .endproc
.proc _send_done
_DisableClock ; disable keyboard while setting up receive
lda #'D'
jsr lcd::print_char jsr lcd::print_char
bra loop ; Sleep 1
@no_char: lda ps2kb::status
lda kb::keycode and #ps2kb::STATUS::SEND_CMD
beq loop beq @status_send_data ; data byte already sent
stz kb::keycode @status_send_cmd:
cmp #kb::K::ESCAPE lda #ps2kb::STATUS::SEND_DATA
beq @esacpe lda ps2kb::status
cmp #kb::K::PRINT ; check if a data byte needs to be sent
beq @clear_display lda ps2kb::send_data
cmp #kb::K::F12 beq @send_data ; if 0
jeq _ps2_keyboard_printer cmp #ps2kb::NO_DATA
bra loop beq @receive_answer ; if not NO_DATA
@esacpe: @send_data:
jmp home jmp _send_byte ; send the data
@clear_display: @status_send_data:
jsr lcd::clear @receive_answer:
bra loop _DisableClock ; disable keyboard while setting up receive
DEBUG_LED_ON 0
lda #ps2kb::STATUS::RECEIVE_ANSWER
sta ps2kb::status
jmp ps2kb::begin_receive
.endproc .endproc
.proc _process_cmd_answer
Strf fmt_answer, out_str, ps2kb::send_cmd, ps2kb::send_data, ps2kb::scancode
PrintNC out_str
DEBUG_LED_ON 1
stz ps2kb::scancode
lda ps2kb::prev_status
sta ps2kb::status
; set pin to input
; lda ps2kb::VIA + ps2kb::CLK_PULL_DDR
; and #<~ps2kb::CLK_PULL_MASK
; sta ps2kb::VIA + ps2kb::CLK_PULL_DDR
rts
.endproc
.byte '@'
out_str: .res 40 out_idx: .res 1
FMT_INIT_FAIL: .asciiz "KB init failed: %x%x > %x%x%x" out_str: .res 40
char: .res 1
keycode: .res 1
keycode_flags: .res 1
fmt_codechar: .asciiz "%x %x %c "
fmt_code: .asciiz "%x %x "
fmt_answer: .asciiz "%x-%x>%x"

141
system/keyboard_handler.h65 Normal file
View File

@ -0,0 +1,141 @@
;;********************************************************************************
;; @module keyboard_handler
;; @type system
;; @details:
;; This module processes keyboard scancodes from the ps2_keyboard module.
;;********************************************************************************
.ifndef INCLUDE_KEYBOARD
INCLUDE_KEYBOARD = 1
.include "ps2_keyboard.h65"
.scope kb
Import kb, CHARS_NOMOD, CHARS_MODSHIFT
ImportZp kb,status
.enum STATUS
RELEASE = %10000000
TWOBYTE = %01000000
SHIFT = %00100000
CAPSLOCK = %00010000
CAPSLOCK_HELD = %00000010
RIGHTALT = %00001000
NUMLOCK = %00000100
.endenum
K_TWOBYTE = $E0
K_RELEASE = $F0
.enum K
F9 = $01
F5 = $03
F3 = $04
F1 = $05
F2 = $06
F12 = $07
F10 = $09
F8 = $0A
F6 = $0B
F4 = $0C
TAB = $0D
GRAVE = $0E
LEFTALT = $11
LEFTSHIFT = $12
LEFTCTRL = $14
K_Q = $15
K_1 = $16
K_Y = $1A
K_S = $1B
K_A = $1C
K_W = $1D
K_2 = $1E
K_C = $21
K_X = $22
K_D = $23
K_E = $24
K_4 = $25
K_3 = $26
SPACE = $29
K_V = $2A
K_F = $2B
K_T = $2C
K_R = $2D
K_5 = $2E
K_N = $31
K_B = $32
K_H = $33
K_G = $34
K_Z = $35
K_6 = $36
K_M = $3A
K_J = $3B
K_U = $3C
K_7 = $3D
K_8 = $3E
COMMA = $41
K_K = $42
K_I = $43
K_O = $44
K_0 = $45
K_9 = $46
DOT = $49
DASH = $4A
K_L = $4B
OUML = $4C
K_P = $4D
SSHARP = $4E
AUML = $52
UUML = $54
BACKTICK = $55
CAPSLOCK = $58
RIGHTSHIFT = $59
ENTER = $5A
PLUS = $5B
HASH = $5D
LESSTHAN = $61
BACKSPACE = $66
KP1 = $69
KP4 = $6B
KP7 = $6C
KP0 = $70
KPDOT = $71
KP2 = $72
KP5 = $73
KP6 = $74
KP8 = $75
ESCAPE = $76
NUMLOCK = $77
F11 = $78
KPPLUS = $79
KP3 = $7A
KPMINUS = $7B
KPASTERISK = $7C
KP9 = $7D
SCROLLLOCK = $7E
F7 = $83
.endenum
.enum K2
RIGHTALT = $11
RIGHTCTRL = $14
LEFTMETA = $1F
RIGHTMETA = $27
MENU = $2F
BREAK = $37
SYSRQ = $3F
KPDIVIDE = $4A
KPENTER = $5A
END = $69
LEFT = $6B
POS1 = $6C
INSERT = $70
DELETE = $71
DOWN = $72
RIGHT = $74
UP = $75
PAGEDOWN = $7A
PAGEUP = $7D
.endenum
.endscope
.endif ; guard

View File

@ -0,0 +1,28 @@
.include "keyboard_handler.h65"
Export kb, CHARS_NOMOD, CHARS_MODSHIFT
ExportZp kb,status
.zeropage
status: .res 1
.rodata
.align 256
CHARS_NOMOD:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00"
.byte "\x00\x00\x00\x00\x00q1\x00\x00\x00ysaw2\x00"
.byte "\x00cxde43\x00\x00 vftr5\x00"
.byte "\x00nbhgz6\x00\x00\x00mju78\x00"
.byte "\x00,kio09\x00\x00.-l\xEFp\xE2\x00"
.byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00#\x00\x00"
.byte "\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.align 256
CHARS_MODSHIFT:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDF\x00"
.byte "\x00\x00\x00\x00\x00Q!\x00\x00\x00YSAW\"\x00"
.byte "\x00CXDE$\xED\x00\x00 VFTR%\x00"
.byte "\x00NBHGZ&\x00\x00\x00MJU/(\x00"
.byte "\x00;KIO=)\x00\x00:_L\xEFP\xE2\x00"
.byte "\x00\x00\xE1\x00\xF5`\x00\x00\x00\x00\x00\x00\x00'\x00\x00"
.byte "\x00>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

View File

@ -293,20 +293,18 @@ scancode_handler: .res 2 ; pointer to a function that handles new scancod
_DisableClock _DisableClock
; set shift register to output ; set shift register to output
; shift out the startbit = data low
; set SR to shift out with PHI2 and shift out zeros
lda ps2kb::VIA + IO::ACR lda ps2kb::VIA + IO::ACR
and #<~IO::ACR_MASK::SR and #<~IO::ACR_MASK::SR
ora #IO::ACR::SR_SOUT_PHI2
sta ps2kb::VIA + IO::ACR
stz ps2kb::VIA + IO::SR
; reset shift register to start the count at 0 again
and #<~IO::ACR_MASK::SR
sta ps2kb::VIA + IO::ACR
; set SR to shift out with external clock
ora #IO::ACR::SR_SOUT_PHIE ora #IO::ACR::SR_SOUT_PHIE
sta ps2kb::VIA + IO::ACR sta ps2kb::VIA + IO::ACR
stz ps2kb::VIA + IO::SR
; shift out the startbit = data low
_EnableClock
_DisableClock
; reset shift register to start the count at 0 again
lda #IO::ACR::SR_SOUT_PHIE
trb ps2kb::VIA + IO::ACR
tsb ps2kb::VIA + IO::ACR
; setup the low timer byte ; setup the low timer byte
lda #<ps2kb::TIMER_SEND lda #<ps2kb::TIMER_SEND