; 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 *** BLOCKDEV_ERR_OUT_OF_BOUNDS .equ 0x03 BLOCKDEV_ERR_UNSUPPORTED .equ 0x04 ; *** VARIABLES *** ; 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_RAMSTART BLOCKDEV_RAMEND .equ BLOCKDEV_SEL+2 ; *** CODE *** ; Select block index specified in A blkSel: push af push hl ld hl, blkDevTbl cp 0 jr z, .afterloop ; index is zero? don't loop push bc ld b, a .loop: ld a, 6 call addHL djnz .loop pop bc .afterloop: ld (BLOCKDEV_SEL), hl pop Hl pop af ret blkBselCmd: .db "bsel", 0b001, 0, 0 ld a, (hl) ; argument supplied cp BLOCKDEV_COUNT jr nc, .error ; if selection >= device count, error call blkSel xor a ret .error: ld a, BLOCKDEV_ERR_OUT_OF_BOUNDS ret ; 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: push ix push de 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 ld ixh, d ld ixl, e pop de ; Before we call... is it zero? We don't want to call a zero. push af ld a, ixh add a, ixl jr c, .ok ; if there's a carry, it isn't zero cp 0 jr z, .error ; if no carry and zero, then both numbers are ; zero .ok: pop af call callIX jr .end .error: pop af ld a, BLOCKDEV_ERR_UNSUPPORTED .end: pop ix ret ; Reads one character from selected device and returns its value in A. Always ; returns a character and waits until read if it has to. blkGetC: ld iyl, 0 jr _blkCall ; Writes character in A in current position in the selected device blkPutC: ld iyl, 2 jr _blkCall blkSeekCmd: .db "seek", 0b011, 0b001, 0 ; HL points to two bytes that contain out address. Seek expects HL ; to directly contain that address. ld a, (hl) ex af, af' inc hl ld a, (hl) ld l, a ex af, af' ld h, a xor a ; Set position of selected device to the value specified in HL blkSeek: ld iyl, 4 jr _blkCall ; 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 3 word addresses: GetC, PutC and Seek. An entry could look like: ; .dw mmapGetC, mmapPutC, mmapSeek blkDevTbl: