solitaire-c64/keyboard.c65

156 lines
4.1 KiB
Text

#IFNDEF __lib_keyboard
#DEFINE __lib_keyboard 1
GOTO __skip_lib_keyboard
// ============================================================================
// KEYBOARD INPUT DRIVER FOR COMMODORE 64
// ============================================================================
// Direct CIA keyboard matrix reading (no IRQ required)
//
// C64 Keyboard Matrix:
// $DC00 (Port A): Column select (write, active-low)
// $DC01 (Port B): Row read (read, active-low)
//
// Usage:
// #INCLUDE "keyboard.c65"
// BYTE key
// key_scan(key)
// SWITCH key
// CASE KEY_F1
// // handle F1
// CASE KEY_F3
// // handle F3
// ENDSWITCH
//
// // Debouncing: wait for KEY_NONE
// WHILE key != KEY_NONE
// key_scan(key)
// WEND
// ============================================================================
// CIA Port addresses
WORD CONST CIA_PORTA = $DC00 // Port A - ROW selection (write)
WORD CONST CIA_PORTB = $DC01 // Port B - COLUMN reading (read)
WORD CONST CIA_DDRA = $DC02 // Port A Data Direction Register
WORD CONST CIA_DDRB = $DC03 // Port B Data Direction Register
// Key constants
BYTE CONST KEY_NONE = 0
BYTE CONST KEY_F1 = 1
BYTE CONST KEY_F3 = 2
BYTE CONST KEY_F7 = 3
BYTE CONST KEY_F8 = 4 // F7 + SHIFT
BYTE CONST KEY_RETURN = 5
BYTE CONST KEY_LEFT_ARROW = 6
// ============================================================================
// FUNC key_scan
// Scan keyboard and return which key is pressed
// Returns KEY_* constant (KEY_NONE if no relevant key pressed)
// C64 Keyboard Matrix:
// - Write to $DC00 (Port A) to select ROW
// - Read from $DC01 (Port B) to detect COLUMN
// ============================================================================
FUNC key_scan(out:{BYTE key_pressed})
BYTE col_data
BYTE temp
BYTE shift_held
BYTE saved_ddra
BYTE saved_ddrb
BYTE saved_porta
BYTE row0_data
BYTE row1_data
BYTE row6_data
key_pressed = KEY_NONE
shift_held = 0
// Save current DDR state
saved_ddra = PEEK CIA_DDRA
saved_ddrb = PEEK CIA_DDRB
saved_porta = PEEK CIA_PORTA
// Set up DDR: Port A = output (rows), Port B = input (columns)
POKE CIA_DDRA , $FF
POKE CIA_DDRB , $00
// Check F8 FIRST with two-read atomic check
// Read 1: Select rows 0+1+6 simultaneously ($BC = 10111100)
POKE CIA_PORTA , $BC
BYTE multi_row_data
multi_row_data = PEEK CIA_PORTB
// Read 2: Select ONLY row 0 ($FE = 11111110)
POKE CIA_PORTA , $FE
col_data = PEEK CIA_PORTB
// F8 detection: F7 pressed AND SHIFT pressed
// F7 at Row 0, Col 3 - check in row0 data
temp = col_data & $08
IF temp == 0
// F7 is pressed, now check if real SHIFT is pressed
// Compare multi-row read vs row-0-only read for cols 4 and 7
// If col 4 or 7 was low in multi-row but NOT in row-0-only, it's SHIFT
BYTE shift_cols
shift_cols = multi_row_data & $90 // Cols 4 and 7 in multi-row read
temp = col_data & $90 // Cols 4 and 7 in row 0 only read
// If multi-row has a low bit that row-0-only doesn't, it's from rows 1 or 6 (SHIFT)
IF shift_cols != temp
key_pressed = KEY_F8
ENDIF
ENDIF
// If not F8, check other Row 0 keys (col_data already has row 0 from above)
IF key_pressed == KEY_NONE
// Check F1 (Row 0, Column 4)
temp = col_data & $10
IF temp == 0
key_pressed = KEY_F1
ENDIF
// Check F3 (Row 0, Column 5)
IF key_pressed == KEY_NONE
temp = col_data & $20
IF temp == 0
key_pressed = KEY_F3
ENDIF
ENDIF
// Check F7 (Row 0, Column 3)
IF key_pressed == KEY_NONE
temp = col_data & $08
IF temp == 0
key_pressed = KEY_F7
ENDIF
ENDIF
// Check RETURN (Row 0, Column 1)
IF key_pressed == KEY_NONE
temp = col_data & $02
IF temp == 0
key_pressed = KEY_RETURN
ENDIF
ENDIF
// Check Left Arrow (Row 0, Column 7)
IF key_pressed == KEY_NONE
temp = col_data & $80
IF temp == 0
key_pressed = KEY_LEFT_ARROW
ENDIF
ENDIF
ENDIF
// Always restore saved DDR and port state
POKE CIA_PORTA , saved_porta
POKE CIA_DDRA , saved_ddra
POKE CIA_DDRB , saved_ddrb
FEND
LABEL __skip_lib_keyboard
#IFEND