// Simple Keyboard Input Library
// C64 Keyboard Matrix Reference - from http://codebase64.org/doku.php?id=base:reading_the_keyboard
// Keyboard Codes are %00rrrccc, where rrr is the row ID (0-7) and ccc is the column ID (0-7)
// +----+----------------------+-------------------------------------------------------------------------------------------------------+
// |    | Write                |                                Read $dc01 (C64 screen code in parenthesis):                              |
// |row:| $dc00: row bits      +------------+------------+------------+------------+------------+------------+------------+------------+
// |    |                      |   BIT 7    |   BIT 6    |   BIT 5    |   BIT 4    |   BIT 3    |   BIT 2    |   BIT 1    |   BIT 0    |
// +----+----------------------+------------+------------+------------+------------+------------+------------+------------+------------+
// |0.  | #%11111110 (254/$fe) | DOWN  ($  )|   F5  ($  )|   F3  ($  )|   F1  ($  )|   F7  ($  )| RIGHT ($  )| RETURN($  )|DELETE ($  )|
// |1.  | #%11111101 (253/$fd) |LEFT-SH($  )|   e   ($05)|   s   ($13)|   z   ($1a)|   4   ($34)|   a   ($01)|   w   ($17)|   3   ($33)|
// |2.  | #%11111011 (251/$fb) |   x   ($18)|   t   ($14)|   f   ($06)|   c   ($03)|   6   ($36)|   d   ($04)|   r   ($12)|   5   ($35)|
// |3.  | #%11110111 (247/$f7) |   v   ($16)|   u   ($15)|   h   ($08)|   b   ($02)|   8   ($38)|   g   ($07)|   y   ($19)|   7   ($37)|
// |4.  | #%11101111 (239/$ef) |   n   ($0e)|   o   ($0f)|   k   ($0b)|   m   ($0d)|   0   ($30)|   j   ($0a)|   i   ($09)|   9   ($39)|
// |5.  | #%11011111 (223/$df) |   ,   ($2c)|   @   ($00)|   :   ($3a)|   .   ($2e)|   -   ($2d)|   l   ($0c)|   p   ($10)|   +   ($2b)|
// |6.  | #%10111111 (191/$bf) |   /   ($2f)|   ^   ($1e)|   =   ($3d)|RGHT-SH($  )|  HOME ($  )|   ;   ($3b)|   *   ($2a)|   £   ($1c)|
// |7.  | #%01111111 (127/$7f) | STOP  ($  )|   q   ($11)|COMMODR($  )| SPACE ($20)|   2   ($32)|CONTROL($  )|  <-   ($1f)|   1   ($31)|
// +----+----------------------+------------+------------+------------+------------+------------+------------+------------+------------+

import "c64.kc"

// Keyboard Codes for all 63 keys.
// Keyboard Codes are %00rrrccc, where rrr is the row ID (0-7) and ccc is the column ID (0-7).
// See C64 Keyboard Matrix Reference http://codebase64.org/doku.php?id=base:reading_the_keyboard
const byte KEY_DEL          = $00;
const byte KEY_RETURN       = $01;
const byte KEY_CRSR_RIGHT   = $02;
const byte KEY_F7           = $03;
const byte KEY_F1           = $04;
const byte KEY_F3           = $05;
const byte KEY_F5           = $06;
const byte KEY_CRSR_DOWN    = $07;
const byte KEY_3            = $08;
const byte KEY_W            = $09;
const byte KEY_A            = $0a;
const byte KEY_4            = $0b;
const byte KEY_Z            = $0c;
const byte KEY_S            = $0d;
const byte KEY_E            = $0e;
const byte KEY_LSHIFT       = $0f;
const byte KEY_5            = $10;
const byte KEY_R            = $11;
const byte KEY_D            = $12;
const byte KEY_6            = $13;
const byte KEY_C            = $14;
const byte KEY_F            = $15;
const byte KEY_T            = $16;
const byte KEY_X            = $17;
const byte KEY_7            = $18;
const byte KEY_Y            = $19;
const byte KEY_G            = $1a;
const byte KEY_8            = $1b;
const byte KEY_B            = $1c;
const byte KEY_H            = $1d;
const byte KEY_U            = $1e;
const byte KEY_V            = $1f;
const byte KEY_9            = $20;
const byte KEY_I            = $21;
const byte KEY_J            = $22;
const byte KEY_0            = $23;
const byte KEY_M            = $24;
const byte KEY_K            = $25;
const byte KEY_O            = $26;
const byte KEY_N            = $27;
const byte KEY_PLUS         = $28;
const byte KEY_P            = $29;
const byte KEY_L            = $2a;
const byte KEY_MINUS        = $2b;
const byte KEY_DOT          = $2c;
const byte KEY_COLON        = $2d;
const byte KEY_AT           = $2e;
const byte KEY_COMMA        = $2f;
const byte KEY_POUND        = $30;
const byte KEY_ASTERISK     = $31;
const byte KEY_SEMICOLON    = $32;
const byte KEY_HOME         = $33;
const byte KEY_RSHIFT       = $34;
const byte KEY_EQUALS       = $35;
const byte KEY_ARROW_UP     = $36;
const byte KEY_SLASH        = $37;
const byte KEY_1            = $38;
const byte KEY_ARROW_LEFT   = $39;
const byte KEY_CTRL         = $3a;
const byte KEY_2            = $3b;
const byte KEY_SPACE        = $3c;
const byte KEY_COMMODORE    = $3d;
const byte KEY_Q            = $3e;
const byte KEY_RUNSTOP      = $3f;

// Keycodes for each screen code character from $00-$3f.
// Chars that do not have an unmodified keycode return $3f (representing RUN/STOP).
const byte[] keyboard_char_keycodes = {
    /*@*/KEY_AT,    /*a*/KEY_A,   /*b*/KEY_B,        /*c*/KEY_C,         /*d*/KEY_D,     /*e*/KEY_E,      /*f*/KEY_F,        /*g*/KEY_G,
    /*h*/KEY_H,     /*i*/KEY_I,   /*j*/KEY_J,        /*k*/KEY_K,         /*l*/KEY_L,     /*m*/KEY_M,      /*n*/KEY_N,        /*o*/KEY_O,
    /*p*/KEY_P,     /*q*/KEY_Q,   /*r*/KEY_R,        /*s*/KEY_S,         /*t*/KEY_T,     /*u*/KEY_U,      /*v*/KEY_V,        /*w*/KEY_W,
    /*x*/KEY_X,     /*y*/KEY_Y,   /*z*/KEY_Z,        /*[*/$3f,           /*£*/KEY_POUND, /*]*/$3f,        /*^*/KEY_ARROW_UP, /*<-*/KEY_ARROW_LEFT,
    /* */KEY_SPACE, /*!*/$3f,     /*"*/$3f,          /*#*/$3f,           /*$*/$3f,       /*%*/$3f,        /*&*/$3f,          /*´*/$3f,
    /*(*/$3f,       /*)*/$3f,     /***/KEY_ASTERISK, /*+*/KEY_PLUS,      /*,*/KEY_COMMA, /*-*/KEY_MINUS,  /*.*/KEY_DOT,      /*/*/KEY_SLASH,
    /*0*/KEY_0,     /*1*/KEY_1,   /*2*/KEY_2,        /*3*/KEY_3,         /*4*/KEY_4,     /*5*/KEY_5,      /*6*/KEY_6,        /*7*/KEY_7,
    /*8*/KEY_8,     /*9*/KEY_9,   /*:*/KEY_COLON,    /*;*/KEY_SEMICOLON, /*<*/$3f,       /*=*/KEY_EQUALS, /*>*/$3f,          /*?*/$3f
};

// Keyboard row bitmask as expected by CIA#1 Port A when reading a specific keyboard matrix row (rows are numbered 0-7)
byte[8] keyboard_matrix_row_bitmask = { %11111110, %11111101, %11111011, %11110111, %11101111, %11011111, %10111111, %01111111 };

// Keyboard matrix column bitmasks for a specific keybooard matrix column when reading the keyboard. (columns are numbered 0-7)
byte[8] keyboard_matrix_col_bitmask = { %00000001, %00000010, %00000100, %00001000, %00010000, %00100000, %01000000, %10000000 };

// Initialize keyboard reading by setting CIA#$ Data Direction Registers
void keyboard_init() {
    // Keyboard Matrix Columns Write Mode
    *CIA1_PORT_A_DDR = $ff;
    // Keyboard Matrix Columns Read Mode
    *CIA1_PORT_B_DDR = $00;
}
// Read a single row of the keyboard matrix
// The row ID (0-7) of the keyboard matrix row to read. See the C64 key matrix for row IDs.
// Returns the keys pressed on the row as bits according to the C64 key matrix.
// Notice: If the C64 normal interrupt is still running it will occasionally interrupt right between the read & write
// leading to erroneous readings. You must disable kill the normal interrupt or sei/cli around calls to the keyboard matrix reader.
byte keyboard_matrix_read(byte rowid) {
    *CIA1_PORT_A = keyboard_matrix_row_bitmask[rowid];
    byte row_pressed_bits = ~*CIA1_PORT_B;
    return row_pressed_bits;
}

// Determines whether a specific key is currently pressed by accessing the matrix directly
// The key is a keyboard code defined from the keyboard matrix by %00rrrccc, where rrr is the row ID (0-7) and ccc is the column ID (0-7)
// All keys exist as as KEY_XXX constants.
// Returns zero if the key is not pressed and a non-zero value if the key is currently pressed
byte keyboard_key_pressed(byte key) {
    byte colidx = key&7;
    byte rowidx = key>>3;
    return keyboard_matrix_read(rowidx) & keyboard_matrix_col_bitmask[colidx];
}

// Get the keycode corresponding to a specific screen code character
// ch is the character to get the key code for ($00-$3f)
// Returns the key code corresponding to the passed character. Only characters with a non-shifted key are handled.
// If there is no non-shifted key representing the char $3f is returned (representing RUN/STOP) .
byte keyboard_get_keycode(byte ch) {
    return keyboard_char_keycodes[ch];
}

// Keyboard event buffer. Contains keycodes for key presses/releases. Presses are represented by the keycode. Releases by keycode | $40. The buffer is filled by keyboard_scan()
byte[8] keyboard_events;
// Keyboard event buffer size. The number of events currently in the event buffer
byte keyboard_events_size = 0;
// Current keyboard modifiers (left shift, right shift, ctrl, commodore)
byte keyboard_modifiers = 0;
// Left shift is pressed
const byte KEY_MODIFIER_LSHIFT = 1;
// Right shift is pressed
const byte KEY_MODIFIER_RSHIFT = 2;
// CTRL is pressed
const byte KEY_MODIFIER_CTRL = 4;
// Commodore is pressed
const byte KEY_MODIFIER_COMMODORE = 8;
// Any shift is pressed
const byte KEY_MODIFIER_SHIFT = KEY_MODIFIER_LSHIFT|KEY_MODIFIER_RSHIFT;
// The values scanned values for each row. Set by keyboard_scan() and used by keyboard_get_event()
byte[8] keyboard_scan_values;

// Scans the entire matrix to determine which keys have been pressed/depressed.
// Generates keyboard events into the event buffer. Events can be read using keyboard_event_get().
// Handles debounce and only generates events when the status of a key changes.
// Also stores current status of modifiers in keyboard_modifiers.
void keyboard_event_scan() {
    byte keycode = 0;
    for(byte row : 0..7) {
        byte row_scan = keyboard_matrix_read(row);
        if(row_scan!=keyboard_scan_values[row]) {
            // Something has changed on the keyboard row - check each column
            for(byte col : 0..7){
                // XOR of row scan with the last seen row scan AND'ed with the col bitmask will be non-0 if the key status is changed
                if(((row_scan^keyboard_scan_values[row])&keyboard_matrix_col_bitmask[col])!=0) {
                    // Key(row, col) status has changed. We have an event.
                    // Only process event if there is still room in the buffer
                    if(keyboard_events_size!=8) {
                        // AND of row scan and bit mask determines if key is pressed or released
                        byte event_type = row_scan&keyboard_matrix_col_bitmask[col];
                        if(event_type==0) {
                            // Key released
                            keyboard_events[keyboard_events_size++] = keycode|$40;
                        } else {
                            // Key pressed
                            keyboard_events[keyboard_events_size++] = keycode;
                        }
                    }
                }
                keycode++;
            }
            // Store the current keyboard status for the row to debounce
            keyboard_scan_values[row] = row_scan;
        } else {
            // Update current keycode
            keycode = keycode + 8;
        }
    }
    // Update the keyboard modifiers
    keyboard_modifiers = 0;
    if(keyboard_event_pressed(KEY_LSHIFT)!= 0) {
        keyboard_modifiers = keyboard_modifiers | KEY_MODIFIER_LSHIFT;
    }
    if(keyboard_event_pressed(KEY_RSHIFT)!= 0) {
        keyboard_modifiers = keyboard_modifiers | KEY_MODIFIER_RSHIFT;
    }
    if(keyboard_event_pressed(KEY_CTRL)!= 0) {
        keyboard_modifiers = keyboard_modifiers | KEY_MODIFIER_CTRL;
    }
    if(keyboard_event_pressed(KEY_COMMODORE)!= 0) {
        keyboard_modifiers = keyboard_modifiers | KEY_MODIFIER_COMMODORE;
    }
}

// Determine if a specific key is currently pressed based on the last keyboard_event_scan()
// Returns 0 is not pressed and non-0 if pressed
byte keyboard_event_pressed(byte keycode) {
    byte row_bits = keyboard_scan_values[keycode>>3];
    return row_bits & keyboard_matrix_col_bitmask[keycode&7];
}

// Get the next event from the keyboard event buffer.
// Returns $ff if there is no event waiting. As all events are <$7f it is enough to examine bit 7 when determining if there is any event to process.
// The buffer is filled by keyboard_event_scan()
byte keyboard_event_get() {
    if(keyboard_events_size==0) {
        return $ff;
    } else {
        return keyboard_events[--keyboard_events_size];
    }
}

