From 0379c13dec00ee9ea95570d2c7ba7d74017e4bfa Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 2 Sep 2018 21:55:00 +0100 Subject: [PATCH] genie-eg64: push work in progress ready for 0.2 --- Kernel/platform-genie-eg64/commonmem.s | 10 + Kernel/platform-genie-eg64/crt0.s | 75 ++++ Kernel/platform-genie-eg64/floppy.s | 451 ++++++++++++++++++++++++ Kernel/platform-genie-eg64/ide.s | 61 ++++ Kernel/platform-genie-eg64/target.mk | 2 + Kernel/platform-genie-eg64/tricks.s | 212 +++++++++++ Kernel/platform-genie-eg64/trs80-bank.s | 110 ++++++ Kernel/platform-genie-eg64/trs80.s | 201 +++++++++++ Kernel/platform-genie-eg64/trs80load.s | 140 ++++++++ 9 files changed, 1262 insertions(+) create mode 100644 Kernel/platform-genie-eg64/commonmem.s create mode 100644 Kernel/platform-genie-eg64/crt0.s create mode 100644 Kernel/platform-genie-eg64/floppy.s create mode 100644 Kernel/platform-genie-eg64/ide.s create mode 100644 Kernel/platform-genie-eg64/target.mk create mode 100644 Kernel/platform-genie-eg64/tricks.s create mode 100644 Kernel/platform-genie-eg64/trs80-bank.s create mode 100644 Kernel/platform-genie-eg64/trs80.s create mode 100644 Kernel/platform-genie-eg64/trs80load.s diff --git a/Kernel/platform-genie-eg64/commonmem.s b/Kernel/platform-genie-eg64/commonmem.s new file mode 100644 index 00000000..bafd590b --- /dev/null +++ b/Kernel/platform-genie-eg64/commonmem.s @@ -0,0 +1,10 @@ +; +; We have no real common on the TRS80so just tuck it up at the top of +; memory leaving room for the keyboard and video (3K) +; + .module commonmem + + .area _COMMONMEM + + .include "../cpu-z80/std-commonmem.s" + diff --git a/Kernel/platform-genie-eg64/crt0.s b/Kernel/platform-genie-eg64/crt0.s new file mode 100644 index 00000000..afc3f70b --- /dev/null +++ b/Kernel/platform-genie-eg64/crt0.s @@ -0,0 +1,75 @@ + ; Order the code into the high 32K + .area _CODE + .area _CODE1 + .area _CODE2 + .area _VIDEO + .area _CONST + .area _DATA + .area _BSEG + .area _BSS + .area _HEAP + ; note that areas below here may be overwritten by the heap at runtime, so + ; put initialisation stuff in here + .area _GSINIT + .area _GSFINAL + ; Low 16K + .area _BOOT + .area _INITIALIZED + .area _COMMONMEM + .area _BUFFERS + ; We want the DISCARD area last as we eventually want to + ; expand all over it for buffers + .area _DISCARD + .area _INITIALIZER + ; Buffers must be directly before discard as they will + ; expand over it + + ; imported symbols + .globl _fuzix_main + .globl init_early + .globl init_hardware + .globl _vtinit + .globl s__DATA + .globl l__DATA + .globl s__COMMONMEM + .globl l__COMMONMEM + .globl s__INITIALIZER + .globl kstack_top + + .include "kernel.def" + .include "../kernel.def" + + ; startup code + .area _BOOT + +; +; On entry the bootloader has put the banker into the +; kernel map and loaded us at 0x100 (it's at 0x0) +; +start: + ld sp, #kstack_top + ; Zero the data area + ld hl, #s__DATA + ld de, #s__DATA + 1 + ld bc, #l__DATA - 1 + ld (hl), #0 + ldir + call init_early + call init_hardware + call _vtinit + call _fuzix_main + di +stop: halt + jr stop + +; +; Buffers (we use asm to set this up as we need them in a special segment +; so we can recover the discard memory into the buffer pool +; + + .globl _bufpool + .area _BUFFERS + +_bufpool: + .ds BUFSIZE * NBUFS + diff --git a/Kernel/platform-genie-eg64/floppy.s b/Kernel/platform-genie-eg64/floppy.s new file mode 100644 index 00000000..ed393d6a --- /dev/null +++ b/Kernel/platform-genie-eg64/floppy.s @@ -0,0 +1,451 @@ +; +; Core floppy routines for the TRS80 1791 FDC +; Based on the 6809 code +; +; FIXME: better drive spin up wait +; FIXME: tandy doubler +; FIXME: correct step rates (per drive ?) +; FIXME: precompensation ?? +; FIXME: 512 byte sector support +; + .globl _fd_reset + .globl _fd_operation + .globl _fd_motor_on + .globl _fd_motor_off + .globl _fd_map + .globl _fd_selected + .globl _fd_tab + .globl _fd_cmd + .globl map_kernel_restore, map_process_always + .globl go_fast, go_slow + + .module floppy +; +; The 1791 is memory mapped +; +FDCREG .equ 0x37EC +FDCTRK .equ 0x37ED +FDCSEC .equ 0x37EE +FDCDATA .equ 0x37EF +; +; Drive select is also more complicated than other models +; +LATCHD0 .equ 0x37E1 ; also drive select +LATCHD1 .equ 0x37E3 +LATCHD2 .equ 0x37E5 +LATCHD3 .equ 0x37E7 + +; +; And the select is arranged as +; bit 0-3 select drives, bit 3 also selects side (so you can't have +; a disk 3 with any double sided drive) +; + +; +; The final bit of weirdness is magic writes control the doubler. This +; actually *changes* the chip currently connected to those addresses +; so things like track must be written in the right order! +; +; Percom (to cmd port) +P_SET_FM .equ 0xFE +P_SET_MFM .equ 0xFF +; Tandy (to track register) +T_SET_FM .equ 0xA0 +T_SET_MFM .equ 0x80 +T_UNSET_PRECOMP .equ 0xC0 +T_SET_PRECOMP .equ 0xE0 + +; +; It's our responsibility to wait 1 second for motor on and to allow +; 80ms for head load. (and we can call 0060 for 14.65 * BC us delay) +; + +; +; interrupt register reports 0x80 for interrupt, 0x40 for drq +; (0x20 is the unrelated reset button) +; + +; +; Structures we use +; +; +; Per disk structure to hold device state +; +TRKCOPY .equ 0 + +; +; Command issue +; +CMD .equ 0 +TRACK .equ 1 +SECTOR .equ 2 +DIRECT .equ 3 ; 0 = read 2 = write 1 = status +DATA .equ 4 + + .area _COMMONMEM +; +; Set up and perform a disk operation +; +; IX points to the command block +; BC points to the buffer +; DE points to the track reg copy +; +; Drive must already be selected and density set up +; +fdsetup: + ld hl,#FDCTRK + ld a, (de) + ld (hl), a ; Load track register + cp TRACK(ix) + jr z, fdiosetup ; Is it the one we wanted + ; + ; So we can verify + ; + inc hl ; now FDCTRK + ld a, TRACK(ix) + ld (hl), a + inc hl ; now FDCSEC + ld a, SECTOR(ix) + ld (hl), a + ; + ; Need to seek the disk + ; + ld hl,#FDCREG + ld a, #0x18 ; seek FIXME: need to set step rate + ld (hl), a + call waitcmd + and #0x18 ; error bits + jr z, fdiosetup + ; seek failed, not good +setuptimeout: ; NE = bad + ld a, #0xff ; we have no idea where we are, force a seek + ld (de), a ; zap track info + ld l,#0xFF ; report failure + ret +; +; Try and kick the controller back into sanity +; +bad_cmd: + ld a,(hl) ; clear status + ld (hl),#0xD0 ; force interrupt + pop af + ld l,#255 + ret + +waitcmd: + ex (sp),hl + ex (sp),hl + ex (sp),hl + ex (sp),hl ; 87 clocks + the call (17) = 104 +waitcmdl: + bit 0,(hl) + jr z, waitfail + rlca + ld a,(last_drive) ; Keep the motor spinning + ld (LATCHD0),a + jr c, waitcmdl + ld a,(hl) ; Status + ret +waitfail: + ld a,#255 + ret +; +; Head in the right place +; +fdiosetup: + ld a, TRACK(ix) + ld (de), a ; save track + + ; FIXME: select which controller first etc + + ld de, #FDCSEC ; sector register + ld a, SECTOR(ix) + ld (FDCSEC), a + + inc de ; now points at FDCDATA + + ld hl, #FDCREG ; status/cmd + ld a,(hl) ; clear status + + ld a, DIRECT(ix) + dec a + ld a, CMD(ix) ; 0 - none, 1 - in, 2 = out + ld (hl),a + push af ; 11 + ex (sp),hl ; 30 Delay 55us (98 clocks) + ex (sp),hl ; 49 + ex (sp),hl ; 68 + ex (sp),hl ; 87 + add a,#0 ; 94 + di ; 98 + bit 0,(hl) ; controller busy ? + jr z, bad_cmd + pop af + jr z, fdio_in + jr nc, fdio_out + jr fd_xferdone +; +; Read from the disk - HL points to the target buffer. This is very +; tight timing on a 2MHz Z80 with a doubler. +; +; We point HL at control/status, and DE at the data port. We can't do +; this with IX offsets as we don't have enough clocks for it. +; +fdio_in: +fdio_do_in: + ld a,#0x83 ; Wait for the controller to go + and (hl) ; ready + jp po, fdio_do_in +fdio_xfer_in: + ld a,(de) ; 7 data from the port ASAP + ld (bc),a ; 7 to memory + inc bc ; 6 aligned buffer would be 4 + ; + ; Old trick. What we are effectively doing is synchronizing + ; the controller and CPU knowing the worst case misalignment + ; + ; We simply don't have time for this to be a loop + ; + ; Our best case is 44 clocks per loop so 25us / loop. We have + ; 31us when running double density so will alternate between + ; 44 clock loops and 63 clock (35us) loops. + ; + bit 1,(hl) ; 12 clocks + jr nz, fdio_xfer_in ; 7 clocks if not taken + bit 1,(hl) + jr nz, fdio_xfer_in + bit 1,(hl) + jr nz, fdio_xfer_in + bit 1,(hl) ; we may have a CPU speed upgrade + jr nz, fdio_xfer_in ; fitted... + bit 1,(hl) + jr nz, fdio_xfer_in + bit 1,(hl) + jr nz, fdio_xfer_in + ; + ; If another byte hasn't turned up in 114 clocks then either + ; it failed or it finished. Until that happens we can't afford + ; to check anything else + ; +fd_xferdone: + ld l,(hl) ; read the status + ld (hl),#0xD0 ; force interrupt + ei + ret ; pass C code the status byte + +; +; Very similar to the above but going the other way +; +fdio_out: + ld (hl),a ; issue command +fdio_do_out: + ld a,#0x83 ; Wait for the controller to go + and (hl) ; ready + jp po, fdio_out +fdio_xfer_out: + ld a,(bc) ; 7 data from the user + ld (de),a ; 7 to the port + inc bc ; 6 aligned buffer would be 4 + ; + ; Old trick. What we are effectively doing is synchronizing + ; the controller and CPU knowing the worst case misalignment + ; + ; We simply don't have time for this to be a loop + ; + ; Our best case is 44 clocks per loop so 25us / loop. We have + ; 31us when running double density so will alternate between + ; 44 clock loops and 63 clock (35us) loops. + ; + bit 1,(hl) ; 12 clocks + jr nz, fdio_xfer_out ; 7 clocks if not taken + bit 1,(hl) + jr nz, fdio_xfer_out + bit 1,(hl) + jr nz, fdio_xfer_out + bit 1,(hl) ; we may have a CPU speed upgrade + jr nz, fdio_xfer_out ; fitted... + bit 1,(hl) + jr nz, fdio_xfer_out + bit 1,(hl) + jr nz, fdio_xfer_out +; +; Now tidy up +; + jr fd_xferdone + + +; +; C glue interface. +; +; +; Reset to track 0, wait for the command then idle +; +; fd_reset(uint8_t *drvptr) +; +_fd_reset: + pop de + pop hl + push hl + push de + call go_slow + ld a, #1 + ld (FDCSEC), a + xor a + ld (FDCTRK), a + ld a, #0x0C + ld (FDCREG), a ; restore + ld a, #0xFF + ld (hl), a ; Zap track pointer + call waitcmd + call go_fast + cp #0xff + ret z + and #0x99 ; Error bit from the reset + ret nz + ld (hl), a ; Track 0 correctly hit (so 0) + ret +; +; fd_operation(uint16_t *cmd, uint16_t *drive) +; +; The caller must ensure the drive has been selected and the motor is +; running. +; +_fd_operation: + pop bc ; return address + pop de ; drive track ptr + push de + push bc + push ix + ld a, (_fd_map) + or a + push af + call nz, map_process_always + call go_slow + ld ix, #_fd_cmd + ld c, DATA(ix) + ld b, DATA+1(ix) + call fdsetup ; Set up for a command + ld h, #0 + call go_fast + pop af + pop ix + ret z + jp map_kernel_restore +; +; Delay loops +; +wait45: ; wait 45ms for head load. HL points to + ld bc,#2662 ; latch - D is latch value, preserve HL/DE +wait45l: + dec bc ; 6 30 cycles per loop (16.9us) + ld a,b ; 4 + or c ; 4 + jr nz, wait45l ; 12/7 + ret + +; +; Wait about 500ms while poking the motor to keep it spinning. +; +waitdisk: + ld b,#11 +waitdiskl: + push bc + call wait45l + ld (hl),d ; tickle the motor + pop bc + djnz waitdiskl + ret +; +; +; C interface fd_motor_on(uint16_t drivesel) +; +; Selects this drive and turns on the motors. Also pass in the +; choice of density +; +; bits 0-3: select that drive (yes side and drive 3 clash) +; bit 3: side (must rewrite each drive change) +; bit 7: set for double density (MFM) +; +; +_fd_motor_on: + pop de + pop bc + push bc + push de + + ; + ; Is the motor running ? + ; + ld a,(LATCHD0) + ld e, a ; save the latch status (motor on bit) + rlca + ld a,(last_drive) + jr nc, must_config ; if the motor is off always do set up + ; + ; Are we changing our selection ? + ; + cp c + jr nz, must_config + ; + ; Motor running, same configuration. Poke the selection + ; so the motor stays running. + ; + ld (LATCHD0),a + ret + +must_config: + call go_slow + ld a,c ; Save the new configuration + ld (last_drive),a + + ld hl,#FDCREG + + and #0x7F ; We borrowed bit 7 for our own use + ld (LATCHD0), a ; Selects the actual disk we want + ld d,a ; Save latch value + rl c ; Bit 7 into C + ld a,#0xFE ; Figure out the density + adc a,#0 ; FE or FF according to density + di + ld (hl),a ; if a doubler is present this switches FDC + ; Do we need a delay here (eg if there is no doubler present) + ; and do we need to avoid the FE/FF scribbles on doubler-less hw + ; as we are writing twice to the FDC a few clocks apart otherwise + ld (hl),#0xD0 ; Hit it over the head with a hammer + ei + + ld hl,#LATCHD0 ; used by waitdisk too + + bit 7,e ; was the motor running + jr z, motor_running + + call waitdisk ; wait 500ms or so for spin up + call go_fast +motor_running: + ld (hl),d + call wait45 ; Wait 45ms for the head to load + ld (hl),d ; Reset timer + ret + +; +; C interface fd_motor_off(void) +; +; Turns off the drive motors, deselects all drives +; +; Not sure we need this. +; +_fd_motor_off: + xor a + ld (LATCHD0),a + ret + +last_drive: + .db 0xff +_fd_map: + .db 0 +_fd_selected: + .db 0xFF +_fd_tab: + .db 0xFF, 0xFF, 0xFF, 0xFF +_fd_cmd: + .ds 7 diff --git a/Kernel/platform-genie-eg64/ide.s b/Kernel/platform-genie-eg64/ide.s new file mode 100644 index 00000000..8ff0fc28 --- /dev/null +++ b/Kernel/platform-genie-eg64/ide.s @@ -0,0 +1,61 @@ + .globl _devide_read_data + .globl _devide_write_data + + .globl map_process_always + .globl map_kernel_restore + + .globl _blk_op + + .module ide +; +; If we ever do a native driver for M3SE we'll want to d something +; different here for that and use ide_select to switch interface types +; +; FIXME: Move these somewhere better +BLKPARAM_ADDR_OFFSET .equ 0 +BLKPARAM_IS_USER_OFFSET .equ 2 +BLKPARAM_SWAP_PAGE .equ 3 + +IDE_REG_DATA .equ 0x40 + + .area _COMMONMEM + +_devide_read_data: + ld a,(_blk_op + BLKPARAM_IS_USER_OFFSET) + ld hl,(_blk_op + BLKPARAM_ADDR_OFFSET) + ld bc,#IDE_REG_DATA + ; Swap not yet supported + or a + jr z, no_map + push af + call map_process_always + pop af +no_map: + inir + inir + or a + ret z + push af + call map_kernel_restore + pop af + ret + +_devide_write_data: + ld a,(_blk_op + BLKPARAM_IS_USER_OFFSET) + ld hl,(_blk_op + BLKPARAM_ADDR_OFFSET) + ld bc,#IDE_REG_DATA + ; Swap not yet supported + or a + jr z, no_mapw + push af + call map_process_always + pop af +no_mapw: + otir + otir + or a + ret z + push af + call map_kernel_restore + pop af + ret diff --git a/Kernel/platform-genie-eg64/target.mk b/Kernel/platform-genie-eg64/target.mk new file mode 100644 index 00000000..86bd316c --- /dev/null +++ b/Kernel/platform-genie-eg64/target.mk @@ -0,0 +1,2 @@ +export CPU = z80 +export Z80_PLATFORM=-ttrs80m1 diff --git a/Kernel/platform-genie-eg64/tricks.s b/Kernel/platform-genie-eg64/tricks.s new file mode 100644 index 00000000..b96992de --- /dev/null +++ b/Kernel/platform-genie-eg64/tricks.s @@ -0,0 +1,212 @@ + .module tricks + + .globl _ptab_alloc + .globl _newproc + .globl _chksigs + .globl _getproc + .globl _platform_monitor + .globl trap_illegal + .globl _platform_switchout + .globl _switchin + .globl _doexec + .globl _dofork + .globl _runticks + .globl unix_syscall_entry + .globl interrupt_handler + .globl _swapper + .globl _swapout + + ; imported debug symbols + .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex + + .include "kernel.def" + .include "../kernel.def" + + .area _COMMONMEM + +; Switchout switches out the current process, finds another that is READY, +; possibly the same process, and switches it in. When a process is +; restarted after calling switchout, it thinks it has just returned +; from switchout(). +; +; This function can have no arguments or auto variables. +_platform_switchout: + di + ; save machine state + + ld hl, #0 ; return code set here is ignored, but _switchin can + ; return from either _switchout OR _dofork, so they must both write + ; U_DATA__U_SP with the following on the stack: + push hl ; return code + push ix + push iy + ld (U_DATA__U_SP), sp ; this is where the SP is restored in _switchin + + ; find another process to run (may select this one again) + call _getproc + + push hl + call _switchin + + ; we should never get here + call _platform_monitor + +badswitchmsg: .ascii "_switchin: FAIL" + .db 13, 10, 0 +swapped: .ascii "_switchin: SWAPPED" + .db 13, 10, 0 + +_switchin: + di + pop bc ; return address + pop de ; new process pointer +; +; FIXME: do we actually *need* to restore the stack ! +; + push de ; restore stack + push bc ; restore stack + + push de + ld hl, #P_TAB__P_PAGE_OFFSET + add hl, de ; process ptr + pop de + + ld a, (hl) + + or a + jr nz, not_swapped + + ; + ; We are still on the departing processes stack, which is + ; fine for now. + ; + ld sp, #_swapstack + push hl + ; We will always swap out the current process + ld hl, (U_DATA__U_PTAB) + push hl + call _swapout + pop hl + pop hl + push de + call _swapper + pop de + pop hl + ld a, (hl) + +not_swapped: + ; check u_data->u_ptab matches what we wanted + ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab + or a ; clear carry flag + sbc hl, de ; subtract, result will be zero if DE==IX + jr nz, switchinfail + + ; wants optimising up a bit + ld ix, (U_DATA__U_PTAB) + ; next_process->p_status = P_RUNNING + ld P_TAB__P_STATUS_OFFSET(ix), #P_RUNNING + + ; Fix the moved page pointers + ; Just do one byte as that is all we use on this platform + ld a, P_TAB__P_PAGE_OFFSET(ix) + ld (U_DATA__U_PAGE), a + ; runticks = 0 + ld hl, #0 + ld (_runticks), hl + + ; restore machine state -- note we may be returning from either + ; _switchout or _dofork + ld sp, (U_DATA__U_SP) + + pop iy + pop ix + pop hl ; return code + + ; enable interrupts, if the ISR isn't already running + ld a, (U_DATA__U_ININTERRUPT) + or a + ret nz ; in ISR, leave interrupts off + ei + ret ; return with interrupts on + +switchinfail: + call outhl + ld hl, #badswitchmsg + call outstring + ; something went wrong and we didn't switch in what we asked for + jp _platform_monitor + +fork_proc_ptr: .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry + +; +; Called from _fork. We are in a syscall, the uarea is live as the +; parent uarea. The kernel is the mapped object. +; +_dofork: + ; always disconnect the vehicle battery before performing maintenance + di ; should already be the case ... belt and braces. + + pop de ; return address + pop hl ; new process p_tab* + push hl + push de + + ld (fork_proc_ptr), hl + + ; prepare return value in parent process -- HL = p->p_pid; + ld de, #P_TAB__P_PID_OFFSET + add hl, de + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + + ; Save the stack pointer and critical registers. + ; When this process (the parent) is switched back in, it will be as if + ; it returns with the value of the child's pid. + push hl ; HL still has p->p_pid from above, the return value in the parent + push ix + push iy + + ; save kernel stack pointer -- when it comes back in the parent we'll be in + ; _switchin which will immediately return (appearing to be _dofork() + ; returning) and with HL (ie return code) containing the child PID. + ; Hurray. + ld (U_DATA__U_SP), sp + + ; now we're in a safe state for _switchin to return in the parent + ; process. + + ld hl, (U_DATA__U_PTAB) + push hl + call _swapout + pop hl + + ; now the copy operation is complete we can get rid of the stuff + ; _switchin will be expecting from our copy of the stack. + pop bc + pop bc + pop bc + + ; Make a new process table entry, etc. + ld hl, (fork_proc_ptr) + push hl + call _newproc + pop bc + + ; runticks = 0; + ld hl, #0 + ld (_runticks), hl + ; in the child process, fork() returns zero. + ; + ; And we exit, with the kernel mapped, the child now being deemed + ; to be the live uarea. The parent is frozen in time and space as + ; if it had done a switchout(). + ret +; +; We can keep a stack in common because we will complete our +; use of it before we switch common block. In this case we have +; a true common so it's even easier. +; + .ds 128 +_swapstack: diff --git a/Kernel/platform-genie-eg64/trs80-bank.s b/Kernel/platform-genie-eg64/trs80-bank.s new file mode 100644 index 00000000..c2f79ba1 --- /dev/null +++ b/Kernel/platform-genie-eg64/trs80-bank.s @@ -0,0 +1,110 @@ +; +; Banking logic for the EG64 board +; + + .module trs80bank + + ; exported symbols + .globl init_hardware + .globl map_kernel + .globl map_kernel_restore + .globl map_process + .globl map_process_always + .globl map_for_swap + .globl map_save + .globl map_restore + + ; imported symbols + .globl _ramsize + .globl _procmem + + .globl interrupt_handler + .globl nmi_handler + .globl unix_syscall_entry + .globl null_handler + + .include "kernel.def" + .include "../kernel.def" + +; +; These live below 0x8000 so they are not switched out +; + .area _COMMONMEM + +init_hardware: + ; write zeroes across all vectors + ld hl, #0 + ld de, #1 + ld bc, #0x007f ; program first 0x80 bytes only + ld (hl), #0x00 + ldir + + ; now install the interrupt vector at 0x0038 + ld a, #0xC3 ; JP instruction + ld (0x0038), a + ld hl, #interrupt_handler + ld (0x0039), hl + + ; set restart vector for UZI system calls + ld (0x0030), a ; (rst 30h is unix function call vector) + ld hl, #unix_syscall_entry + ld (0x0031), hl + + ; Set vector for jump to NULL + ld (0x0000), a + ld hl, #null_handler ; to Our Trap Handler + ld (0x0001), hl + + ld (0x0066), a ; Set vector for NMI + ld hl, #nmi_handler + ld (0x0067), hl + + ld hl,#96 + ld (_ramsize),hl + ld hl,#48 + ld (_procmem),hl + im 1 ; set CPU interrupt mode + ret +; +; Mapping for us is fairly simple but it's not blank because we do +; some mapping. +; +; We could mark I/O unmapped except for GFX_MAP users and we could +; mark the low 16K R/O to protect it from user but then need to +; flip r/w on syscall and irq entry/exit.. FIXME. +; +map_kernel: +map_kernel_restore: + push af + ld a,#0xC0 ; Internal memory, ROM unmapped, IO + ld (map_state),a + out (0xC0),a + pop af + ret +map_process: +map_for_swap: +map_process_always: + push af + ld a,#0xD0 ; External high, ROM unmapped, IO + ld (map_state),a ; (so we can do screen mapping) + out (0xC0),a + pop af + ret +map_save: + push af + ld a,(map_state) + ld (map_save_val),a + pop af + ret +map_restore: + push af + ld a,(map_save_val) + ld (map_state),a + out (0xC0),a + pop af + ret + +map_state: + .db 0 +map_save_val: + .db 0 \ No newline at end of file diff --git a/Kernel/platform-genie-eg64/trs80.s b/Kernel/platform-genie-eg64/trs80.s new file mode 100644 index 00000000..ba35f722 --- /dev/null +++ b/Kernel/platform-genie-eg64/trs80.s @@ -0,0 +1,201 @@ +; +; TRS 80 hardware support +; + + .module trs80 + + ; exported symbols + .globl init_early + .globl interrupt_handler + .globl _program_vectors + .globl platform_interrupt_all + .globl map_kernel + .globl map_process + .globl map_process_always + .globl map_save + .globl map_restore + + .globl go_fast + .globl go_slow + + .globl s__COMMONMEM + .globl l__COMMONMEM + + .globl _trs80_model + + ; hard disk helpers + .globl _hd_xfer_in + .globl _hd_xfer_out + ; and the page from the C code + .globl _hd_page + + ; exported debugging tools + .globl _platform_monitor + .globl _platform_reboot + .globl outchar + + ; imported symbols + .globl _ramsize + .globl _procmem + .globl istack_top + .globl istack_switched_sp + .globl outcharhex + .globl null_handler + + .include "kernel.def" + .include "../kernel.def" + +; ----------------------------------------------------------------------------- +; COMMON MEMORY BANK (0x0000 upwards after the udata etc) +; ----------------------------------------------------------------------------- + .area _COMMONMEM + +_platform_monitor: +monitor_spin: + di + jr monitor_spin + +platform_interrupt_all: + ret + +_platform_reboot: + di + ld sp,#0xffff + xor a + out (0x43),a + rst 0 + +; ----------------------------------------------------------------------------- +; BOOT MEMORY BANK (0x0100) +; ----------------------------------------------------------------------------- + .area _BOOT + + +init_early: + ld a,(4) + cp #0x30 + jr nz, not_m3 + ld a,#1 + ld (_trs80_model),a + ld a,#0x74 + out (0xE0),a ; Mask iobus, cassette + xor a + out (0xE4),a ; and NMI sources + jr not_vg +not_m3: + ; Detect machine type (Model 1 or LNW80 or VideoGenie ?) + ld a,#8 + out (0xFE),a ; turn off ROM on the LNW80 + ld hl,#0 + ld a,(hl) + inc (hl) + cp (hl) + jr z, not_lnw ; if it's RAM it's an LNW80 + dec (hl) + xor a + out (0xFE),a ; ROM back on, normal video mode for now + ld a,#2 ; LNW80 + ld (_trs80_model), a + jr not_vg +not_lnw: + ld hl,(0x18F5) + ld de,#0x4E53 ; 'SN' for VG, 'L3' for TRS80 Model 1 + or a + sbc hl,de + jr nz, not_vg + ld a,#3 + ld (_trs80_model),a ; Video Genie +not_vg: + ret + +;------------------------------------------------------------------------------ +; COMMON MEMORY PROCEDURES FOLLOW + + .area _COMMONMEM +; +; TRS80 speed control. Save anything used except AF +; The LNW80 does automatic slowing on floppy disk access +; The Model 3 sprinter type card uses port 95 the same way but +; also does automatic slow down when needed. +; +; Only allowed to mess with AF +; +go_slow: + ld a,(_trs80_model) + or a + jr nz, snot_model_1 + ; A = 0 + out (254),a + ret +snot_model_1: + cp #1 ; model III ? + ret nz + xor a + out (95),a + ret +go_fast: + ld a,(_trs80_model) + or a + jr nz,fnot_model_1 + ; A = 0 + inc a + out (254),a + ret +fnot_model_1: + cp #1 + ret nz + ; A = 1 + out (95),a + ret + +_program_vectors: + ret + +; outchar: Wait for UART TX idle, then print the char in A +; destroys: AF +; FIXME: need to do different things for Video Genie and Model III +outchar: + ld (0x37E8), a + ret + +; +; Swap helpers. +; We have our buffers mapepd in Bank 2 but we don't need to do +; anything here as we are in common memory and we've carefully +; arranged that the device driver callers are in BANK2 thus we'll +; have BANK2 mapped by default, although we may map a user bank +; in temporarily if going direct to user. +; +_hd_xfer_in: + pop de + pop bc + pop hl + push hl + push bc + push de + ld a, (_hd_page) + or a + push af + call nz, map_process_always + ld bc, #0xC8 ; 256 bytes from 0xC8 + inir + pop af + call nz, map_kernel + ret + +_hd_xfer_out: + pop de + pop bc + pop hl + push hl + push bc + push de + ld a, (_hd_page) + or a + push af + call nz, map_process_always + ld bc, #0xC8 ; 256 bytes to 0xC8 + otir + pop af + call nz, map_kernel + ret diff --git a/Kernel/platform-genie-eg64/trs80load.s b/Kernel/platform-genie-eg64/trs80load.s new file mode 100644 index 00000000..ec8b5d72 --- /dev/null +++ b/Kernel/platform-genie-eg64/trs80load.s @@ -0,0 +1,140 @@ +.z80 +; +; TRS80 bootblock (256 bytes) +; +; We load from 0x0100-0xFFFF avoiding the I/O and screen area +; +; We could optimize this once we have the layout better planned so we +; load less empty space. On the other hand we've only got 256 bytes +; of space to play in. +; +.area BOOT (ABS) +.org 0x0080 ; loaded at 4200 and ldir's itself + +; +; The 1791 is memory mapped +; +FDCREG .equ 0x37EC +FDCTRK .equ 0x37ED +FDCSEC .equ 0x37EE +FDCDATA .equ 0x37EF +; +; Drive select is also more complicated than other models +; +LATCHD0 .equ 0x37E1 ; also drive select +LATCHD1 .equ 0x37E3 +LATCHD2 .equ 0x37E5 +LATCHD3 .equ 0x37E7 + +start: + ; + ; Copy ourself down to 0x0000 + ; + di + ld a,#0xC0 + out (0xC0),a ; RAM low + + ; Relocate to the bottom of memory + ld hl,#0x4200 + ld de,#0x0000 + ld bc,#0x0100 + ld sp,#0x0100 + jp go + +go: + call 0x04C3 ; 64 columns + ; Loader message + ld de, #0x3C00 + call prints + .ascii 'VGLOAD 0.3EG64\0' + + ; Set up memory mapping + ld a,#0xE0 ; Hide display and I/O + out (0xC0),a ; so we can just load over them + ld de, #FDCDATA ; data port + ld hl, #FDCREG ; command port + ; Loading base address + ld bc, #0x0100 ; page aligned buffer we read into + +lastsec: ld a, #0 ; self modifying to save space + inc a + ld (lastsec+1), a ; track number... (start on 1) + ld (de),a ; Desired track into data + ld (hl),#0x1B ; seek, lowest step speed + push bc + ld b, #0 ; 0x00 256 delays +cmd1: djnz cmd1 +seekstat: + pop bc + bit 0, (hl) + jr nz, seekstat + ld a,(hl) + and #0x18 + jr z, secmove + call printse + .ascii 'seek\0' +bad2: jr bad2 +secmove: xor a + dec a + ld (nextsec+1), a +nextsec: + ld a, #255 ; self modifying sector count + inc a + ld (nextsec+1), a + cp #10 + jr z, lastsec + call floppy_read + jr nextsec +; +; Assume the boot ROM left us on track 0 and motor on +; +floppy_read: + ld (FDCSEC), a ; sector please + ld a, #0x01 ; drive 0 select + ld (LATCHD0), a ; keep motor on + ld a, #0x8C ; READ + ld (hl), a + push bc + ld b, a ; 8C is an acceptable delay! +l1: djnz l1 + pop bc +flopin: bit 1,(hl) + jr z, flopin + ld a,(de) + ld (bc),a + inc c ; page aligned + jr nz,flopin + ; Load up unil + inc b ; correct bc for next call + jr z, booted ; passed FFFF ? +flopstat: + ld a, (hl) + and #0x19 + rra ; test bit 0 + jr nz, flopstat + or a ; safe even though we rotated right + ret z + call printse + .ascii 'READ\0' +fail: jr fail + + +printse: + ld de, #0x3C40 +prints: + pop hl + ld a,#0xC0 ;map display + out (0xC0),a +printsl: + ld a, (hl) + inc hl + or a + jr z, out + ld (de), a + inc de + jr printsl +out: jp (hl) + +booted: call printse + .ascii 'BOOTING..\0' + jr 0x100 -- 2.34.1