1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-26 04:38:05 +11:00

Compare commits

..

3 Commits

Author SHA1 Message Date
Virgil Dupras
1f26879cd3 Add TRICKS.txt 2019-10-30 21:00:10 -04:00
Virgil Dupras
d5c9bf5eec Add travis support 2019-10-30 19:12:02 -04:00
Virgil Dupras
b745f49186 Rename blockdev's API routines to GetB/PutB
The goal is to avoid mixing those routines with "character devices"
(acia, vpd, kbd) which aren't block devices and have routines that
have different expectations.

This is a first step to fixing #64.
2019-10-30 16:59:35 -04:00
36 changed files with 317 additions and 279 deletions

2
.travis.yml Normal file
View File

@ -0,0 +1,2 @@
language: c
script: ./runtests.sh

15
TRICKS.txt Normal file
View File

@ -0,0 +1,15 @@
This file describe tricks that are used throughout the code and might need
explanation.
or a: Equivalent to "cp 0", but results in a shorter opcode.
xor a: sets A to 0 more efficiently than ld a, 0
and 0xbf: Given a letter in the a-z range, changes it to its uppercase value
if it's already uppercased, then it stays that way.
Z if almost always used as a success indicator for routines. Set for success,
Reset for failure. "xor a" (destroys A) and "cp a" (preserves A) are used to
ensure Z is set. To ensure that it is reset, it's a bit more complicated and
"unsetZ" routine exists for that, although that in certain circumstances,
"inc a \ dec a" or "or a" can work.

View File

@ -9,7 +9,7 @@
; is reached on the block device.
; *** Requirements ***
; blkGetC
; blkGetB
; parseArgs
;
; *** Includes ***

View File

@ -7,7 +7,7 @@
; *** Variables ***
.equ AT28W_MAXBYTES AT28W_RAMSTART
.equ AT28W_RAMEND AT28W_MAXBYTES+2
.equ AT28W_RAMEND @+2
; *** Code ***
at28wMain:
@ -33,7 +33,7 @@ at28wInner:
; BC is zero, default to 0x2000 (8k, the size of the AT28)
ld bc, 0x2000
.loop:
call blkGetC
call blkGetB
jr nz, .loopend
ld (hl), a
ld e, a ; save expected data for verification

View File

@ -4,14 +4,14 @@
; Number of lines currently in the buffer
.equ BUF_LINECNT BUF_RAMSTART
; List of pointers to strings in scratchpad
.equ BUF_LINES BUF_LINECNT+2
.equ BUF_LINES @+2
; Points to the end of the scratchpad, that is, one byte after the last written
; char in it.
.equ BUF_PADEND BUF_LINES+ED_BUF_MAXLINES*2
.equ BUF_PADEND @+ED_BUF_MAXLINES*2
; The in-memory scratchpad
.equ BUF_PAD BUF_PADEND+2
.equ BUF_PAD @+2
.equ BUF_RAMEND BUF_PAD+ED_BUF_PADMAXLEN
.equ BUF_RAMEND @+ED_BUF_PADMAXLEN
; *** Code ***
@ -25,7 +25,7 @@ bufInit:
; init pad end in case we have an empty file.
ld (BUF_PADEND), hl
.loop:
call ioGetC
call ioGetB
jr nz, .loopend
or a ; null? hum, weird. same as LF
jr z, .lineend

View File

@ -13,9 +13,9 @@
; An address is a one byte type and a two bytes line number (0-indexed)
.equ CMD_ADDR1 CMD_RAMSTART
.equ CMD_ADDR2 CMD_ADDR1+3
.equ CMD_TYPE CMD_ADDR2+3
.equ CMD_RAMEND CMD_TYPE+1
.equ CMD_ADDR2 @+3
.equ CMD_TYPE @+3
.equ CMD_RAMEND @+1
; *** Code ***

View File

@ -9,10 +9,10 @@
; Handle of the target file
.equ IO_FILE_HDL IO_RAMSTART
; block device targeting IO_FILE_HDL
.equ IO_BLK IO_FILE_HDL+FS_HANDLE_SIZE
.equ IO_BLK @+FS_HANDLE_SIZE
; Buffer for lines read from I/O.
.equ IO_LINE IO_BLK+BLOCKDEV_SIZE
.equ IO_RAMEND IO_LINE+IO_MAXLEN+1 ; +1 for null
.equ IO_LINE @+BLOCKDEV_SIZE
.equ IO_RAMEND @+IO_MAXLEN+1 ; +1 for null
; *** Code ***
; Given a file name in (HL), open that file in (IO_FILE_HDL) and open a blkdev
@ -25,26 +25,26 @@ ioInit:
ld de, IO_BLK
ld hl, .blkdev
jp blkSet
.fsGetC:
.fsGetB:
ld ix, IO_FILE_HDL
jp fsGetC
.fsPutC:
jp fsGetB
.fsPutB:
ld ix, IO_FILE_HDL
jp fsPutC
jp fsPutB
.blkdev:
.dw .fsGetC, .fsPutC
.dw .fsGetB, .fsPutB
ioGetC:
ioGetB:
push ix
ld ix, IO_BLK
call _blkGetC
call _blkGetB
pop ix
ret
ioPutC:
ioPutB:
push ix
ld ix, IO_BLK
call _blkPutC
call _blkPutB
pop ix
ret
@ -76,14 +76,14 @@ ioPutLine:
ld a, (hl)
or a
jr z, .loopend ; null, we're finished
call ioPutC
call ioPutB
jr nz, .error
inc hl
jr .loop
.loopend:
; Wrote the whole line, write ending LF
ld a, 0x0a
call ioPutC
call ioPutB
jr z, .end ; success
; continue to error
.error:

View File

@ -25,16 +25,16 @@
; *** Requirements ***
; BLOCKDEV_SIZE
; FS_HANDLE_SIZE
; _blkGetC
; _blkPutC
; _blkGetB
; _blkPutB
; _blkSeek
; _blkTell
; addHL
; cpHLDE
; fsFindFN
; fsOpen
; fsGetC
; fsPutC
; fsGetB
; fsPutB
; fsSetSize
; intoHL
; printstr
@ -46,7 +46,7 @@
; *** Variables ***
;
.equ ED_CURLINE ED_RAMSTART
.equ ED_RAMEND ED_CURLINE+2
.equ ED_RAMEND @+2
edMain:
; because ed only takes a single string arg, we can use HL directly

View File

@ -4,15 +4,15 @@
; sectors. Collapse OS doesn't have a random number generator, so we'll simply
; rely on initial SRAM value, which tend is random enough for our purpose.
;
; How it works is simple. From its designated RAMSTART, it calls PutC until it
; How it works is simple. From its designated RAMSTART, it calls PutB until it
; reaches the end of RAM (0xffff). Then, it starts over and this time it reads
; every byte and compares.
;
; If there's an error, prints out where.
;
; *** Requirements ***
; sdcPutC
; sdcGetC
; sdcPutB
; sdcGetB
; printstr
; printHexPair
;

View File

@ -9,7 +9,7 @@ sdctMain:
; we set DE to 12 instead of zero
push de ; <|
ld de, 12 ; |
call sdcPutC ; |
call sdcPutB ; |
pop de ; <|
jr nz, .error
inc hl
@ -30,7 +30,7 @@ sdctMain:
.rLoop:
push de ; <|
ld de, 12 ; |
call sdcGetC ; |
call sdcGetB ; |
pop de ; <|
jr nz, .error
ex de, hl

View File

@ -54,7 +54,7 @@ handleDB:
or a ; cp 0
jr nz, .overflow ; not zero? overflow
ld a, l
call ioPutC
call ioPutB
jr nz, .ioError
.stopStrLit:
call readComma
@ -84,7 +84,7 @@ handleDB:
or a ; when we encounter 0, that was what used to
jr z, .stopStrLit ; be our closing quote. Stop.
; Normal character, output
call ioPutC
call ioPutB
jr nz, .ioError
jr .stringLiteral
@ -98,10 +98,10 @@ handleDW:
jr nz, .badarg
push ix \ pop hl
ld a, l
call ioPutC
call ioPutB
jr nz, .ioError
ld a, h
call ioPutC
call ioPutB
jr nz, .ioError
call readComma
jr z, .loop
@ -208,7 +208,7 @@ handleFIL:
or c
jr z, .loopend
xor a
call ioPutC
call ioPutB
jr nz, .ioError
dec bc
jr .loop
@ -314,7 +314,7 @@ getDirectiveID:
; Parse directive specified in A (D_* const) with args in I/O and act in
; an appropriate manner. If the directive results in writing data at its
; current location, that data is directly written through ioPutC.
; current location, that data is directly written through ioPutB.
; Each directive has the same return value pattern: Z on success, not-Z on
; error, A contains the error number (ERR_*).
parseDirective:

View File

@ -5,9 +5,9 @@
;
; We don't buffer the whole source in memory, so we need our input blkdev to
; support Seek so we can read the file a second time. So, for input, we need
; GetC and Seek.
; GetB and Seek.
;
; For output, we only need PutC. Output doesn't start until the second pass.
; For output, we only need PutB. Output doesn't start until the second pass.
;
; The goal of the second pass is to assign values to all symbols so that we
; can have forward references (instructions referencing a label that happens
@ -33,11 +33,11 @@
; blkSet
; fsFindFN
; fsOpen
; fsGetC
; fsGetB
; cpHLDE
; parseArgs
; _blkGetC
; _blkPutC
; _blkGetB
; _blkPutB
; _blkSeek
; _blkTell
; printstr

View File

@ -888,7 +888,7 @@ parseInstruction:
ld hl, INS_UPCODE
.loopWrite:
ld a, (hl)
call ioPutC
call ioPutB
jr nz, .ioError
inc hl
djnz .loopWrite

View File

@ -8,7 +8,7 @@
; maintaining IO_PC and of properly disabling output on first pass.
;
; On top of that, this unit has the responsibility of keeping track of the
; current lineno. Whenever GetC is called, we check if the fetched char is a
; current lineno. Whenever GetB is called, we check if the fetched byte is a
; newline. If it is, we increase our lineno. This unit is the best place to
; keep track of this because we have to handle ioRecallPos.
;
@ -17,7 +17,7 @@
; mechanism, that is, a way to say "you see that character I've just read? that
; was out of my bounds. Could you make it as if I had never read it?". That
; buffer is one character big and is made with the expectation that ioPutBack
; is always called right after a ioGetC (when it's called).
; is always called right after a ioGetB (when it's called).
;
; ioPutBack will mess up seek and tell offsets, so thath "put back" should be
; consumed before having to seek and tell.
@ -25,7 +25,7 @@
; That's for the general rules.
;
; Now, let's enter includes. To simplify processing, we make include mostly
; transparent to all other units. They always read from ioGetC and a include
; transparent to all other units. They always read from ioGetB and a include
; directive should have the exact same effect as copy/pasting the contents of
; the included file in the caller.
;
@ -33,7 +33,7 @@
; can include.
;
; When we include, all we do here is open the file with fsOpen and set a flag
; indicating that we're inside an include. When that flag is on, GetC, Seek and
; indicating that we're inside an include. When that flag is on, GetB, Seek and
; Tell are transparently redirected to their fs* counterpart.
;
; When we reach EOF in an included file, we transparently unset the "in include"
@ -73,7 +73,7 @@ ioInit:
call blkSet
jp ioResetCounters
ioGetC:
ioGetB:
ld a, (IO_PUTBACK_BUF)
or a ; cp 0
jr nz, .getback
@ -81,7 +81,7 @@ ioGetC:
jr z, .normalmode
; We're in "include mode", read from FS
ld ix, IO_INCLUDE_BLK
call _blkGetC
call _blkGetB
jr nz, .includeEOF
cp 0x0a ; newline
ret nz ; not newline? nothing to do
@ -111,7 +111,7 @@ ioGetC:
.normalmode:
; normal mode, read from IN stream
ld ix, IO_IN_BLK
call _blkGetC
call _blkGetB
cp 0x0a ; newline
ret nz ; not newline? return
; inc current lineno
@ -133,14 +133,14 @@ _callIX:
jp (ix)
ret
; Put back non-zero character A into the "ioGetC stack". The next ioGetC call,
; Put back non-zero character A into the "ioGetB stack". The next ioGetB call,
; instead of reading from IO_IN_BLK, will return that character. That's the
; easiest way I found to handle the readWord/gotoNextLine problem.
ioPutBack:
ld (IO_PUTBACK_BUF), a
ret
ioPutC:
ioPutB:
push hl
ld hl, (IO_PC)
inc hl
@ -151,7 +151,7 @@ ioPutC:
jr z, .skip
pop af
ld ix, IO_OUT_BLK
jp _blkPutC
jp _blkPutB
.skip:
pop af
cp a ; ensure Z
@ -240,7 +240,7 @@ ioOpenInclude:
cp a ; ensure Z
ret
; Open file specified in (HL) and spit its contents through ioPutC
; Open file specified in (HL) and spit its contents through ioPutB
; Sets Z on success.
ioSpitBin:
call fsFindFN
@ -251,9 +251,9 @@ ioSpitBin:
ld hl, 0
.loop:
ld ix, IO_BIN_HDL
call fsGetC
call fsGetB
jr nz, .loopend
call ioPutC
call ioPutB
inc hl
jr .loop
.loopend:
@ -274,12 +274,12 @@ ioLineNo:
pop af
ret
_ioIncGetC:
_ioIncGetB:
ld ix, IO_INCLUDE_HDL
jp fsGetC
jp fsGetB
_ioIncBlk:
.dw _ioIncGetC, unsetZ
.dw _ioIncGetB, unsetZ
; call printstr followed by newline
ioPrintLN:

View File

@ -128,7 +128,7 @@ zasmParseFile:
ret
; Parse next token and accompanying args (when relevant) in I/O, write the
; resulting opcode(s) through ioPutC and increases (IO_PC) by the number of
; resulting opcode(s) through ioPutB and increases (IO_PC) by the number of
; bytes written. BC is set to the result of the call to tokenize.
; Sets Z if parse was successful, unset if there was an error. EOF is not an
; error. If there is an error, A is set to the corresponding error code (ERR_*).

View File

@ -74,12 +74,12 @@ isLabel:
; Read I/O as long as it's whitespace. When it's not, stop and return the last
; read char in A
_eatWhitespace:
call ioGetC
call ioGetB
call isSep
ret nz
jr _eatWhitespace
; Read ioGetC until a word starts, then read ioGetC as long as there is no
; Read ioGetB until a word starts, then read ioGetB as long as there is no
; separator and put that contents in (scratchpad), null terminated, for a
; maximum of SCRATCHPAD_SIZE-1 characters.
; If EOL (\n, \r or comment) or EOF is hit before we could read a word, we stop
@ -104,7 +104,7 @@ readWord:
.loop:
ld (hl), a
inc hl
call ioGetC
call ioGetB
call isSepOrLineEnd
jr z, .success
cp ','
@ -130,7 +130,7 @@ readWord:
; inside quotes, we accept literal whitespaces, but not line ends.
ld (hl), a
inc hl
call ioGetC
call ioGetB
cp '"'
jr z, .loop ; ending the quote ends the word
call isLineEnd
@ -143,12 +143,12 @@ readWord:
; single quote is more straightforward: we have 3 chars and we put them
; right in scratchpad
ld (hl), a
call ioGetC
call ioGetB
or a
jr z, .error
inc hl
ld (hl), a
call ioGetC
call ioGetB
cp 0x27 ; '
jr nz, .error
inc hl
@ -165,7 +165,7 @@ readComma:
call unsetZ
ret
; Read ioGetC until we reach the beginning of next line, skipping comments if
; Read ioGetB until we reach the beginning of next line, skipping comments if
; necessary. This skips all whitespace, \n, \r, comments until we reach the
; first non-comment character. Then, we put it back (ioPutBack) and return.
;
@ -176,7 +176,7 @@ readComma:
gotoNextLine:
.loop1:
; first loop is "strict", that is: we error out on non-whitespace.
call ioGetC
call ioGetB
call isSepOrLineEnd
ret nz ; error
or a ; cp 0
@ -189,7 +189,7 @@ gotoNextLine:
.loop2:
; second loop is the "comment loop": anything is valid and we just run
; until EOL.
call ioGetC
call ioGetB
or a ; cp 0
jr z, .eof
cp '\' ; special case: '\' doesn't count as a line end
@ -201,7 +201,7 @@ gotoNextLine:
.loop3:
; Loop 3 happens after we reach our first line sep. This means that we
; wade through whitespace until we reach a non-whitespace character.
call ioGetC
call ioGetB
or a ; cp 0
jr z, .eof
cp 0x3b ; ';'

View File

@ -13,11 +13,11 @@ Definition of block devices happen at include time. It would look like:
BLOCKDEV_COUNT .equ 1
#include "blockdev.asm"
; List of devices
.dw aciaGetC, aciaPutC
.dw sdcGetB, sdcPutB
[...]
That tells `blockdev` that we're going to set up one device, that its GetC and
PutC are the ones defined by `acia.asm`.
That tells `blockdev` that we're going to set up one device, that its GetB and
PutB are the ones defined by `sdc.asm`.
If your block device is read-only or write-only, use dummy routines. `unsetZ`
is a good choice since it will return with the `Z` flag unset, indicating an
@ -28,16 +28,16 @@ seek pointer. This seek pointer is used in shell commands described below.
## Routine definitions
Parts that implement GetC and PutC do so in a loosely-coupled manner, but
Parts that implement GetB and PutB do so in a loosely-coupled manner, but
they should try to adhere to the convention, that is:
**GetC**: Get the character at position specified by `HL`. If it supports 32-bit
**GetB**: Get the byte at position specified by `HL`. If it supports 32-bit
addressing, `DE` contains the high-order bytes. Return the result in
`A`. If there's an error (for example, address out of range), unset
`Z`. This routine is not expected to block. We expect the result to be
immediate.
**PutC**: The opposite of GetC. Write the character in `A` at specified
**PutB**: The opposite of GetB. Write the character in `A` at specified
position. `Z` unset on error.
## Shell usage

View File

@ -2,34 +2,34 @@
;
; 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.
; A device that fits this abstraction puts the proper 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 module exposes a seek/tell/getc/putc API that is then re-routed to
; This module exposes a seek/tell/getb/putb API that is then re-routed to
; underlying drivers. There will eventually be more than one driver type, but
; for now we sit on only one type of driver: random access driver.
;
; *** Random access drivers ***
;
; Random access drivers are expected to supply two routines: GetC and PutC.
; Random access drivers are expected to supply two routines: GetB and PutB.
;
; GetC:
; Reads one character at address specified in DE/HL and returns its value in A.
; GetB:
; Reads one byte at address specified in DE/HL and returns its value in A.
; Sets Z according to whether read was successful: Set if successful, unset
; if not.
;
; Unsuccessful reads generally mean that requested addr is out of bounds (we
; reached EOF).
;
; PutC:
; Writes character in A at address specified in DE/HL. Sets Z according to
; whether the operation was successful.
; PutB:
; Writes byte in A at address specified in DE/HL. Sets Z according to whether
; the operation was successful.
;
; Unsuccessful writes generally mean that we're out of bounds for writing.
;
; All routines are expected to preserve unused registers except IX which is
; explicitly protected during GetC/PutC calls. This makes quick "handle+jump"
; explicitly protected during GetB/PutB calls. This makes quick "handle+jump"
; definitions possible.
@ -46,9 +46,9 @@
.equ BLOCKDEV_SIZE 8
; *** VARIABLES ***
; Pointer to the selected block device. A block device is a 8 bytes block of
; memory with pointers to GetC, PutC, and a 32-bit counter, in that order.
; memory with pointers to GetB, PutB, and a 32-bit counter, in that order.
.equ BLOCKDEV_SEL BLOCKDEV_RAMSTART
.equ BLOCKDEV_RAMEND BLOCKDEV_SEL+BLOCKDEV_SIZE
.equ BLOCKDEV_RAMEND @+BLOCKDEV_SIZE
; *** CODE ***
; Select block index specified in A and place them in routine pointers at (DE).
@ -121,16 +121,16 @@ _blkInc:
pop af
ret
; Reads one character from selected device and returns its value in A.
; Reads one byte from selected device and returns its value in A.
; Sets Z according to whether read was successful: Set if successful, unset
; if not.
blkGetC:
blkGetB:
push ix
ld ix, BLOCKDEV_SEL
call _blkGetC
call _blkGetB
pop ix
ret
_blkGetC:
_blkGetB:
push hl
push de
call _blkTell
@ -139,20 +139,20 @@ _blkGetC:
pop hl
jr _blkInc ; advance and return
; Writes character in A in current position in the selected device. Sets Z
; according to whether the operation was successful.
blkPutC:
; Writes byte in A in current position in the selected device. Sets Z according
; to whether the operation was successful.
blkPutB:
push ix
ld ix, BLOCKDEV_SEL
call _blkPutC
call _blkPutB
pop ix
ret
_blkPutC:
_blkPutB:
push ix
push hl
push de
call _blkTell
inc ix ; make IX point to PutC
inc ix ; make IX point to PutB
inc ix
call callIXI
pop de
@ -160,7 +160,7 @@ _blkPutC:
pop ix
jr _blkInc ; advance and return
; Reads B chars from blkGetC and copy them in (HL).
; Reads B chars from blkGetB and copy them in (HL).
; Sets Z if successful, unset Z if there was an error.
blkRead:
push ix
@ -172,7 +172,7 @@ _blkRead:
push hl
push bc
.loop:
call _blkGetC
call _blkGetB
jr nz, .end ; Z already unset
ld (hl), a
inc hl
@ -183,7 +183,7 @@ _blkRead:
pop hl
ret
; Writes B chars to blkPutC from (HL).
; Writes B chars to blkPutB from (HL).
; Sets Z if successful, unset Z if there was an error.
blkWrite:
push ix
@ -196,7 +196,7 @@ _blkWrite:
push bc
.loop:
ld a, (hl)
call _blkPutC
call _blkPutB
jr nz, .end ; Z already unset
inc hl
djnz .loop
@ -217,11 +217,11 @@ _blkWrite:
; (high). DE is only used for mode 0.
;
; When seeking to an out-of-bounds position, the resulting position will be
; one position ahead of the last valid position. Therefore, GetC after a seek
; one position ahead of the last valid position. Therefore, GetB after a seek
; to end would always fail.
;
; If the device is "growable", it's possible that seeking to end when calling
; PutC doesn't necessarily result in a failure.
; PutB doesn't necessarily result in a failure.
blkSeek:
push ix
ld ix, BLOCKDEV_SEL
@ -309,6 +309,6 @@ _blkTell:
; This label is at the end of the file on purpose: the glue file should include
; a list of device routine table entries just after the include. Each line
; has 4 word addresses: GetC, PutC and Seek, Tell. An entry could look like:
; .dw mmapGetC, mmapPutC, mmapSeek, mmapTell
; has 2 word addresses: GetB and PutB. An entry could look like:
; .dw mmapGetB, mmapPutB
blkDevTbl:

View File

@ -50,7 +50,7 @@ blkSeekCmd:
; stop loading.
;
; Returns a SHELL_ERR_IO_ERROR only if we couldn't read any byte (if the first
; call to GetC failed)
; call to GetB failed)
;
; Example: load 42
blkLoadCmd:
@ -62,12 +62,12 @@ blkLoad:
ld a, (hl)
ld b, a
ld hl, (SHELL_MEM_PTR)
call blkGetC
call blkGetB
jr nz, .ioError
jr .intoLoop ; we'v already called blkGetC. don't call it
jr .intoLoop ; we'v already called blkGetB. don't call it
; again.
.loop:
call blkGetC
call blkGetB
.intoLoop:
ld (hl), a
inc hl
@ -86,7 +86,7 @@ blkLoad:
; Load the specified number of bytes (max 0x100, 0 means 0x100) from the current
; memory pointer and write them to I/O. Memory pointer doesn't move. This puts
; chars to blkPutC. Raises error if not all bytes could be written.
; chars to blkPutB. Raises error if not all bytes could be written.
;
; Example: save 42
blkSaveCmd:
@ -101,7 +101,7 @@ blkSave:
.loop:
ld a, (hl)
inc hl
call blkPutC
call blkPutB
jr nz, .ioError
djnz .loop
.loopend:

View File

@ -9,6 +9,6 @@
.equ BLOCKDEV_ERR_OUT_OF_BOUNDS 0x03
.equ BLOCKDEV_ERR_UNSUPPORTED 0x04
; IO routines (GetC, PutC) returned an error in a load/save command
; IO routines (GetB, PutB) returned an error in a load/save command
.equ SHELL_ERR_IO_ERROR 0x05

View File

@ -3,7 +3,7 @@
; Collapse OS filesystem (CFS) is not made to be convenient, but to be simple.
; This is little more than "named storage blocks". Characteristics:
;
; * a filesystem sits upon a blockdev. It needs GetC, PutC, Seek.
; * a filesystem sits upon a blockdev. It needs GetB, PutB, Seek.
; * No directory. Use filename prefix to group.
; * First block of each file has metadata. Others are raw data.
; * No FAT. Files are a chain of blocks of a predefined size. To enumerate
@ -98,12 +98,12 @@
; This pointer is 32 bits. 32 bits pointers are a bit awkward: first two bytes
; are high bytes *low byte first*, and then the low two bytes, same order.
; When loaded in HL/DE, the four bytes are loaded in this order: E, D, L, H
.equ FS_START FS_BLK+BLOCKDEV_SIZE
.equ FS_START @+BLOCKDEV_SIZE
; This variable below contain the metadata of the last block we moved
; to. We read this data in memory to avoid constant seek+read operations.
.equ FS_META FS_START+4
.equ FS_HANDLES FS_META+FS_METASIZE
.equ FS_RAMEND FS_HANDLES+FS_HANDLE_COUNT*FS_HANDLE_SIZE
.equ FS_META @+4
.equ FS_HANDLES @+FS_METASIZE
.equ FS_RAMEND @+FS_HANDLE_COUNT*FS_HANDLE_SIZE
; *** DATA ***
P_FS_MAGIC:
@ -333,10 +333,10 @@ fsIsDeleted:
; we can still access the FS even if blkdev selection changes. These routines
; below mimic blkdev's methods, but for our private mount.
fsblkGetC:
fsblkGetB:
push ix
ld ix, FS_BLK
call _blkGetC
call _blkGetB
pop ix
ret
@ -347,10 +347,10 @@ fsblkRead:
pop ix
ret
fsblkPutC:
fsblkPutB:
push ix
ld ix, FS_BLK
call _blkPutC
call _blkPutB
pop ix
ret
@ -458,27 +458,27 @@ fsSetSize:
; cache.
ld a, l
ld (ix+4), a
call fsblkPutC
call fsblkPutB
ld a, h
ld (ix+5), a
call fsblkPutC
call fsblkPutB
pop hl ; <-- lvl 1
xor a ; ensure Z
ret
; Read a byte in handle at (IX) at position HL and put it into A.
; Z is set on success, unset if handle is at the end of the file.
fsGetC:
fsGetB:
call fsWithinBounds
jr z, .proceed
; We want to unset Z, but also return 0 to ensure that a GetC that
; We want to unset Z, but also return 0 to ensure that a GetB that
; doesn't check Z doesn't end up with false data.
xor a
jp unsetZ ; returns
.proceed:
push hl
call fsPlaceH
call fsblkGetC
call fsblkGetB
cp a ; ensure Z
pop hl
ret
@ -486,10 +486,10 @@ fsGetC:
; Write byte A in handle (IX) at position HL.
; Z is set on success, unset if handle is at the end of the file.
; TODO: detect end of block alloc
fsPutC:
fsPutB:
push hl
call fsPlaceH
call fsblkPutC
call fsblkPutB
pop hl
; if HL is out of bounds, increase bounds
call fsWithinBounds

View File

@ -24,7 +24,7 @@ _mmapAddr:
pop de
jp unsetZ
mmapGetC:
mmapGetB:
push hl
call _mmapAddr
jr nz, .end
@ -35,7 +35,7 @@ mmapGetC:
ret
mmapPutC:
mmapPutB:
push hl
call _mmapAddr
jr nz, .end

View File

@ -16,7 +16,7 @@
;
; *** Variables ***
.equ PGM_HANDLE PGM_RAMSTART
.equ PGM_RAMEND PGM_HANDLE+FS_HANDLE_SIZE
.equ PGM_RAMEND @+FS_HANDLE_SIZE
; Routine suitable to plug into SHELL_CMDHOOK. HL points to the full cmdline.
; which has been processed to replace the first ' ' with a null char.
@ -48,7 +48,7 @@ pgmRun:
ld hl, 0 ; addr that we read in file handle
ld de, PGM_CODEADDR ; addr in mem we write to
.loop:
call fsGetC ; we use Z at end of loop
call fsGetB ; we use Z at end of loop
ld (de), a ; Z preserved
inc hl ; Z preserved in 16-bit
inc de ; Z preserved in 16-bit

View File

@ -20,14 +20,14 @@
;
; SD card's lowest common denominator in terms of block size is 512 bytes, so
; that's what we deal with. To avoid wastefully reading entire blocks from the
; card for one byte read ops, we buffer the last read block. If a GetC or PutC
; card for one byte read ops, we buffer the last read block. If a GetB or PutB
; operation is within that buffer, then no interaction with the SD card is
; necessary.
;
; As soon as a GetC or PutC operation is made that is outside the current
; As soon as a GetB or PutB operation is made that is outside the current
; buffer, we load a new block.
;
; When we PutC, we flag the buffer as "dirty". On the next buffer change (during
; When we PutB, we flag the buffer as "dirty". On the next buffer change (during
; an out-of-buffer request or during an explicit "flush" operation), bytes
; currently in the buffer will be written to the SD card.
;
@ -37,10 +37,10 @@
; right away, in another file on the same card (zasm), on a different sector.
;
; If we only have one buffer in this scenario, we'll end up loading a new sector
; at each GetC/PutC operation and, more importantly, writing a whole block for
; at each GetB/PutB operation and, more importantly, writing a whole block for
; a few bytes each time. This will wear the card prematurely (and be very slow).
;
; With 2 buffers, we solve the problem. Whenever GetC/PutC is called, we first
; With 2 buffers, we solve the problem. Whenever GetB/PutB is called, we first
; look if one of the buffer holds our sector. If not, we see if one of the
; buffer is clean (not dirty). If yes, we use this one. If both are dirty or
; clean, we use any. This way, as long as writing isn't made to random
@ -721,7 +721,7 @@ _sdcPlaceBuf:
xor a ; ensure Z
ret
sdcGetC:
sdcGetB:
push hl
call _sdcPlaceBuf
jr nz, .error
@ -736,7 +736,7 @@ sdcGetC:
pop hl
ret
sdcPutC:
sdcPutB:
push hl
push af ; let's remember the char we put, _sdcPlaceBuf
; destroys A.

View File

@ -24,7 +24,7 @@ jp aciaInt
.equ BLOCKDEV_COUNT 1
.inc "blockdev.asm"
; List of devices
.dw mmapGetC, mmapPutC
.dw mmapGetB, mmapPutB
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
.inc "stdio.asm"

View File

@ -29,8 +29,8 @@ jp aciaInt
.equ BLOCKDEV_COUNT 2
.inc "blockdev.asm"
; List of devices
.dw sdcGetC, sdcPutC
.dw blk2GetC, blk2PutC
.dw sdcGetB, sdcPutB
.dw blk2GetB, blk2PutB
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
@ -83,10 +83,10 @@ init:
; *** blkdev 2: file handle 0 ***
blk2GetC:
blk2GetB:
ld ix, FS_HANDLES
jp fsGetC
jp fsGetB
blk2PutC:
blk2PutB:
ld ix, FS_HANDLES
jp fsPutC
jp fsPutB

View File

@ -27,7 +27,7 @@
jp blkSet
jp fsFindFN
jp fsOpen
jp fsGetC
jp fsGetB
jp cpHLDE ; approaching 0x38...
; interrupt hook
@ -37,14 +37,14 @@ jp aciaInt
; *** Jump Table (cont.) ***
jp parseArgs
jp printstr
jp _blkGetC
jp _blkPutC
jp _blkGetB
jp _blkPutB
jp _blkSeek
jp _blkTell
jp printHexPair
jp sdcGetC
jp sdcPutC
jp blkGetC
jp sdcGetB
jp sdcPutB
jp blkGetB
.inc "err.h"
.inc "core.asm"
@ -55,10 +55,10 @@ jp aciaInt
.equ BLOCKDEV_COUNT 4
.inc "blockdev.asm"
; List of devices
.dw sdcGetC, sdcPutC
.dw blk1GetC, blk1PutC
.dw blk2GetC, blk2PutC
.dw mmapGetC, mmapPutC
.dw sdcGetB, sdcPutB
.dw blk1GetB, blk1PutB
.dw blk2GetB, blk2PutB
.dw mmapGetB, mmapPutB
.equ MMAP_START 0xe000
@ -116,20 +116,20 @@ init:
; *** blkdev 1: file handle 0 ***
blk1GetC:
blk1GetB:
ld ix, FS_HANDLES
jp fsGetC
jp fsGetB
blk1PutC:
blk1PutB:
ld ix, FS_HANDLES
jp fsPutC
jp fsPutB
; *** blkdev 2: file handle 1 ***
blk2GetC:
blk2GetB:
ld ix, FS_HANDLES+FS_HANDLE_SIZE
jp fsGetC
jp fsGetB
blk2PutC:
blk2PutB:
ld ix, FS_HANDLES+FS_HANDLE_SIZE
jp fsPutC
jp fsPutB

View File

@ -5,30 +5,31 @@
; *** JUMP TABLE ***
.equ strncmp 0x03
.equ addDE 0x06
.equ addHL 0x09
.equ upcase 0x0c
.equ unsetZ 0x0f
.equ intoDE 0x12
.equ intoHL 0x15
.equ writeHLinDE 0x18
.equ findchar 0x1b
.equ parseHex 0x1e
.equ parseHexPair 0x21
.equ blkSel 0x24
.equ blkSet 0x27
.equ fsFindFN 0x2a
.equ fsOpen 0x2d
.equ fsGetC 0x30
.equ cpHLDE 0x33
.equ addDE @+3
.equ addHL @+3
.equ upcase @+3
.equ unsetZ @+3
.equ intoDE @+3
.equ intoHL @+3
.equ writeHLinDE @+3
.equ findchar @+3
.equ parseHex @+3
.equ parseHexPair @+3
.equ blkSel @+3
.equ blkSet @+3
.equ fsFindFN @+3
.equ fsOpen @+3
.equ fsGetB @+3
.equ cpHLDE @+3
; now at 0x36
.equ parseArgs 0x3b
.equ printstr 0x3e
.equ _blkGetC 0x41
.equ _blkPutC 0x44
.equ _blkSeek 0x47
.equ _blkTell 0x4a
.equ printHexPair 0x4d
.equ sdcGetC 0x50
.equ sdcPutC 0x53
.equ blkGetC 0x56
.equ printstr @+3
.equ _blkGetB @+3
.equ _blkPutB @+3
.equ _blkSeek @+3
.equ _blkTell @+3
.equ printHexPair @+3
.equ sdcGetB @+3
.equ sdcPutB @+3
.equ blkGetB @+3

View File

@ -22,14 +22,14 @@
jp blkSet
jp fsFindFN
jp fsOpen
jp fsGetC
jp fsPutC
jp fsGetB
jp fsPutB
jp fsSetSize
jp cpHLDE
jp parseArgs
jp printstr
jp _blkGetC
jp _blkPutC
jp _blkGetB
jp _blkPutB
jp _blkSeek
jp _blkTell
jp printcrlf
@ -63,9 +63,9 @@
.equ BLOCKDEV_COUNT 3
.inc "blockdev.asm"
; List of devices
.dw mmapGetC, mmapPutC
.dw f0GetC, f0PutC
.dw f1GetC, f1PutC
.dw mmapGetB, mmapPutB
.dw f0GetB, f0PutB
.dw f1GetB, f1PutB
.equ FS_RAMSTART BLOCKDEV_RAMEND
@ -121,21 +121,21 @@ init:
ld (SHELL_CMDHOOK), hl
jp shellLoop
f0GetC:
f0GetB:
ld ix, FS_HANDLES
jp fsGetC
jp fsGetB
f0PutC:
f0PutB:
ld ix, FS_HANDLES
jp fsPutC
jp fsPutB
f1GetC:
f1GetB:
ld ix, FS_HANDLES+FS_HANDLE_SIZE
jp fsGetC
jp fsGetB
f1PutC:
f1PutB:
ld ix, FS_HANDLES+FS_HANDLE_SIZE
jp fsPutC
jp fsPutB
edCmd:
.db "ed", 0, 0, 0b1001, 0, 0

View File

@ -30,14 +30,14 @@
.equ blkSet 0x27
.equ fsFindFN 0x2a
.equ fsOpen 0x2d
.equ fsGetC 0x30
.equ fsPutC 0x33
.equ fsGetB 0x30
.equ fsPutB 0x33
.equ fsSetSize 0x36
.equ cpHLDE 0x39
.equ parseArgs 0x3c
.equ printstr 0x3f
.equ _blkGetC 0x42
.equ _blkPutC 0x45
.equ _blkGetB 0x42
.equ _blkPutB 0x45
.equ _blkSeek 0x48
.equ _blkTell 0x4b
.equ printcrlf 0x4e

20
runtests.sh Executable file
View File

@ -0,0 +1,20 @@
#/usr/bin/env bash
set -e
git submodule init
git submodule update
git clean -fxd
cd tools/emul
make
cd ../tests
make
# let's try again with an updated zasm
cd ../emul
make updatebootstrap all
cd ../tests
make

View File

@ -26,14 +26,14 @@
jp blkSet
jp fsFindFN
jp fsOpen
jp fsGetC
jp fsPutC
jp fsGetB
jp fsPutB
jp fsSetSize
jp cpHLDE
jp parseArgs
jp printstr
jp _blkGetC
jp _blkPutC
jp _blkGetB
jp _blkPutB
jp _blkSeek
jp _blkTell
jp printcrlf
@ -48,10 +48,10 @@
.equ BLOCKDEV_COUNT 4
.inc "blockdev.asm"
; List of devices
.dw fsdevGetC, fsdevPutC
.dw stdoutGetC, stdoutPutC
.dw stdinGetC, stdinPutC
.dw mmapGetC, mmapPutC
.dw fsdevGetB, fsdevPutB
.dw stdoutGetB, stdoutPutB
.dw stdinGetB, stdinPutB
.dw mmapGetB, mmapPutB
.equ MMAP_START 0xe000
@ -84,8 +84,8 @@ init:
; setup stack
ld hl, KERNEL_RAMEND
ld sp, hl
ld hl, emulGetC
ld de, emulPutC
ld hl, emulGetB
ld de, emulPutB
call stdioInit
call fsInit
ld a, 0 ; select fsdev
@ -97,17 +97,17 @@ init:
ld (SHELL_CMDHOOK), hl
jp shellLoop
emulGetC:
emulGetB:
; Blocks until a char is returned
in a, (STDIO_PORT)
cp a ; ensure Z
ret
emulPutC:
emulPutB:
out (STDIO_PORT), a
ret
fsdevGetC:
fsdevGetB:
ld a, e
out (FS_ADDR_PORT), a
ld a, h
@ -121,7 +121,7 @@ fsdevGetC:
cp a ; ensure Z
ret
fsdevPutC:
fsdevPutB:
push af
ld a, e
out (FS_ADDR_PORT), a
@ -142,21 +142,21 @@ fsdevPutC:
.equ STDOUT_HANDLE FS_HANDLES
stdoutGetC:
stdoutGetB:
ld ix, STDOUT_HANDLE
jp fsGetC
jp fsGetB
stdoutPutC:
stdoutPutB:
ld ix, STDOUT_HANDLE
jp fsPutC
jp fsPutB
.equ STDIN_HANDLE FS_HANDLES+FS_HANDLE_SIZE
stdinGetC:
stdinGetB:
ld ix, STDIN_HANDLE
jp fsGetC
jp fsGetB
stdinPutC:
stdinPutB:
ld ix, STDIN_HANDLE
jp fsPutC
jp fsPutB

View File

@ -19,14 +19,14 @@
.equ blkSet @+3
.equ fsFindFN @+3
.equ fsOpen @+3
.equ fsGetC @+3
.equ fsPutC @+3
.equ fsGetB @+3
.equ fsPutB @+3
.equ fsSetSize @+3
.equ cpHLDE @+3
.equ parseArgs @+3
.equ printstr @+3
.equ _blkGetC @+3
.equ _blkPutC @+3
.equ _blkGetB @+3
.equ _blkPutB @+3
.equ _blkSeek @+3
.equ _blkTell @+3
.equ printcrlf @+3

View File

@ -24,11 +24,11 @@ jp blkSel
jp blkSet
jp fsFindFN
jp fsOpen
jp fsGetC
jp fsGetB
jp cpHLDE
jp parseArgs
jp _blkGetC
jp _blkPutC
jp _blkGetB
jp _blkPutB
jp _blkSeek
jp _blkTell
jp printstr
@ -40,9 +40,9 @@ jp printstr
.equ BLOCKDEV_COUNT 3
.inc "blockdev.asm"
; List of devices
.dw emulGetC, unsetZ
.dw unsetZ, emulPutC
.dw fsdevGetC, fsdevPutC
.dw emulGetB, unsetZ
.dw unsetZ, emulPutB
.dw fsdevGetB, fsdevPutB
.equ STDIO_RAMSTART BLOCKDEV_RAMEND
.inc "stdio.asm"
@ -71,7 +71,7 @@ init:
.db "0 1", 0
; *** I/O ***
emulGetC:
emulGetB:
; the STDIN_SEEK port works by poking it twice. First poke is for high
; byte, second poke is for low one.
ld a, h
@ -87,7 +87,7 @@ emulGetC:
call unsetZ
ret
emulPutC:
emulPutB:
out (STDIO_PORT), a
cp a ; ensure Z
ret
@ -97,7 +97,7 @@ stderrPutC:
cp a ; ensure Z
ret
fsdevGetC:
fsdevGetB:
ld a, e
out (FS_SEEK_PORT), a
ld a, h
@ -111,7 +111,7 @@ fsdevGetC:
cp a ; ensure Z
ret
fsdevPutC:
fsdevPutB:
push af
ld a, e
out (FS_SEEK_PORT), a

View File

@ -5,25 +5,25 @@
; *** JUMP TABLE ***
.equ strncmp 0x03
.equ addDE 0x06
.equ addHL 0x09
.equ upcase 0x0c
.equ unsetZ 0x0f
.equ intoDE 0x12
.equ intoHL 0x15
.equ writeHLinDE 0x18
.equ findchar 0x1b
.equ parseHex 0x1e
.equ parseHexPair 0x21
.equ blkSel 0x24
.equ blkSet 0x27
.equ fsFindFN 0x2a
.equ fsOpen 0x2d
.equ fsGetC 0x30
.equ cpHLDE 0x33
.equ parseArgs 0x36
.equ _blkGetC 0x39
.equ _blkPutC 0x3c
.equ _blkSeek 0x3f
.equ _blkTell 0x42
.equ printstr 0x45
.equ addDE @+3
.equ addHL @+3
.equ upcase @+3
.equ unsetZ @+3
.equ intoDE @+3
.equ intoHL @+3
.equ writeHLinDE @+3
.equ findchar @+3
.equ parseHex @+3
.equ parseHexPair @+3
.equ blkSel @+3
.equ blkSet @+3
.equ fsFindFN @+3
.equ fsOpen @+3
.equ fsGetB @+3
.equ cpHLDE @+3
.equ parseArgs @+3
.equ _blkGetB @+3
.equ _blkPutB @+3
.equ _blkSeek @+3
.equ _blkTell @+3
.equ printstr @+3

View File

@ -9,7 +9,7 @@
.equ BLOCKDEV_ERR_OUT_OF_BOUNDS 0x03
.equ BLOCKDEV_ERR_UNSUPPORTED 0x04
; IO routines (GetC, PutC) returned an error in a load/save command
; IO routines (GetB, PutB) returned an error in a load/save command
.equ SHELL_ERR_IO_ERROR 0x05