--- /dev/null
+;
+; 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"
+
--- /dev/null
+ ; 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
+
--- /dev/null
+;
+; 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
--- /dev/null
+ .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
--- /dev/null
+export CPU = z80
+export Z80_PLATFORM=-ttrs80m1
--- /dev/null
+ .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:
--- /dev/null
+;
+; 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
--- /dev/null
+;
+; 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
--- /dev/null
+.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