recipes/rc2014/ps2: drive a shell with ps/2 kbd!

This commit is contained in:
Virgil Dupras 2019-06-29 14:25:18 -04:00
parent 3cdb25bfda
commit e44ebb08b2
6 changed files with 154 additions and 16 deletions

81
kernel/kbd.asm Normal file
View File

@ -0,0 +1,81 @@
; kbd - implement GetC for PS/2 keyboard
;
; Status: Work in progress. See recipes/rc2014/ps2
;
; *** Defines ***
; The port of the device where we read scan codes. See recipe rc2014/ps2.
; KBD_PORT
; *** Variables ***
.equ KBD_SKIP_NEXT KBD_RAMSTART
.equ KBD_RAMEND KBD_SKIP_NEXT+1
kbdInit:
xor a
ld (KBD_SKIP_NEXT), a
ret
kbdGetC:
in a, (KBD_PORT)
or a ; cp 0
ret z
; scan code not zero, maybe we have something.
; Do we need to skip it?
push af ; <|
ld a, (KBD_SKIP_NEXT) ;|
or a ; |
jr nz, .skip ; |
pop af ; <|
cp 0x80
jr nc, .outOfBounds
; No need to skip, code within bounds, we have something! Let's see if
; there's a ASCII code associated to it.
push hl ; <|
ld hl, kbdScanCodes ; |
call addHL ; |
ld a, (hl) ; |
pop hl ; <|
or a ; cp 0
jp z, unsetZ ; no code. Keep A at 0, but unset Z
; We have something!
cp a ; ensure Z
ret
.outOfBounds:
; A scan code over 0x80 is out of bounds. Ignore.
; If F0 (break code) or E0 (extended code), we also skip the next code
cp 0xf0
jr z, .skipNext
cp 0xe0
jr z, .skipNext
xor a
jp unsetZ
.skipNext:
ld (KBD_SKIP_NEXT), a
xor a
jp unsetZ
.skip:
pop af ; equilibrate stack
xor a
ld (KBD_SKIP_NEXT), a
jp unsetZ
; A list of the value associated with the 0x80 possible scan codes of the set
; 2 of the PS/2 keyboard specs. 0 means no value. That value is a character than
; can be read in a GetC routine. No make code in the PS/2 set 2 reaches 0x80.
kbdScanCodes:
; 0x00 1 2 3 4 5 6 7 8 9 a b c d e f
.db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,'`', 0
; 0x10 9 = TAB
.db 0, 0, 0, 0, 0,'Q','1', 0, 0, 0,'Z','S','A','W','2', 0
; 0x20 32 = SPACE
.db 0,'C','X','D','E','4','3', 0, 0, 32,'V','F','T','R','5', 0
; 0x30
.db 0,'N','B','H','G','Y','6', 0, 0, 0,'M','J','U','7','8', 0
; 0x40 59 = ;
.db 0,',','K','I','O','0','9', 0, 0,'.','/','L', 59,'P','-', 0
; 0x50 13 = RETURN 39 = '
.db 0, 0, 39, 0,'[','=', 0, 0, 0, 0, 13,']', 0,'\', 0, 0
; 0x60 8 = BKSP
.db 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0
; 0x70 27 = ESC
.db 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0

2
recipes/.gitignore vendored
View File

@ -1,2 +1,4 @@
*.bin
*.cfs
*.hex
*.obj

View File

@ -1,7 +1,9 @@
PROGNAME = ps2ctl
AVRDUDEMCU ?= t45
AVRDUDEARGS ?= -c usbtiny -P usb
TARGETS = $(PROGNAME).hex
TARGETS = $(PROGNAME).hex os.bin
ZASM = ../../../tools/zasm.sh
KERNEL = ../../../kernel
# Rules
@ -14,9 +16,10 @@ send: $(PROGNAME).hex
avrdude $(AVRDUDEARGS) -p $(AVRDUDEMCU) -U flash:w:$<
$(PROGNAME).hex: $(PROGNAME).asm
$(TARGETS):
avra -o $@ $<
clean:
rm -f $(TARGETS) *.eep.hex *.obj
os.bin: glue.asm
$(ZASM) $(KERNEL) < $< > $@
clean:
rm -f $(TARGETS) *.eep.hex *.obj os.bin

View File

@ -36,16 +36,15 @@ address selection + `IORQ` + `RO`
## Using the PS/2 interface
As of now, the interface is incomplete and can only be queried through the
shell's `iord`. I've set my device up for addr `8` (that is, I wired `A3`
through the inverter, the rest through diodes, and hooked this pudding to `OE`).
After having built and flashed the `glue.asm` supplied with this recipe, you end
up with a shell driven by the PS/2 keyboard (but it still outputs to ACIA).
When doing `iord 8` in the shell, I get the scan code of the last key I pressed,
unless the 595 was "busy" with another code. For example, if I press `A`, my
next `iord 8` will yield `1C` (the "make" code for "A" in the PS/2 protocol).
You will see, by typing on the keyboard, that it kinda works, but in a very
basic and glitchy way. You will get double letters sometimes, and at some point,
communications are likely to become "corrupted" (you reliably get the wrong
letters). That's because parity checks, timeouts and reset procedures aren't
implemented yet.
Doing a second `iord 8` right after a first will yield `0`, indicating that the
device properly detect the first reading attempt and properly flushes the value
from the 595.
But still, it kinda works!
[avra]: https://github.com/hsoft/avra

View File

@ -0,0 +1,42 @@
.equ RAMSTART 0x8000
.equ RAMEND 0xffff
.equ ACIA_CTL 0x80 ; Control and status. RS off.
.equ ACIA_IO 0x81 ; Transmit. RS on.
.equ KBD_PORT 0x08
jp init
; interrupt hook
.fill 0x38-$
jp aciaInt
#include "err.h"
#include "core.asm"
#include "parse.asm"
.equ ACIA_RAMSTART RAMSTART
#include "acia.asm"
.equ KBD_RAMSTART ACIA_RAMEND
#include "kbd.asm"
.equ STDIO_RAMSTART KBD_RAMEND
#include "stdio.asm"
.equ SHELL_RAMSTART STDIO_RAMEND
.equ SHELL_EXTRA_CMD_COUNT 0
#include "shell.asm"
init:
di
; setup stack
ld hl, RAMEND
ld sp, hl
im 1
call aciaInit
ld hl, kbdGetC
ld de, aciaPutC
call stdioInit
call shellInit
ei
jp shellLoop

View File

@ -233,16 +233,27 @@ sendTo595Loop:
dec r16
brne sendTo595Loop ; not zero yet? loop
; toggle RCLK
sbi PORTB, RCLK
cbi PORTB, RCLK
; We're finished sending our data to the 595 and we're ready to go back
; to business as usual. However, timing is important here. The z80 is
; very fast and constantly hammers our 595 with polls. While this
; routine was running, it was getting zeroes, which is fine, but as soon
; as we trigger RCLK, the z80 is going to fetch that value. What we want
; to do is to enable back the interrupts as soon as RCLK is triggered
; so that the z80 doesn't have enough time to poll twice. If it did, we
; would return a double character. This is why RCLK triggering is the
; last operation.
; release PS/2
cbi DDRB, DATA
; Set R2 to "595 is busy"
inc r2
; toggle RCLK
sbi PORTB, RCLK
cbi PORTB, RCLK
sei
rjmp loop
; Check that Y is within bounds, reset to SRAM_START if not.