2019-04-13 05:16:12 +10:00
|
|
|
; shell
|
|
|
|
;
|
|
|
|
; Runs a shell over an asynchronous communication interface adapter (ACIA).
|
|
|
|
|
2019-04-13 11:28:50 +10:00
|
|
|
; Incomplete. For now, this outputs a welcome prompt and then waits for input.
|
|
|
|
; Whenever input is CR or LF, we echo back what we've received and empty the
|
|
|
|
; input buffer. This also happen when the buffer is full.
|
2019-04-13 05:16:12 +10:00
|
|
|
|
2019-04-13 05:53:05 +10:00
|
|
|
#include "platform.inc"
|
2019-04-13 05:16:12 +10:00
|
|
|
|
|
|
|
; *** CONSTS ***
|
|
|
|
CR .equ 0x0d
|
|
|
|
LF .equ 0x0a
|
|
|
|
|
2019-04-13 11:28:50 +10:00
|
|
|
; size of the input buffer. If our input goes over this size, we echo
|
|
|
|
; immediately.
|
|
|
|
BUFSIZE .equ 0x20
|
|
|
|
|
|
|
|
; *** VARIABLES ***
|
|
|
|
; Our input buffer starts there
|
|
|
|
INPTBUF .equ RAMSTART
|
|
|
|
|
|
|
|
; index, in the buffer, where our next character will go. 0 when the buffer is
|
|
|
|
; empty, BUFSIZE-1 when it's almost full.
|
|
|
|
BUFIDX .equ INPTBUF+BUFSIZE
|
|
|
|
|
2019-04-13 05:16:12 +10:00
|
|
|
; *** CODE ***
|
|
|
|
jr init
|
|
|
|
|
2019-04-14 05:02:29 +10:00
|
|
|
.fill 0x38-$
|
|
|
|
jr handleInterrupt
|
|
|
|
|
2019-04-13 05:16:12 +10:00
|
|
|
init:
|
2019-04-13 11:28:50 +10:00
|
|
|
di
|
2019-04-13 05:16:12 +10:00
|
|
|
|
|
|
|
; setup stack
|
|
|
|
ld hl, RAMEND
|
|
|
|
ld sp, hl
|
|
|
|
|
2019-04-13 11:28:50 +10:00
|
|
|
; initialize variables
|
|
|
|
xor a
|
|
|
|
ld (BUFIDX), a ; starts at 0
|
|
|
|
|
2019-04-14 05:02:29 +10:00
|
|
|
; RC2014's serial I/O is based on interrupt mode 1. We'd prefer im 2,
|
|
|
|
; but for now, let's go with the simpler im 1.
|
|
|
|
im 1
|
|
|
|
|
2019-04-13 05:16:12 +10:00
|
|
|
; setup ACIA
|
2019-04-14 05:02:29 +10:00
|
|
|
; CR7 (1) - Receive Interrupt enabled
|
2019-04-13 05:16:12 +10:00
|
|
|
; CR6:5 (00) - RTS low, transmit interrupt disabled.
|
|
|
|
; CR4:2 (101) - 8 bits + 1 stop bit
|
|
|
|
; CR1:0 (10) - Counter divide: 64
|
2019-04-14 05:02:29 +10:00
|
|
|
ld a, 0b10010110
|
2019-04-13 05:16:12 +10:00
|
|
|
out (ACIA_CTL), a
|
|
|
|
|
|
|
|
; print prompt
|
|
|
|
ld hl, d_welcome
|
|
|
|
call printstr
|
2019-04-13 11:28:50 +10:00
|
|
|
call printcrlf
|
|
|
|
|
2019-04-14 05:02:29 +10:00
|
|
|
; alright, ready to receive
|
|
|
|
ei
|
|
|
|
|
2019-04-13 11:28:50 +10:00
|
|
|
mainloop:
|
|
|
|
call chkbuf
|
|
|
|
jr mainloop
|
2019-04-13 05:16:12 +10:00
|
|
|
|
2019-04-14 05:02:29 +10:00
|
|
|
; read char in the ACIA and put it in the read buffer
|
|
|
|
handleInterrupt:
|
|
|
|
push af
|
|
|
|
push hl
|
|
|
|
|
|
|
|
; Read our character from ACIA into our BUFIDX
|
|
|
|
in a, (ACIA_CTL)
|
|
|
|
bit 0, a ; is our ACIA rcv buffer full?
|
|
|
|
jr z, .end ; no? a interrupt was triggered for nothing.
|
|
|
|
|
|
|
|
call getbufptr ; HL set, A set
|
|
|
|
; is our input buffer full? If yes, we don't read anything. Something
|
|
|
|
; is wrong: we don't process data fast enough.
|
|
|
|
cp BUFSIZE
|
|
|
|
jr z, .end ; if BUFIDX == BUFSIZE, our buffer is full.
|
|
|
|
|
|
|
|
; increase our buf ptr while we still have it in A
|
|
|
|
inc a
|
|
|
|
ld (BUFIDX), a
|
|
|
|
|
|
|
|
in a, (ACIA_IO)
|
|
|
|
ld (hl), a
|
|
|
|
|
|
|
|
.end:
|
|
|
|
pop hl
|
|
|
|
pop af
|
|
|
|
ei
|
|
|
|
reti
|
|
|
|
|
2019-04-13 05:16:12 +10:00
|
|
|
; spits character in A in port SER_OUT
|
|
|
|
printc:
|
|
|
|
push af
|
|
|
|
.stwait:
|
|
|
|
in a, (ACIA_CTL) ; get status byte from SER
|
|
|
|
bit 1, a ; are we still transmitting?
|
|
|
|
jr z, .stwait ; if yes, wait until we aren't
|
|
|
|
pop af
|
|
|
|
out (ACIA_IO), a ; push current char
|
|
|
|
ret
|
|
|
|
|
|
|
|
; print null-terminated string pointed to by HL
|
|
|
|
printstr:
|
|
|
|
ld a, (hl) ; load character to send
|
|
|
|
or a ; is it zero?
|
|
|
|
ret z ; if yes, we're finished
|
|
|
|
call printc
|
|
|
|
inc hl
|
|
|
|
jr printstr
|
|
|
|
; no ret because our only way out is ret z above
|
|
|
|
|
2019-04-13 11:28:50 +10:00
|
|
|
printcrlf:
|
|
|
|
ld a, CR
|
|
|
|
call printc
|
|
|
|
ld a, LF
|
|
|
|
call printc
|
|
|
|
ret
|
|
|
|
|
|
|
|
|
|
|
|
; check if the input buffer is full or ends in CR or LF. If it does, prints it
|
|
|
|
; back and empty it.
|
|
|
|
chkbuf:
|
2019-04-14 05:02:29 +10:00
|
|
|
call getbufptr
|
2019-04-13 11:28:50 +10:00
|
|
|
cp 0
|
|
|
|
ret z ; BUFIDX is zero? nothing to check.
|
|
|
|
|
|
|
|
cp BUFSIZE
|
|
|
|
jr z, .do ; if BUFIDX == BUFSIZE? do!
|
|
|
|
|
|
|
|
; our previous char is in BUFIDX - 1. Fetch this
|
|
|
|
dec hl
|
|
|
|
ld a, (hl) ; now, that's our char we have in A
|
|
|
|
inc hl ; put HL back where it was
|
|
|
|
|
|
|
|
cp CR
|
|
|
|
jr z, .do ; char is CR? do!
|
|
|
|
cp LF
|
|
|
|
jr z, .do ; char is LF? do!
|
|
|
|
|
|
|
|
; nothing matched? don't do anything
|
|
|
|
ret
|
|
|
|
|
|
|
|
.do:
|
|
|
|
; terminate our string with 0
|
|
|
|
xor a
|
|
|
|
ld (hl), a
|
|
|
|
; reset buffer index
|
|
|
|
ld (BUFIDX), a
|
|
|
|
|
|
|
|
; alright, let's go!
|
|
|
|
ld hl, INPTBUF
|
|
|
|
call printstr
|
|
|
|
call printcrlf
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Set current buffer pointer in HL. The buffer pointer is where our *next* char
|
2019-04-14 05:02:29 +10:00
|
|
|
; will be written. A is set to the value of (BUFIDX)
|
2019-04-13 11:28:50 +10:00
|
|
|
getbufptr:
|
2019-04-14 05:02:29 +10:00
|
|
|
push bc
|
|
|
|
|
|
|
|
ld a, (BUFIDX)
|
2019-04-13 11:28:50 +10:00
|
|
|
ld hl, INPTBUF
|
|
|
|
xor b
|
|
|
|
ld c, a
|
|
|
|
add hl, bc ; hl now points to INPTBUF + BUFIDX
|
2019-04-14 05:02:29 +10:00
|
|
|
|
|
|
|
pop bc
|
2019-04-13 11:28:50 +10:00
|
|
|
ret
|
|
|
|
|
2019-04-13 05:16:12 +10:00
|
|
|
; *** DATA ***
|
2019-04-13 11:28:50 +10:00
|
|
|
d_welcome: .byte "Welcome to Collapse OS", 0
|