1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-12-25 17:18:06 +11:00

sdc: support 24-bit addressing

Needed if we want to compile the kernel and zasm from within a SD card.
I didn't go straight for 32-bit because it was significantly more
complex and 24-bit give us 16M. Enough to go on for a while...
This commit is contained in:
Virgil Dupras 2019-06-15 13:41:20 -04:00
parent 605c631dc0
commit 92a04f4627
5 changed files with 112 additions and 65 deletions

View File

@ -5,7 +5,12 @@ sdctMain:
ld de, SDCT_RAMSTART ld de, SDCT_RAMSTART
.wLoop: .wLoop:
ld a, (de) ld a, (de)
call sdcPutC ; To avoid overwriting important data and to test the 24-bit addressing,
; we set DE to 12 instead of zero
push de ; <|
ld de, 12 ; |
call sdcPutC ; |
pop de ; <|
jr nz, .error jr nz, .error
inc hl inc hl
inc de inc de
@ -23,7 +28,10 @@ sdctMain:
ld hl, 0 ld hl, 0
ld de, SDCT_RAMSTART ld de, SDCT_RAMSTART
.rLoop: .rLoop:
call sdcGetC push de ; <|
ld de, 12 ; |
call sdcGetC ; |
pop de ; <|
jr nz, .error jr nz, .error
ex de, hl ex de, hl
cp (hl) cp (hl)

View File

@ -59,16 +59,16 @@
; This is a pointer to the currently selected buffer. This points to the BUFSEC ; This is a pointer to the currently selected buffer. This points to the BUFSEC
; part, that is, two bytes before actual content begins. ; part, that is, two bytes before actual content begins.
.equ SDC_BUFPTR SDC_RAMSTART .equ SDC_BUFPTR SDC_RAMSTART
; Sector number currently in SDC_BUF1. ; Sector number currently in SDC_BUF1. Little endian like any other z80 word.
.equ SDC_BUFSEC1 SDC_BUFPTR+2 .equ SDC_BUFSEC1 SDC_BUFPTR+2
; 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_BUFDIRTY1 SDC_BUFSEC1+1 .equ SDC_BUFDIRTY1 SDC_BUFSEC1+2
; The contents of the buffer. ; The contents of the buffer.
.equ SDC_BUF1 SDC_BUFDIRTY1+1 .equ SDC_BUF1 SDC_BUFDIRTY1+1
; second buffer has the same structure as the first. ; second buffer has the same structure as the first.
.equ SDC_BUFSEC2 SDC_BUF1+SDC_BLKSIZE .equ SDC_BUFSEC2 SDC_BUF1+SDC_BLKSIZE
.equ SDC_BUFDIRTY2 SDC_BUFSEC2+1 .equ SDC_BUFDIRTY2 SDC_BUFSEC2+2
.equ SDC_BUF2 SDC_BUFDIRTY2+1 .equ SDC_BUF2 SDC_BUFDIRTY2+1
.equ SDC_RAMEND SDC_BUF2+SDC_BLKSIZE .equ SDC_RAMEND SDC_BUF2+SDC_BLKSIZE
@ -301,21 +301,18 @@ sdcSetBlkSize:
pop hl pop hl
ret ret
; Read block index specified in A and place the contents in buffer pointed to ; Read block index specified in DE and place the contents in buffer pointed to
; by (SDC_BUFPTR). ; by (SDC_BUFPTR).
; Doesn't check CRC. If the operation is a success, updates buffer's sector to ; Doesn't check CRC. If the operation is a success, updates buffer's sector to
; the value of A. ; the value of DE.
; 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
push de
push hl push hl
out (SDC_PORT_CSLOW), a out (SDC_PORT_CSLOW), a
ld hl, 0 ; read single block at addr A ld hl, 0
ld d, 0 ; DE already has the correct value
ld e, a ; E isn't touched in the rest of the routine
; and holds onto our original A
ld a, 0b01010001 ; CMD17 ld a, 0b01010001 ; CMD17
call sdcCmd call sdcCmd
or a ; cp 0 or a ; cp 0
@ -341,7 +338,10 @@ sdcReadBlk:
; actual data, but at this point, we don't have any error conditions ; actual data, but at this point, we don't have any error conditions
; left, success is guaranteed. To avoid needlesssly INCing hl, let's ; left, success is guaranteed. To avoid needlesssly INCing hl, let's
; set sector and dirty along the way ; set sector and dirty along the way
ld a, e ; sector number ld a, e ; sector number LSB
ld (hl), a
inc hl ; sector number MSB
ld a, d
ld (hl), a ld (hl), a
inc hl ; dirty flag inc hl ; dirty flag
xor a ; unset xor a ; unset
@ -368,7 +368,6 @@ sdcReadBlk:
.end: .end:
out (SDC_PORT_CSHIGH), a out (SDC_PORT_CSHIGH), a
pop hl pop hl
pop de
pop bc pop bc
ret ret
@ -377,7 +376,8 @@ sdcReadBlk:
; 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:
push hl push hl
ld hl, (SDC_BUFPTR) ; HL points to sector ld hl, (SDC_BUFPTR) ; HL points to sector LSB
inc hl ; sector MSB
inc hl ; now to dirty flag inc hl ; now to dirty flag
xor a xor a
cp (hl) cp (hl)
@ -389,11 +389,13 @@ sdcWriteBlk:
push de push de
out (SDC_PORT_CSLOW), a out (SDC_PORT_CSLOW), a
dec hl ; sector dec hl ; sector MSB
ld a, (hl)
ld d, a
dec hl ; sector LSB
ld a, (hl) ld a, (hl)
ld hl, 0 ; write single block at addr A
ld d, 0
ld e, a ld e, a
ld hl, 0 ; high addr word always zero, DE already set
ld a, 0b01011000 ; CMD24 ld a, 0b01011000 ; CMD24
call sdcCmd call sdcCmd
or a ; cp 0 or a ; cp 0
@ -411,6 +413,7 @@ sdcWriteBlk:
; Sending our data token! ; Sending our data token!
ld bc, SDC_BLKSIZE ld bc, SDC_BLKSIZE
ld hl, (SDC_BUFPTR) ld hl, (SDC_BUFPTR)
inc hl ; sector MSB
inc hl ; dirty flag inc hl ; dirty flag
inc hl ; beginning of contents inc hl ; beginning of contents
@ -434,6 +437,7 @@ sdcWriteBlk:
call sdcWaitResp call sdcWaitResp
; Success! Now let's unset the dirty flag ; Success! Now let's unset the dirty flag
ld hl, (SDC_BUFPTR) ld hl, (SDC_BUFPTR)
inc hl ; sector MSB
inc hl ; dirty flag inc hl ; dirty flag
xor a xor a
ld (hl), a ld (hl), a
@ -456,72 +460,83 @@ sdcWriteBlk:
pop hl pop hl
ret ret
; Considering the first 7 bits of HL, select the most appropriate of our two ; Considering the first 15 bits of EHL, select the most appropriate of our two
; buffers and, if necessary, sync that buffer with the SD card. If the selected ; buffers and, if necessary, sync that buffer with the SD card. If the selected
; buffer doesn't have the same sector as what HL asks, load that buffer from ; buffer doesn't have the same sector as what EHL asks, load that buffer from
; the SD card. ; the SD card.
; If the dirty flag is set, we write the content of the in-memory buffer to the ; 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. ; 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. Let's push de
; extract the wanted sector from this. ; Given a 24-bit address in EHL, extracts the 15-bit sector from it and
; place it in DE.
; We need to shift both E and H right by one bit
srl e ; sets Carry
ld d, e
ld a, h ld a, h
srl a ; A --> the requested sector number rra ; takes Carry
push hl ; Save the requested addr for later ld e, a
ld l, a
; Let's first see if our first buffer has our sector ; Let's first see if our first buffer has our sector
ld a, (SDC_BUFSEC1) ld a, (SDC_BUFSEC1) ; sector LSB
cp l cp e
jr nz, .notBuf1
ld a, (SDC_BUFSEC1+1) ; sector MSB
cp d
jr z, .buf1Ok jr z, .buf1Ok
.notBuf1:
; Ok, let's check for buf2 then ; Ok, let's check for buf2 then
ld a, (SDC_BUFSEC2) ld a, (SDC_BUFSEC2) ; sector LSB
cp l cp e
jr nz, .notBuf2
ld a, (SDC_BUFSEC2+1) ; sector MSB
cp d
jr z, .buf2Ok jr z, .buf2Ok
.notBuf2:
; None of our two buffers have the sector we need, we'll need to load ; None of our two buffers have the sector we need, we'll need to load
; a new one. ; a new one.
; We select our buffer depending on which is dirty. If both are on the ; We select our buffer depending on which is dirty. If both are on the
; same status of dirtiness, we pick any (the first in our case). If one ; same status of dirtiness, we pick any (the first in our case). If one
; of them is dirty, we pick the clean one. ; of them is dirty, we pick the clean one.
ld hl, SDC_BUFSEC1 push de ; <|
ld a, (SDC_BUFDIRTY1) ld de, SDC_BUFSEC1 ; |
or a ; is buf1 dirty? ld a, (SDC_BUFDIRTY1) ; |
jr z, .ready ; no? good, that's our buffer or a ; | is buf1 dirty?
; yes? then buf2 is our buffer. jr z, .ready ; | no? good, that's our buffer
ld hl, SDC_BUFSEC2 ; yes? then buf2 is our buffer. ; |
ld de, SDC_BUFSEC2 ; |
.ready: ; |
; At this point, HL points to one of our two buffers, the good one. .ready: ; |
; Let's save it to SDC_BUFPTR ; At this point, DE points to one o|f our two buffers, the good one.
ld (SDC_BUFPTR), hl ; Let's save it to SDC_BUFPTR |
ld (SDC_BUFPTR), de ; |
; You remember that HL we saved a long time ago? Now's the time to ; |
; bring it back. pop de ; <|
pop hl
; 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.
call sdcWriteBlk call sdcWriteBlk
ret nz ; error jr nz, .end ; error
; Let's read our new sector ; Let's read our new sector in DE
ld a, h call sdcReadBlk
srl a jr .end
jp sdcReadBlk ; returns
.buf1Ok: .buf1Ok:
ld hl, SDC_BUFSEC1 ld de, SDC_BUFSEC1
ld (SDC_BUFPTR), hl ld (SDC_BUFPTR), de
pop hl jr .end
ret
.buf2Ok: .buf2Ok:
ld hl, SDC_BUFSEC2 ld de, SDC_BUFSEC2
ld (SDC_BUFPTR), hl ld (SDC_BUFPTR), de
pop hl ; to .end
.end:
pop de
ret ret
; *** shell cmds *** ; *** shell cmds ***
@ -539,12 +554,13 @@ sdcInitializeCmd:
; way, no need for special conditions. ; way, no need for special conditions.
; initialize variables ; initialize variables
ld hl, SDC_BUFSEC1 ld hl, SDC_BUFSEC1
xor a
ld (SDC_BUFPTR), hl ld (SDC_BUFPTR), hl
ld de, 0
call sdcReadBlk ; read sector 0 in buf1 call sdcReadBlk ; read sector 0 in buf1
ret nz
ld hl, SDC_BUFSEC2 ld hl, SDC_BUFSEC2
inc a
ld (SDC_BUFPTR), hl ld (SDC_BUFPTR), hl
inc de
jp sdcReadBlk ; read sector 1 in buf2, returns jp sdcReadBlk ; read sector 1 in buf2, returns
; Flush the current SDC buffer if dirty ; Flush the current SDC buffer if dirty
@ -561,13 +577,17 @@ sdcFlushCmd:
; *** blkdev routines *** ; *** blkdev routines ***
; Make HL point to its proper place in SDC_BUF. ; Make HL point to its proper place in SDC_BUF.
; HL currently is an offset to read in the SD card. Load the proper sector in ; EHL currently is a 24-bit offset to read in the SD card. E=high byte,
; memory and make HL point to the correct data in the memory buffer. ; HL=low word. Load the proper sector in memory and make HL point to the
; correct data in the memory buffer.
_sdcPlaceBuf: _sdcPlaceBuf:
call sdcSync call sdcSync
ret nz ; error ret nz ; error
; At this point, we have the proper buffer in place and synced in
; (SDC_BUFPTR). Only the 9 low bits of HL are important.
push de push de
ld de, (SDC_BUFPTR) ld de, (SDC_BUFPTR)
inc de ; sector LSB
inc de ; dirty flag inc de ; dirty flag
inc de ; contents inc de ; contents
ld a, h ; high byte ld a, h ; high byte
@ -617,6 +637,7 @@ sdcPutC:
; Now, let's set the dirty flag ; Now, let's set the dirty flag
ld a, 1 ld a, 1
ld hl, (SDC_BUFPTR) ld hl, (SDC_BUFPTR)
inc hl ; sector MSB
inc hl ; point to dirty flag inc hl ; point to dirty flag
ld (hl), a ; set dirty flag ld (hl), a ; set dirty flag
xor a ; ensure Z xor a ; ensure Z

View File

@ -1,4 +1,9 @@
CFSTARGETS = cfsin/zasm cfsin/sdct cfsin/user.h # source files required to build os.bin. We copy them in cfsin so that we can
# build the same kernel from within the RC2014.
KERNEL_SRCS = err.h acia.asm blockdev.asm blockdev_cmds.asm core.asm fs.asm \
fs_cmds.asm mmap.asm parse.asm pgm.asm sdc.asm shell.asm stdio.asm
CFSTARGETS = $(addprefix cfsin/, zasm sdct at28w user.h $(KERNEL_SRCS))
BASE = ../../.. BASE = ../../..
TOOLS = $(BASE)/tools TOOLS = $(BASE)/tools
ZASM = $(TOOLS)/zasm.sh ZASM = $(TOOLS)/zasm.sh
@ -24,5 +29,16 @@ cfsin/zasm: $(ZASMBIN)
cfsin/sdct: $(ZASMBIN) cfsin/sdct: $(ZASMBIN)
$(ZASM) $(APPS) user.h < $(APPS)/sdct/glue.asm > $@ $(ZASM) $(APPS) user.h < $(APPS)/sdct/glue.asm > $@
cfsin/at28w: $(ZASMBIN)
$(ZASM) $(APPS) $(KERNEL) user.h < $(APPS)/at28w/glue.asm > $@
cfsin/user.h: user.h cfsin/user.h: user.h
cp $< $@ cp $< $@
cfsin/err.h: $(KERNEL)/err.h
cp $< $@
cfsin/%.asm: $(KERNEL)/%.asm
cp $< $@
.PHONY: clean
clean:
rm -f $(CFSTARGETS) sdcard.cfs os.bin

View File

@ -1,10 +1,10 @@
; classic RC2014 setup (8K ROM + 32K RAM) and a stock Serial I/O module ; classic RC2014 setup (8K ROM + 32K RAM) and a stock Serial I/O module
; The RAM module is selected on A15, so it has the range 0x8000-0xffff ; The RAM module is selected on A15, so it has the range 0x8000-0xffff
.equ RAMSTART 0x8000 .equ RAMSTART 0x8000
; kernel RAM usage, because of SDC, is a bit high and bring us almost to 0x9c00 ; kernel RAM usage, because of SDC, is a bit high and bring us almost to 0x9e00
; We allocate at least 0x100 bytes for RAM, which is why we have this threshold. ; We allocate at least 0x100 bytes for RAM, which is why we have this threshold.
; for the stack. ; for the stack.
.equ RAMEND 0x9d00 .equ RAMEND 0xa000
.equ PGM_CODEADDR RAMEND .equ PGM_CODEADDR RAMEND
.equ ACIA_CTL 0x80 ; Control and status. RS off. .equ ACIA_CTL 0x80 ; Control and status. RS off.
.equ ACIA_IO 0x81 ; Transmit. RS on. .equ ACIA_IO 0x81 ; Transmit. RS on.
@ -44,11 +44,12 @@ jp aciaInt
jp printHexPair jp printHexPair
jp sdcGetC jp sdcGetC
jp sdcPutC jp sdcPutC
jp blkGetC
#include "err.h" #include "err.h"
#include "core.asm" #include "core.asm"
#include "parse.asm" #include "parse.asm"
.equ ACIA_RAMSTART RAMSTART .equ ACIA_RAMSTART RAMSTART
#include "acia.asm" #include "acia.asm"
.equ BLOCKDEV_RAMSTART ACIA_RAMEND .equ BLOCKDEV_RAMSTART ACIA_RAMEND
.equ BLOCKDEV_COUNT 4 .equ BLOCKDEV_COUNT 4

View File

@ -1,4 +1,4 @@
.equ USER_CODE 0x9d00 .equ USER_CODE 0xa000
.equ USER_RAMSTART USER_CODE+0x1800 .equ USER_RAMSTART USER_CODE+0x1800
.equ FS_HANDLE_SIZE 6 .equ FS_HANDLE_SIZE 6
.equ BLOCKDEV_SIZE 8 .equ BLOCKDEV_SIZE 8
@ -31,3 +31,4 @@
.equ printHexPair 0x4d .equ printHexPair 0x4d
.equ sdcGetC 0x50 .equ sdcGetC 0x50
.equ sdcPutC 0x53 .equ sdcPutC 0x53
.equ blkGetC 0x56