that space if possible. We can hide the font/video below that under the top of
the ROM however.
-
-Intended Memory Map
-
-
-0x0000-0x00FF Vectors
-0x0100-0x7CFF (RAM) User program (and discard area)
-(could go to 0xBFFF for 48K but our swap is very limiting and might need
- smarter algorithms for swap - eg 16K banking for swap alone)
-0x7D00-0xCFFF Space for things we can bank under the ROM
- (secondary floppy cache ??)
-0xD000-0xDFFF Framebuffer, font, video code
-0xE000-0xF7FFish Data for kernel
-0xF800-0xFCFF Common (currently ends FBCC)
-0xFD00-0xFFFF Udata
-
-In kernel execution modes we then are either
-
-0x0000-0x00FF Vectors
-0x0100-0x5FFF Lower part of user
-0x6000-0xDFFF Kernel code in ROM
-0xE000-0xF7FFish Data for kernel
-0xF800-0xFCFF Common (currently ends FBCC)
-0xFD00-0xFFFF Udata
-
-and we will need to tuck the video code/font/framebuffer at 0xD000 or
-similar in RAM under the ROM area. May need to hide the floppy driver
-there too as its all a little bit tight
-
-In practice it appears the answer is
-
-1. Yes
-2. But only if the linker is taught to do banking. In fact once the linker
-does its own banking there is plenty of space although the bootloader might
-be a bit odd to make it work.
-
-A better layout given the 128K total swap limit might appear to be
-
-0x0000-0x00FF Vectors
-0x0100-0x7CFF Application
-0x7D00-0x7FFF Uarea
-
-but we actually can't do this because the 32K ROM is always mapped from
-0x6000-0xDFFF and needs to access the Uarea
-
+Banked Memory Map
+-----------------
+
+0x0000-0x00FF Vectors (which IM ? PX8 is IM2)
+0x0100-0x5FFF Kernel mappings
+0x6000-0xDFFF Switched segments 8/16/32K ROM x 2 or RAM
+ 0x6000-0xDFFF Application space (32K) [need to use a bit under
+ as 120K not 128K of swap]
+ 0x6000-0xDFFF CODE1/CODE2 } Both with copies of some of common
+ 0x6000-0xDFFF CODE3/Video } and rodata at same address range
+ [ Do we want to generate interleaved banking ? ]
+
+0xE000-0xFCFF Writable kernel data
+ Framebuffer (4K)
+0xFD00-0xFFFF UDATA
+
+We also need to put a catalogue in the middle of one of the two ROM blocks
+annoyingly. However that plays into our hands in other ways as we can
+minimally fake a disk rom holding a CP/M app which in fact runs the OS.
+
+
+Applications are swapped to/from the PX4Plus RAM disc (120K) allowing about
+4 apps of just under 32K each. Storage I/O is via the serial floppy and/or
+optional RAM/ROM cartriges. External 128K cartridges would also work for
+swap with floppy for OS. If the swap is custom handled in switching then
+we can switch the 32K rather than copy and gain another process, plus real
+floppy swap option.
+
+CP/M BIOS can be used (it's another 32K map over 0-7FFF, but as that maps
+over the vectors we must ensure its always called with interrupts off).
+
+The PX-8 is different: There is only a single 32K system ROM, but no video
+overhead. In theory its sufficient if we are replacing the OS ROM, and using
+the intelligent RAM disc, or the homebrew 512K one, plus floppies
#define CONFIG_VT
/* We want the 8x8 font for now (actually we want 6x8) */
#define CONFIG_FONT8X8
-/* And we only want 128 symbols of it (1K) for now */
-#define CONFIG_FONT8X8SMALL
/* CP/M emulation */
#undef CONFIG_CPM_EMU
/* Fixed banking */
/* FIXME: the OVL timer isn't quite 100/sec and we have an accurate 1Hz
timer available, so needs some tweaking */
#define TICKSPERSEC 100 /* Ticks per second */
-#define PROGBASE 0x0000 /* also data base */
-#define PROGLOAD 0x0100
-#define PROGTOP 0x4000 /* Top of program for debug */
+#define PROGBASE 0x6000 /* also data base */
+#define PROGLOAD 0x6000
+#define PROGTOP 0xDD00 /* Top of program */
-#define SWAP_SIZE 0x40 /* 32K in blocks (with uarea means 31K max app size) */
-#define SWAPBASE 0x0000 /* We swap the lot in one, include the */
-#define SWAPTOP 0x8000 /* vectors so its a round number of sectors */
-#define MAX_SWAPS 4 /* We have a whopping 128K of RAMDISC! */
+/* FIXME: treat the banks of the ramdisc as memory not swap, also trim
+ to 30K as only have 120K of RAMdisc */
+#define SWAP_SIZE 0x3C /* 30K in blocks (with uarea means 29.25K max app size) */
+#define SWAPBASE (uint8_t *)0x5D00 /* We swap the lot in one, include the */
+#define SWAPTOP (uint8_t *)0xDD00 /* vectors so its a round number of sectors */
+#define MAX_SWAPS 4 /* We have a whopping 120K of RAMDISC! */
#define BOOT_TTY (512 + 1)/* Set this to default device for stdio, stderr */
/* In this case, the default is the first TTY device */
#define VT_BOTTOM 7
-#define PFTABSIZE 4 /* All we have room for right now */
+#define PFTABSIZE 5 /* All we have room for right now */
; WRS: Note we list all our segments here, even though
; we don't use them all, because their ordering is set
; when they are first seen.
- .area _CODE
- .area _CODE2
+
+ ; Stuff that ends up in RAM, initialized bits first then data
+ ; We will need to pull the initialized bits into ROM for crt0.s
+ ; to ldir down
+ .area _COMMONMEM
.area _CONST
- .area _DATA
.area _INITIALIZED
- .area _COMMONMEM
+ .area _STUBS
+ .area _DATA
.area _BSEG
.area _BSS
.area _HEAP
.area _GSINIT
.area _GSFINAL
-
- .area _DISCARD
+ ; Udata ends up just below program code (so we can swap it easily)
.area _UDATA
- ; This is fixed up by the compile tool but we need it somewhere
- ; so the copier can fix it up !
- .area _INITIALIZER
-
+ ; First ROM is CODE (CODE2 folded in)
+ .area _CODE
+ ; Second ROM is CODE3 VIDEO FONT and initialized data to copy down
+ .area _CODE3
.area _FONT
.area _VIDEO
+ .area _INITIALIZER
+ ; Discard needs splitting code/data!
+ .area _DISCARD
+ ; and the bitmap display lives at E000-FFFF
+
; imported symbols
.globl _fuzix_main
stop: halt
jr stop
+ .area _STUBS
+stubs:
+ .ds 768
-mwxuy
-i fuzix.ihx
+-b _COMMONMEM=0x0100
+-b _UDATA=0x5D00
-b _DISCARD=0x4000
--b _UDATA=0xFD00
-b _CODE=0x6000
--b _DATA=0xE000
--b _FONT=0x5000
--b _INITIALIZER=0x3000
+-b _CODE3=0x6000
-l z80
+-r
platform-px4plus/crt0.rel
platform-px4plus/commonmem.rel
platform-px4plus/px4plus.rel
platform-px4plus/main.rel
start.rel
version.rel
-lowlevel-z80.rel
+lowlevel-z80-banked.rel
platform-px4plus/tricks.rel
timer.rel
kdata.rel
.globl _ramd_uaddr
+ .globl __bank_0_1
+ .globl __bank_0_2
+ .globl __bank_0_3
+ .globl __bank_1_2
+ .globl __bank_1_3
+ .globl __bank_2_1
+ .globl __bank_2_3
+ .globl __bank_3_1
+ .globl __bank_3_2
+
+ .globl __stub_0_1
+ .globl __stub_0_2
+ .globl __stub_0_3
+ .globl __stub_1_2
+ .globl __stub_1_3
+ .globl __stub_2_1
+ .globl __stub_2_3
+ .globl __stub_3_1
+ .globl __stub_3_2
+
+
.include "kernel.def"
.include "../kernel.def"
; set up interrupt vectors for the kernel
ld hl, #0
push hl
+ push af
call _program_vectors
+ pop af
pop hl
; IRQ enables
ld a, #0xB ; OVF (timer), RXRDY (gapnio), 7508
out (0x04), a
+ push af
call _vtinit
+ pop af
im 1 ; set CPU interrupt mode
ret
inc (hl)
dec hl
jr hd_xiloop
+
+
+;
+; FIXME
+current_map:
+ .byte 0
+switch_bank:
+ ret
+;
+;
+; Banking helpers
+;
+; FIXME: these are from zx128 - not yet converted to px4plus. We
+; should also be able to dump all to/from bank2 versions
+;
+;
+; Logical Physical
+; 0 COMMON (0x4000)
+; 1 0
+; 2 1
+; 3 7
+;
+;
+__bank_0_1:
+ xor a ; switch to physical bank 0 (logical 1)
+bankina0:
+ ;
+ ; Get the target address first, otherwise we will change
+ ; bank and read it from the wrong spot!
+ ;
+ pop hl ; Return address (points to true function address)
+ ld e, (hl) ; DE = function to call
+ inc hl
+ ld d, (hl)
+ inc hl
+ push hl ; Restore corrected return pointer
+ ld bc, (current_map) ; get current bank into B
+ call switch_bank ; Move to new bank
+ ; figure out which bank to map on the return path
+ ld a, c
+ or a
+ jr z, __retmap1
+ dec a
+ jr z, __retmap2
+ jr __retmap3
+
+callhl: jp (hl)
+__bank_0_2:
+ ld a, #1 ; logical 2 -> physical 1
+ jr bankina0
+__bank_0_3:
+ ld a, #7 ; logical 3 -> physical 7
+ jr bankina0
+
+__bank_1_2:
+ ld a, #1
+bankina1:
+ pop hl ; Return address (points to true function address)
+ ld e, (hl) ; DE = function to call
+ inc hl
+ ld d, (hl)
+ inc hl
+ push hl ; Restore corrected return pointer
+ call switch_bank ; Move to new bank
+__retmap1:
+ ex de, hl
+ call callhl ; call the function
+ xor a ; return to bank 1 (physical 0)
+ jp switch_bank
+
+__bank_1_3:
+ ld a, #7
+ jr bankina1
+__bank_2_1:
+ xor a
+bankina2:
+ pop hl ; Return address (points to true function address)
+ ld e, (hl) ; DE = function to call
+ inc hl
+ ld d, (hl)
+ inc hl
+ push hl ; Restore corrected return pointer
+ call switch_bank ; Move to new bank
+__retmap2:
+ ex de, hl
+ call callhl ; call the function
+ ld a, #1 ; return to bank 2
+ jp switch_bank
+__bank_2_3:
+ ld a, #7
+ jr bankina2
+__bank_3_1:
+ xor a
+bankina3:
+ pop hl ; Return address (points to true function address)
+ ld e, (hl) ; DE = function to call
+ inc hl
+ ld d, (hl)
+ inc hl
+ push hl ; Restore corrected return pointer
+ call switch_bank ; Move to new bank
+__retmap3:
+ ex de, hl
+ call callhl ; call the function
+ ld a, #7 ; return to bank 0
+ jp switch_bank
+
+__bank_3_2:
+ ld a, #1
+ jr bankina3
+
+;
+; Stubs need some stack munging and use DE
+;
+
+__stub_0_1:
+ xor a
+__stub_0_a:
+ pop hl ; the return
+ ex (sp), hl ; write it over the discard
+ ld bc, (current_map)
+ call switch_bank
+ ld a, c
+ or a
+ jr z, __stub_1_ret
+ dec a
+ jr z, __stub_2_ret
+ jr __stub_3_ret
+__stub_0_2:
+ ld a, #1
+ jr __stub_0_a
+__stub_0_3:
+ ld a, #7
+ jr __stub_0_a
+
+__stub_1_2:
+ ld a, #1
+__stub_1_a:
+ pop hl ; the return
+ ex (sp), hl ; write it over the discad
+ call switch_bank
+__stub_1_ret:
+ ex de, hl
+ call callhl
+ xor a
+ call switch_bank
+ pop de
+ push de ; dummy the caller will discard
+ push de
+ ret
+__stub_1_3:
+ ld a, #7
+ jr __stub_1_a
+
+__stub_2_1:
+ xor a
+__stub_2_a:
+ pop hl ; the return
+ ex (sp), hl ; write it over the discad
+ call switch_bank
+__stub_2_ret:
+ ex de, hl ; DE is our target
+ call callhl
+ ld a,#1
+ call switch_bank
+ pop de
+ push de ; dummy the caller will discard
+ push de
+ ret
+__stub_2_3:
+ ld a, #7
+ jr __stub_2_a
+
+__stub_3_1:
+ xor a
+__stub_3_a:
+ pop hl ; the return
+ ex (sp), hl ; write it over the discad
+ call switch_bank
+__stub_3_ret:
+ ex de, hl
+ call callhl
+ ld a,#7
+ call switch_bank
+ pop de
+ push de ; dummy the caller will discard
+ push de
+ ret
+__stub_3_2:
+ ld a, #1
+ jr __stub_3_a
; This function can have no arguments or auto variables.
_switchout:
di
+ push af
call _chksigs
+ pop af
; save machine state
ld hl, #0 ; return code set here is ignored, but _switchin can
ld (_inint), a
; find another process to run (may select this one again)
+ push af
call _getproc
+ pop af
push hl
+ push af
call _switchin
+ pop af
; we should never get here
call _trap_monitor
_switchin:
di
+ pop hl
pop bc ; return address
pop de ; new process pointer
;
;
push de ; restore stack
push bc ; restore stack
+ push hl
push de
ld hl, #P_TAB__P_PAGE_OFFSET
; We will always swap out the current process
ld hl, (U_DATA__U_PTAB)
push hl
+ push af
call _swapout
+ pop af
pop hl
pop hl
push de
+ push af
call _swapper
+ pop af
pop de
pop hl
ld a, (hl)
; always disconnect the vehicle battery before performing maintenance
di ; should already be the case ... belt and braces.
+ pop bc
pop de ; return address
pop hl ; new process p_tab*
push hl
push de
+ push bc
ld (fork_proc_ptr), hl
ld hl, (U_DATA__U_PTAB)
push hl
+ push af
call _swapout
+ pop af
pop hl
; now the copy operation is complete we can get rid of the stuff
; Make a new process table entry, etc.
ld hl, (fork_proc_ptr)
push hl
+ push af
call _newproc
+ pop af
pop bc
; runticks = 0;