"wait for keyrelease" is something to do after a delay, I think - it's a good idea, if you don't want an auto-repeat, but doing it too early could be merely sampling keybounce.
Bruce wrote a stupendously clever and tiny delay loop - see
viewtopic.php?p=62581#p62581
And while it is, indeed, stupendously clever and tiny ... in the DROM context, unless A and Y already hold appropriate values, that is 13 bytes and so too big to fit.
And going to the very smallest version of the DROM routine with just GO and CR functions to make the maximum possible space means a software-only debounce has to be
really good, not just "mostly OK", because there is no way to go back other than to do over 65,000 CR's to loop around an address at a time.
In a very simple key-based monitor, that could be done by structuring the main loop as
- wait for keypress
- perform action
- delay
- back to waiting
The system at the moment - remember, I am very tight for code space...
- tests for keypress
- does things if true, and
- waits for keyrelease
- small delay while display is updated
- rinse and repeat
However, it handles each column independently; a keypress is only returned if it's in the same colum that's currently being displayed. So there are some tens or hundreds of microseconds before the same key - in general, the only one being pressed, is read again.
Except the wait for keyrelease is a tight loop which pauses the column cycling until a non-press releases. 5 clock cycles at 1.8MHz is sampling the key driver about once every 3 microseconds, never mind milliseconds.
Looking at the switches actually planned to be used in an oscilloscope moves it from generic pictures of bouncing switch timelines to the actual one the code is looking at, but those generic pictures show a stochastic process that is roughly one or a few separated early spikes, then mostly contacted with one or a few narrow dropouts, then full contact, then one or a few narrow dropouts on release, and one or a few separated spikes at the tail end of release.
If you have any ST inverters or NAND's on hand, looking at the cap/resister alone, with hysteresis after only, and with hysteresis before and after would be instructive. With hysteresis after, to have Press=0, that would be charging the cap when the switch is closed. Hysteresis before doesn't affect whether the cap charges or drains when the switch is closed, because there is both a press=high and press=low signal available from the priority encoder.
That's why the Logi Switch parts work as they do, to ride through the early noise period (which copes with different switches, as bouncier switches will have a longer noise period), then 20ms rides through a few dropouts, then the transition is passed through, and the same on the release.
And the whole process would take multiple swings through the LED cycle, so as written, it seems likely that a lot of key strokes will be read as double or triple strikes, some key strokes catching the key while its still bouncing enough for the hold loop to think it sees the release, and some release bounces being captured as fresh keystrokes.
A 10ms cap/resister rising delay preceded by hysteresis would filter out much of the "early scattered spikes", follow it by hysteresis and it would likely clean up 0/1 transition unless there's really bad luck when a drop-out hits, and then the main and followed by hysteresis would eliminate most key strokes caught during the early "mostly not pressed" part of the timeline, ride out most "dropouts while not quite fully pressed".
So depending on whether the oscilloscope on those switches shows too long a period of "halfway pressed" whether the cap might oscillate enough to pass the hysteresis boundaries, putting hysteresis before to filter the earliest and latest noise and also after to avoid transition noise might be pretty good.
Of course, there is the question of how good is good enough. One bounce in 100 keystrokes might be use-able in the version of the user interface with CR going forward on and "-" going back one. However, testing for wrap-around takes too many bytes, so "+" decrements both the high and low address byte, which goes back one when when the low byte is 0 (and zooms down 257 bytes when it isn't).
This also requires using the absurd trick that the base routine address "addr0" is page aligned, so the "GO" function jumps to the BRK/IRQ vector location holding addr0, which is a BRK instruction, which when executed does a BRK call to the $0200 location. Obviously this leaves clutter on the stack, but since the stack is not yet set up, that's a harmless side effect.
That would be like this, completely filling the 128 byte quota:
Code: Select all
code
org $ff80
; System Reset starts here
start: ; +6
; Set base address, this is also "review"
lda #>addr0
sta memptr+1
stz memptr ; start at page aligned addr0
loop_main: ; +19
; current state to leds
; put memptr and (memptr) into leds
; [x][x][x][x][x][x][ ][ ]
;
ldx #0
lda memptr+1
jsr to_leds
lda memptr
jsr to_leds
lda (memptr)
jsr to_leds
; Initialize the display count
; Need to drive six leds for display
; The sixth is redundant for 4x5 "keyboard"
; Top two leds are never driven, so dark
ldy #5
loop_digit: ; +10
; for each LED digit (right to left)...
lda leds,y
sta led_addr,y ; display bit pattern
lda keys
; d7=NOR(row1,row2,row3,row4,row5)
bmi loop_99 ; no key pressed
; +12
; do stuff to sort out the key value
; a hex key if below smallest command code
; above, set up to be KEY_GO
cmp #KEY_GO
bmi loop_hex ; enter hex value
beq exec ; jump punched in code
cmp #KEY_GB
beq loop_ob ; decrement memptr
bpl loop_pb
; +8
; remaining special key is return
; accept current byte value, increment memptr
inc memptr
bne loop_90
inc memptr+1
bra loop_90
loop_pb: ; +6
dec memptr+1
loop_ob:
dec memptr
bra loop
loop_hex: ; +14
; else shuffle key value into (memptr)
; In this version, d7=Press
; d7 set if hex key
pha
lda (memptr)
asl a
asl a
asl a
asl a
sta (memptr)
pla
ora (memptr)
sta (memptr)
loop_90: ; +5
; wait for key to be released
; this will cause display glitch, but...
lda keys
bmi loop_90
loop_99: ; +5
dey
bpl loop_digit
bra loop_main
to_leds: ; +23
; unpack byte into two successive LED patterns
; x has character position
pha
lsr a
lsr a
lsr a
lsr a ; high nibble
tay
lda LED_PATTERNS,y
sta leds,x ; this byte comes first
inx ; advance char position
pla
and #$0f ; low nibble
tay
lda LED_PATTERNS,y ; index into the patterns
sta leds,x ; and this byte comes next
inx ; advance char position
rts
org $ffec ; +16
LED_PATTERNS:
db 0b00111111 ;0
db 0b00000110 ;1
db 0b01011011 ;2
db 0b01001111 ;3
db 0b01100110 ;4
db 0b01101101 ;5
db 0b01111101 ;6
db 0b00000111 ;7
db 0b01111111 ;8
db 0b01101111 ;9
db 0b01011111 ;a
db 0b00111100 ;b
db 0b00101000 ;c
db 0b01011110 ;d
db 0b01111101 ;e
db 0b01110001 ;f
org $fffc ; +4
dw start
exec: dw addr0 ; "GO" key