Make blockdev pointers 32 bits

This allows us to break through the 64K limit for includes CFS in zasm,
a limit we were dangerously close to breaking. In fact, this commit
makes us go over that limit. Right in time!
This commit is contained in:
Virgil Dupras 2019-05-28 15:56:39 -04:00
parent 6b1679c811
commit 328f44814e
9 changed files with 201 additions and 83 deletions

View File

@ -33,10 +33,12 @@
; Unsuccessful writes generally mean that we reached EOF. ; Unsuccessful writes generally mean that we reached EOF.
; ;
; Seek: ; Seek:
; Place device "pointer" at position dictated by HL. ; Place device "pointer" at position dictated by HL (low 16 bits) and DE (high
; 16 bits).
; ;
; Tell: ; Tell:
; Return the position of the "pointer" in HL ; Return the position of the "pointer" in HL (low 16 bits) and DE (high 16
; bits).
; ;
; All routines are expected to preserve unused registers. ; All routines are expected to preserve unused registers.
@ -197,12 +199,14 @@ _blkWrite:
ret ret
; Seeks the block device in one of 5 modes, which is the A argument: ; Seeks the block device in one of 5 modes, which is the A argument:
; 0 : Move exactly to X, X being the HL argument. ; 0 : Move exactly to X, X being the HL/DE argument.
; 1 : Move forward by X bytes, X being the HL argument ; 1 : Move forward by X bytes, X being the HL argument (no DE)
; 2 : Move backwards by X bytes, X being the HL argument ; 2 : Move backwards by X bytes, X being the HL argument (no DE)
; 3 : Move to the end ; 3 : Move to the end
; 4 : Move to the beginning ; 4 : Move to the beginning
; Set position of selected device to the value specified in HL
; Set position of selected device to the value specified in HL (low) and DE
; (high). DE is only used for mode 0.
; ;
; When seeking to an out-of-bounds position, the resulting position will be ; 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, GetC after a seek
@ -214,6 +218,8 @@ blkSeek:
ld ix, (BLOCKDEV_SEEK) ld ix, (BLOCKDEV_SEEK)
ld iy, (BLOCKDEV_TELL) ld iy, (BLOCKDEV_TELL)
_blkSeek: _blkSeek:
; we preserve DE so that it's possible to call blkSeek in mode != 0
; while not discarding our current DE value.
push de push de
cp BLOCKDEV_SEEK_FORWARD cp BLOCKDEV_SEEK_FORWARD
jr z, .forward jr z, .forward
@ -224,30 +230,41 @@ _blkSeek:
cp BLOCKDEV_SEEK_END cp BLOCKDEV_SEEK_END
jr z, .end jr z, .end
; all other modes are considered absolute ; all other modes are considered absolute
jr .seek ; for absolute mode, HL is already correct jr .seek ; for absolute mode, HL and DE are already
; correct
.forward: .forward:
ex de, hl ; DE has our offset push bc
push hl
; We want to be able to plug our own TELL function, which is why we ; We want to be able to plug our own TELL function, which is why we
; don't call blkTell directly here. ; don't call blkTell directly here.
; Calling TELL ; Calling TELL
call callIY ; HL has our curpos ld de, 0 ; in case out Tell routine doesn't return DE
add hl, de call callIY ; HL/DE now have our curpos
jr nc, .seek ; no carry? alright! pop bc ; pop HL into BC
; we have carry? out of bounds, set to maximum add hl, bc
pop bc ; pop orig BC back
jr nc, .seek ; no carry? let's seek.
; carry, adjust DE
inc de
jr .seek
.backward: .backward:
; TODO - subtraction are more complicated... ; TODO - subtraction are more complicated...
jr .seek jr .seek
.beginning: .beginning:
ld hl, 0 ld hl, 0
ld de, 0
jr .seek jr .seek
.end: .end:
ld hl, 0xffff ld hl, 0xffff
ld de, 0xffff
.seek: .seek:
call _blkCall
pop de pop de
jp _blkCall ret
; Returns the current position of the selected device in HL. ; Returns the current position of the selected device in HL (low) and DE (high).
blkTell: blkTell:
ld de, 0 ; in case device ignores DE.
ld ix, (BLOCKDEV_TELL) ld ix, (BLOCKDEV_TELL)
jp _blkCall jp _blkCall

View File

@ -57,6 +57,19 @@ addHL:
pop af pop af
ret ret
; subtract the value of A from HL
subHL:
push af
; To avoid having to swap L and A, we sub "backwards", that is, we add
; a NEGated value. This means that the carry flag is inverted
neg
add a, l
jr c, .end ; if carry, no carry. :)
dec h
.end:
ld l, a
pop af
ret
; Write the contents of HL in (DE) ; Write the contents of HL in (DE)
writeHLinDE: writeHLinDE:

View File

@ -85,12 +85,9 @@
.equ FS_META_FSIZE_OFFSET 4 .equ FS_META_FSIZE_OFFSET 4
.equ FS_META_FNAME_OFFSET 6 .equ FS_META_FNAME_OFFSET 6
; Size in bytes of a FS handle: ; Size in bytes of a FS handle:
; * 2 bytes for current position. (absolute) ; * 4 bytes for starting offset of the FS block
; * 2 bytes for starting offset, after metadata ; * 2 bytes for current position relative to block's position
; * 2 bytes for maximum offset .equ FS_HANDLE_SIZE 6
; * 2 bytes for file size (we could fetch it from metadata all the time, but it
; could be time consuming depending on the underlying device).
.equ FS_HANDLE_SIZE 8
.equ FS_ERR_NO_FS 0x5 .equ FS_ERR_NO_FS 0x5
.equ FS_ERR_NOT_FOUND 0x6 .equ FS_ERR_NOT_FOUND 0x6
@ -101,14 +98,17 @@
.equ FS_SEEK FS_PUTC+2 .equ FS_SEEK FS_PUTC+2
.equ FS_TELL FS_SEEK+2 .equ FS_TELL FS_SEEK+2
; Offset at which our FS start on mounted device ; Offset at which our FS start on mounted device
; 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_TELL+2 .equ FS_START FS_TELL+2
; Offset at which we are currently pointing to with regards to our routines ; Offset at which we are currently pointing to with regards to our routines
; below, which all assume this offset as a context. This offset is not relative ; below, which all assume this offset as a context. This offset is not relative
; to FS_START. It can be used directly with fsblkSeek. ; to FS_START. It can be used directly with fsblkSeek. 32 bits.
.equ FS_PTR FS_START+2 .equ FS_PTR FS_START+4
; This variable below contain the metadata of the last block FS_PTR was moved ; This variable below contain the metadata of the last block FS_PTR was moved
; to. We read this data in memory to avoid constant seek+read operations. ; to. We read this data in memory to avoid constant seek+read operations.
.equ FS_META FS_PTR+2 .equ FS_META FS_PTR+4
.equ FS_HANDLES FS_META+FS_METASIZE .equ FS_HANDLES FS_META+FS_METASIZE
.equ FS_RAMEND FS_HANDLES+FS_HANDLE_COUNT*FS_HANDLE_SIZE .equ FS_RAMEND FS_HANDLES+FS_HANDLE_COUNT*FS_HANDLE_SIZE
@ -133,10 +133,11 @@ fsBegin:
push hl push hl
ld hl, (FS_START) ld hl, (FS_START)
ld (FS_PTR), hl ld (FS_PTR), hl
ld hl, (FS_START+2)
ld (FS_PTR+2), hl
pop hl pop hl
call fsReadMeta call fsReadMeta
call fsIsValid ; sets Z jp fsIsValid ; sets Z, returns
ret
; Change current position to the next block with metadata. If it can't (if this ; Change current position to the next block with metadata. If it can't (if this
; is the last valid block), doesn't move. ; is the last valid block), doesn't move.
@ -156,8 +157,9 @@ fsNext:
call fsblkSeek call fsblkSeek
djnz .loop djnz .loop
; Good, were here. We're going to read meta from our current position. ; Good, were here. We're going to read meta from our current position.
call fsblkTell ; --> HL call fsblkTell ; --> HL, --> DE
ld (FS_PTR), hl ld (FS_PTR), de
ld (FS_PTR+2), hl
call fsReadMeta call fsReadMeta
jr nz, .createChainEnd jr nz, .createChainEnd
call fsIsValid call fsIsValid
@ -226,13 +228,16 @@ fsInitMeta:
pop af pop af
ret ret
; Make sure that our underlying blockdev is correcly placed. ; Make sure that our underlying blockdev is correctly placed.
fsPlace: fsPlace:
push af push af
push hl push hl
push de
xor a xor a
ld hl, (FS_PTR) ld de, (FS_PTR)
ld hl, (FS_PTR+2)
call fsblkSeek call fsblkSeek
pop de
pop hl pop hl
pop af pop af
ret ret
@ -284,7 +289,8 @@ fsAlloc:
; Good, FS_META ready. Now, let's update FS_PTR because it hasn't been ; Good, FS_META ready. Now, let's update FS_PTR because it hasn't been
; changed yet. ; changed yet.
call fsblkTell call fsblkTell
ld (FS_PTR), hl ld (FS_PTR), de
ld (FS_PTR+2), hl
; Ok, now we can write our metadata ; Ok, now we can write our metadata
call fsWriteMeta call fsWriteMeta
.end: .end:
@ -361,6 +367,7 @@ fsblkSeek:
jp _blkSeek jp _blkSeek
fsblkTell: fsblkTell:
ld de, 0
ld ix, (FS_TELL) ld ix, (FS_TELL)
jp _blkCall jp _blkCall
@ -368,57 +375,56 @@ fsblkTell:
; Open file at current position into handle at (HL) ; Open file at current position into handle at (HL)
fsOpen: fsOpen:
push bc
push hl push hl
push de push de
push af push af
ex de, hl ex de, hl
ld hl, (FS_PTR) ; Starting pos
ld a, FS_METASIZE ld hl, FS_PTR
call addHL ld bc, 4
call writeHLinDE ldir
inc de ; Current pos
inc de ld hl, FS_METASIZE
call writeHLinDE
inc de
inc de
; Maximum offset is starting offset + (numblocks * 0x100) - 1
ld a, (FS_META+FS_META_ALLOC_OFFSET)
; Because our blocks are exactly 0x100 in size, we simple have to
; increase the H in HL to have our result.
add a, h
ld h, a
call writeHLinDE
inc de
inc de
ld hl, (FS_META+FS_META_FSIZE_OFFSET)
call writeHLinDE call writeHLinDE
pop af pop af
pop de pop de
pop hl pop hl
pop bc
ret ret
; Place FS blockdev at proper position for file handle in (DE). ; Place FS blockdev at proper position for file handle in (DE).
fsPlaceH: fsPlaceH:
push af push af
push bc
push hl push hl
push de push de
pop ix pop ix
push ix push ix
ld l, (ix) ld e, (ix)
ld h, (ix+1) ld d, (ix+1)
ld l, (ix+2)
ld h, (ix+3)
ld c, (ix+4)
ld b, (ix+5)
add hl, bc
jr nc, .nocarry
inc de
.nocarry:
ld a, BLOCKDEV_SEEK_ABSOLUTE ld a, BLOCKDEV_SEEK_ABSOLUTE
call fsblkSeek call fsblkSeek
pop ix pop ix
pop hl pop hl
pop bc
pop af pop af
ret ret
; Advance file handle in (IX) by one byte ; Advance file handle in (IX) by one byte
fsAdvanceH: fsAdvanceH:
push af push af
inc (ix) inc (ix+4)
jr nz, .end jr nz, .end
inc (ix+1) inc (ix+5)
.end: .end:
pop af pop af
ret ret
@ -426,6 +432,7 @@ fsAdvanceH:
; Read a byte in handle at (DE), put it into A and advance the handle's ; Read a byte in handle at (DE), put it into A and advance the handle's
; position. ; position.
; Z is set on success, unset if handle is at the end of the file. ; Z is set on success, unset if handle is at the end of the file.
; TODO: detect end of file
fsGetC: fsGetC:
push ix push ix
call fsPlaceH call fsPlaceH
@ -441,6 +448,7 @@ fsGetC:
; Write byte A in handle (DE) and advance the handle's position. ; Write byte A in handle (DE) and advance the handle's position.
; Z is set on success, unset if handle is at the end of the file. ; Z is set on success, unset if handle is at the end of the file.
; TODO: detect end of block alloc
fsPutC: fsPutC:
call fsPlaceH call fsPlaceH
push ix push ix
@ -455,15 +463,18 @@ fsPutC:
; Sets Z if offset is within bounds, unsets Z if it isn't. ; Sets Z if offset is within bounds, unsets Z if it isn't.
fsSeek: fsSeek:
push de \ pop ix push de \ pop ix
ld (ix), l ld a, FS_METASIZE
ld (ix+1), h call addHL
ld (ix+4), l
ld (ix+5), h
ret ret
fsTell: fsTell:
push de \ pop ix push de \ pop ix
ld l, (ix) ld l, (ix+4)
ld h, (ix+1) ld h, (ix+5)
ret ld a, FS_METASIZE
jp subHL ; returns
; Mount the fs subsystem upon the currently selected blockdev at current offset. ; Mount the fs subsystem upon the currently selected blockdev at current offset.
; Verify is block is valid and error out if its not, mounting nothing. ; Verify is block is valid and error out if its not, mounting nothing.
@ -479,8 +490,10 @@ fsOn:
ld bc, 8 ; we have 8 bytes to copy ld bc, 8 ; we have 8 bytes to copy
ldir ; copy! ldir ; copy!
call fsblkTell call fsblkTell
ld (FS_START), hl ld (FS_START), de
ld (FS_PTR), hl ld (FS_START+2), hl
ld (FS_PTR), de
ld (FS_PTR+2), hl
call fsReadMeta call fsReadMeta
jr nz, .error jr nz, .error
call fsIsValid call fsIsValid
@ -491,8 +504,7 @@ fsOn:
.error: .error:
; couldn't mount. Let's reset our variables. ; couldn't mount. Let's reset our variables.
xor a xor a
ld b, 10 ; blkdev routines + FS_START which is just ld b, FS_META-FS_GETC ; reset routine pointers and FS ptrs
; after.
ld hl, FS_GETC ld hl, FS_GETC
call fill call fill

View File

@ -31,12 +31,13 @@
#define FS_DATA_PORT 0x01 #define FS_DATA_PORT 0x01
#define FS_SEEKL_PORT 0x02 #define FS_SEEKL_PORT 0x02
#define FS_SEEKH_PORT 0x03 #define FS_SEEKH_PORT 0x03
#define FS_SEEKE_PORT 0x04
static Z80Context cpu; static Z80Context cpu;
static uint8_t mem[0xffff] = {0}; static uint8_t mem[0xffff] = {0};
static uint8_t fsdev[0xffff] = {0}; static uint8_t fsdev[0x20000] = {0};
static uint16_t fsdev_size = 0; static uint32_t fsdev_size = 0;
static uint16_t fsdev_ptr = 0; static uint32_t fsdev_ptr = 0;
static int running; static int running;
static uint8_t io_read(int unused, uint16_t addr) static uint8_t io_read(int unused, uint16_t addr)
@ -57,7 +58,9 @@ static uint8_t io_read(int unused, uint16_t addr)
} else if (addr == FS_SEEKL_PORT) { } else if (addr == FS_SEEKL_PORT) {
return fsdev_ptr & 0xff; return fsdev_ptr & 0xff;
} else if (addr == FS_SEEKH_PORT) { } else if (addr == FS_SEEKH_PORT) {
return fsdev_ptr >> 8; return (fsdev_ptr >> 8) & 0xff;
} else if (addr == FS_SEEKE_PORT) {
return (fsdev_ptr >> 16) & 0xff;
} else { } else {
fprintf(stderr, "Out of bounds I/O read: %d\n", addr); fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
return 0; return 0;
@ -78,9 +81,11 @@ static void io_write(int unused, uint16_t addr, uint8_t val)
fsdev[fsdev_ptr++] = val; fsdev[fsdev_ptr++] = val;
} }
} else if (addr == FS_SEEKL_PORT) { } else if (addr == FS_SEEKL_PORT) {
fsdev_ptr = (fsdev_ptr & 0xff00) | val; fsdev_ptr = (fsdev_ptr & 0xffff00) | val;
} else if (addr == FS_SEEKH_PORT) { } else if (addr == FS_SEEKH_PORT) {
fsdev_ptr = (fsdev_ptr & 0x00ff) | (val << 8); fsdev_ptr = (fsdev_ptr & 0xff00ff) | (val << 8);
} else if (addr == FS_SEEKE_PORT) {
fsdev_ptr = (fsdev_ptr & 0x00ffff) | (val << 16);
} else { } else {
fprintf(stderr, "Out of bounds I/O write: %d / %d\n", addr, val); fprintf(stderr, "Out of bounds I/O write: %d / %d\n", addr, val);
} }

View File

@ -5,6 +5,7 @@
.equ FS_DATA_PORT 0x01 .equ FS_DATA_PORT 0x01
.equ FS_SEEKL_PORT 0x02 .equ FS_SEEKL_PORT 0x02
.equ FS_SEEKH_PORT 0x03 .equ FS_SEEKH_PORT 0x03
.equ FS_SEEKE_PORT 0x04
jp init jp init
@ -80,6 +81,8 @@ fsdevSeek:
out (FS_SEEKL_PORT), a out (FS_SEEKL_PORT), a
ld a, h ld a, h
out (FS_SEEKH_PORT), a out (FS_SEEKH_PORT), a
ld a, e
out (FS_SEEKE_PORT), a
pop af pop af
ret ret
@ -89,6 +92,8 @@ fsdevTell:
ld l, a ld l, a
in a, (FS_SEEKH_PORT) in a, (FS_SEEKH_PORT)
ld h, a ld h, a
in a, (FS_SEEKE_PORT)
ld e, a
pop af pop af
ret ret

View File

@ -98,6 +98,8 @@ fsdevPutC:
fsdevSeek: fsdevSeek:
push af push af
ld a, e
out (FS_SEEK_PORT), a
ld a, h ld a, h
out (FS_SEEK_PORT), a out (FS_SEEK_PORT), a
ld a, l ld a, l
@ -108,6 +110,8 @@ fsdevSeek:
fsdevTell: fsdevTell:
push af push af
in a, (FS_SEEK_PORT) in a, (FS_SEEK_PORT)
ld e, a
in a, (FS_SEEK_PORT)
ld h, a ld h, a
in a, (FS_SEEK_PORT) in a, (FS_SEEK_PORT)
ld l, a ld l, a

Binary file not shown.

View File

@ -47,10 +47,10 @@ static int inpt_size;
static int inpt_ptr; static int inpt_ptr;
static uint8_t middle_of_seek_tell = 0; static uint8_t middle_of_seek_tell = 0;
static uint8_t fsdev[0xffff] = {0}; static uint8_t fsdev[0x20000] = {0};
static uint16_t fsdev_size = 0; static uint32_t fsdev_size = 0;
static uint16_t fsdev_ptr = 0; static uint32_t fsdev_ptr = 0;
static uint8_t fsdev_middle_of_seek_tell = 0; static uint8_t fsdev_seek_tell_cnt = 0;
static uint8_t io_read(int unused, uint16_t addr) static uint8_t io_read(int unused, uint16_t addr)
{ {
@ -79,15 +79,18 @@ static uint8_t io_read(int unused, uint16_t addr)
return 0; return 0;
} }
} else if (addr == FS_SEEK_PORT) { } else if (addr == FS_SEEK_PORT) {
if (fsdev_middle_of_seek_tell) { if (fsdev_seek_tell_cnt == 0) {
fsdev_middle_of_seek_tell = 0;
return fsdev_ptr & 0xff;
} else {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "FS tell %d\n", fsdev_ptr); fprintf(stderr, "FS tell %d\n", fsdev_ptr);
#endif #endif
fsdev_middle_of_seek_tell = 1; fsdev_seek_tell_cnt = 1;
return fsdev_ptr >> 8; return fsdev_ptr >> 16;
} else if (fsdev_seek_tell_cnt == 1) {
fsdev_seek_tell_cnt = 2;
return (fsdev_ptr >> 8) & 0xff;
} else {
fsdev_seek_tell_cnt = 0;
return fsdev_ptr & 0xff;
} }
} else { } else {
fprintf(stderr, "Out of bounds I/O read: %d\n", addr); fprintf(stderr, "Out of bounds I/O read: %d\n", addr);
@ -119,15 +122,18 @@ static void io_write(int unused, uint16_t addr, uint8_t val)
fsdev[fsdev_ptr++] = val; fsdev[fsdev_ptr++] = val;
} }
} else if (addr == FS_SEEK_PORT) { } else if (addr == FS_SEEK_PORT) {
if (fsdev_middle_of_seek_tell) { if (fsdev_seek_tell_cnt == 0) {
fsdev_ptr = val << 16;
fsdev_seek_tell_cnt = 1;
} else if (fsdev_seek_tell_cnt == 1) {
fsdev_ptr |= val << 8;
fsdev_seek_tell_cnt = 2;
} else {
fsdev_ptr |= val; fsdev_ptr |= val;
fsdev_middle_of_seek_tell = 0; fsdev_seek_tell_cnt = 0;
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "FS seek %d\n", fsdev_ptr); fprintf(stderr, "FS seek %d\n", fsdev_ptr);
#endif #endif
} else {
fsdev_ptr = (val << 8) & 0xff00;
fsdev_middle_of_seek_tell = 1;
} }
} else if (addr == STDERR_PORT) { } else if (addr == STDERR_PORT) {
fputc(val, stderr); fputc(val, stderr);

View File

@ -0,0 +1,56 @@
jp test
#include "core.asm"
testNum: .db 1
test:
ld hl, 0xffff
ld sp, hl
ld hl, 0x123
ld a, 0x25
call subHL
ld a, h
cp 0
jp nz, fail
ld a, l
cp 0xfe
jp nz, fail
call nexttest
ld hl, 0x125
ld a, 0x23
call subHL
ld a, h
cp 1
jp nz, fail
ld a, l
cp 0x02
jp nz, fail
call nexttest
ld hl, 0x125
ld a, 0x25
call subHL
ld a, h
cp 1
jp nz, fail
ld a, l
cp 0
jp nz, fail
call nexttest
; success
xor a
halt
nexttest:
ld a, (testNum)
inc a
ld (testNum), a
ret
fail:
ld a, (testNum)
halt