genie-eg64: push work in progress ready for 0.2
authorAlan Cox <alan@linux.intel.com>
Sun, 2 Sep 2018 20:55:00 +0000 (21:55 +0100)
committerAlan Cox <alan@linux.intel.com>
Sun, 2 Sep 2018 20:55:00 +0000 (21:55 +0100)
Kernel/platform-genie-eg64/commonmem.s [new file with mode: 0644]
Kernel/platform-genie-eg64/crt0.s [new file with mode: 0644]
Kernel/platform-genie-eg64/floppy.s [new file with mode: 0644]
Kernel/platform-genie-eg64/ide.s [new file with mode: 0644]
Kernel/platform-genie-eg64/target.mk [new file with mode: 0644]
Kernel/platform-genie-eg64/tricks.s [new file with mode: 0644]
Kernel/platform-genie-eg64/trs80-bank.s [new file with mode: 0644]
Kernel/platform-genie-eg64/trs80.s [new file with mode: 0644]
Kernel/platform-genie-eg64/trs80load.s [new file with mode: 0644]

diff --git a/Kernel/platform-genie-eg64/commonmem.s b/Kernel/platform-genie-eg64/commonmem.s
new file mode 100644 (file)
index 0000000..bafd590
--- /dev/null
@@ -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 (file)
index 0000000..afc3f70
--- /dev/null
@@ -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 (file)
index 0000000..ed393d6
--- /dev/null
@@ -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 (file)
index 0000000..8ff0fc2
--- /dev/null
@@ -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 (file)
index 0000000..86bd316
--- /dev/null
@@ -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 (file)
index 0000000..b96992d
--- /dev/null
@@ -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 (file)
index 0000000..c2f79ba
--- /dev/null
@@ -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 (file)
index 0000000..ba35f72
--- /dev/null
@@ -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 (file)
index 0000000..ec8b5d7
--- /dev/null
@@ -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