Read multiple keys from keyboard?

All aspects of programming on the Commander X16.
Dacobi
Posts: 292
Joined: Sun Dec 19, 2021 1:44 am
Location: Samsø, Denmark
Contact:

Re: Read multiple keys from keyboard?

Post by Dacobi »

I just don't understand the purpose of the code.

If $9f27 is VERA ISR and bit 1 is VBLANK would those code lines mean that the keyboard scancode statuses would only be updated during VBLANK interrupt?
User avatar
Kevin Murphy Games
Posts: 15
Joined: Tue Feb 21, 2023 2:42 pm

Re: Read multiple keys from keyboard?

Post by Kevin Murphy Games »

As i understand it, they keyboard generates an irq whenever a key is pressed or released, regardless of whether raster irq's are enabled or not, or triggered.

There are 2 software vectors i use, one for normal irq's ( raster irq) the other for keyboard irqs.

I'm guessing the kernal irq routines determine which software irq vector address to jump to.

So an irq triggers, either raster or keyboard, jumps to a kernal routine which determines it's source and jumps to the relevent software vector address.

Thats my current understanding, but i too don't have a thorough understanding of the process.

So i see raster and keyboard irq's being totally different.

Including $9f27 in my keyboard code is probably an error on my part. It has now been removed.
Last edited by Kevin Murphy Games on Sun Mar 19, 2023 4:51 pm, edited 2 times in total.
User avatar
Kevin Murphy Games
Posts: 15
Joined: Tue Feb 21, 2023 2:42 pm

Re: Read multiple keys from keyboard?

Post by Kevin Murphy Games »

For future reference.

I've been double checking my code and found an error.

There are 94 entries instead of 96 and i have removed the $9f27 section.
As it seems as though it's not needed.

key_loop_3
inx
cpx #94 ; 94 entries instead of instead of 96
bne key_loop_2

And amended the code.


I also found a bug, if i placed a jmp after this line

sta keyboard_scancodes_status , x

jmp key_loop_4

as an early exit after a key has been processed, but found for some strange reason the cursor keys stopped working, but all the other keys semed to work normally.

I put this down to some sort of issue related to the joystick, as that is mapped to the cursor keys.
If i remove the jmp instruction, the cursor keys work again.

So i have left it out of the listing.

All my testing has been in the Emulator R42.
User avatar
Yazwho
Posts: 165
Joined: Fri Feb 19, 2021 2:59 pm
Contact:

Re: Read multiple keys from keyboard?

Post by Yazwho »

As a heads up, there is talk of changing how the scancodes are sent from the SMC, which does look like it could effect your code.

https://github.com/X16Community/x16-rom/pull/53
DragWx
Posts: 324
Joined: Tue Mar 07, 2023 9:07 pm

Re: Read multiple keys from keyboard?

Post by DragWx »

Kevin Murphy Games wrote: Sun Mar 19, 2023 4:50 pm I also found a bug, if i placed a jmp after this line

sta keyboard_scancodes_status , x

jmp key_loop_4

as an early exit after a key has been processed, but found for some strange reason the cursor keys stopped working, but all the other keys semed to work normally.
This happens because the scancodes for the cursor keys are the exact same as the scancodes for numpad keys 8 (up), 2 (down), 4 (left), and 6 (right). The "prefix" byte (in X at the time the custom keyboard handler is called from the kernal) is how you differentiate between the two: it'll be $E0 when it's a cursor key and $00 when it's a numpad key.

The way I would revise your code is, have two tables; one for X == $00 and another for X == $E0. There's also technically one key that has a prefix of E1: the pause/break key.

I found this reference for PS/2 keyboard scan codes (we use set 2) if you want to take a look. The kernal already handles the make/break stuff, so just look at the "make" codes. (EDIT: The leftmost column of this table ("IBM Key No.") is also what's being proposed as the new scancode set in the pull request listed below! I didn't notice that originally)
Yazwho wrote: Sun Mar 19, 2023 4:58 pm As a heads up, there is talk of changing how the scancodes are sent from the SMC, which does look like it could effect your code.

https://github.com/X16Community/x16-rom/pull/53
I'm a-ok with this, especially if it simplifies the software-side of working with keyboard input.
Last edited by DragWx on Mon Mar 20, 2023 5:33 pm, edited 2 times in total.
User avatar
Kevin Murphy Games
Posts: 15
Joined: Tue Feb 21, 2023 2:42 pm

Re: Read multiple keys from keyboard?

Post by Kevin Murphy Games »

Thanks for the heads up Yazwho.

I'll keep my eye on that.




Thankyou DragWx.

Great info.

I was unaware of.

I'll get to work amending my code.
User avatar
Kevin Murphy Games
Posts: 15
Joined: Tue Feb 21, 2023 2:42 pm

Re: Read multiple keys from keyboard?

Post by Kevin Murphy Games »

Update 20.03.23
-------------------

I've reposted my irq keyboard routines as the first version contained some errors.

It turns out several keys share the same scancode, which i was unaware of and the result was that some keys were getting tested and others not.

The reposted code now also has an early exit after the key has been processed.

Cursor keys are now also working.

Feel free to use,share and modify the code as you see fit.
voidstar
Posts: 445
Joined: Thu Apr 15, 2021 8:05 am

Re: Read multiple keys from keyboard?

Post by voidstar »

Resurrecting this thread, to try to make this "easier" to from BASIC/BASLOAD and verify working in R47...

EDIT: it is working now with R47 and the System ROM built in BASLOAD! however, there is a fundamental limit to how many keys at the same time that can be detected. It may be a PS/2 interface limitation, or also just a limitation of your particular keyboard. Generally it is between 3-6 keys, but the combination of keys supported isn't well known or consistent across devices. Anyway, this works, but "YMMV" !


Here is the BASLOAD code that should initialize this new handler, and the handler itself to set the key flags in a region of "golden RAM":

Code: Select all

REM VOIDSTAR - JUNE 2024
REM
REM USING GOLDEN RAM $0400-$07FF AS FOLLOWS:
REM $0400  INTERNAL PROGRAM DATA
REM $0500  NEW KBD HANDLER ROUTINE
REM $0600  KEY SCANCODE COMPARISON TABLE
REM $0700  PROGRAM TO INSTALL THE NEW KBD HANDLER

REM ************************************************
REM SETUP: PREPARE THE KBD HANDLER, ITS INSTALLER, AND INVOKE IT

  REM OPTIONAL: TRY TO MAKE THE PROGRAM RE-RUNNABLE BY DETECTING
  REM IF THE KBD HANDLER WAS ALREADY INSTALLED ONCE BEFORE...
  
  A=PEEK($0400) : REM IF ALREADY INSTALLED, THIS WILL EQUAL THE ORIGINAL KBD HANDLER ADDR.
  B=PEEK($0401)
  C=PEEK($032E) : REM IF ALREADY INSTALLEd, THIS WILL EQUAL THE NEW KBD HANDLER ADDR.
  D=PEEK($032F)
  
  IF (A=$A6) AND (B=$D1) AND (C=$00) AND (D=$05) GOTO SKIP.INSTALL
  
  REM PREPARE SUPPORTING DATA-STRUCTURE REGION
  RESTORE BLOB0400
  ADDR.BLOB=$0400
  GOSUB LOADBLOB

  REM PATCH IN THE ACTUAL KBD HANDLER ROUTINE
  RESTORE BLOB0500
  ADDR.BLOB=$0500
  GOSUB LOADBLOB
  
  REM DEFINE SET OF SCAN CODES THAT WE CARE ABOUT (WILL BE USER DEFINED)
  RESTORE BLOB0600
  ADDR.BLOB=$0600
  GOSUB LOADBLOB

  REM PREPARE AN "INSTALLER" OF THE KBD HANDLER ROUTINE
  RESTORE BLOB0700
  ADDR.BLOB=$0700
  GOSUB LOADBLOB 

  REM INVOKE THE INSTALLER OF THE KBD HANDLER
  REM WARNING: THIS HANDLER REMAINS IN EFFECT UNTIL SYSTEM RESTARTED
  REM (SINCE WE STORE THE ORIGINAL HANDLER ADDR AT $0400, WE COULD UNINSTALL
  REM THE CUSTOMER HANDLER)
  SYS $0700  
  
  PRINT "KBD HANDLER INSTALLED!"
  GOTO START.TEST
  
SKIP.INSTALL:
  PRINT "KBD HANDLER (PROBABLY) ALREAD INSTALLED!"
  
START.TEST:  
  REM OPTIONAL TEST PROGRAM. EVEN IF YOU "NEW" OR LOAD A NEW PROGRAM,
  REM THE KBD HANDLER REMAINS IN EFFECT (UNTIL UNLOADED OR SYSTEM RESET)
  GOTO TEST.KBD
  
  END
REM ************************************************

REM ************************************************
TEST.KBD:
  KBD$="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  CLS
TEST.KBD.STARTOVER:  
  KEYS$=""
  LOCATE 1,1  
  I=$0404
TEST.KBD.CHECKNEXT:  
  A=PEEK(I)
  IF A > 0 THEN KEYS$ = KEYS$+MID$(KBD$,I-$0404+1,1)
  I=I+1
  
  REM STOP AT BASE ($0404) + 36 (OR HOWEVER MANY SCANCODES SUPPORTED)    
  IF I >= $0428 THEN GOTO SHOWKEYS
  GOTO TEST.KBD.CHECKNEXT
  
SHOWKEYS:
  X=LEN(KEYS$)
  IF X < 39 THEN KEYS$=KEYS$+" ":GOTO SHOWKEYS  
  PRINT KEYS$;
  GOTO TEST.KBD.STARTOVER  
  END

REM ************************************************
REM RE-USABLE "FUNCTION" TO LOAD DATA VALUES AS MACHINE CODE SEQUENCES
LOADBLOB:
  PRINT "LOADING ",HEX$(ADDR.BLOB)," ";
  BYTES.READ=0
LOADBLOBNEXT:
  READ DV
  BYTES.READ=BYTES.READ+1
  PRINT HEX$(DV);
  IF DV = 999 THEN PRINT" DONE (";BYTES.READ;")":RETURN
  POKE ADDR.BLOB,DV
  ADDR.BLOB=ADDR.BLOB+1
  GOTO LOADBLOBNEXT
REM ************************************************

REM ************************************************
REM PROGRAM DATA STORAGE
REM ----
BLOB0400:REM DATA BUFFER
DATA $00:REM 0400  ORIGINAL KBD ISR ADDR
DATA $00:REM 0401  ORIGINAL KBD ISR ADDR+1

DATA $00:REM 0402  SCAN KEY COPY  KEY_SCANCODE

DATA $00:REM 0403  FLAG STATUS (0 OR 1)  KEY_CARRY_FS

DATA $00:REM 0404  KEY STATUS TABLE (100 BYTES RESERVED, ONLY 36 USED)
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA $00,$00,$00,$00,$00,$00,$00,$00,$00,$00
DATA 999

REM ************************************************
REM USER KBD HANDLER ROUTINE.  THIS IS "INLINE" TO THE ORIGINAL SYSTEM ISR,
REM AND IS CALLED AUTOMATICALLY WITH REG.A SET TO THE KEY.  THE HIGH BIT
REM OF THAT KEY TELLS WHETHER THE KEY IS PRESSED DOWN OR UP.
REM ----
BLOB0500:REM EXPECTED TO BE LOADED INTO $0500
DATA  $DA        :REM   $0500      PHX  ; PUSH X REGISTER TO STACK
DATA  $48        :REM   $0501      PHA  ; PUSH A REGISTER TO STACK

DATA  $30,$08    :REM   $0502      BMI $050C    ; check Carry Flag Status
REM                              KEY_FS_CLEAR:
DATA  $A9,$01    :REM   $0504      LDA #$01
DATA  $8D,$03,$04:REM   $0506      STA $0403 ; fs = flag status
DATA  $4C,$11,$05:REM   $0509      JMP $0511
REM                              KEY_FS_SET
DATA  $A9,$00    :REM   $050C      LDA #$00
DATA  $8D,$03,$04:REM   $050E      STA $0403
REM                              KEY_LOOP_1
REM                                (NOTE: WE COULD PLACE THE STA $0403 ONCE...)
DATA  $68        :REM   $0511      PLA  ; PULL A FROM STACK
DATA  $48        :REM   $0512      PHA  ; PUSH A REGISTER TO STACK
DATA  $29,$7F    :REM   $0513      AND #$7F  ; clear the 7bit (negative)
DATA  $8D,$02,$04:REM   $0515      STA $0402 ; store modified copy of A (high bit cleared)
DATA  $A2,$00    :REM   $0518      LDX #$00     ; start at beginning of table
REM                              KEY_LOOP_NEX
DATA  $AD,$02,$04:REM   $051A      LDA $0402    ; LDA KEY_SCANCODE
DATA  $DD,$00,$06:REM   $051D      CMP $0600,X  ; CMP KBD_SCANCODE,X
DATA  $D0,$09    :REM   $0520      BNE $052B    ; BNE KEY_LOOP_3 (is regA == this scancode?)
DATA  $AD,$03,$04:REM   $0522      LDA $0403    ; LDA KEY_CARRY_FS
DATA  $9D,$04,$04:REM   $0525      STA $0404,X  ; STA KBD_SSTATUS,X
DATA  $4C,$30,$05:REM   $0528      JMP $0530    ; JMP key_loop_end
REM                              KEY_LOOP_INC
DATA  $E8        :REM   $052B      INX          ; index to next table entry
DATA  $E0,$24    :REM   $052C      CPX #$24     ; did reach table end? (#$53 WAS ORIG. FOR 83 KEY TABLE)
DATA  $D0,$EA    :REM   $052E      BNE $051A    ; BNE KEY_LOOP_NEX

REM                              KEY_LOOP_END
DATA  $68        :REM   $0530      PLA   ; PULL A FROM STACK
DATA  $FA        :REM   $0531      PLX   ; PULL X FROM STACK
DATA  $60        :REM   $0532      RTS
DATA  999        :REM END OF THIS BLOB OF CODE
REM ************************************************


REM ************************************************
REM REFER TO IBM KEYNUMBERS DEFINED HERE:
REM https://github.com/X16Community/x16-rom/blob/master/inc/keycode.inc
REM SEE ALSO:
REM https://www.nirsoft.net/utils/keyboard_state_view.html
REM ----
BLOB0600:REM KEY TABLES
REM NOTE: WE COULD JUST PUT ALL 126 KEYS IN HERE, BUT IT IS BETTER
REM TO JUST PUT ONLY THE KEYS YOU NEED FOR YOUR APPLICATION - BECAUSE
REM YOU WANT THE ISR TO BE BRIEF/SHORT RUNTIME, AND YOUR APPLICATION
REM WILL HAVE TO SCAN THE RESULT TABLE TO ACTAULLY CHECK THE CLEAR/SET FLAG
REM SO YOU DON'T WANT THAT UNNECESSARILY SLOW EITHER.
DATA  $1F                         :REM   Key ( A ) ( Array Index 0 )
DATA  $32                         :REM   Key ( B ) ( Array Index 1 )
DATA  $30                         :REM   Key ( C ) ( Array Index 2 )
DATA  $21                         :REM   Key ( D ) ( Array Index 3 )
DATA  $13                         :REM   Key ( E ) ( Array Index 4 )
DATA  $22                         :REM   Key ( F ) ( Array Index 5 )
DATA  $23                         :REM   Key ( G ) ( Array Index 6 )
DATA  $24                         :REM   Key ( H ) ( Array Index 7 )
DATA  $18                         :REM   Key ( I ) ( Array Index 8 )
DATA  $25                         :REM   Key ( J ) ( Array Index 9 )
DATA  $26                         :REM   Key ( K ) ( Array Index 10 )
DATA  $27                         :REM   Key ( L ) ( Array Index 11 )
DATA  $34                         :REM   Key ( M ) ( Array Index 12 )
DATA  $33                         :REM   Key ( N ) ( Array Index 13 )
DATA  $19                         :REM   Key ( O ) ( Array Index 14 )
DATA  $1A                         :REM   Key ( P ) ( Array Index 15 )
DATA  $11                         :REM   Key ( Q ) ( Array Index 16 )
DATA  $14                         :REM   Key ( R ) ( Array Index 17 )
DATA  $20                         :REM   Key ( S ) ( Array Index 18 )
DATA  $15                         :REM   Key ( T ) ( Array Index 19 )
DATA  $17                         :REM   Key ( U ) ( Array Index 20 )
DATA  $31                         :REM   Key ( V ) ( Array Index 21 )
DATA  $12                         :REM   Key ( W ) ( Array Index 22 )
DATA  $2F                         :REM   Key ( X ) ( Array Index 23 )
DATA  $16                         :REM   Key ( Y ) ( Array Index 24 )
DATA  $2E                         :REM   Key ( Z ) ( Array Index 25 )
DATA  $0B                         :REM   Key ( 0 ) ( Array Index 26 )
DATA  $02                         :REM   Key ( 1 ) ( Array Index 27 )
DATA  $03                         :REM   Key ( 2 ) ( Array Index 28 )
DATA  $04                         :REM   Key ( 3 ) ( Array Index 29 )
DATA  $05                         :REM   Key ( 4 ) ( Array Index 30 )
DATA  $06                         :REM   Key ( 5 ) ( Array Index 31 )
DATA  $07                         :REM   Key ( 6 ) ( Array Index 32 )
DATA  $08                         :REM   Key ( 7 ) ( Array Index 33 )
DATA  $09                         :REM   Key ( 8 ) ( Array Index 34 )
DATA  $0A                         :REM   Key ( 9 ) ( Array Index 35 )
DATA  999

REM ************************************************
REM ----
REM THIS IS A BRIEF SET OF CODE TO INSTALL THE KBD HANDLER.  WE HAVE
REM TO WRITE THE NEW KBD HANDLER ADDRESS INTO $032E/$032F.  NOTE, WE
REM STORE THE ORIGINAL KBD HANDLER INTO $0400 - THIS ISN'T VITAL, BUT
REM SUPPORTS A KEY TO "UNINSTALL" OUR CUSTOM HANDLER (BY REVERTING $032E/$032F
REM BACK TO THAT STORED VALUE), IF WE WANT TO HAVE A NICE EXIT FROM
REM OUR APPLICATION.
REM ---
BLOB0700:
                 :REM         ; SAVE ADDRESS OF THE CURRENT INTERRUPT HANDLER (WILL THEN BE REFERRED TO AS "THE PRIOR" ISR)
DATA  $AD,$2E,$03:REM         LDA $032E   LDA IRQVECTOR (standard ROM kbd handler)
DATA  $8D,$00,$04:REM         STA $0400   STA PRIOR_IRQ
DATA  $AD,$2F,$03:REM         LDA $032F   LDA IRQVECTOR+1
DATA  $8D,$01,$04:REM         STA $0401   STA PRIOR_IRQ+1

                 :REM         ; INSERT "MY_ISR" AS NEW INTERRUPT HANDLER
DATA  $78        :REM         SEI ; DISABLE INTERRUPTS
DATA  $A9,$00    :REM         LDA #$00 ; WRITE LO ADDRESS OF CUSTOM INTERRUPT MY_ISR
DATA  $8D,$2E,$03:REM         STA $032E   STA KBDIRQVECTOR
DATA  $A9,$05    :REM         LDA #$05 ; Write HI ADDRESS OF CUSTOM INTERRUPT MY_ISR
DATA  $8D,$2F,$03:REM         STA $032F   STA KBDIRQVECTOR+1
DATA  $58        :REM         CLI ; ENABLE INTERRUPTS

                 :REM         ; PROCEED WITH APPLICATION... (ISR IS NOW ACTIVE)
DATA  $60        :REM         RETURN TO BASIC (FROM SYS CALL)
DATA  999        :REM END OF THIS BLOB OF CODE



Below is what loading and running the BASLOAD portion looks like. You can run it independently from your own BASIC code after the kbd_handler is installed, or this version includes a how-to-use example also, to show how you can package it all as a single BASL file. So if you just RUN, then start pressing multiple keys and it will show which ones are being pressed. Try combinations of letters A-Z and numbers 0-9.

CTRL+C to stop the program, or CTRL+R to just reset if you're in the emulator.
Attachments
KBDTEST.BASL.TXT
(10.65 KiB) Downloaded 55 times
KBDTEST.PRG
(3.19 KiB) Downloaded 50 times
kbdtest_sample.png
kbdtest_sample.png (275.99 KiB) Viewed 1684 times
unartic
Posts: 136
Joined: Sat Oct 28, 2023 3:26 pm

Re: Read multiple keys from keyboard?

Post by unartic »

Just a tip to make this less complicated and way faster:

You need 16 bytes in total to store the key state of 128 keys. One bit per key.

So when the isr is being called, just set/unset the bit of that scancode within the 16 bytes. No need to worry about which keys you care about.

When checking the state of a key, just return the bit at that position.
voidstar
Posts: 445
Joined: Thu Apr 15, 2021 8:05 am

Re: Read multiple keys from keyboard?

Post by voidstar »

Agreed, but problem is BASIC only supports up to 15-bit masks in its integers (they are signed). Not a complete show stopper, but causes some wrinkles.

Also, the intent was to keep use of GET keyword still working as-is - but looks like that inadvertently got botched.
Post Reply