shell: implement seek and peek
This introduces `core.asm` which includes routines used by other parts.
This commit is contained in:
parent
9580cc3994
commit
21168f6c4e
193
parts/core.asm
Normal file
193
parts/core.asm
Normal file
@ -0,0 +1,193 @@
|
||||
; core
|
||||
;
|
||||
; Routines used by pretty much all parts. You will want to include it first
|
||||
; in your glue file.
|
||||
|
||||
; *** CONSTS ***
|
||||
ASCII_CR .equ 0x0d
|
||||
ASCII_LF .equ 0x0a
|
||||
|
||||
; *** CODE ***
|
||||
|
||||
; add the value of A into DE
|
||||
addDE:
|
||||
add a, e
|
||||
jr nc, .end ; no carry? skip inc
|
||||
inc d
|
||||
.end:
|
||||
ld e, a
|
||||
ret
|
||||
|
||||
; Format the lower nibble of A into a hex char and stores the result in A.
|
||||
fmtHex:
|
||||
and a, 0xf
|
||||
cp 10
|
||||
jr nc, .alpha ; if >= 10, we have alpha
|
||||
add a, '0'
|
||||
ret
|
||||
.alpha:
|
||||
add a, 'A'-10
|
||||
ret
|
||||
|
||||
; Formats value in A into a string hex pair. Stores it in the memory location
|
||||
; that HL points to. Does *not* add a null char at the end.
|
||||
fmtHexPair:
|
||||
push af
|
||||
|
||||
; let's start with the rightmost char
|
||||
inc hl
|
||||
call fmtHex
|
||||
ld (hl), a
|
||||
|
||||
; and now with the leftmost
|
||||
dec hl
|
||||
pop af
|
||||
push af
|
||||
and a, 0xf0
|
||||
rra \ rra \ rra \ rra
|
||||
call fmtHex
|
||||
ld (hl), a
|
||||
|
||||
pop af
|
||||
ret
|
||||
|
||||
; jump to the location pointed to by HL. This allows us to call HL instead of
|
||||
; just jumping it.
|
||||
jumpHL:
|
||||
jp hl
|
||||
ret
|
||||
|
||||
; Parse the hex char at A and extract it's 0-15 numerical value. Put the result
|
||||
; in A.
|
||||
;
|
||||
; On success, the carry flag is reset. On error, it is set.
|
||||
parseHex:
|
||||
; First, let's see if we have an easy 0-9 case
|
||||
cp '0'
|
||||
jr c, .error ; if < '0', we have a problem
|
||||
cp '9'+1
|
||||
jr nc, .alpha ; if >= '9'+1, we might have alpha
|
||||
; We are in the 0-9 range
|
||||
sub a, '0' ; C is clear
|
||||
ret
|
||||
|
||||
.alpha:
|
||||
call upcase
|
||||
cp 'A'
|
||||
jr c, .error ; if < 'A', we have a problem
|
||||
cp 'F'+1
|
||||
jr nc, .error ; if >= 'F', we have a problem
|
||||
; We have alpha.
|
||||
sub a, 'A'-10 ; C is clear
|
||||
ret
|
||||
|
||||
.error:
|
||||
scf
|
||||
ret
|
||||
|
||||
; Parses 2 characters of the string pointed to by HL and returns the numerical
|
||||
; value in A. If the second character is a "special" character (<0x21) we don't
|
||||
; error out: the result will be the one from the first char only.
|
||||
;
|
||||
; On success, the carry flag is reset. On error, it is set.
|
||||
parseHexPair:
|
||||
push bc
|
||||
push hl
|
||||
|
||||
ld a, (hl)
|
||||
call parseHex
|
||||
jr c, .end ; error? goto end, keeping the C flag on
|
||||
rla \ rla \ rla \ rla ; let's push this in MSB
|
||||
ld b, a
|
||||
inc hl
|
||||
ld a, (hl)
|
||||
;cp 0x21
|
||||
;jr c, .single ; special char? single digit
|
||||
call parseHex
|
||||
jr c, .end ; error?
|
||||
or b ; join left-shifted + new. we're done!
|
||||
; C flag was set on parseHex and is necessarily clear at this point
|
||||
jr .end
|
||||
|
||||
.single:
|
||||
; If we have a single digit, our result is already stored in B, but
|
||||
; we have to right-shift it back.
|
||||
ld a, b
|
||||
and a, 0xf0
|
||||
rra \ rra \ rra \ rra
|
||||
|
||||
.end:
|
||||
pop hl
|
||||
pop bc
|
||||
ret
|
||||
|
||||
; print null-terminated string pointed to by HL
|
||||
printstr:
|
||||
push af
|
||||
push hl
|
||||
|
||||
.loop:
|
||||
ld a, (hl) ; load character to send
|
||||
or a ; is it zero?
|
||||
jr z, .end ; if yes, we're finished
|
||||
call aciaPutC
|
||||
inc hl
|
||||
jr .loop
|
||||
|
||||
.end:
|
||||
pop hl
|
||||
pop af
|
||||
ret
|
||||
|
||||
; print A characters from string that HL points to
|
||||
printnstr:
|
||||
push bc
|
||||
push hl
|
||||
|
||||
ld b, a
|
||||
.loop:
|
||||
ld a, (hl) ; load character to send
|
||||
call aciaPutC
|
||||
inc hl
|
||||
djnz .loop
|
||||
|
||||
.end:
|
||||
pop hl
|
||||
pop bc
|
||||
ret
|
||||
|
||||
; Compares strings pointed to by HL and DE up to A count of characters. If
|
||||
; equal, Z is set. If not equal, Z is reset.
|
||||
strncmp:
|
||||
push bc
|
||||
push hl
|
||||
push de
|
||||
|
||||
ld b, a
|
||||
.loop:
|
||||
ld a, (de)
|
||||
cp (hl)
|
||||
jr nz, .end ; not equal? break early
|
||||
inc hl
|
||||
inc de
|
||||
djnz .loop
|
||||
|
||||
.end:
|
||||
pop de
|
||||
pop hl
|
||||
pop bc
|
||||
; Because we don't call anything else than CP that modify the Z flag,
|
||||
; our Z value will be that of the last cp (reset if we broke the loop
|
||||
; early, set otherwise)
|
||||
ret
|
||||
|
||||
; Transforms the character in A, if it's in the a-z range, into its upcase
|
||||
; version.
|
||||
upcase:
|
||||
cp 'a'
|
||||
ret c ; A < 'a'. nothing to do
|
||||
cp 'z'+1
|
||||
ret nc ; A >= 'z'+1. nothing to do
|
||||
; 'a' - 'A' == 0x20
|
||||
sub 0x20
|
||||
ret
|
177
parts/shell.asm
177
parts/shell.asm
@ -13,8 +13,6 @@
|
||||
; See constants below for error codes.
|
||||
|
||||
; *** CONSTS ***
|
||||
CR .equ 0x0d
|
||||
LF .equ 0x0a
|
||||
|
||||
; number of entries in shellCmdTbl
|
||||
SHELL_CMD_COUNT .equ 2
|
||||
@ -25,8 +23,19 @@ SHELL_ERR_UNKNOWN_CMD .equ 0x01
|
||||
; Arguments for the command weren't properly formatted
|
||||
SHELL_ERR_BAD_ARGS .equ 0x02
|
||||
|
||||
; *** VARIABLES ***
|
||||
; Memory address that the shell is currently "pointing at" for peek and deek
|
||||
; operations. Set with seek.
|
||||
SHELL_MEM_PTR .equ SHELL_RAMSTART
|
||||
; Used to store formatted hex values just before printing it.
|
||||
SHELL_HEX_FMT .equ SHELL_MEM_PTR+2
|
||||
SHELL_RAMEND .equ SHELL_HEX_FMT+2
|
||||
|
||||
; *** CODE ***
|
||||
shellInit:
|
||||
xor a
|
||||
ld (SHELL_MEM_PTR), a
|
||||
|
||||
; print prompt
|
||||
ld hl, .prompt
|
||||
call printstr
|
||||
@ -40,32 +49,14 @@ shellLoop:
|
||||
call chkbuf
|
||||
jr shellLoop
|
||||
|
||||
; print null-terminated string pointed to by HL
|
||||
printstr:
|
||||
push af
|
||||
push hl
|
||||
|
||||
.loop:
|
||||
ld a, (hl) ; load character to send
|
||||
or a ; is it zero?
|
||||
jr z, .end ; if yes, we're finished
|
||||
call aciaPutC
|
||||
inc hl
|
||||
jr .loop
|
||||
|
||||
.end:
|
||||
pop hl
|
||||
pop af
|
||||
ret
|
||||
|
||||
printcrlf:
|
||||
ld a, CR
|
||||
ld a, ASCII_CR
|
||||
call aciaPutC
|
||||
ld a, LF
|
||||
ld a, ASCII_LF
|
||||
call aciaPutC
|
||||
ret
|
||||
|
||||
|
||||
; check if the input buffer is full or ends in CR or LF. If it does, prints it
|
||||
; back and empty it.
|
||||
chkbuf:
|
||||
@ -81,9 +72,9 @@ chkbuf:
|
||||
ld a, (hl) ; now, that's our char we have in A
|
||||
inc hl ; put HL back where it was
|
||||
|
||||
cp CR
|
||||
cp ASCII_CR
|
||||
jr z, .do ; char is CR? do!
|
||||
cp LF
|
||||
cp ASCII_LF
|
||||
jr z, .do ; char is LF? do!
|
||||
|
||||
; nothing matched? don't do anything
|
||||
@ -101,51 +92,12 @@ chkbuf:
|
||||
call shellParse
|
||||
ret
|
||||
|
||||
; Compares strings pointed to by HL and DE up to A count of characters. If
|
||||
; equal, Z is set. If not equal, Z is reset.
|
||||
strncmp:
|
||||
push bc
|
||||
push hl
|
||||
push de
|
||||
|
||||
ld b, a
|
||||
.loop:
|
||||
ld a, (de)
|
||||
cp (hl)
|
||||
jr nz, .end ; not equal? break early
|
||||
inc hl
|
||||
inc de
|
||||
djnz .loop
|
||||
|
||||
.end:
|
||||
pop de
|
||||
pop hl
|
||||
pop bc
|
||||
; Because we don't call anything else than CP that modify the Z flag,
|
||||
; our Z value will be that of the last cp (reset if we broke the loop
|
||||
; early, set otherwise)
|
||||
ret
|
||||
|
||||
; add the value of A into DE
|
||||
addDE:
|
||||
add a, e
|
||||
jr nc, .end ; no carry? skip inc
|
||||
inc d
|
||||
.end:
|
||||
ld e, a
|
||||
ret
|
||||
|
||||
; jump to the location pointed to by HL. This allows us to call HL instead of
|
||||
; just jumping it.
|
||||
jumpHL:
|
||||
jp hl
|
||||
ret
|
||||
|
||||
; Parse command (null terminated) at HL and calls it
|
||||
shellParse:
|
||||
push af
|
||||
push bc
|
||||
push de
|
||||
push hl
|
||||
|
||||
ld de, shellCmdTbl
|
||||
ld a, SHELL_CMD_COUNT
|
||||
@ -165,19 +117,34 @@ shellParse:
|
||||
jr .end
|
||||
|
||||
.found:
|
||||
; all right, we're almost ready to call the cmd. Let's just have DE
|
||||
; point to the cmd jump line.
|
||||
ld a, 4
|
||||
call addDE
|
||||
ex hl, de
|
||||
; Now, let's swap HL and DE because, welll because that's how we're set.
|
||||
ex hl, de ; HL = jump line, DE = cmd str pointer
|
||||
|
||||
; Before we call our command, we want to set up the pointer to the arg
|
||||
; list. Normally, it's DE+5 (DE+4 is the space) unless DE+4 is null,
|
||||
; which means no arg.
|
||||
ld a, 4
|
||||
call addDE
|
||||
ld a, (DE)
|
||||
cp 0
|
||||
jr z, .noarg ; char is null? we have no arg
|
||||
inc de
|
||||
.noarg:
|
||||
; DE points to args, HL points to jump line. Ready to roll!
|
||||
call jumpHL
|
||||
ex hl, de
|
||||
|
||||
.end:
|
||||
pop hl
|
||||
pop de
|
||||
pop bc
|
||||
pop af
|
||||
ret
|
||||
|
||||
; Print the error code set in A (doesn't work for codes > 9 yet...)
|
||||
; Print the error code set in A (in hex)
|
||||
shellPrintErr:
|
||||
push af
|
||||
push hl
|
||||
@ -185,9 +152,10 @@ shellPrintErr:
|
||||
ld hl, .str
|
||||
call printstr
|
||||
|
||||
; ascii for '0' is 0x30
|
||||
add a, 0x30
|
||||
call aciaPutC
|
||||
ld hl, SHELL_HEX_FMT
|
||||
call fmtHexPair
|
||||
ld a, 2
|
||||
call printnstr
|
||||
call printcrlf
|
||||
|
||||
pop hl
|
||||
@ -198,19 +166,66 @@ shellPrintErr:
|
||||
.db "ERR ", 0
|
||||
|
||||
; *** COMMANDS ***
|
||||
shellSeek:
|
||||
ld hl, .str
|
||||
call printstr
|
||||
ret
|
||||
.str:
|
||||
.db "seek called", CR, LF, 0
|
||||
; When these commands are called, DE points to the first character of the
|
||||
; command args.
|
||||
|
||||
shellPeek:
|
||||
ld hl, .str
|
||||
call printstr
|
||||
; Set memory pointer to the specified address.
|
||||
; Example: seek 01fe
|
||||
|
||||
shellSeek:
|
||||
push de
|
||||
push hl
|
||||
|
||||
ex de, hl
|
||||
call parseHexPair
|
||||
jr c, .error
|
||||
ld (SHELL_MEM_PTR), a
|
||||
inc hl
|
||||
inc hl
|
||||
call parseHexPair
|
||||
jr c, .error
|
||||
ld (SHELL_MEM_PTR+1), a
|
||||
jr .success
|
||||
|
||||
.error:
|
||||
ld a, SHELL_ERR_BAD_ARGS
|
||||
call shellPrintErr
|
||||
jr .end
|
||||
|
||||
.success:
|
||||
ld a, (SHELL_MEM_PTR)
|
||||
ld hl, SHELL_HEX_FMT
|
||||
call fmtHexPair
|
||||
ld a, 2
|
||||
call printnstr
|
||||
ld a, (SHELL_MEM_PTR+1)
|
||||
call fmtHexPair
|
||||
ld a, 2
|
||||
call printnstr
|
||||
call printcrlf
|
||||
|
||||
.end:
|
||||
pop hl
|
||||
pop de
|
||||
ret
|
||||
|
||||
|
||||
; peek byte where memory pointer points to aby display its value
|
||||
shellPeek:
|
||||
push af
|
||||
push hl
|
||||
|
||||
ld hl, (SHELL_MEM_PTR)
|
||||
ld a, (hl)
|
||||
ld hl, SHELL_HEX_FMT
|
||||
call fmtHexPair
|
||||
ld a, 2
|
||||
call printnstr
|
||||
call printcrlf
|
||||
|
||||
pop hl
|
||||
pop af
|
||||
ret
|
||||
.str:
|
||||
.db "peek called", CR, LF, 0
|
||||
|
||||
; Format: 4 bytes name followed by 2 bytes jump. fill names with zeroes
|
||||
shellCmdTbl:
|
||||
|
@ -47,7 +47,7 @@ This is what your glue code would look like:
|
||||
jr init
|
||||
|
||||
.fill 0x38-$
|
||||
jr aciaInt
|
||||
jp aciaInt
|
||||
|
||||
init:
|
||||
di
|
||||
@ -63,8 +63,10 @@ init:
|
||||
ei
|
||||
call shellLoop
|
||||
|
||||
#include "core.asm"
|
||||
ACIA_RAMSTART .equ RAMSTART
|
||||
#include "acia.asm"
|
||||
SHELL_RAMSTART .equ ACIA_RAMEND
|
||||
#include "shell.asm"
|
||||
```
|
||||
|
||||
@ -83,11 +85,9 @@ We set interrupt mode to 1 because that's what `acia.asm` is written around.
|
||||
Then, we init ACIA, shell, enable interrupt and give control of the main loop
|
||||
to `shell.asm`.
|
||||
|
||||
What comes below is actual code include from the acia and shell modules. As you
|
||||
can see, we need to tell each module where to put their variables. `shell.asm`
|
||||
doesn't have variables, but if it did, we would have a `SHELL_RAMSTART .equ
|
||||
ACIA_RAMEND` just below the `acia.asm` include. `ACIA_RAMEND` is defined in
|
||||
`acia.asm`.
|
||||
What comes below is actual code include from parts we want to include in our
|
||||
OS. As you can see, we need to tell each module where to put their variables.
|
||||
See `parts/README.md` for details.
|
||||
|
||||
### Build the image
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user