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!
This commit is contained in:
parent
08328b86a9
commit
438c71ad3d
236
kernel/trs80/floppy.asm
Normal file
236
kernel/trs80/floppy.asm
Normal file
@ -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
|
@ -6,14 +6,23 @@
|
|||||||
jp init
|
jp init
|
||||||
|
|
||||||
.inc "err.h"
|
.inc "err.h"
|
||||||
|
.inc "blkdev.h"
|
||||||
.inc "ascii.h"
|
.inc "ascii.h"
|
||||||
.inc "core.asm"
|
.inc "core.asm"
|
||||||
.inc "str.asm"
|
.inc "str.asm"
|
||||||
|
|
||||||
.inc "trs80/kbd.asm"
|
.inc "trs80/kbd.asm"
|
||||||
.inc "trs80/vid.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_GETC trs80GetC
|
||||||
.equ STDIO_PUTC trs80PutC
|
.equ STDIO_PUTC trs80PutC
|
||||||
.inc "stdio.asm"
|
.inc "stdio.asm"
|
||||||
@ -39,12 +48,21 @@
|
|||||||
.inc "basic/var.asm"
|
.inc "basic/var.asm"
|
||||||
.equ BUF_RAMSTART VAR_RAMEND
|
.equ BUF_RAMSTART VAR_RAMEND
|
||||||
.inc "basic/buf.asm"
|
.inc "basic/buf.asm"
|
||||||
|
.inc "basic/blk.asm"
|
||||||
.equ BAS_RAMSTART BUF_RAMEND
|
.equ BAS_RAMSTART BUF_RAMEND
|
||||||
.inc "basic/main.asm"
|
.inc "basic/main.asm"
|
||||||
|
|
||||||
init:
|
init:
|
||||||
ld sp, RAMEND
|
ld sp, RAMEND
|
||||||
|
call floppyInit
|
||||||
call basInit
|
call basInit
|
||||||
|
ld hl, basFindCmdExtra
|
||||||
|
ld (BAS_FINDHOOK), hl
|
||||||
|
|
||||||
|
xor a
|
||||||
|
ld de, BLOCKDEV_SEL
|
||||||
|
call blkSel
|
||||||
|
|
||||||
jp basStart
|
jp basStart
|
||||||
|
|
||||||
printcr:
|
printcr:
|
||||||
@ -54,4 +72,8 @@ printcr:
|
|||||||
pop af
|
pop af
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
basFindCmdExtra:
|
||||||
|
ld hl, basBLKCmds
|
||||||
|
jp basFindCmd
|
||||||
|
|
||||||
RAMSTART:
|
RAMSTART:
|
||||||
|
Loading…
Reference in New Issue
Block a user