I already posted about it in the Discord, but I might as well post it here as I believe it warrants a spot in the Wiki.
I was implementing Family BASIC keyboard functionalities in my Wordle homebrew project, when i noticed that the keyboard behaved differently on real hardware (Tested on black 505 Twin) than it did on MESEN.
After reverse-engineering the Family BASIC ROM, it turned out that the reason I was getting ghost presses is that when you write a value to $4016 on the expansion port, you need to burn some cycles in order not to get ghost presses when reading from $4017 later on. Family BASIC seems like it burns about 50 cycles between a $4016 write and a $4017 read, though the actual amount might be lower than that.
Fiskbit and Jekuthiel also helped me test the game out on real hardware and I can confirm this is not a fluke, rather an actual quirk of the keyboard. Anyone else try working with the keyboard/faced this issue?
Undocumented Family BASIC keyboard I/O behaviour
Moderator: Moderators
- Vectrex2809
- Posts: 102
- Joined: Mon Jul 14, 2014 6:05 am
- Location: Tokyo, Japan
Re: Undocumented Family BASIC keyboard I/O behaviour
I haven't encountered such a problem, but I found another problem. On some emulators in Family BASIC, the keys responsible for special characters, such as ":", don't work.
Re: Undocumented Family BASIC keyboard I/O behaviour
Yeah you need some delays, but it seems it's not mentioned on the wiki. It is mentioned on Enri's page (in Japanese) linked at the bottom in the wiki.
The process is basically like this:
1) Write $05 to $4016 (reset to row 0, column 0), then use 6 NOPs as a delay.
2) Write $04 to $4016 (select column 0, from 2nd lap this also selects the next row), wait to give time to scan all keys.
3) Read column 0 data from $4017. Shift right once to get key states in the low nibble, then AND with $0F to clear high nibble and save the value in RAM.
4) Write $06 to $4016 (select column 1), wait to give time to scan all keys.
5) Read column 1 data from $4017. Rotate left 3 times to get key states in the high nibble, then AND with $F0 to clear low nibble and save the value in RAM using ORA to merge the nibble with the column 0 data. Finally invert all bits (using EOR with $FF) if you want and store it back to RAM.
6) Repeat steps 2-5 eight more times to read both columns of all 9 rows.
This is how the code may look like, it's the same method as used in Family BASIC:
Lancuster, I suppose you talk about VirtualNES or Nestopia? I remember some of those older emulators missing one of the keys, and having no way to remap the keyboard either. FCEux might have some similar problem, I forgot. Mesen should support all keys and allow remapping them thankfully, but it's missing the paste feature of Nestopia.
The process is basically like this:
1) Write $05 to $4016 (reset to row 0, column 0), then use 6 NOPs as a delay.
2) Write $04 to $4016 (select column 0, from 2nd lap this also selects the next row), wait to give time to scan all keys.
3) Read column 0 data from $4017. Shift right once to get key states in the low nibble, then AND with $0F to clear high nibble and save the value in RAM.
4) Write $06 to $4016 (select column 1), wait to give time to scan all keys.
5) Read column 1 data from $4017. Rotate left 3 times to get key states in the high nibble, then AND with $F0 to clear low nibble and save the value in RAM using ORA to merge the nibble with the column 0 data. Finally invert all bits (using EOR with $FF) if you want and store it back to RAM.
6) Repeat steps 2-5 eight more times to read both columns of all 9 rows.
This is how the code may look like, it's the same method as used in Family BASIC:
Code: Select all
@read:
;Reads the keyboard matrix state.
lda #$05
sta $4016 ;reset keyboard
nop
nop
nop
nop
nop
nop ;wait for keyboard to get ready
;Read all 9 rows in column 0 and 1:
ldx #$00 ;loop counter
@loop_r:
;Read column 0 keys:
lda #$04 ;select colum 0 (from second lap: also select next row)
sta $4016
ldy #$0A ;loop counter
@wait1:
dey
bne @wait1
nop
nop ;wait to give time to scan all keys
lda $4017 ;read key state of selected row and column
lsr a ;right shift to get key state into low nibble
and #$0F ;clear high nibble
sta key_state+0,x ;save column 0 key states in RAM
;Read column 1 keys:
lda #$06 ;select colum 1
sta $4016
ldy #$0A ;loop counter
@wait2:
dey
bne @wait2
nop
nop ;wait to give time to scan all keys
lda $4017 ;read key status of selected row and column
rol a
rol a
rol a ;rotate left to get key status to high nibble
and #$F0 ;clear low nibble
ora key_state+0,x ;merge A with column 0 key status
eor #$FF ;invert key states so that 0=unpressed
ldy #$08 ;loop counter for 8 bits
@store:
asl a ;shift bit 7 left into carry
ror key_state+0,x ;store key state bit to RAM from carry
dey
bne @store ;loop for storing all 8 bits in RAM
inx
cpx #$09
bne @loop_r ;loop for all 9 rows
rts
Lancuster, I suppose you talk about VirtualNES or Nestopia? I remember some of those older emulators missing one of the keys, and having no way to remap the keyboard either. FCEux might have some similar problem, I forgot. Mesen should support all keys and allow remapping them thankfully, but it's missing the paste feature of Nestopia.