Somewhat of a redesign of the original guesswork.
TRS80 Model 4/4P
+(or right now more accurately sdltrs/xtrs)
+
+Emulator Bugs:
+ The emulator is horribly insecure, it's default is to allow all
+ sorts of direct access to things. Even if you turn this off I've
+ had it segfault with FUZIX bugs which suggests its not too secure.
+
+ Repeating instructions like LDIR appear to be misemulated. LDIR
+ is performed an emulated block copy, not as an iterated LDI. The
+ real processor actually implements LDIR as "LDI, if not done
+ PC -= 2". FUZIX doesn't do any overlapped LDIR tricks so shouldn't
+ be affected.
Requirements:
128K RAM fitted
Hard disk drive (will be used for swap), or a suitable memory
expander board could be used with a bit of tweaking (or both!)
-Bank mapping on the trash 80 is a bit weird. We want to get two independent
-64K banks, but we can only have
-
-Port 0x84:
-
-
- 0 / 1
- 0 / 2
- 0 / 3
- 2 / 1
- 3 / 1
-
-So we put the kernel in 0/3 which allows us to put the apps in 2/1. This
-means we need our kernel logic for things like bank copies not in common
-but in the low 32K.
-
-
-Unfortunately for anyone who wants to do expander board hacks the usual
-expander board hacks exchange 2/3 with other banks so unless you've also got
-the HD64180 mod you may be out of luck, and if you have well its not much
-like a Trash80 any more. Anyway if you want try you'd need to extend the
-0x84 port poking to also poke port 0x94 bits 0-4.
-
-Also we have to deal with the mapping maze on the TRS 80, not only are we
-banked but we have modes and also a pop up boot rom
-
-0x9C bit 0 controls the boot prom
-
-0x80 controls the mapping mode (bits 0/1 select a mode)
-
-Boot occurs in mode 0 with RAM at 0x4000-FFFF ready to load
-
-We need to stick it into mode 2 or 3
-
-2 = 0000-F3FF RAM
- F400-F7FF keyboard
- F800-FFFF Video
-
-3 = 0-FFFF RAM
-
-(and probably want to be in mode 3 and flip to 2 for video/kbd work)
-
-Need vid and kbd helps out of common therefore and to save/restore video map
-option on an irq while in kernel mode
-
+Memory Map:
+ Base memory 0-FFFF (with a fair bit of slack) is used for the kernel
+ User processes run 0-7FFF in bank U64L32 or U64U32, in both cases
+ with the upper 32K being kept as the kernel bank.
+
+ It would be good to support 64K processes using the bank32 model.
+ Before that can be done however the TRS80 will need a custom
+ user copy function to deal with access to the upper 32K by mapping
+ it low instead. It will also need the location of the uarea stash to
+ be binary size dependent. Swapper write out is also fairly hairy
+ for the same reasons. So for now we just handle a pair of 32K
+ processes.
+
+ Processes that don't fit are swapped to hard disk. Without swap you
+ can run a pair of 32K processes, just enough for stuff like
+ bootstrap.
+
+Drivers:
+ 80 column display is done
+ Keyboard is not started
+ Floppy and Hard disk are fleshed out but not yet tested (need kbd
+ first)
+ Hard disk needs to read block 0, and handle partitions of some form
+ including finding where 'swap' lives
+
+Adding Support For Other Banked RAM:
+ See trs80.s, and the various map_* functions. These can be extended
+ to use U_DATA__U_PAGE+1 to carry a second byte of data. The
+ main.c for the platform sets up the pagemaps as a 16bit value
+ which currently is just the opreg bits for the two banks.
+
+ map_save/restore will also need to handle any sub banking
+ arrangement.
+
+ Finally up MAX_BANKS in platform/config.h accordingly.
+
+
+Setting It Up
+
+ # Tool to make disk images
+ make tools/makejv3
+ # Double density single sided 40 track boot disk
+ tools/makejv3 dd40s /dev/zero mydisk.jv3
+ # Build boot block (not yet converted to sdasz80)
+ zmac platform/z80boot.s
+ # Build kernel (edit Makefile, set target then)
+ make
+ # Add pieces to the disk (boot block in sector 0, kernel at end)
+ dd if=zout/trs80load.cim of=mydisk.jv3 bs=1 seek=8704 conv=notrunc
+ dd if=fuzix.bin of=mydisk.jv3 bs=1 seek=142336 conv=notrunc
+ # Once we get that far you can also put a filesystem in the lower
+ # blocks
+ #
+ sdltrs -emtsafe -disk0 mydisk.jv3 -model 4p
+ #
/* Profil syscall support (not yet complete) */
#define CONFIG_PROFIL
/* Multiple processes in memory at once */
-#undef CONFIG_MULTI
+#define CONFIG_MULTI
/* Single tasking */
-#define CONFIG_SINGLETASK
+#undef CONFIG_SINGLETASK
/* Video terminal, not a serial tty */
#define CONFIG_VT
/* Simple character addressed device */
#define CONFIG_VT_SIMPLE
+/* Banked memory set up */
+#define CONFIG_BANK_FIXED
+#define MAX_MAPS 2
+#define MAP_SIZE 0x8000
#define CONFIG_BANKS 2 /* 2 x 32K */
#define TICKSPERSEC 60 /* Ticks per second */
#define PROGBASE 0x0000 /* Base of user */
#define PROGLOAD 0x0100 /* Load and run here */
-#define PROGTOP 0xEA00 /* Top of program, base of U_DATA */
-#define PROC_SIZE 64 /* Memory needed per process */
+#define PROGTOP 0x7D00 /* Top of program, base of U_DATA */
+#define PROC_SIZE 32 /* Memory needed per process */
-#define SWAP_SIZE 0x80 /* 64K in blocks (we actually don't need quite all) */
+#define SWAP_SIZE 0x40 /* 32K in blocks */
#define SWAPBASE 0x0000 /* We swap the lot in one, include the */
-#define SWAPTOP 0xF400 /* vectors so its a round number of sectors */
+#define SWAPTOP 0x8000 /* vectors so its a round number of sectors */
-#define MAX_SWAPS 8 /* Should be plenty */
+#define MAX_SWAPS 16 /* Should be plenty */
#define BOOT_TTY (512 + 1) /* Set this to default device for stdio, stderr */
/* In this case, the default is the first TTY device */
/* Device parameters */
#define NUM_DEV_TTY 3
#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
-#define SWAPDEV 5 /* Device for swapping. */
+#define SWAPDEV (258) /* Device for swapping (2nd hd). */
#define NBUFS 10 /* Number of block buffers */
#define NMOUNTS 4 /* Number of mounts at a time */
-
-
-
-
ld de, #s__COMMONMEM
ld bc, #l__COMMONMEM
ldir
+ ; then the discard
ld de, #s__DISCARD
ld bc, #l__DISCARD
ldir
di
stop: halt
jr stop
-
-
-
-
-clear:
- ld a, b
- or a
- jr nz, clear_1
- ld a, c
- cp #2
- ret c
-clear_1:
- dec bc
- ld (hl), #0
- ld d, h
- ld e, l
- inc de
- ldir
- ret
#include <printf.h>
#include <stdbool.h>
#include <tty.h>
+#include <vt.h>
#include <devtty.h>
#include <stdarg.h>
void tty_putc(uint8_t minor, unsigned char c)
{
- minor;c;
- /* call vt driver */
+ if (minor == 1)
+ vtoutput(&c, 1);
}
void tty_pollirq(void)
U_DATA .equ 0xEA00 ; (this is struct u_data from kernel.h)
U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes.
+U_DATA_STASH .equ 0x7D00 ; BD00-BFFF
+
NMOS_Z80 .equ 1
void map_init(void)
{
}
+
+void pagemap_init(void)
+{
+ pagemap_add(0x63); /* Mode 3, U64K low 32K mapped as low 32K */
+ pagemap_add(0x73); /* Mode 3, U64K high 32K mapped as low 32K */
+}
-; 2013-12-21 William R Sowerbutts
-
+;
+; This is heavily based on the Z80Pack platform code.
+;
.module tricks
.globl _ptab_alloc
.globl interrupt_handler
.globl dispatch_process_signal
.globl _swapper
- .globl _swapout
+
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_a
+ .globl map_process_always
; imported debug symbols
.globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
; possibly the same process, and switches it in. When a process is
; restarted after calling switchout, it thinks it has just returned
; from switchout().
-;
-; FIXME: make sure we optimise the switch to self case higher up the stack!
;
; This function can have no arguments or auto variables.
_switchout:
xor a
ld (_inint), a
+ ; Stash the uarea back into process memory
+ ld hl, (U_DATA__U_PAGE)
+ call map_process_always
+ ld hl, #U_DATA
+ ld de, #U_DATA_STASH
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+ call map_kernel
+
; find another process to run (may select this one again)
call _getproc
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
+ call map_kernel
+
+ 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
push de
call _swapper
pop de
-
+ pop hl
+ ld a, (hl)
not_swapped:
+ ; Pages please !
+ call map_process_a
+
+ ; bear in mind that the stack will be switched now, so we can't use it
+ ; to carry values over this point
+
+ exx ; thank goodness for exx 8)
+ ld hl, #U_DATA_STASH
+ ld de, #U_DATA
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+ exx
+
+ call map_kernel
+
; 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==HL
+ sbc hl, de ; subtract, result will be zero if DE==IX
jr nz, switchinfail
- ld hl, #P_TAB__P_STATUS_OFFSET
- add hl, de
+ ; wants optimising up a bit
+ ld ix, (U_DATA__U_PTAB)
; next_process->p_status = P_RUNNING
- ld (hl), #P_RUNNING
- ld de, #P_TAB__P_PAGE_OFFSET - P_TAB__P_STATUS_OFFSET
- add hl, de
- ld a, (hl)
- ld (U_DATA__U_PAGE), a
+ 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
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
; 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
+ ; Need to write a new 47.25K bank copy here, then copy the live uarea
+ ; into the stash of the new process
+
+ ; --------- copy process ---------
+
+ ld hl, (fork_proc_ptr)
+ ld de, #P_TAB__P_PAGE_OFFSET
+ add hl, de
+ ; load p_page
+ ld c, (hl)
+ ; load existing page ptr
+ push af
+ ld a, c
+ call outcharhex
+ pop af
+ ld a, (U_DATA__U_PAGE)
+
+ call bankfork ; do the bank to bank copy
+
+ ; Copy done
+
+ call map_process_always
+
+ ; We are going to copy the uarea into the parents uarea stash
+ ; we must not touch the parent uarea after this point, any
+ ; changes only affect the child
+ ld hl, #U_DATA ; copy the udata from common into the
+ ld de, #U_DATA_STASH ; target process
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+ ; Return to the kernel mapping
+ call map_kernel
; now the copy operation is complete we can get rid of the stuff
; _switchin will be expecting from our copy of the stack.
; if it had done a switchout().
ret
+;
+; This is related so we will keep it here. Copy the process memory
+; for a fork. a is the page base of the parent, c of the child
+;
+; Assumption - fits into a fixed number of whole 256 byte blocks
+;
+bankfork:
+; ld bc, #(0xC000 - 768) ; 48K minus the uarea stash
+
+ ld b, #0xBD ; C0 x 256 minus 3 sets for the uarea stash
+ ld hl, #0 ; base of memory to fork (vectors included)
+bankfork_1:
+ push bc ; Save our counter and also child offset
+ push hl
+ call map_process_a
+ ld de, #bouncebuffer
+ ld bc, #256
+ ldir ; copy into the bounce buffer
+ pop de ; recover source of copy to bounce
+ ; as destination in new bank
+ pop bc ; recover child page number
+ push bc
+ ld b, a ; save the parent bank id
+ ld a, c ; switch to the child
+ call map_process_a
+ push bc ; save the bank pointers
+ ld hl, #bouncebuffer
+ ld bc, #256
+ ldir ; copy into the child
+ pop bc ; recover the bank pointers
+ ex de, hl ; destination is now source for next bank
+ ld a, b ; parent bank is wanted in a
+ pop bc
+ djnz bankfork_1 ; rinse, repeat
+ ret
+
+;
+; For the moment
+;
+bouncebuffer:
+ .ds 256
+;
+; 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. This can share with the bounce
+; buffer used by bankfork as we won't switchin mid way through the
+; banked fork() call.
+;
+_swapstack:
.globl _system_tick_counter
.globl map_kernel
.globl map_process
+ .globl map_process_a
.globl map_process_always
.globl map_save
.globl map_restore
.globl fd_nmi_handler
.globl null_handler
+ .globl s__COMMONMEM
+ .globl l__COMMONMEM
+
.include "kernel.def"
.include "../kernel.def"
; -----------------------------------------------------------------------------
-; COMMON MEMORY BANK (0xF000 upwards)
+; COMMON MEMORY BANK (0xEA00 upwards)
; -----------------------------------------------------------------------------
.area _COMMONMEM
out (0x28), a
; -----------------------------------------------------------------------------
-; KERNEL MEMORY BANK (below 0xF000, only accessible when the kernel is mapped)
+; KERNEL MEMORY BANK (below 0xEA00, only accessible when the kernel is mapped)
; -----------------------------------------------------------------------------
.area _CODE
; load the 6845 parameters
ld hl, #_ctc6845
- ld b, #1588
+ ld bc, #1588
ctcloop: out (c), b ; register
ld a, (hl)
out (0x89), a ; data
.area _COMMONMEM
-opsave: .db 0x36
-_opreg: .db 0x36 ; kernel map, 80 columns
+opsave: .db 0x06
+_opreg: .db 0x06 ; kernel map, 80 columns
_modout: .db 0x50 ; 80 column, sound enabled, altchars off,
; external I/O enabled, 4MHz
_kernel_flag:
ld (0x0067), hl
;
-; Fixed mapping set up for the TRS80 4/4P
+; Mapping set up for the TRS80 4/4P
;
-; Kernel runs mode 2, U64K/U32 mapped at L64K/U32
+; The top 32K bank holds kernel code and pieces of common memory
+; The lower 32K is switched between the various user banks. On a
+; 4 or 4P without add in magic thats 0x62 and 0x63 mappings.
;
-map_kernel_a:
- ld a, i
+map_kernel:
push af
- di
ld a, (_opreg)
- and #0xAC
- or #0x12
+ and #0x8C ; keep video bits
+ or #0x02 ; map 2, base memory
ld (_opreg), a
out (0x84), a
pop af
- ret po
- ei
- ret
-map_kernel:
- push af
- call map_kernel_a
- pop af
ret
;
; Userspace mapping is mode 3, U64K/L32 mapped at L64K/L32
ld a, h
or l
jr z, map_kernel
- call map_process_always_a
- ret
+map_process_hl:
+ ld a, (_opreg)
+ and #0x8C
+ or (hl) ; udata page
+ ld (_opreg), a
+ out (0x84), a
+ ret
-map_process_always_a:
+map_process_a: ; used by bankfork
push af
+ push bc
+ ld b, a
ld a, (_opreg)
- and #0xAC
- or #0x43
+ and #0x8C
+ or b
ld (_opreg), a
out (0x84), a
+ pop bc
pop af
- ret
+ ret
map_process_always:
- ld a, i
push af
- di
- call map_process_always_a
+ ld hl, #U_DATA__U_PAGE
+ call map_process_hl
pop af
- ret po
- ei
ret
map_save: push af
ld a, (_opreg)
- and #0x53
+ and #0x73
ld (opsave), a
pop af
ret
ld a, (opsave)
ld b, a
ld a, (_opreg)
- and #0xAC
+ and #0x8C
or b
ld (_opreg), a
out (0x84), a
+ pop bc
+ pop af
ret
; outchar: Wait for UART TX idle, then print the char in A
; destroys: AF
outchar:
- out (0x01), a
+; out (0x01), a
ret
-
-nap:
- ; FIXME
- ret
-;
-; Idle the WD1772
-;
-_fd_idle:
- in a, (0xF0)
- rra
- jr c, _fd_idle
- ld l, a
- ld h, #0
- ret
-
-;
-; See to a given track in C
-;
-_fd_seek:
- ld a, c
- out (0xF1), a ; track #
- ld a, #0x1B ; Seek
- out (0xF0), a
- call nap
- call _fd_idle
- and #0x10
- ret
-
-
-;
-; Write a 256 byte block to disk. Need to add precomp etc to this
-; HL = pointer to data
-; A = bank info (0 kernel, !0 user). We don't handle swap to floppy.
-;
-;
-_fd_write:
- or a
- jr z, _fd_kwrite
- call map_process_always
-_fd_kwrite:
- ld bc, #0x00F3 ; port F3, 256 bytes
- ld a, i
- push af
- ld a, #0xAC ; Write
- out (0xF0), a
- call nap
-_fd_writel:
- in a, (0xF0)
- bit 1, a
- jr z, _fd_writec
- di
-_fd_writeb:
- otir
- ei
- call _fd_idle
- and #0x5C
-fd_wout:
- ld l, a
- call map_kernel
- pop af
- ret po
- ei
- ret
-_fd_writec:
- bit 2, a
- jr nz, _fd_writel
- ld a, #0xff
- jr fd_wout
-
-
-
-;
-; Read a 256 byte block from disk. Need to add precomp etc to this
-; HL = pointer to data
-; A = bank info (0 kernel, !0 user). We don't handle swap to floppy.
-;
-;
-_fd_read:
- or a
- jr z, _fd_kwrite
- call map_process_always
-_fd_kread:
- ld bc, #0x00F3 ; port F3, 256 bytes
- ld a, i
- push af
- ld a, #0x8C ; Read
- out (0xF0), a
- call nap
-_fd_readl:
- in a, (0xF0)
- bit 1, a
- jr z, _fd_readc
- di
-_fd_readb:
- inir
- ei
- call _fd_idle
- and #0x5C
-fd_rout:
- ld l, a
- pop af
- ret po
- ei
- ret
-_fd_readc:
- bit 2, a
- jr nz, _fd_readl
- ld a, #0xff
- jr fd_rout
-
-_fdc_idle:
- ; FIXME
- ret
-;
-; Restore the current drive to track 0 (error recovery)
-;
-_fd_reset:
- ld a, #0xB
- out (0xF0), a
- call nap
- call _fdc_idle
- and #0x10
- ret
;
.org 0x0
start:
- ld a, 0x36 ; kernel map, 80 column
+ ld a, 0x06 ; kernel map, 80 column, no remap
out (0x84), a
ld a, 0x50 ; 80 column, sound, altchars off,
; ext I/O on , 4MHz
ld a, (secnum)
inc a
ld (secnum), a
- cp 11
+ cp 19
jr z, lastsec
push hl
call floppy_read
tty.rel
mm.rel
swap.rel
-single.rel
+bankfixed.rel
vt.rel
devsys.rel
platform-trs80/devlpr.rel