sdc: add layer of indirection to buffer system
Also, lay out the plan for adding a second buffer.
This commit is contained in:
parent
b742d48b75
commit
db24e21276
162
kernel/sdc.asm
162
kernel/sdc.asm
@ -15,6 +15,39 @@
|
|||||||
; then placed on a buffer that can be read by reading the same port.
|
; then placed on a buffer that can be read by reading the same port.
|
||||||
;
|
;
|
||||||
; It's through that kind of device that this code below is supposed to work.
|
; It's through that kind of device that this code below is supposed to work.
|
||||||
|
;
|
||||||
|
; *** SDC buffers ***
|
||||||
|
;
|
||||||
|
; 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
|
||||||
|
; 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
|
||||||
|
; buffer, we load a new block.
|
||||||
|
;
|
||||||
|
; When we PutC, 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.
|
||||||
|
;
|
||||||
|
; We hold 2 buffers in memory, each targeting a different sector and with its
|
||||||
|
; own dirty flag. We do that to avoid wasteful block writing in the case where
|
||||||
|
; we read data from a file in the SD card, process it and write the result
|
||||||
|
; 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
|
||||||
|
; 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
|
||||||
|
; 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
|
||||||
|
; addresses, we ensure that we don't write wastefully because read operations,
|
||||||
|
; even if random, will always use the one buffer that isn't dirty.
|
||||||
|
;
|
||||||
|
; NOTE: the 2-buffers thing is still a work in progress...
|
||||||
|
|
||||||
; *** Defines ***
|
; *** Defines ***
|
||||||
; SDC_PORT_CSHIGH: Port number to make CS high
|
; SDC_PORT_CSHIGH: Port number to make CS high
|
||||||
@ -25,15 +58,16 @@
|
|||||||
.equ SDC_BLKSIZE 512
|
.equ SDC_BLKSIZE 512
|
||||||
|
|
||||||
; *** Variables ***
|
; *** Variables ***
|
||||||
; Whenever we read a sector, we read a whole block at once and we store it
|
; This is a pointer to the currently selected buffer. This points to the BUFSEC
|
||||||
; in memory. That's where it goes.
|
; part, that is, two bytes before actual content begins.
|
||||||
.equ SDC_BUF SDC_RAMSTART
|
.equ SDC_BUFPTR SDC_RAMSTART
|
||||||
; Sector number currently in SDC_BUF. 0xff, it's initial value, means "no
|
; Sector number currently in SDC_BUF.
|
||||||
; sector.
|
.equ SDC_BUFSEC SDC_BUFPTR+2
|
||||||
.equ SDC_BUFSEC SDC_BUF+SDC_BLKSIZE
|
|
||||||
; Whether the buffer has been written to. 0 means clean. 1 means dirty.
|
; Whether the buffer has been written to. 0 means clean. 1 means dirty.
|
||||||
.equ SDC_BUFDIRTY SDC_BUFSEC+1
|
.equ SDC_BUFDIRTY SDC_BUFSEC+1
|
||||||
.equ SDC_RAMEND SDC_BUFDIRTY+1
|
; The contents of the buffer.
|
||||||
|
.equ SDC_BUF SDC_BUFDIRTY+1
|
||||||
|
.equ SDC_RAMEND SDC_BUF+SDC_BLKSIZE
|
||||||
|
|
||||||
; *** Code ***
|
; *** Code ***
|
||||||
; Wake the SD card up. After power up, a SD card has to receive at least 74
|
; Wake the SD card up. After power up, a SD card has to receive at least 74
|
||||||
@ -218,6 +252,8 @@ sdcInitialize:
|
|||||||
jr nz, .error
|
jr nz, .error
|
||||||
; Success! out of idle mode!
|
; Success! out of idle mode!
|
||||||
; initialize variables
|
; initialize variables
|
||||||
|
ld hl, SDC_BUFSEC
|
||||||
|
ld (SDC_BUFPTR), hl
|
||||||
ld a, 0xff
|
ld a, 0xff
|
||||||
ld (SDC_BUFSEC), a
|
ld (SDC_BUFSEC), a
|
||||||
xor a
|
xor a
|
||||||
@ -248,9 +284,10 @@ sdcSetBlkSize:
|
|||||||
pop hl
|
pop hl
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; Read block index specified in A and place the contents in (SDC_BUF).
|
; Read block index specified in A and place the contents in buffer pointed to
|
||||||
; Doesn't check CRC. If the operation is a success, updates (SDC_BUFSEC) to the
|
; by (SDC_BUFPTR).
|
||||||
; value of A.
|
; Doesn't check CRC. If the operation is a success, updates buffer's sector to
|
||||||
|
; the value of A.
|
||||||
; Returns 0 in A if success, non-zero if error.
|
; Returns 0 in A if success, non-zero if error.
|
||||||
sdcReadBlk:
|
sdcReadBlk:
|
||||||
push bc
|
push bc
|
||||||
@ -281,7 +318,17 @@ sdcReadBlk:
|
|||||||
; We received our data token!
|
; We received our data token!
|
||||||
; Data packets follow immediately, we have 512 of them to read
|
; Data packets follow immediately, we have 512 of them to read
|
||||||
ld bc, SDC_BLKSIZE
|
ld bc, SDC_BLKSIZE
|
||||||
ld hl, SDC_BUF
|
ld hl, (SDC_BUFPTR) ; HL --> active buffer's sector
|
||||||
|
; It sounds a bit wrong to set bufsec and dirty flag before we get our
|
||||||
|
; actual data, but at this point, we don't have any error conditions
|
||||||
|
; left, success is guaranteed. To avoid needlesssly INCing hl, let's
|
||||||
|
; set sector and dirty along the way
|
||||||
|
ld a, e ; sector number
|
||||||
|
ld (hl), a
|
||||||
|
inc hl ; dirty flag
|
||||||
|
xor a ; unset
|
||||||
|
ld (hl), a
|
||||||
|
inc hl ; actual contents
|
||||||
.loop2:
|
.loop2:
|
||||||
call sdcIdle
|
call sdcIdle
|
||||||
ld (hl), a
|
ld (hl), a
|
||||||
@ -291,11 +338,7 @@ sdcReadBlk:
|
|||||||
; Read our 2 CRC bytes
|
; Read our 2 CRC bytes
|
||||||
call sdcIdle
|
call sdcIdle
|
||||||
call sdcIdle
|
call sdcIdle
|
||||||
; success! Let's recall our orginal A arg and put it in SDC_BUFSEC
|
; success!
|
||||||
ld a, e
|
|
||||||
ld (SDC_BUFSEC), a
|
|
||||||
xor a
|
|
||||||
ld (SDC_BUFDIRTY), a
|
|
||||||
jr .end
|
jr .end
|
||||||
.error:
|
.error:
|
||||||
; try to preserve error code
|
; try to preserve error code
|
||||||
@ -308,19 +351,25 @@ sdcReadBlk:
|
|||||||
pop bc
|
pop bc
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; Write the contents of (SDC_BUF) in sector number (SDC_BUFSEC). Unsets the
|
; Write the contents of buffer where (SDC_BUFPTR) points to in sector associated
|
||||||
; (SDC_BUFDIRTY) flag on success.
|
; to it. Unsets the the buffer's dirty flag on success.
|
||||||
; A returns 0 in A on success (with Z set), non-zero (with Z unset) on error.
|
; A returns 0 in A on success (with Z set), non-zero (with Z unset) on error.
|
||||||
sdcWriteBlk:
|
sdcWriteBlk:
|
||||||
ld a, (SDC_BUFDIRTY)
|
push hl
|
||||||
or a ; cp 0
|
ld hl, (SDC_BUFPTR) ; HL points to sector
|
||||||
ret z ; return success, but do nothing.
|
inc hl ; now to dirty flag
|
||||||
|
xor a
|
||||||
|
cp (hl)
|
||||||
|
jr z, .dontWrite ; A is already 0
|
||||||
|
|
||||||
|
; At this point, HL points to dirty flag of the proper buffer
|
||||||
|
|
||||||
push bc
|
push bc
|
||||||
push hl
|
push de
|
||||||
|
|
||||||
out (SDC_PORT_CSLOW), a
|
out (SDC_PORT_CSLOW), a
|
||||||
ld a, (SDC_BUFSEC)
|
dec hl ; sector
|
||||||
|
ld a, (hl)
|
||||||
ld hl, 0 ; write single block at addr A
|
ld hl, 0 ; write single block at addr A
|
||||||
ld d, 0
|
ld d, 0
|
||||||
ld e, a
|
ld e, a
|
||||||
@ -340,7 +389,10 @@ sdcWriteBlk:
|
|||||||
|
|
||||||
; Sending our data token!
|
; Sending our data token!
|
||||||
ld bc, SDC_BLKSIZE
|
ld bc, SDC_BLKSIZE
|
||||||
ld hl, SDC_BUF
|
ld hl, (SDC_BUFPTR)
|
||||||
|
inc hl ; dirty flag
|
||||||
|
inc hl ; beginning of contents
|
||||||
|
|
||||||
.loop:
|
.loop:
|
||||||
ld a, (hl)
|
ld a, (hl)
|
||||||
call sdcSendRecv
|
call sdcSendRecv
|
||||||
@ -359,8 +411,11 @@ sdcWriteBlk:
|
|||||||
; good! Now, we need to let the card process this data. It will return
|
; good! Now, we need to let the card process this data. It will return
|
||||||
; 0xff when it's not busy any more.
|
; 0xff when it's not busy any more.
|
||||||
call sdcWaitResp
|
call sdcWaitResp
|
||||||
|
; Success! Now let's unset the first flag
|
||||||
|
ld hl, (SDC_BUFPTR)
|
||||||
|
inc hl ; dirty flag
|
||||||
xor a
|
xor a
|
||||||
ld (SDC_BUFDIRTY), a
|
ld (hl), a
|
||||||
jr .end
|
jr .end
|
||||||
.error:
|
.error:
|
||||||
; try to preserve error code
|
; try to preserve error code
|
||||||
@ -369,29 +424,31 @@ sdcWriteBlk:
|
|||||||
inc a ; zero, adjust
|
inc a ; zero, adjust
|
||||||
.end:
|
.end:
|
||||||
out (SDC_PORT_CSHIGH), a
|
out (SDC_PORT_CSHIGH), a
|
||||||
pop hl
|
pop de
|
||||||
pop bc
|
pop bc
|
||||||
|
.dontWrite:
|
||||||
|
pop hl
|
||||||
ret
|
ret
|
||||||
|
|
||||||
; Ensures that (SDC_BUFSEC) is in sync with HL, that is, that the current
|
; Ensures that the sector of the current buffer is in sync with HL, that is,
|
||||||
; buffer in memory corresponds to where HL points to. If it doesn't, loads
|
; that the current buffer in memory corresponds to where HL points to in the SD
|
||||||
; the sector that HL points to in (SDC_BUF) and update (SDC_BUFSEC).
|
; card. If it doesn't, loads the sector specified in the highest 7 bits of HL
|
||||||
; If the (SDC_BUFDIRTY) flag is set, we write the content of the in-memory
|
; in memory and update the buffer's sector.
|
||||||
; buffer to the SD card before we read a new sector.
|
; If the dirty flag is set, we write the content of the in-memory buffer to the
|
||||||
|
; SD card before we read a new sector.
|
||||||
; Returns Z on success, not-Z on error (with the error code from either
|
; Returns Z on success, not-Z on error (with the error code from either
|
||||||
; sdcReadBlk or sdcWriteBlk)
|
; sdcReadBlk or sdcWriteBlk)
|
||||||
sdcSync:
|
sdcSync:
|
||||||
; HL points to the character we're supposed to read or right now,
|
; HL points to the character we're supposed to read or right now,
|
||||||
; but we first have to check whether we need to load a new sector in
|
; but we first have to check whether we need to load a new sector in
|
||||||
; memory. To do this, we compare the high 7 bits of HL with
|
; memory. To do this, we compare the high 7 bits of HL with
|
||||||
; (SDC_BUFSEC). If they're different, we need to load a new block.
|
; the buffer's sector. If they're different, we need to load a new block.
|
||||||
push hl
|
|
||||||
ld a, (SDC_BUFSEC)
|
|
||||||
ld l, a
|
|
||||||
ld a, h
|
ld a, h
|
||||||
srl a
|
srl a ; A --> the requested sector number
|
||||||
cp l
|
push hl ; <|
|
||||||
pop hl
|
ld hl, (SDC_BUFPTR) ; | HL points to sector number
|
||||||
|
cp (hl) ; |
|
||||||
|
pop hl ; <|
|
||||||
ret z ; equal? nothing to do
|
ret z ; equal? nothing to do
|
||||||
; We have to read a new sector, but first, let's write the current one
|
; We have to read a new sector, but first, let's write the current one
|
||||||
; if needed.
|
; if needed.
|
||||||
@ -423,22 +480,25 @@ sdcFlushCmd:
|
|||||||
_sdcPlaceBuf:
|
_sdcPlaceBuf:
|
||||||
call sdcSync
|
call sdcSync
|
||||||
ret nz ; error
|
ret nz ; error
|
||||||
|
push de
|
||||||
|
ld de, (SDC_BUFPTR)
|
||||||
|
inc de ; dirty flag
|
||||||
|
inc de ; contents
|
||||||
ld a, h ; high byte
|
ld a, h ; high byte
|
||||||
and 0x01 ; is first bit set?
|
and 0x01 ; is first bit set?
|
||||||
ld a, l ; doesn't change flags
|
jr z, .read ; first bit reset? we're in the "lowbuf" zone.
|
||||||
jr nz, .highbuf ; first bit set? we're in the "highbuf" zone.
|
; DE already points to the right place.
|
||||||
; lowbuf zone
|
; We're in the highbuf zone, let's inc DE by 0x100, which, as it turns
|
||||||
; Read byte from memory at proper offset in lowbuf (first 0x100 bytes)
|
; out, is quite easy.
|
||||||
ld hl, SDC_BUF
|
inc d
|
||||||
jr .read
|
|
||||||
.highbuf:
|
|
||||||
; Read byte from memory at proper offset in highbuf (0x100-0x1ff)
|
|
||||||
ld hl, SDC_BUF+0x100
|
|
||||||
.read:
|
.read:
|
||||||
; HL is now placed either on the lower or higher half of SDC_BUF and
|
; DE is now placed either on the lower or higher half of the active
|
||||||
; all we need is to increase HL by the number in A which is the lower
|
; buffer and all we need is to increase DE the lower half of HL.
|
||||||
; half of our former HL value.
|
ld a, l
|
||||||
call addHL
|
call addDE
|
||||||
|
ex de, hl
|
||||||
|
pop de
|
||||||
|
; Now, HL points exactly at the right byte in the active buffer.
|
||||||
xor a ; ensure Z
|
xor a ; ensure Z
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user