|
- ; 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 module exposes a seek/tell/getc/putc 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.
- ;
- ; GetC:
- ; Reads one character 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.
- ;
- ; 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"
- ; definitions possible.
-
-
- ; *** DEFINES ***
- ; BLOCKDEV_COUNT: The number of devices we manage.
-
- ; *** CONSTS ***
- .equ BLOCKDEV_SEEK_ABSOLUTE 0
- .equ BLOCKDEV_SEEK_FORWARD 1
- .equ BLOCKDEV_SEEK_BACKWARD 2
- .equ BLOCKDEV_SEEK_BEGINNING 3
- .equ BLOCKDEV_SEEK_END 4
-
- .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.
- .equ BLOCKDEV_SEL BLOCKDEV_RAMSTART
- .equ BLOCKDEV_RAMEND BLOCKDEV_SEL+BLOCKDEV_SIZE
-
- ; *** CODE ***
- ; Select block index specified in A and place them in routine pointers at (DE).
- ; For example, for a "regular" blkSel, you will want to set DE to BLOCKDEV_SEL.
- blkSel:
- push af
- push de
- push hl
-
- ld hl, blkDevTbl
- or a ; cp 0
- jr z, .end ; index is zero? don't loop
- push bc ; <|
- ld b, a ; |
- .loop: ; |
- ld a, 4 ; |
- call addHL ; |
- djnz .loop ; |
- pop bc ; <|
- .end:
- call blkSet
- pop hl
- pop de
- pop af
- ret
-
- ; Setup blkdev handle in (DE) using routines at (HL).
- blkSet:
- push af
- push de
- push hl
-
- ; Write GETC
- push hl ; <|
- call intoHL ; |
- call writeHLinDE ; |
- inc de ; |
- inc de ; |
- pop hl ; <|
- inc hl
- inc hl
- ; Write PUTC
- call intoHL
- call writeHLinDE
- inc de
- inc de
- ; Initialize pos
- xor a
- ld (de), a
- inc de
- ld (de), a
- inc de
- ld (de), a
- inc de
- ld (de), a
-
- pop hl
- pop de
- pop af
- ret
-
- _blkInc:
- ret nz ; don't advance when in error condition
- push af
- push hl
- ld a, BLOCKDEV_SEEK_FORWARD
- ld hl, 1
- call _blkSeek
- pop hl
- pop af
- ret
-
- ; Reads one character from selected device and returns its value in A.
- ; Sets Z according to whether read was successful: Set if successful, unset
- ; if not.
- blkGetC:
- push ix
- ld ix, BLOCKDEV_SEL
- call _blkGetC
- pop ix
- ret
- _blkGetC:
- push hl
- push de
- call _blkTell
- call callIXI
- pop de
- 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:
- push ix
- ld ix, BLOCKDEV_SEL
- call _blkPutC
- pop ix
- ret
- _blkPutC:
- push ix
- push hl
- push de
- call _blkTell
- inc ix ; make IX point to PutC
- inc ix
- call callIXI
- pop de
- pop hl
- pop ix
- jr _blkInc ; advance and return
-
- ; Reads B chars from blkGetC and copy them in (HL).
- ; Sets Z if successful, unset Z if there was an error.
- blkRead:
- push ix
- ld ix, BLOCKDEV_SEL
- call _blkRead
- pop ix
- ret
- _blkRead:
- push hl
- push bc
- .loop:
- call _blkGetC
- jr nz, .end ; Z already unset
- ld (hl), a
- inc hl
- djnz .loop
- cp a ; ensure Z
- .end:
- pop bc
- pop hl
- ret
-
- ; Writes B chars to blkPutC from (HL).
- ; Sets Z if successful, unset Z if there was an error.
- blkWrite:
- push ix
- ld ix, BLOCKDEV_SEL
- call _blkWrite
- pop ix
- ret
- _blkWrite:
- push hl
- push bc
- .loop:
- ld a, (hl)
- call _blkPutC
- jr nz, .end ; Z already unset
- inc hl
- djnz .loop
- cp a ; ensure Z
- .end:
- pop bc
- pop hl
- ret
-
- ; Seeks the block device in one of 5 modes, which is the A argument:
- ; 0 : Move exactly to X, X being the HL/DE 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 (no DE)
- ; 3 : Move to the end
- ; 4 : Move to the beginning
-
- ; 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
- ; one position ahead of the last valid position. Therefore, GetC 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.
- blkSeek:
- push ix
- ld ix, BLOCKDEV_SEL
- call _blkSeek
- pop ix
- ret
- _blkSeek:
- cp BLOCKDEV_SEEK_FORWARD
- jr z, .forward
- cp BLOCKDEV_SEEK_BACKWARD
- jr z, .backward
- cp BLOCKDEV_SEEK_BEGINNING
- jr z, .beginning
- cp BLOCKDEV_SEEK_END
- jr z, .end
- ; all other modes are considered absolute
- ld (ix+4), e
- ld (ix+5), d
- ld (ix+6), l
- ld (ix+7), h
- ret
- .forward:
- push bc ; <-|
- push hl ; <||
- ld l, (ix+6) ; || low byte
- ld h, (ix+7) ; ||
- pop bc ; <||
- add hl, bc ; |
- pop bc ; <-|
- ld (ix+6), l
- ld (ix+7), h
- ret nc ; no carry? no need to adjust high byte
- ; carry, adjust high byte
- inc (ix+4)
- ret nz
- inc (ix+5)
- ret
- .backward:
- and a ; clear carry
- push bc ; <-|
- push hl ; <||
- ld l, (ix+6) ; || low byte
- ld h, (ix+7) ; ||
- pop bc ; <||
- sbc hl, bc ; |
- pop bc ; <-|
- ld (ix+6), l
- ld (ix+7), h
- ret nc ; no carry? no need to adjust high byte
- ld a, 0xff
- dec (ix+4)
- cp (ix+4)
- ret nz
- ; we decremented from 0
- dec (ix+5)
- ret
- .beginning:
- xor a
- ld (ix+4), a
- ld (ix+5), a
- ld (ix+6), a
- ld (ix+7), a
- ret
- .end:
- ld a, 0xff
- ld (ix+4), a
- ld (ix+5), a
- ld (ix+6), a
- ld (ix+7), a
- ret
-
- ; Returns the current position of the selected device in HL (low) and DE (high).
- blkTell:
- push ix
- ld ix, BLOCKDEV_SEL
- call _blkTell
- pop ix
- ret
- _blkTell:
- ld e, (ix+4)
- ld d, (ix+5)
- ld l, (ix+6)
- ld h, (ix+7)
- ret
-
- ; 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
- blkDevTbl:
|