aboutsummaryrefslogtreecommitdiffstats
path: root/src/blk.asm
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-16 11:42:38 +0200
committeruser <user@clank>2026-07-16 11:42:38 +0200
commit057e5377af2d086917b9cd0ffb336231d7d098cb (patch)
treefd814d32d88a0eede32a69f45b729310b182e13a /src/blk.asm
parentjobs: background execution (&), non-blocking reap, spin demo (diff)
downloadgbos-057e5377af2d086917b9cd0ffb336231d7d098cb.tar.gz
gbos-057e5377af2d086917b9cd0ffb336231d7d098cb.tar.xz
gbos-057e5377af2d086917b9cd0ffb336231d7d098cb.zip
fs rewrite stage 1: persistent block device (bounce buffer, cart SRAM)
- blk.asm: read_block/write_block move 256-byte blocks between battery-backed cart-RAM banks (8-11, the 'disk') and a WRAM bounce buffer. Banking is hidden in those two routines; they inline the 'restore my bank' step so they never touch the stack while the disk bank is mapped over the process's $A000 window (the stack lives there too -- a call/ret would rug-pull it). - format-on-first-boot: superblock magic in block 0; otherwise the disk persists. - cart RAM bumped to 128 KiB (-r 4); process banks 0-7, disk banks 8-11. - debug tool 'blk fill/peek' + syscalls to validate round-trip and persistence. - verified: round-trip incl. cross-bank (block 100 -> bank 11); block survives a reboot via .sav; magic intact (no reformat on 2nd boot). - old WRAM 8-slot FS still backs the tools until stages 2-3.
Diffstat (limited to '')
-rw-r--r--src/blk.asm161
1 files changed, 161 insertions, 0 deletions
diff --git a/src/blk.asm b/src/blk.asm
new file mode 100644
index 0000000..ea04e95
--- /dev/null
+++ b/src/blk.asm
@@ -0,0 +1,161 @@
+; =============================================================================
+; blk.asm - persistent block device on battery-backed cart RAM.
+;
+; The "disk" is cart-RAM banks FS_BANK0.. (256-byte blocks). Blocks are read
+; and written through a WRAM bounce buffer, so only read_block/write_block ever
+; touch cart-RAM banking; the rest of the filesystem works on WRAM buffers.
+;
+; block B lives in cart-RAM bank FS_BANK0 + B/32, at $A000 + (B & 31)*256.
+;
+; After each transfer we remap the current process's own RAM bank, so a syscall
+; that does disk I/O leaves the caller's $A000 window exactly as it found it.
+; =============================================================================
+INCLUDE "include/gbos.inc"
+
+SECTION "blk", ROM0
+
+; A process's stack lives in the $A000 window too, so while the disk bank is
+; mapped there NOTHING may touch the stack (no call/ret/push/pop). The block
+; routines therefore inline the "restore my bank" step instead of calling it.
+; (MACRO because it must not be a `call`.)
+MACRO restore_bank
+ ld a, [wCurProc]
+ add PROC_RAMB
+ ld l, a
+ ld a, [wCurProc+1]
+ adc 0
+ ld h, a
+ ld a, [hl]
+ ld [MBC5_RAMB], a
+ENDM
+
+; -----------------------------------------------------------------------------
+; read_block - in: A = block, HL = dest (WRAM buffer, 256 bytes)
+; -----------------------------------------------------------------------------
+read_block::
+ ld c, a
+ srl a
+ srl a
+ srl a
+ srl a
+ srl a ; A = block / 32
+ add FS_BANK0
+ ld [MBC5_RAMB], a ; map the disk bank (stack now hidden!)
+ ld a, c
+ and 31
+ add $A0
+ ld d, a
+ ld e, 0 ; DE = $A000 + (block&31)*256
+ ld b, 0 ; 256 bytes
+.cp
+ ld a, [de]
+ ld [hl+], a
+ inc de
+ dec b
+ jr nz, .cp
+ restore_bank ; inline - no stack while the disk bank is mapped
+ ret
+
+; -----------------------------------------------------------------------------
+; write_block - in: A = block, HL = src (WRAM buffer, 256 bytes)
+; -----------------------------------------------------------------------------
+write_block::
+ ld c, a
+ srl a
+ srl a
+ srl a
+ srl a
+ srl a
+ add FS_BANK0
+ ld [MBC5_RAMB], a
+ ld a, c
+ and 31
+ add $A0
+ ld d, a
+ ld e, 0 ; DE = dest in the disk bank
+ ld b, 0
+.cp
+ ld a, [hl+]
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .cp
+ restore_bank
+ ret
+
+; -----------------------------------------------------------------------------
+; blk_format - write a fresh superblock (magic) to block 0. (mkfs, stage 1)
+; -----------------------------------------------------------------------------
+blk_format::
+ ld hl, wCopyBuf ; zero the buffer
+ ld b, 0
+ xor a
+.z
+ ld [hl+], a
+ dec b
+ jr nz, .z
+ ld hl, wCopyBuf
+ ld a, SUPER_MAG0
+ ld [hl+], a
+ ld a, SUPER_MAG1
+ ld [hl+], a
+ ld a, SUPER_MAG2
+ ld [hl+], a
+ ld a, SUPER_MAG3
+ ld [hl], a
+ ld a, SUPER_BLOCK
+ ld hl, wCopyBuf
+ call write_block
+ ret
+
+; -----------------------------------------------------------------------------
+; blk_init - format the disk only if it isn't already formatted (magic check).
+; Called at boot; the disk otherwise persists across power cycles (.sav).
+; -----------------------------------------------------------------------------
+blk_init::
+ ld a, SUPER_BLOCK
+ ld hl, wCopyBuf
+ call read_block
+ ld hl, wCopyBuf
+ ld a, [hl+]
+ cp SUPER_MAG0
+ jr nz, .fmt
+ ld a, [hl+]
+ cp SUPER_MAG1
+ jr nz, .fmt
+ ld a, [hl+]
+ cp SUPER_MAG2
+ jr nz, .fmt
+ ld a, [hl]
+ cp SUPER_MAG3
+ jr nz, .fmt
+ ret ; already formatted
+.fmt
+ call blk_format
+ ret
+
+; =============================================================================
+; [debug] block poke/peek syscalls, to validate the layer + persistence.
+; =============================================================================
+; sys_bfill(B = block, E = byte) - fill the whole block with a byte
+sys_bfill::
+ ld a, e
+ ld hl, wCopyBuf
+ ld c, 0
+.f
+ ld [hl+], a
+ dec c
+ jr nz, .f
+ ld a, b
+ ld hl, wCopyBuf
+ call write_block
+ xor a
+ ret
+
+; sys_bpeek(B = block) -> A = block's first byte
+sys_bpeek::
+ ld a, b
+ ld hl, wCopyBuf
+ call read_block
+ ld a, [wCopyBuf]
+ ret