Keyboard scan ...

Get help from the community & developers with the X16 hardware if you can't find the solution elsewhere
Post Reply
User avatar
svenvandevelde
Posts: 488
Joined: Wed Dec 23, 2020 6:30 am
Location: Belgium, Antwerpen

Keyboard scan ...

Post by svenvandevelde »


Hello everyone,

 

I am in search for an easy method in assembly language to scan if a key is pressed on the X16 keyboard. I've been peering into the emulator source code and also I've been looking into the C64 best practices.

I have found out that it's not so easy to get this method answered through self research and discovery/dedection. So, I've decided to ask here the forum ...

Let's start with my main question!

What is the main chip that is connected to the keyboard and detects the keyboard presses? I see in the ROM two methods implemented!

The old C64 method and a new PS/2 keyboard method!

 

Method 1: Is it the old C64 type of keyboard driver using the CIA chipset on ports $dc00

or ...

Method 2: Is it through the new PS/2 keyboard through VIA chipset on the X16 using the via # 2 on ports $9f70, $9f71, $9f72 ...

I've tried both methods, and non of them seem to work ...

Let me explain each method that I tried to apply ... Note that the code is not optimal, it's trial and error code trying to figure our things ...

 

Method 1 explanation:

// Return true if there's a key waiting, return false if not
unsigned char kbhit(void) {
// CIA#1 Port A: keyboard matrix columns and joystick #2
char* const CIA1_PORT_A = 0xdc00;
// CIA#1 Port B: keyboard matrix rows and joystick #1.
char* const CIA1_PORT_B = 0xdc01;
*CIA1_PORT_A = 0;
return ~*CIA1_PORT_B;
}

 

 

Method 2 explanation:

This code I've taken from the X16 emulator ROM kernal code, and I've reworked it to a kickc / kickasm source code.

 This code uses the VIA#2 chip of the X16 as documented, at the addresses, and this logic should fill ps2byte with the scan...

Unfortunately, this does not seem to work and I really don't understand why not...

 

char ps2byte = 0;
while(!ps2byte) {
kickasm(uses ps2byte) {{

jsr ps2_init
jmp continue1
.var via2 =$9f70 //VIA 6522 #2
.var d2prb =via2+0
.var d2pra =via2+1
.var d2ddrb =via2+2
.var d2ddra =via2+3

.var port_ddr =d2ddrb
.var port_data =d2prb
.var bit_data=1 // 6522 IO port data bit mask (PA0/PB0)
.var bit_clk =2 // 6522 IO port clock bit mask (PA1/PB1)

// inhibit PS/2 communication on both ports
ps2_init:
ldx #1 // PA: keyboard
jsr ps2dis
dex // PB: mouse
ps2dis: lda port_ddr,x
ora #bit_clk+bit_data
sta port_ddr,x // set CLK and DATA as output
lda port_data,x
and #$ff - bit_clk // CLK=0
ora #bit_data // DATA=1
sta port_data,x
rts
continue1: nop
}}

kickasm(uses ps2byte) {{
jsr ps2_receive_byte
jmp continue2
//****************************************
// RECEIVE BYTE
// out: A: byte (0 = none)
// Z: byte available
// 0: yes
// 1: no
// C: 0: parity OK
// 1: parity error
//****************************************
ps2_receive_byte:
// set input, bus idle
lda port_ddr,x // set CLK and DATA as input
and #$ff-bit_clk-bit_data
sta port_ddr,x // -> bus is idle, keyboard can start sending

lda #bit_clk+bit_data
//ldy #10 * mhz
ldy #10 * 8
// : dey
loop: dey
beq lc08c
bit port_data,x
// bne :- // wait for CLK=0 and DATA=0 (start bit)
bne loop // wait for CLK=0 and DATA=0 (start bit)

lda #bit_clk
lc044: bit port_data,x // wait for CLK=1 (not ready)
beq lc044
ldy #9 // 9 bits including parity
lc04a: bit port_data,x
bne lc04a // wait for CLK=0 (ready)
lda port_data,x
and #bit_data
cmp #bit_data
ror ps2byte // save bit
lda #bit_clk
lc058: bit port_data,x
beq lc058 // wait for CLK=1 (not ready)
dey
bne lc04a
rol ps2byte // get parity bit into C
lc061: bit port_data,x
bne lc061 // wait for CLK=0 (ready)
lc065: bit port_data,x
beq lc065 // wait for CLK=1 (not ready)
lc069: jsr ps2dis
lda ps2byte
php // save parity
//lc07c: lsr a // calculate parity
lc07c: lsr // calculate parity
bcc lc080
iny
lc080: cmp #0
bne lc07c
tya
plp // transmitted parity
adc #1
// lsr a // C=0: parity OK
lsr // C=0: parity OK
lda ps2byte
ldy #1 // Z=0
rts

lc08c: jsr ps2dis
clc
lda #0 // Z=1
rts
continue2: nop
}}
printf( "ps2byte = %x\n", ps2byte );
}

 

Who can help here?

 

Sven

KICKC home page by Jesper Gravgaard.
My KICKC alpha with Commander X16 extensions.
User avatar
svenvandevelde
Posts: 488
Joined: Wed Dec 23, 2020 6:30 am
Location: Belgium, Antwerpen

Keyboard scan ...

Post by svenvandevelde »


I found out by looking into the cc65 assembly code of the cx16 section, that RAM bank 0 has a vector that contains the count of key presses waiting, which is stored in address $a00a.

So, the following code works:

// Return true if there's a key waiting, return false if not
unsigned char kbhit(void) {

char keys = 0;
char* keysptr = &keys;

kickasm(uses keysptr) {{

jsr _kbhit
jmp continue1
.var via1 = $9f60 //VIA#1
.var d1pra = via1+1

_kbhit:
ldy d1pra // The count of keys pressed is stored in RAM bank 0.
stz d1pra // Set d1pra to zero to access RAM bank 0.
lda $A00A // Get number of characters from this address in the ROM of the CX16 (ROM 38).
sta keys
sty d1pra // Set d1pra to previous value.
rts // ... state matters)
continue1: nop
}}

return keys;
}

Thanks to Greg King for his contribution! cc65/kbhit.s at master · cc65/cc65 (github.com)

Sven

KICKC home page by Jesper Gravgaard.
My KICKC alpha with Commander X16 extensions.
voidstar
Posts: 497
Joined: Thu Apr 15, 2021 8:05 am

Keyboard scan ...

Post by voidstar »


Any idea why the cc65 equivalent doesn't seem to be working?

 

unsigned char check_kbd()

{    

  unsigned char keys = 42;    

    

  __asm__("ldy     $9f61");  // save current RAM bank

    __asm__("stz     $9f61");  // store 0 to VIA1::PRA (RAM bank 0)

    __asm__("lda     $a00a");     // KEY_COUNT as defined in cx16.inc    

    __asm__("sta     %v", keys);      // %o didn't work

    __asm__("sty     $9f61");  // restore Y back    

    

    return keys;    

}    

 

A disassembly of the assembled PRG seems correct:    (#$2A aka 42 was just a test -- the function is return 0, so  I think that the STA is working ok -- $2cdf is just what address was decided for the local "keys")

image.png.9973dcae9ec63190f665a99fa1a012e7.png

 

 

I couldn't (yet) recreate a .o of the kbhit.s of cc65 for cx16.  I figured that code should be somewhere in the cx16.lib (if anything, the 9f61 and a00a addresses should be somewhere such that the code looks similar to the above).  But it's taking awhile to disassemble that (ran out of time for today).

 

The regular call to kbhit() is working, so I know the emulator is working ok to support this.  Was just trying to exercise the cc65 inline assembly (as it gives you a little more control on inlining, that cc65 doesn't do very well on its own).

 

 

 

 

User avatar
Yazwho
Posts: 169
Joined: Fri Feb 19, 2021 2:59 pm
Contact:

Keyboard scan ...

Post by Yazwho »



On 10/23/2022 at 11:11 AM, voidstar said:




Any idea why the cc65 equivalent doesn't seem to be working?



 



unsigned char check_kbd()

{    

  unsigned char keys = 42;    

    

  __asm__("ldy     $9f61");  // save current RAM bank

    __asm__("stz     $9f61");  // store 0 to VIA1::PRA (RAM bank 0)

    __asm__("lda     $a00a");     // KEY_COUNT as defined in cx16.inc    

    __asm__("sta     %v", keys);      // %o didn't work

    __asm__("sty     $9f61");  // restore Y back    

    

    return keys;    

}    



 



A disassembly of the assembled PRG seems correct:    (#$2A aka 42 was just a test -- the function is return 0, so  I think that the STA is working ok -- $2cdf is just what address was decided for the local "keys")



image.png.9973dcae9ec63190f665a99fa1a012e7.png



 



 



I could (yet) recreate a .o of the kbhit.s of cc65 for cx16.  I figured that code should be somewhere in the cx16.lib (if anything, the 9f61 and a00a addresses should be somewhere such that the code looks similar to the above).  But it's taking awhile to disassemble that (ran out of time for today).



 



The regular call to kbhit() is working, so I know the emulator is working ok to support this.  Was just trying to exercise the cc65 inline assembly (as it gives you a little more control on inlining, that cc65 doesn't do very well on its own).



 



 



 



 



Things have changed since then. VIA is now at $9f00, and the banking is at $00 / $01.

User avatar
kliepatsch
Posts: 247
Joined: Thu Oct 08, 2020 9:54 pm

Keyboard scan ...

Post by kliepatsch »


Was the question whether any key is being pressed or whether a certain key is being pressed? (Asking because I am interested in finding out whether a certain key is being held down).

voidstar
Posts: 497
Joined: Thu Apr 15, 2021 8:05 am

Keyboard scan ...

Post by voidstar »


My question was just how to use CC65 inline assembly to replicate the query for the depth of the keyboard buffer.

And now I see that the cc65 develop branch (with the libsrc folder) is old relative to the current release branch (that contains a prebuilt cx16.lib).   i.e. #include conio.h and calling kbhit() works with the release pre-built, but the code in the libsrc is not what is actually getting linked in.

I disassembled into what kbhit becomes in my .PRG, and now I see it's like this (the following does work, insofar as it does return the current keyboard buffer depth and matches the kbhit return):

unsigned char check_kbd()

{    

  static unsigned char keys = 42;    

    __asm__("jsr $febd");

    __asm__("txa");

    __asm__("sta     %v", keys);      // %o (local) or %v (global)

    

    return keys;    

}    

 

And, I see now why %o wasn't working for me, because I had the following pragmas in the sequence of includes... (as my test was mingled in with another project)

#pragma static-locals (on)

#pragma register-vars (on)

 

Removing those lets me use %o in the __asm__ (as far as no type mismatch is flagged), however it still doesn't actually work (i.e. "sta %o,keys" doesn't seem to actually store reg A into local var "keys").  When I use %o (and remove "static" from "keys"), it compiles and links -- but the result always stays 42.  And I'm not finding much in examples of how to use %o vs %v  (but I see %o says it is a "local index", perhaps the argument isn't what I think it is).

Just experimenting to see how the inline assembly works.  I wouldn't actually use the check_kbd call as presented, but trying to see what it allows.

 

So... Just curious, in the cx16 rom.bin  where does this $febd map to?  Offset $febd in that file is an empty no-mans-land of 0xAA's.  Is an annotated disassembly of rom.bin public available?  

 

 

 

 

 

Post Reply