2019-04-16 06:53:11 +10:00
|
|
|
; 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 ***
|
2019-04-16 10:38:25 +10:00
|
|
|
BLOCKDEV_ERR_OUT_OF_BOUNDS .equ 0x03
|
|
|
|
|
2019-04-16 06:53:11 +10:00
|
|
|
; *** VARIABLES ***
|
|
|
|
; A memory pointer to a device table. A device table is a list of addresses
|
2019-04-16 11:56:15 +10:00
|
|
|
; pointing to GetC, PutC and Seek routines.
|
2019-04-16 06:53:11 +10:00
|
|
|
BLOCKDEV_TBL .equ BLOCKDEV_RAMSTART
|
2019-04-16 11:56:15 +10:00
|
|
|
; Pointer to the selected block device. A block device is a 6 bytes block of
|
|
|
|
; memory with pointers to GetC, PutC and Seek routines, in that order. 0 means
|
|
|
|
; unsupported.
|
|
|
|
BLOCKDEV_SEL .equ BLOCKDEV_TBL+(BLOCKDEV_COUNT*2)
|
|
|
|
BLOCKDEV_RAMEND .equ BLOCKDEV_SEL+2
|
2019-04-16 06:53:11 +10:00
|
|
|
|
|
|
|
; *** 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
|
|
|
|
.loop:
|
2019-04-16 11:56:15 +10:00
|
|
|
inc de
|
|
|
|
inc de
|
2019-04-16 06:53:11 +10:00
|
|
|
djnz .loop
|
|
|
|
pop bc
|
|
|
|
ret
|
|
|
|
|
2019-04-16 11:56:15 +10:00
|
|
|
; Set the pointer of device id A to the value in HL
|
|
|
|
blkSet:
|
2019-04-16 06:53:11 +10:00
|
|
|
call blkFind
|
|
|
|
call writeHLinDE
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Select block index specified in A
|
|
|
|
blkSel:
|
2019-04-16 11:56:15 +10:00
|
|
|
push de
|
|
|
|
push hl
|
2019-04-16 06:53:11 +10:00
|
|
|
call blkFind
|
2019-04-16 11:56:15 +10:00
|
|
|
ld hl, BLOCKDEV_SEL
|
2019-04-16 06:53:11 +10:00
|
|
|
ex hl, de
|
|
|
|
ldi
|
2019-04-16 11:56:15 +10:00
|
|
|
pop hl
|
|
|
|
pop de
|
2019-04-16 06:53:11 +10:00
|
|
|
ret
|
|
|
|
|
|
|
|
blkBselCmd:
|
|
|
|
.db "bsel", 0b001, 0, 0
|
|
|
|
blkBsel:
|
|
|
|
ld a, (hl) ; argument supplied
|
|
|
|
cp BLOCKDEV_COUNT
|
2019-04-16 10:38:25 +10:00
|
|
|
jr nc, .error ; if selection >= device count, error
|
2019-04-16 06:53:11 +10:00
|
|
|
call blkSel
|
2019-04-16 10:38:25 +10:00
|
|
|
xor a
|
|
|
|
ret
|
|
|
|
.error:
|
|
|
|
ld a, BLOCKDEV_ERR_OUT_OF_BOUNDS
|
2019-04-16 06:53:11 +10:00
|
|
|
ret
|
|
|
|
|
2019-04-16 11:56:15 +10:00
|
|
|
; In those routines below, IY is destroyed (we don't push it to the stack). We
|
|
|
|
; seldom use it anyways...
|
|
|
|
|
|
|
|
; call routine in BLOCKDEV_SEL with offset IYL.
|
|
|
|
_blkCall:
|
2019-04-16 06:53:11 +10:00
|
|
|
push ix
|
|
|
|
push de
|
2019-04-16 11:56:15 +10:00
|
|
|
ld de, (BLOCKDEV_SEL)
|
|
|
|
; DE now points to the *address table*, not the routine addresses
|
|
|
|
; themselves. One layer of indirection left.
|
|
|
|
; slide by offset
|
|
|
|
push af
|
|
|
|
ld a, iyl
|
|
|
|
call addDE ; slide by offset
|
|
|
|
pop af
|
|
|
|
call intoDE
|
|
|
|
; Alright, now de points to what we want to call
|
2019-04-16 06:53:11 +10:00
|
|
|
ld ixh, d
|
|
|
|
ld ixl, e
|
|
|
|
pop de
|
|
|
|
call callIX
|
|
|
|
pop ix
|
|
|
|
ret
|
|
|
|
|
2019-04-16 11:56:15 +10:00
|
|
|
; 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:
|
|
|
|
ld iyl, 0
|
|
|
|
jr _blkCall
|
|
|
|
|
2019-04-16 06:53:11 +10:00
|
|
|
blkPutC:
|
2019-04-16 11:56:15 +10:00
|
|
|
ld iyl, 2
|
|
|
|
jr _blkCall
|
|
|
|
|
|
|
|
blkSeek:
|
|
|
|
ld iyl, 4
|
|
|
|
jr _blkCall
|
|
|
|
|