Multi-key Test Program (cc65 C and ISR example)

Post Reply
voidstar
Posts: 488
Joined: Thu Apr 15, 2021 8:05 am

Multi-key Test Program (cc65 C and ISR example)

Post by voidstar »

As a follow up to the discussion here:
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);	  
	}
  }
}
Steps to compile:

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
Attachments
KEYTEST.PRG
(1.37 KiB) Downloaded 66 times
keytest.jpg
keytest.jpg (36.94 KiB) Viewed 1334 times
Post Reply