viewtopic.php?t=6429&start=20
EDIT: corrected the missing RTS in the ISR of the original post and updated the PRG.
The program here is adapted to C (using cc65) as an example of installing the "multi-key test" ISR, and then visualizing the results in an interesting way (mainly by converting the IBM keynumbers back into a PETSCII representation). That ISR enables a way to test for multiple-keys being pressed at the same time (but through PS/2 and SMC, there is an inherent limit to how many keys that can be identified as being pressed; and the combinations allowed isn't always consistent).
Can run this in either 40 (SCREEN 3, or SCREEN 8 or 9 are good) or 80 col (SCREEN 1 or 0). There is no "graceful exit" so you will have to CTRL+ALT+DEL to reset the system. For "non-printable characters" (like ESC, TAB, ENTER, etc) I just show the "@" symbol in that corresponding keys offset.
After LOAD and RUN it, just start pressing keys to see which combinations your setup supports. Typically it is about 3-4 keys, the most I've ever found was 8.
Here is the full C source, which doesn't require any #include's:
Code: Select all
// V* JUNE 2024
// **** UTILITY MACROS ****
#define POKE(addr,val) (*(unsigned char*) (addr) = (val))
#define PEEK(addr) (*(unsigned char*) (addr))
#define CLRSCR \
__asm__("lda #$93"); \
__asm__("jsr $ffd2");
#define TRUE 1
// **** SCREEN WRITE UTILITIES ****
static unsigned char cx16_screen_row_offset[] = {
0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,
0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0xC1,0xC2,0xC3,
0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,
0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,
0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,
0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB
};
#define WRITE_CHAR(x,y,ch) \
POKE(0x9F20, x << 1); \
POKE(0x9F21, cx16_screen_row_offset[y] ); \
POKE(0x9F22, 0x21); \
POKE(0x9F23, ch);
// **** "IBM Keynumber" to PETSCII "screen code" table ****
// https://github.com/X16Community/x16-rom/blob/master/inc/keycode.inc
static unsigned char ibm_key[] = {
'\0', // 0x00 not a keycode (placeholder for "no key")
'\0', // '`', // BACKTICK
49,50,51,52,53,54,55,56,57,48, // '1','2','3','4','5','6','7','8','9','0',
45,61, // '-','=',\
'\0', // 0x0E UNDEFINED
'\0', // 0x0F BACKSPACE
'\0', // 0x10 TAB
17,23,5,18,20,25,21,9,15,16, // 'Q','W','E','R','T','Y','U','I','O','P',
27,29, // '[',']',
'\0', // '\\', BACKSLASH
'\0', // CAPSLOCK
1, 19, 4, 6, 7, 8, 10, 11, 12, 59, // $1F 'A','S','D','F','G','H','J','K','L',';',
39, // '\'', // SINGLE QUOTE
'\0', // 0x2A UNDEFINED
'\0', // ENTER
'\0', // LEFT SHIFT
'\0', // NONUSLESS (NON-US LESS)
26, 24, 3, 22, 2, 14, 13, 44, 46, 47, // 'Z','X','C','V','B','N','M',',','.','/',
'\0', // INTL1
'\0', // RSHIFT
'\0', // LCTRL
'\0', // LGUI
'\0', // LALT
'\0', // SPACEBAR
'\0', // RALT
'\0', // RGUI
'\0', // RCTRL
'\0', // MENU
'\0', // $42 UNDEFINED
'\0', // $43 UNDEFINED
'\0', // $44 UNDEFINED
'\0', // $45 UNDEFINED
'\0', // $46 UNDEFINED
'\0', // $47 UNDEFINED
'\0', // $48 UNDEFINED
'\0', // $49 UNDEFINED
'\0', // $4A UNDEFINED
'\0', // $4B INSERT
'\0', // $4C DELETE
'\0', // $4D UNDEFINED
'\0', // $4E UNDEFINED
'\0', // $4F LEFTARROW
'\0', // $50 HOME
'\0', // $51 END
'\0', // $52 UNDEFINED
'\0', // $53 UPARROW
'\0', // $54 DOWNARROW
'\0', // $55 PGUP
'\0', // $56 PGDN
'\0', // $57 UNDEFINED
'\0', // $58 UNDEFINED
'\0', // $59 RIGHT ARROW
'\0', // $60 NUMLOCK
'\0', '\0', '\0', // '7','4','1', // KP7 KP=KEYPAD
'\0', // $5E UNDEFINED
'\0', // $5F KPDIV
'\0', '\0', '\0', '\0', // '8','5','2','0', // KEYPAD
42, // '\0', // KPMULT
'\0', '\0', '\0', '\0', // '9','6','3','.', // KEYPAD
'\0', '\0', // '-','+', // KEYPAD
'\0', // $6B UNDEFINED
'\0', // $6C KP ENTER
'\0', // $6D UNDEFINED
'\0', // $6E ESC
'\0', // $6F UNDEFINED
'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0', // F1..F12
'\0', // PRSCRN
'\0', // SRLCK
'\0' // PAUSEBRK
};
// keyboard handler ISR, where REG.A contains the IBM keynumber of what was pressed
// and the press vs release state (state is in the high bit)
static unsigned char the_kbd_isr[] = {
0x48, // PHX store X
0xDA, // PHA store A
0x30, 0x06, // BMI key_fs_set (check NEGATIVE flag)
// key_fs_clear:
0xAA, // TAX transfer A to X, use reg.A as-is for offset into $0600
0xA9, 0x01, // LDA let reg.A == 1, indicating key was pressed
0x4C, 0x0F, 0x05, // JMP key_store_1 (adjust this if relocated from 0x0500)
// key_fs_set:
0x29, 0x7F, // AND clear the negative mask on the key-code in reg.A
0xAA, // TAX transfer A to X, use reg.A as-is for offset into $0600
0xA9, 0x00, // LDA let reg.A == 0, indicating key was released
// key_store_1:
0x9D, 0x00, 0x06, // STA store state-value in reg.A into memory offset for this key
0xFA, // PLA restore A
0x68, // PLX restore X
0x60 // RTS
};
void isr_setup()
{
// this ISR can go at other addresses, just picked 0x0500 that is inside the "golden RAM" 1KB region
unsigned int address = 0x0500;
unsigned char n = sizeof(the_kbd_isr) / sizeof(unsigned char);
unsigned char i;
for (i = 0; i < n; ++i)
{
POKE(address, the_kbd_isr[i]);
++address;
}
}
void isr_handler_init()
{
// this part isn't 100% necessary - in a full proper program, this
// should be done so that our ISR can be removed by restoring the
// original ISR address on exit.
__asm__("lda $032E"); // load current ISR address (LO)
__asm__("sta $0400"); // save it into $0400
__asm__("lda $032F"); // load current ISR address (HI)
__asm__("sta $0401"); // save it into $0401
// $0400 is start of "golden RAM" and is just arbitrarily chosen. In general,
// we don't know (and shouldn't assume) on how many ISRs were
// already installed, and programs should try to "play nice" with each other.
// but cc65 has other issues that prevent a full graceful exit, so we need
// a C-A-D restart anyway.
__asm__("sei"); // disable interrupts
__asm__("lda #$00"); // load new user-ISR address (LO)
__asm__("sta $032E"); // store new address into ISR handler (LO)
__asm__("lda #$05"); // load new user-ISR address (HI)
__asm__("sta $032F"); // store new address into ISR handler (HI)
__asm__("cli"); // re-enable interrupts
// __asm__("rts"); // not needed, cc65 function call will take care of this
}
void main(void)
{
unsigned char x, y, max_y, v;
unsigned int i, width, temp_start;
const unsigned int start = 0x0600;
char ch;
CLRSCR;
width = 36;
// clear out the key-state buffer
for (i = start; i < (start+128); ++i) // max 128 IBM keynumber codes
{
POKE(i, 0x00);
}
isr_setup();
isr_handler_init();
y=1;
max_y = (128/width)+1;
for (x=1; x<(width+4); ++x)
{
WRITE_CHAR(x, y, 160);
WRITE_CHAR(x, y+max_y+1, 160);
}
for (y=2; y<=(2+max_y); ++y)
{
WRITE_CHAR(1, y, 160);
WRITE_CHAR(width+3, y, 160);
}
while (TRUE)
{
y = 1;
i = 0;
temp_start = start;
while (i <= (start+128)) // only have 128 IBM keynumbers
{
++y;
for (i=temp_start, x=2; i <= (temp_start+width); ++i, ++x)
{
ch = ibm_key[i-start];
v = PEEK(i);
if (v != 0) { WRITE_CHAR(x, y, ch) }
else { WRITE_CHAR(x, y, 32) }
}
temp_start += (width+1);
}
}
}
Code: Select all
SET CC65PATH=<use_your_path>\cc65-snapshot-win32
%CC65PATH%\bin\cc65 -Oi --target cx16 --include-dir %CC65PATH%\include main.c
%CC65PATH%\bin\ca65 --target cx16 main.s
%CC65PATH%\bin\ld65 --target cx16 --obj main.o --lib %CC65PATH%\lib\cx16.lib -o KEYTEST.PRG