+++ /dev/null
-;
-; Interface glue for the beta disk.
-;
-; This is a work in progress. The betadisk is only accessible when the
-; built in ROM is mapped. It unmaps itself when we return to RAM. It
-; has a sort of "BIOS" type ABI but that assumes we are in ZX Basic
-; and also screws around with memory behind the caller.
-;
-; So we have to do things differently. Instead we treat the TR-DOS 5
-; ROM as a ROP exploit target.
-;
-; This is a WIP and assumes the TR-DOS v5 ROM
-;
-
- .module betadisk
-
- .globl _betadev, _betaaddr, _betasector, _betatrack
- .globl _betauser, _betacount
-
- .globl _trdos_init, _trdos_read, _trdos_write
-
- .globl map_process_save, map_kernel_restore
-
- .globl outhl
-
- .area _COMMONDATA
-;
-; Ordering matters as we load some as pairs
-;
-_betadev: .db 0 ; disk number
-_betaaddr: .dw 0 ; buffer ptr
-_betasector: .db 0 ; sector (1 based)
-_betatrack: .db 0 ; track (low bit is side)
-_betauser: .db 0 ; paging info
-_betacount: .db 0 ; count of blocks
-_betacmd: .db 0 ; command byte
-_betatrackreg: .db 0 ; used to track h/w
-saved_sp: .dw 0 ; stack for unwinds
-saved_hl: .dw 0 ; HL for trdos entry
-
- .area _COMMONMEM
-
-_trdos_read:
- ld a, #0x80
- ld (_betacmd), a
-trdos_op:
- ld bc, (_betasector)
- call seekhead
- jr nz, error
- ld a, (_betauser)
- or a
- push af
- call z, map_process_save
- call trdos_doit
- pop af
- call z, map_kernel_restore
- ld h, b
- ld c, l
- ret
-error:
- ld hl, #-1
- ret
-
-_trdos_write:
- ld a, #0xA0
- ld (_betacmd), a
- jr trdos_op
-
-
-;
-; Head is on the right track, density is right, drive is right
-;
-trdos_doit:
- ld hl, (_betaaddr)
- ld a, (_betacmd)
- cp #0x80
- jr nz, dowrite
- call write_cmd_delay
- call wait_drq_and_read
- call read_status
- ret
-dowrite: call write_cmd_delay
- call wait_drq_and_write
- call read_status
- ; Check status
- ret
-
-
-;
-; Hook into the ROM to issue a reset and seek to 0
-;
-trdos_reset:
- ; Set the frame up for the exception path
- push bc
- push de
- ld (saved_sp), sp
- ld hl, #0x3d98
- call trdos_call
- xor a
- ; We can't easily read the real register so shadow it
- ld (_betatrackreg), a
- pop de
- pop bc
- ret
-
-;
-; Drive head and seek management. Assumes track register is
-; correctly loaded
-;
-seekhead:
- ld a, (_betadev)
- or #0x24 ; MFM, don't reset
- ld b, a
- ld a, (_betatrack)
- or a
- rra
- jr c, topside
- ; bottom
- set 3,b
-topside:
- ld (_betatrack), a
- ld a, b
- call trdos_outsys ; Select the drive/side
-
- ; need delays on the various side/disk switches
- ; check if need to seek
- call trdos_outdata
-; call nap
- ld a, #0x18 ; head load, 6ms
- ld hl, #0x2f57 ; issue command, wait for INT
- call trdos_call
- call read_status
- ; Check status - save new value
- ret
-;
-; Arbitrary code execution in the TRDOS ROM
-;
-trdos_call: push hl
- ld hl, (saved_hl)
- jp 0x3D2F
-
-;
-; An entry point that writes A to the command register and returns
-;
-trdos_outctrl: ld hl, #0x2fc3
- call trdos_call
- ret
-;
-; An entry point that writes A into the track register and returns
-;
-trdos_outtrk: push hl
- ld hl, #0x1e3a
- call trdos_call
- pop hl
- ret
-;
-; Write A into the interface control register
-;
-trdos_outsys: push hl
- ld hl,#0x1ff3
- call trdos_call
- pop hl
- ret
-;
-; Write the other registers via 0x20B8
-;
-trdos_outsec: push bc
- push de
- push hl
- ld bc, #0x015F
-;
-; Write D into register C B times
-;
-trdos_outit:
- ld d, a
- ld hl, #0x20B8
- call trdos_call
- pop hl
- pop de
- pop bc
- ret
-
-trdos_outdata: push bc
- push de
- push hl
- ld bc, #0x017F
- jr trdos_outit
-
-;
-; Low level I/O routines we can borrow
-;
-
-wait_drq_and_read:
- ld (saved_hl), hl
- ld hl, #0x3fd5
- call trdos_call
- ret
-
-wait_drq_and_write:
- ld (saved_hl), hl
- ld hl, #0x3fba
- call trdos_call
- ret
-
-write_cmd_delay:
- push hl
- ld hl, #0x2f57
- call trdos_call
- pop hl
- ret
-
-;
-; This is a somewhat evil dive into the middle of some code which in
-; some cases will attempt to jump back into BASIC. We catch it trying
-; to do so on errors and longjmp out of it.
-;
-read_status:
- push bc
- push de
- ld (saved_sp), sp
- ld d, #1
- xor a
- ld (0x5d15), a
- dec a
- ld (0x5d0e), a
- ld hl, #0x3f33
- call trdos_call
-unwind:
- ; b now holds status
- ld l, b
- ld h, #0
- pop de
- pop bc
- ret
-
-;
-; TRDOS tried to print an error message. Grab it as it tries to
-; go to the BASIC ROM and instead recover our stack and return an
-; error
-;
-_trdos_exception:
- pop hl
- ld a, l
- cp #0x54 ; checking break key
- jr z, cbreak
- ld sp, (saved_sp)
- jr unwind
-cbreak:
- scf ; no break (make timer based)
- ret
-
-
-;
-; Interceptor for ROM
-;
-_trdos_init:
- ld a, #0xc3
- ld (0x5cc2), a
- ld hl, #_trdos_exception
- ld (0x5cc3), hl
- ret
+++ /dev/null
-#include <kernel.h>
-#include <kdata.h>
-#include <printf.h>
-#include <devfd.h>
-#include <devbeta.h>
-
-#define MAX_FD 4
-
-static uint8_t lastdisc = 0xFF;
-
-/*
- * First cut at a beta driver. This uses the ROM entry points as the
- * beta has a design where you can't access the disk except with the
- * ROM paged in.
- *
- * We treat the ROM as a set of pieces to build a ROP type exploit.
- *
- * Note: the ROM has 39xx all set to 0xFF which means that we can
- * continue to take interrupts with this ROM paged in as we'll read
- * 0xFFFF and then take the vector as JR xx (where 0000 is still 0xF3
- * as this rom also starts with a DI).
- */
-
-static int beta_transfer(uint8_t minor, bool is_read, uint8_t rawflag)
-{
- blkno_t block;
- uint16_t ret;
-
- if(rawflag == 2)
- goto bad2;
-
- /* Select the right disc */
-
- if (lastdisc != minor) {
- betadev = minor;
- if (trdos_init())
- goto bad2;
- lastdisc = minor;
- }
-
- /* 16 sectors/track, 40 or 80 tracks but the interface expects logical
- tracks 0-n alternating sides. 256 bytes/sector always */
-
- if (rawflag == 0) {
- betaaddr = (uint16_t)udata.u_buf->bf_data;
- block = udata.u_buf->bf_blk;
- betacount = 2;
- } else {
- if (((uint16_t)udata.u_offset|udata.u_count) & BLKMASK)
- goto bad2;
- betaaddr = (uint16_t)udata.u_base;
- block = udata.u_offset >> 9;
- betacount = udata.u_count >> 8;
- }
- betasector = ((block >> 5) & 0x0F) + 1;
- betatrack = block >> 5;
- betauser = rawflag;
- betadev = minor;
- di();
- if (is_read)
- ret = trdos_read();
- else
- ret = trdos_write();
- ei();
- if (ret == 0)
- return betacount >> 1;
- kprintf("bfd%d: error %d\n", ret);
-bad2:
- udata.u_error = EIO;
- return -1;
-}
-
-/* FIXME: how do we detect beta is present sanely ? */
-int beta_open(uint8_t minor, uint16_t flag)
-{
- int ret;
-
- flag;
-
- if(minor >= MAX_FD) {
- udata.u_error = ENODEV;
- return -1;
- }
- betadev = minor;
-
- /* Stop the TR-DOS code trying to make workspaces and crap into
- basic by putting its 'initialized' token into place */
- *(uint8_t *)0x5CB6 = 0xF4;
- *(uint8_t *)0x5D16 = 0x00; /*FIXME: syscfg bits */
-
- ret = trdos_init();
-
- if (ret) {
- udata.u_error = EIO;
- lastdisc = 0xFF;
- return -1;
- }
- lastdisc = minor;
- return 0;
-}
-
-int beta_read(uint8_t minor, uint8_t rawflag, uint8_t flag)
-{
- flag;
- return beta_transfer(minor, true, rawflag);
-}
-
-int beta_write(uint8_t minor, uint8_t rawflag, uint8_t flag)
-{
- flag;
- return beta_transfer(minor, false, rawflag);
-}