Also, add the ability for parts to define extra shell commands.pull/10/head
@@ -0,0 +1,108 @@ | |||
; blockdev | |||
; | |||
; A block device is an abstraction over something we can read from, write to. | |||
; | |||
; A device that fits this abstraction puts the properly hook into itself, and | |||
; then the glue code assigns a blockdev ID to that device. It then becomes easy | |||
; to access arbitrary devices in a convenient manner. | |||
; | |||
; This part exposes a new "bsel" command to select the currently active block | |||
; device. | |||
; *** DEFINES *** | |||
; BLOCKDEV_COUNT: The number of devices we manage. | |||
; *** CONSTS *** | |||
; *** VARIABLES *** | |||
; A memory pointer to a device table. A device table is a list of addresses | |||
; pointing to GetC and PutC routines. | |||
BLOCKDEV_TBL .equ BLOCKDEV_RAMSTART | |||
; Index of the current blockdev selection | |||
BLOCKDEV_SELIDX .equ BLOCKDEV_TBL+(BLOCKDEV_COUNT*4) | |||
; Address of the current GetC routine | |||
BLOCKDEV_GETC .equ BLOCKDEV_SELIDX+1 | |||
; Address of the current PutC routine | |||
BLOCKDEV_PUTC .equ BLOCKDEV_GETC+2 | |||
BLOCKDEV_RAMEND .equ BLOCKDEV_PUTC+2 | |||
; *** CODE *** | |||
; set DE to point to the table entry at index A. | |||
blkFind: | |||
ld de, BLOCKDEV_TBL | |||
cp 0 | |||
ret z ; index is zero? don't loop | |||
push bc | |||
ld b, a | |||
push af | |||
ld a, 4 | |||
.loop: | |||
call addDE | |||
djnz .loop | |||
pop af | |||
pop bc | |||
ret | |||
; Set the GetC pointer of device id A to the value in HL | |||
blkSetGetC: | |||
call blkFind | |||
call writeHLinDE | |||
ret | |||
; Set the GetC pointer of device id A to the value in HL | |||
blkSetPutC: | |||
call blkFind | |||
inc de | |||
inc de | |||
call writeHLinDE | |||
ret | |||
; Select block index specified in A | |||
blkSel: | |||
call blkFind | |||
ld (BLOCKDEV_SELIDX), a | |||
ex hl, de | |||
; now, HL points to the table entry | |||
ld de, BLOCKDEV_GETC | |||
ldi ; copy (HL) into (BLOCKDEV_GETC) | |||
ldi ; .. and into +1 | |||
ld de, BLOCKDEV_PUTC | |||
ldi ; same thing for (BLOCKDEV_PUTC) | |||
ldi | |||
ret | |||
blkBselCmd: | |||
.db "bsel", 0b001, 0, 0 | |||
blkBsel: | |||
ret | |||
push af | |||
ld a, (hl) ; argument supplied | |||
cp BLOCKDEV_COUNT | |||
ret nz ; if selection >= device count, don't do anything | |||
; (will devise a unified cmd error system later) | |||
call blkSel | |||
pop af | |||
ret | |||
; Reads one character from blockdev ID specified at A and returns its value | |||
; in A. Always returns a character and waits until read if it has to. | |||
blkGetC: | |||
push ix | |||
push de | |||
ld de, (BLOCKDEV_GETC) | |||
ld ixh, d | |||
ld ixl, e | |||
pop de | |||
call callIX | |||
pop ix | |||
ret | |||
blkPutC: | |||
push ix | |||
push de | |||
ld de, (BLOCKDEV_PUTC) | |||
ld ixh, d | |||
ld ixl, e | |||
pop de | |||
call callIX | |||
pop ix | |||
ret |
@@ -41,6 +41,18 @@ addHL: | |||
ld l, a | |||
ret | |||
; Write the contents of HL in (DE) | |||
writeHLinDE: | |||
push af | |||
ld a, l | |||
ld (de), a | |||
inc de | |||
ld a, h | |||
ld (de), a | |||
pop af | |||
ret | |||
; jump to the location pointed to by IX. This allows us to call IX instead of | |||
; just jumping it. We use IX because we never use this for arguments. | |||
callIX: | |||
@@ -14,14 +14,17 @@ | |||
; hexadecimal form, without prefix or suffix. | |||
; *** DEFINES *** | |||
; SHELL_GETC: Macro that calls a GetC routine | |||
; SHELL_PUTC: Macro that calls a PutC routine | |||
; SHELL_GETC: Macro that calls a GetC routine for tty interface | |||
; SHELL_PUTC: Macro that calls a PutC routine for tty interface | |||
; SHELL_IO_GETC: Macro that calls a GetC routine for I/O ("load" cmd) | |||
; SHELL_EXTRA_CMD_COUNT: Number of extra cmds to be expected after the regular | |||
; ones. See comment in COMMANDS section for details. | |||
; SHELL_RAMSTART | |||
; *** CONSTS *** | |||
; number of entries in shellCmdTbl | |||
SHELL_CMD_COUNT .equ 4 | |||
SHELL_CMD_COUNT .equ 4+SHELL_EXTRA_CMD_COUNT | |||
; maximum number of bytes to receive as args in all commands. Determines the | |||
; size of the args variable. | |||
@@ -144,6 +147,7 @@ shellParse: | |||
pop de | |||
jr z, .found | |||
inc de | |||
inc de | |||
djnz .loop | |||
; exhausted loop? not found | |||
@@ -313,7 +317,12 @@ shellParseArgs: | |||
; | |||
; When these commands are called, HL points to the first byte of the | |||
; parsed command args. | |||
; | |||
; Extra commands: Other parts might define new commands. You can add these | |||
; commands to your shell. First, set SHELL_EXTRA_CMD_COUNT to | |||
; the number of extra commands to add, then add a ".dw" | |||
; directive *just* after your '#include "shell.asm"'. Voila! | |||
; | |||
; Set memory pointer to the specified address (word). | |||
; Example: seek 01fe | |||
@@ -388,9 +397,9 @@ shellPeek: | |||
ret | |||
; Load the specified number of bytes (max 0xff) from IO and write them in the | |||
; current memory pointer (which doesn't change). For now, we can only load from | |||
; SHELL_GETC, but a method of selecting IO sources is coming, making this | |||
; command much more useful. | |||
; current memory pointer (which doesn't change). This gets chars from | |||
; SHELL_IO_GETC, which can be different from SHELL_GETC. Coupled with the | |||
; "blockdev" part, this allows you to dynamically select your IO source. | |||
; Control is returned to the shell only after all bytes are read. | |||
; | |||
; Example: load 42 | |||
@@ -404,7 +413,7 @@ shellLoad: | |||
ld a, (hl) | |||
ld b, a | |||
ld hl, (SHELL_MEM_PTR) | |||
.loop: SHELL_GETC | |||
.loop: SHELL_IO_GETC | |||
ld (hl), a | |||
inc hl | |||
djnz .loop | |||
@@ -455,6 +464,8 @@ shellCall: | |||
pop af | |||
ret | |||
; This table is at the very end of the file on purpose. The idea is to be able | |||
; to graft extra commands easily after an include in the glue file. | |||
shellCmdTbl: | |||
.dw shellSeekCmd, shellPeekCmd, shellLoadCmd, shellCallCmd | |||