From 438c71ad3dcba8c119dcf7a54aa71b0d7dd5f49e Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 21 Feb 2020 19:30:57 -0500 Subject: [PATCH] trs80/floppy: new module Read-only support for now, hardcoded on drive :1. But it seems to work. Can't wait to get a CFS running on this! --- kernel/trs80/floppy.asm | 236 ++++++++++++++++++++++++++++++++++++++++ recipes/trs80/glue.asm | 24 +++- 2 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 kernel/trs80/floppy.asm diff --git a/kernel/trs80/floppy.asm b/kernel/trs80/floppy.asm new file mode 100644 index 0000000..072334b --- /dev/null +++ b/kernel/trs80/floppy.asm @@ -0,0 +1,236 @@ +; floppy +; +; Implement a block device around a TRS-80 floppy. It uses SVCs supplied by +; TRS-DOS to do so. +; +; *** Floppy buffers *** +; +; The dual-buffer system is exactly the same as in the "sdc" module. See +; comments there. +; +; *** Consts *** +; Number of sector per cylinder. We only support single density for now. +.equ FLOPPY_SEC_PER_CYL 10 +.equ FLOPPY_MAX_CYL 40 +.equ FLOPPY_BLKSIZE 256 + +; *** Variables *** +; This is a pointer to the currently selected buffer. This points to the BUFSEC +; part, that is, two bytes before actual content begins. +.equ FLOPPY_BUFPTR FLOPPY_RAMSTART +; Sector number currently in FLOPPY_BUF1. Little endian like any other z80 word. +.equ FLOPPY_BUFSEC1 @+2 +; Whether the buffer has been written to. 0 means clean. 1 means dirty. +.equ FLOPPY_BUFDIRTY1 @+2 +; The contents of the buffer. +.equ FLOPPY_BUF1 @+1 + +; second buffer has the same structure as the first. +.equ FLOPPY_BUFSEC2 @+FLOPPY_BLKSIZE +.equ FLOPPY_BUFDIRTY2 @+2 +.equ FLOPPY_BUF2 @+1 +.equ FLOPPY_RAMEND @+FLOPPY_BLKSIZE + +; *** Code *** +floppyInit: + ; Make sure that both buffers are flagged as invalid + ld a, 0xff + ld (FLOPPY_BUFSEC1), a + ld (FLOPPY_BUFSEC2), a + ret + +; Read sector index specified in E and cylinder specified in D and place the +; contents in buffer pointed to by (FLOPPY_BUFPTR). +; If the operation is a success, updates buffer's sector to the value of DE. +; Z on success +floppyRdSec: + ld a, e + cp FLOPPY_SEC_PER_CYL + jp nc, unsetZ + ld a, d + cp FLOPPY_MAX_CYL + jp nc, unsetZ + + push bc + push hl + + ld a, 0x28 ; @DCSTAT + ld c, 1 ; hardcoded to drive :1 for now + rst 0x28 + jr nz, .end + + ld hl, (FLOPPY_BUFPTR) ; HL --> active buffer's sector + ld (hl), e ; sector + inc hl + ld (hl), d ; cylinder + inc hl ; dirty + xor a + ld (hl), a ; clear dirty + inc hl ; data + ld a, 0x31 ; @RDSEC + rst 0x28 ; sets Z appropriately +.end: + pop hl + pop bc + ret + +; not implemented yet. +floppyWrSec: + xor a + ret + +; Considering the first 15 bits of EHL, select the most appropriate of our two +; buffers and, if necessary, sync that buffer with the floppy. If the selected +; buffer doesn't have the same sector as what EHL asks, load that buffer from +; the floppy. +; If the dirty flag is set, we write the content of the in-memory buffer to the +; floppy before we read a new sector. +; Returns Z on success, NZ on error +floppySync: + push de + ; Given a 24-bit address in EHL, extracts the 16-bit sector from it and + ; place it in DE, following cylinder and sector rules. + ; EH is our sector index, L is our offset within the sector. + + ld d, e ; cylinder + ld a, h ; sector + ; Let's process D first. Because our maximum number of sectors is 400 + ; (40 * 10), D can only be either 0 or 1. If it's 1, we set D to 25 and + ; add 6 to A + inc d \ dec d + jr z, .loop1 ; skip + ld d, 25 + add a, 6 +.loop1: + cp FLOPPY_SEC_PER_CYL + jr c, .loop1end + sub FLOPPY_SEC_PER_CYL + inc d + jr .loop1 +.loop1end: + ld e, a ; write final sector in E + ; Let's first see if our first buffer has our sector + ld a, (FLOPPY_BUFSEC1) ; sector + cp e + jr nz, .notBuf1 + ld a, (FLOPPY_BUFSEC1+1) ; cylinder + cp d + jr z, .buf1Ok + +.notBuf1: + ; Ok, let's check for buf2 then + ld a, (FLOPPY_BUFSEC2) ; sector + cp e + jr nz, .notBuf2 + ld a, (FLOPPY_BUFSEC2+1) ; cylinder + cp d + jr z, .buf2Ok + +.notBuf2: + ; None of our two buffers have the sector we need, we'll need to load + ; a new one. + + ; 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 + ; of them is dirty, we pick the clean one. + push de ; --> lvl 1 + ld de, FLOPPY_BUFSEC1 + ld a, (FLOPPY_BUFDIRTY1) + or a ; is buf1 dirty? + jr z, .ready ; no? good, that's our buffer + ; yes? then buf2 is our buffer. + ld de, FLOPPY_BUFSEC2 + +.ready: + ; At this point, DE points to one of our two buffers, the good one. + ; Let's save it to FLOPPY_BUFPTR + ld (FLOPPY_BUFPTR), de + + pop de ; <-- lvl 1 + + ; We have to read a new sector, but first, let's write the current one + ; if needed. + call floppyWrSec + jr nz, .end ; error + ; Let's read our new sector in DE + call floppyRdSec + jr .end + +.buf1Ok: + ld de, FLOPPY_BUFSEC1 + ld (FLOPPY_BUFPTR), de + ; Z already set + jr .end + +.buf2Ok: + ld de, FLOPPY_BUFSEC2 + ld (FLOPPY_BUFPTR), de + ; Z already set + ; to .end +.end: + pop de + ret + +; *** blkdev routines *** + +; Make HL point to its proper place in FLOPPY_BUF. +; EHL currently is a 24-bit offset to read in the floppy. E=high byte, +; HL=low word. Load the proper sector in memory and make HL point to the +; correct data in the memory buffer. +_floppyPlaceBuf: + call floppySync + ret nz ; error + ; At this point, we have the proper buffer in place and synced in + ; (FLOPPY_BUFPTR). Only L is important + ld a, l + ld hl, (FLOPPY_BUFPTR) + inc hl ; sector MSB + inc hl ; dirty flag + inc hl ; contents + ; DE is now placed on the data part of the active buffer and all we need + ; is to increase DE by L. + call addHL + ; Now, HL points exactly at the right byte in the active buffer. + xor a ; ensure Z + ret + +floppyGetB: + push hl + call _floppyPlaceBuf + jr nz, .end ; NZ already set + + ; This is it! + ld a, (hl) + cp a ; ensure Z +.end: + pop hl + ret + +floppyPutB: + push hl + push af ; --> lvl 1. let's remember the char we put, + ; _floppyPlaceBuf destroys A. + call _floppyPlaceBuf + jr nz, .error + + ; HL points to our dest. Recall A and write + pop af ; <-- lvl 1 + ld (hl), a + + ; Now, let's set the dirty flag + ld a, 1 + ld hl, (FLOPPY_BUFPTR) + inc hl ; sector MSB + inc hl ; point to dirty flag + ld (hl), a ; set dirty flag + xor a ; ensure Z + jr .end +.error: + ; preserve error code + ex af, af' + pop af ; <-- lvl 1 + ex af, af' + call unsetZ +.end: + pop hl + ret diff --git a/recipes/trs80/glue.asm b/recipes/trs80/glue.asm index 164938e..500de26 100644 --- a/recipes/trs80/glue.asm +++ b/recipes/trs80/glue.asm @@ -6,14 +6,23 @@ jp init .inc "err.h" +.inc "blkdev.h" .inc "ascii.h" .inc "core.asm" .inc "str.asm" .inc "trs80/kbd.asm" .inc "trs80/vid.asm" +.equ FLOPPY_RAMSTART RAMSTART +.inc "trs80/floppy.asm" -.equ STDIO_RAMSTART RAMSTART +.equ BLOCKDEV_RAMSTART FLOPPY_RAMEND +.equ BLOCKDEV_COUNT 1 +.inc "blockdev.asm" +; List of devices +.dw floppyGetB, floppyPutB + +.equ STDIO_RAMSTART BLOCKDEV_RAMEND .equ STDIO_GETC trs80GetC .equ STDIO_PUTC trs80PutC .inc "stdio.asm" @@ -39,12 +48,21 @@ .inc "basic/var.asm" .equ BUF_RAMSTART VAR_RAMEND .inc "basic/buf.asm" +.inc "basic/blk.asm" .equ BAS_RAMSTART BUF_RAMEND .inc "basic/main.asm" init: ld sp, RAMEND + call floppyInit call basInit + ld hl, basFindCmdExtra + ld (BAS_FINDHOOK), hl + + xor a + ld de, BLOCKDEV_SEL + call blkSel + jp basStart printcr: @@ -54,4 +72,8 @@ printcr: pop af ret +basFindCmdExtra: + ld hl, basBLKCmds + jp basFindCmd + RAMSTART: