Add apps/sdct

A new app to stress test the SD card driver. Also, accompanying this
commit, changes solidifying the SD card driver so that stress tests
actually pass :)
This commit is contained in:
Virgil Dupras 2019-06-10 15:13:46 -04:00
parent 8b638f6899
commit 145b48efb7
7 changed files with 136 additions and 22 deletions

4
apps/sdct/README.md Normal file
View File

@ -0,0 +1,4 @@
# sdct - test SD Card
This program stress-tests a SD card by repeatedly reading and writing to it and
verify that data stays the same.

27
apps/sdct/glue.asm Normal file
View File

@ -0,0 +1,27 @@
; sdct
;
; We want to test reading and writing random data in random sequences of
; sectors. Collapse OS doesn't have a random number generator, so we'll simply
; rely on initial SRAM value, which tend is random enough for our purpose.
;
; How it works is simple. From its designated RAMSTART, it calls PutC until it
; reaches the end of RAM (0xffff). Then, it starts over and this time it reads
; every byte and compares.
;
; If there's an error, prints out where.
;
; *** Requirements ***
; sdcPutC
; sdcGetC
; printstr
; printHexPair
;
; *** Includes ***
#include "user.h"
.org USER_CODE
.equ SDCT_RAMSTART USER_RAMSTART
jp sdctMain
#include "sdct/main.asm"

64
apps/sdct/main.asm Normal file
View File

@ -0,0 +1,64 @@
sdctMain:
ld hl, .sWriting
call printstr
ld hl, 0
ld de, SDCT_RAMSTART
.wLoop:
ld a, (de)
call sdcPutC
jr nz, .error
inc hl
inc de
; Stop looping if DE == 0
xor a
cp e
jr nz, .wLoop
; print some kind of progress
call printHexPair
cp d
jr nz, .wLoop
; Finished writing
ld hl, .sReading
call printstr
ld hl, 0
ld de, SDCT_RAMSTART
.rLoop:
call sdcGetC
jr nz, .error
ex de, hl
cp (hl)
ex de, hl
jr nz, .notMatching
inc hl
inc de
; Stop looping if DE == 0
xor a
cp d
jr nz, .rLoop
cp e
jr nz, .rLoop
; Finished checking
xor a
ld hl, .sOk
jp printstr ; returns
.notMatching:
; error position is in HL, let's preserve it
ex de, hl
ld hl, .sNotMatching
call printstr
ex de, hl
jp printHexPair ; returns
.error:
ld hl, .sErr
jp printstr ; returns
.sWriting:
.db "Writing", 0xd, 0xa, 0
.sReading:
.db "Reading", 0xd, 0xa, 0
.sNotMatching:
.db "Not matching at pos ", 0xd, 0xa, 0
.sErr:
.db "Error", 0xd, 0xa, 0
.sOk:
.db "OK", 0xd, 0xa, 0

View File

@ -126,15 +126,12 @@ sdcWaitResp:
; This has no error condition and preserves A
sdcWaitReady:
push af
push bc
ld b, 20
; for now, we have no timeout for waiting. It means that broken SD
; cards can cause infinite loops.
.loop:
call sdcIdle
inc a ; if 0xff, it's going to become zero
jr z, .end ; zero? good, we're not busy any more
djnz .loop
.end:
pop bc
jr nz, .loop ; not zero? still busy. loop
pop af
ret
@ -169,6 +166,9 @@ sdcCmd:
call sdcSendRecv
; send CRC
ld a, c
; Most of the time, we don't care about C, but in all cases, we want
; the last bit to be high. It's the stop bit.
or 0x01
call sdcSendRecv
; And now we just have to wait for a valid response...
@ -275,20 +275,6 @@ sdcInitialize:
or a ; cp 0
jr nz, .error
; Success! out of idle mode!
; At this point, our buffers are innitialized. We could have some logic
; that determines whether a buffer is initialized in appropriate SDC
; routines and act appropriately, but why bother when we could, instead,
; just buffer the first two sectors of the card on initialization? This
; way, no need for special conditions.
; initialize variables
ld hl, SDC_BUFSEC1
xor a
ld (SDC_BUFPTR), hl
call sdcReadBlk ; read sector 0 in buf1
ld hl, SDC_BUFSEC2
inc a
ld (SDC_BUFPTR), hl
call sdcReadBlk ; read sector 1 in buf2
jr .end
.error:
@ -322,6 +308,7 @@ sdcSetBlkSize:
; Returns 0 in A if success, non-zero if error.
sdcReadBlk:
push bc
push de
push hl
out (SDC_PORT_CSLOW), a
@ -371,6 +358,7 @@ sdcReadBlk:
call sdcIdle
; success! wait until card is ready
call sdcWaitReady
xor a ; success
jr .end
.error:
; try to preserve error code
@ -380,6 +368,7 @@ sdcReadBlk:
.end:
out (SDC_PORT_CSHIGH), a
pop hl
pop de
pop bc
ret
@ -451,6 +440,8 @@ sdcWriteBlk:
; Before returning, wait until card is ready
call sdcWaitReady
xor a
; A is already 0
jr .end
.error:
; try to preserve error code
@ -538,7 +529,23 @@ sdcSync:
sdcInitializeCmd:
.db "sdci", 0, 0, 0
call sdcInitialize
jp sdcSetBlkSize ; returns
ret nz
call sdcSetBlkSize
ret nz
; At this point, our buffers are unnitialized. We could have some logic
; that determines whether a buffer is initialized in appropriate SDC
; routines and act appropriately, but why bother when we could, instead,
; just buffer the first two sectors of the card on initialization? This
; way, no need for special conditions.
; initialize variables
ld hl, SDC_BUFSEC1
xor a
ld (SDC_BUFPTR), hl
call sdcReadBlk ; read sector 0 in buf1
ld hl, SDC_BUFSEC2
inc a
ld (SDC_BUFPTR), hl
jp sdcReadBlk ; read sector 1 in buf2, returns
; Flush the current SDC buffer if dirty
sdcFlushCmd:
@ -615,7 +622,10 @@ sdcPutC:
xor a ; ensure Z
jr .end
.error:
; preserve error code
ex af, af'
pop af
ex af, af'
call unsetZ
.end:
pop hl

View File

@ -1,4 +1,4 @@
CFSTARGETS = cfsin/zasm cfsin/user.h
CFSTARGETS = cfsin/zasm cfsin/sdct cfsin/user.h
BASE = ../../..
TOOLS = $(BASE)/tools
ZASM = $(TOOLS)/zasm.sh
@ -21,5 +21,8 @@ sdcard.cfs: $(CFSTARGETS) $(CFSPACK)
cfsin/zasm: $(ZASMBIN)
$(ZASM) $(KERNEL) $(APPS) user.h < $(APPS)/zasm/glue.asm > $@
cfsin/sdct: $(ZASMBIN)
$(ZASM) $(APPS) user.h < $(APPS)/sdct/glue.asm > $@
cfsin/user.h: user.h
cp $< $@

View File

@ -41,6 +41,9 @@ jp aciaInt
jp _blkPutC
jp _blkSeek
jp _blkTell
jp printHexPair
jp sdcGetC
jp sdcPutC
#include "err.h"
#include "core.asm"

View File

@ -28,3 +28,6 @@
.equ _blkPutC 0x44
.equ _blkSeek 0x47
.equ _blkTell 0x4a
.equ printHexPair 0x4d
.equ sdcGetC 0x50
.equ sdcPutC 0x53