I have been toying a little with encodings and keyboard layouts etc.
I tried to develop a generic solution (supporting multiple languages etc.) so this may be overkill for your use case...
1. Low level hardware reading routine returns two bytes (one byte keycode, one byte shift states). I do some basic remapping of the hardware key codes to ASCII. The ascii variants try to depict the symbol drawn on the keyboard. This way, the programmer using the routine does not have to remember or use special codes.
2. KeyMap can be used to convert keystrokes to actual characters.
The routine for keymap is as follows:
Code:
;Input maps
;==========
;
;Input maps convert raw input from keyboard to ascii characters.
;Different keyboard maps for different languages may be defined.
;
;Key maps support:
;
; shifts
; dead keys
;
;Input map is basically used when writing text into text editor.
;Therefore it does not have to be too fast (it will be called only when user pressed the key).
;
;Input map format
;----------------
;
;Input map defines one or more layouts.
;Layout is used based on the state of shift keys, lock keys and dead keys.
;
;shift - changes the input map when pressed (does not change when released)
;lock - permanently changes the layout, until pressed again (or some other key changes the state)
;dead - changes the state until next key pressed
;
;Up to 8 shift, lock and dead keys are supported.
;
;Common states are SHIFT, CAPS_LOCK.
;We support 4 lock keys and 4 dead keys.
LOCK_STATE = 200 ;lock state
KEY_MAP_PTR = 198;199
DEAD_KEY_MASK = %00111111
;Keyboard layout format
;----------------------
;shift_state: byte
;lock_state: byte
;number_of_ranges: byte
; first,last:byte
; chars:byte [last-first+1]
;There is an exception -> when the first code is 0, the next code defines byte, that will be xored with the key to produce the code
;Range definition
;
;<first_key>, <count>, <array>
;Key definition
;key - if key is 0, tuple from-to follows
;char - if there is tuple - array of replacements follows
; if the first byte in array is 0, one byte that is XORed with the key follows (it can even be 0, meaning the key remains same)
;Alt:
; if the key was not found, original key is returned?
MAP_KEY_START
InitKeyMap .PROC
sta KEY_MAP_PTR
stx KEY_MAP_PTR+1
rts
.ENDP
MapKey .PROC
;Purpose:
; Translate key press to character.
;Input:
; A key
; X shift state
layout = aux ;aux2
key = aux3
shift = aux4
layout_len = aux6
sta key
stx shift
mwa KEY_MAP_PTR layout
;find appropriate layout based on shift state and lock state
find_layout
ldy #1
lda (layout),y
cmp shift
bne nextl
iny
lda (layout),y
cmp LOCK_STATE
beq layout_found
nextl
jsr NextLayout
bne find_layout
;No rule for this key was found, return the original key and shift state,
;so it can be reused by caller.
no_char
lda key
ldx shift
clc
rts
NextLayout
ldy #0
lda (layout),y
beq lastl
clc
adc layout
sta layout
scc
inc layout+1
lastl rts
;We didn't find the key in the curren layout, but another layout can be chained.
key_not_found
ldy #3
lda (layout),y
beq no_char
tax
mwa KEY_MAP_PTR layout
dex
beq layout_found
@ jsr NextLayout
dex
bne @-
layout_found
ldy #0
lda (layout),y
sta layout_len
ldy #4 ;first sequence
.def :key_test
cpy layout_len ;If this is end of the layout, we didn't find the key
beq key_not_found
lda (layout),y ;range length
beq dead_key
iny
sta aux5 ;range length
lda key
sec
sbc (layout),y ;first key
iny
bcc skip_range
cmp aux5 ;range length
bcs skip_range
sta shift ;reuse
lda (layout),y ;first char
bne array_range
eor_range
iny
lda key
eor (layout),y
bcc key_found ;jump always
array_range
tya
clc
adc shift ;reuse
tay
lda (layout),y
bcc key_found
skip_range
lda (layout),y ;first char
beq skip2 ;if range starts with 0, skip 2 bytes
tya ;otherwise skip range size
clc
adc aux5
tay
bne key_test
skip2 iny
skip1 iny
bne key_test
dead_key ;0, key, lock_state
iny
lda (layout),y ;key
iny
cmp key
bne skip1 ;not the same key
lda lock_state
and #DEAD_KEY_MASK ;dead key bits are always set while lock bits are always switched
eor (layout),y
sta lock_state
lda #0
clc ;key was consumed
rts
key_found
sec
key_done
pha
lda lock_state ;clear dead key state
and #DEAD_KEY_MASK
sta lock_state
pla
rts
.ENDP
.print "Key mapping:", * - MAP_KEY_START
Input map can look like this:
Code:
;Czech keyboard for TCECHO encoding.
;TODO: Maybe we could use accute command to modify the char of keyboard.
KEYBOARD_CZECH .PROC
DEAD_CARON = 128
DEAD_ACUTE = 64
normal
.byte normal_end-*
.byte 0,0
.byte 0 ;no base
.byte 26, 'A', 0, 32 ;this will make the basic key lowercase
.byte 10, '0', $17, $2b, $05, $13, $03, $12, $1a, $19, $01, $09
.byte 1, '-', $15 ;ú
.byte 1, ';', $0a ;ů
.byte 0, '<' ,DEAD_ACUTE
.byte 0, KEY_CAPS_LOCK, $01
.byte 31, 32, 0, 0 ;other keys are used without change (otherwise they are reported as unused)
normal_end
cps_lock
.byte cps_lock_end-*
.byte 0, 1 ;CAPS LOCK
.byte 1
.byte 26, 'A', 0, 0
.byte 1, '-', $08 ;Ú
.byte 1, ';', $0b ;Ů
.byte 10, '0', $07, $2b, $06, $1d, $16, $1c, $18, $1f, $11, $0c
cps_lock_end
caron
.byte caron_end-*
.byte 0, DEAD_CARON
.byte 1
; C D E
.byte 3, 'C', $03, $04, $05
; R S T U
.byte 4, 'R', $12, $13, $14, $0a
.byte 1, 'N', $0e
.byte 1, 'Z', $1a
caron_end
shift_caron
.byte shift_caron_end-*
.byte KEY_SHIFT, DEAD_CARON
.byte 1
; C D E
.byte 3, 'C', $16, $02, $06
; R S T U
.byte 4, 'R', $1c, $1d, $1e, $0b
.byte 1, 'N', $0d
.byte 1, 'Z', $18
shift_caron_end
acute
.byte accute_end-*
.byte 0, DEAD_ACUTE
.byte 1
.byte 1, 'A', $01
.byte 1, 'E', $17
.byte 1, 'I', $09
.byte 1, 'O', $0f
.byte 1, 'U', $15
.byte 1, 'Y', $19
accute_end
shift_acute
.byte shift_accute_end-*
.byte KEY_SHIFT, DEAD_ACUTE
.byte 1
.byte 1, 'A', $11
.byte 1, 'E', $07
.byte 1, 'I', $0c
.byte 1, 'O', $10
.byte 1, 'U', $08
.byte 1, 'Y', $1f
shift_accute_end
shift
.byte shift_end - *
.byte KEY_SHIFT, 0 ;SHIFT
.byte 2
.byte 10, '0', 0, 0
.byte 0, '<' ,DEAD_CARON
shift_end
; .byte '='-'*'+1, '*'
; *+,-./01 2 3456789:;<=
; .byte '^\[_]?)!', 34, '#$%&/@(::<|'
shift_caps_lock
.byte shift_caps_lock_end - *
.byte KEY_SHIFT, 1 ;SHIFT + CAPS_LOCK
.byte 3 ;use shift as base keyboard
.byte 26, 'A', 0, 32 ;letters are small again, otherwise this is normal shift keyboard
shift_caps_lock_end
end
.byte 0
.print "Czech key map:", * - KEYBOARD_ENGLISH
.ENDP