From fa49287dc5728a1a69b546cd7adb5b8b7b4008f6 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 10 Feb 2015 23:38:35 +0000 Subject: [PATCH] zx128: update the various ZX128 bits needed --- Kernel/platform-zx128/README | 64 ++++++++++++-- Kernel/platform-zx128/crt0.s | 143 ++++++++++++-------------------- Kernel/platform-zx128/fuzix.lnk | 2 +- Kernel/platform-zx128/zx128.s | 120 ++++++++++++++++++++------- 4 files changed, 200 insertions(+), 129 deletions(-) diff --git a/Kernel/platform-zx128/README b/Kernel/platform-zx128/README index b40fc837..e716efe7 100644 --- a/Kernel/platform-zx128/README +++ b/Kernel/platform-zx128/README @@ -1,22 +1,29 @@ An FUZIX target for ZX Spectrum 128. -0000-3FFF Fixed BASIC ROM +0000-3FFF Fixed BASIC ROM (including ROM font) + (Strictly speaking this is a choice of ROMs and magic + overlaying hardware ROMs like the interface 1). We need the + 48K one so we can use the font and the 0x3900 hack 4000-7FFF Kernel data, common read/write space, constants, common read only, plenty of space 8000-BFFF _DISCARD area - blown away when we exec init + followed by the initializing copies of common etc that are + copied down post boot C000-FFFF 0: Kernel CODE (full) 1: Kernel CODE2 (full) - 2: Mapped at 0x8000-0xBFFF + 2: Mapped at 0x8000-0xBFFF (exchanged with user process buffer) 3: User process 4: User process - 5: Mapped at 0x4000-0x7FFF + 5: Mapped at 0x4000-0x7FFF (Kernel data/common) 6: User process - 7: CODE3, Display + Video, Font (fairly full) + 7: CODE3, Display + Video, Font (fairly full - font not needed + however) Which gives us the ability to run 16K and 32K processes although we'd need -to do bank exchanges to keep 2 x32K in memory at once (one in 2/3 one in -4/7 and doing a block exchange of the memory). +to do bank exchanges to keep 2 x 32K in memory at once (one in 2/3 one in +4/7 and doing a block exchange of the memory). From the TRS80 this seems +to be worth the extra effort. Making the base kernel use the low ROM space is surprisingly pointless. We could just about squash it in with screen at 0x4000 and claim back page 7. @@ -42,6 +49,47 @@ on the 128. TODO: -Use IM2 so we can capture timer interrupts correctly. This is a hack but -it's necessary and is also how all the spectrum games do it. +Set vectors for syscalls somewhere safe and common (can't use the normal RST +ones). Need a reloc hack to pick syscall method ?? +Support the +2A and +3 platform. These have the same banking options but +also "special mode". In special mode we get a choice of four other pure RAM +bank setups + + 00 40 80 C0 +conf0 [0] [1] [2] [3] +conf1 [4] [5] [6] [7] +conf2 [4] [5] [6] [3] +conf3 [4] [7] [6] [3] + + +That gives us a conventional low 0/1 and 4/5 for user space with the kernel +using 2/3/6/7. + +Kernel maps are then + +3 = common (always mapped high) +2/6 = banked (at 0x8000) +7 = banked with screen (at 0x4000) + +although 7 does not appear to be part of the banks we cannot map 2 and 7 +together so it's effectively banked, but we can hack the linker to do smart +optimisations for calls between banks 6 and 7. + +Alternatively we could go with a single 64K swapping user space with +kernel mapped normally at 4-7 (with screen hole at C000) and user at 0-3 +and a small copied common in bank 3 and 7 + +No IM2 hack needed. + + +The Timx is different again. Memory is 3 banks of 64K. The main bank is laid +out like the Spectrum 48K but with the screen at 0x4000 (or for 80 col mode +most of 0x4000-0x8000). Each 8K can come from either main memory or an +external bank (EX-ROM or DOCK). Dock and EX_ROM pages cannot be mapped at +the same time. Not clear how well it can be supported + +Kernel in base 0x4000->0xFFFF (minus one screen), user in dock perhaps ? +Would be tight for kernel but give 2 x 32K apps if supported relocations. + +Might need OS partly in cartridge ROM to make it work (certainly for 80col) diff --git a/Kernel/platform-zx128/crt0.s b/Kernel/platform-zx128/crt0.s index b3609da1..588987a7 100644 --- a/Kernel/platform-zx128/crt0.s +++ b/Kernel/platform-zx128/crt0.s @@ -1,34 +1,39 @@ .module crt0 - ; Loaded at 0x4000 and the lowest available RAM (the display we keep - ; in bank 7 and mapped high). + ; + ; Our common and data live in 0x4000-0x7FFF + ; + .area _COMMONMEM + .area _STUBS .area _COMMONDATA + .area _CONST .area _INITIALIZED + ; + ; We move INITIALIZER into INITIALIZED at preparation time + ; then pack COMMONMEM.. end of INITIALIZED after DISCARD + ; in the load image. Beyond that point we just zero. + ; .area _DATA .area _BSEG .area _BSS - ; These are loaded as low as we can in memory. If we are using an - ; interface 1 cartridge then _CONST to the end of _STUBS can live in - ; ROM. Right now we still need to fiddle with RO as we don't use the - ; IM2 hack and we'll need to modify SDCC and the bank linker not to - ; use RST to avoid this. - .area _CONST - .area _COMMONMEM - .area _STUBS .area _HEAP - ; note that areas below here may be overwritten by the heap at runtime, so - ; put initialisation stuff in here .area _INITIALIZER .area _GSINIT .area _GSFINAL + ; + ; All our code is banked at 0xC000 + ; .area _CODE .area _CODE2 ; ; Code3 sits above the display area along with the font and video - ; code so that they can access the display easily. + ; code so that they can access the display easily. It lives at + ; 0xDB00 therefore ; .area _CODE3 .area _VIDEO + + ; FIXME: We should switch to the ROM font and an ascii remap ? .area _FONT ; Discard is dumped in at 0x8000 and will be blown away later. .area _DISCARD @@ -37,13 +42,13 @@ .globl _fuzix_main .globl init_early .globl init_hardware - .globl s__INITIALIZER - .globl l__INITIALIZER - .globl s__INITIALIZED - .globl s__COMMONMEM .globl l__COMMONMEM - .globl s__DATA - .globl l__DATA + .globl l__STUBS + .globl l__COMMONDATA + .globl l__INITIALIZED + .globl l__CONST + .globl s__DISCARD + .globl l__DISCARD .globl kstack_top .globl unix_syscall_entry @@ -55,72 +60,36 @@ ; startup code .area _CODE init: - jp 0x003 ; workaround for lowlevel-z80.s check for C3 at 0000 + jp init1 ; 0xC000 - entry point + jp init2 ; 0xC003 - entry point for .sna debug hacks +init1: di - ; if any button is pressed during reset - boot BASIC48 - in a, (#0xFE) ; only low 5 bits of 0xFE port contains key info. Bit is 0 when corresponding key of any half row is pressed. - or #0xE0 ; so setting high 3 bits to 1 - add #1 ; and check if we got 0xFF - - jp z, init_continue - - ; otherwise perform ROM bank switch and goto 0x0000 - ld de, #0x4000 - ld hl, #jump_to_basic_start - ld bc, #jump_to_basic_start - #jump_to_basic_end - ldir - jp 0x4000 - -jump_to_basic_start: - ld bc, #0x7FFD - ld a, #0x10 - out (c), a - jp 0 -jump_to_basic_end: - - ; spacer - .ds 0x0B - - ; .org 0x0030 ; syscall entry - jp unix_syscall_entry + ; We need to wipe the BSS etc, then copy the initialized data + ; and common etc from where they've been stuffed above the + ; discard segment loaded into 0x8000 - .ds 0x05 ; spacer - - ; .org 0x0038 ; interrupt handler - jp interrupt_handler - .ds 0x2B - - ; .org 0x0066 ; nmi handler - jp nmi_handler - -init_continue: - ; hack for emulator. Read remaining fuzix part to RAM from fuzix.bin -; ld bc, #0x1ee7 -; in a, (c) ld hl, #0x4000 ld de, #0x4001 + ld bc, #0x3FFF ld (hl), #0 - ld bc, #0x1800 ldir - ld (hl), #0x7 - ld bc, #0x02ff + ld hl, #s__DISCARD + ld de, #l__DISCARD + add hl, de ; linker dumbness workarounds + ld de, #0x4000 + ld bc, #l__COMMONMEM + ldir + ld bc, #l__STUBS + ldir + ld bc, #l__COMMONDATA + ldir + ld bc, #l__CONST + ldir + ld bc, #l__INITIALIZED ldir -; -; These hooks will be platform/dev specific -; - .if MICRODRIVE_BOOT - .globl mdv_boot - -boot_stack .equ 0xc000 - ld sp, #boot_stack - push af - call mdv_boot - pop af - .endif - - +init2: ld sp, #kstack_top ; Configure memory map @@ -128,27 +97,17 @@ boot_stack .equ 0xc000 call init_early pop af - ; our COMMONMEM is located in main code-data blob, so we - ; do not need to move it manually - - ; initialized data - ld hl, #s__INITIALIZER - ld de, #s__INITIALIZED - ld bc, #l__INITIALIZER - ldir - - ; then zero the data area - ld hl, #s__DATA - ld de, #s__DATA + 1 - ld bc, #l__DATA - 1 - ld (hl), #0 - ldir - ; Hardware setup push af call init_hardware pop af +l1: ld a, #7 + out (0xfe), a + xor a + out (0xfe), a + jr l1 + ; Call the C main routine push af call _fuzix_main diff --git a/Kernel/platform-zx128/fuzix.lnk b/Kernel/platform-zx128/fuzix.lnk index 78652dd6..2ad58f43 100644 --- a/Kernel/platform-zx128/fuzix.lnk +++ b/Kernel/platform-zx128/fuzix.lnk @@ -1,7 +1,7 @@ -mwxuy -r -i fuzix.ihx --b _COMMONDATA=0x4000 +-b _COMMONMEM=0x4000 -b _CODE=0xC000 -b _CODE2=0xC000 -b _CODE3=0xDB00 diff --git a/Kernel/platform-zx128/zx128.s b/Kernel/platform-zx128/zx128.s index 9ff6d29b..2ddfd956 100644 --- a/Kernel/platform-zx128/zx128.s +++ b/Kernel/platform-zx128/zx128.s @@ -9,6 +9,7 @@ .globl init_hardware .globl _program_vectors .globl platform_interrupt_all + .globl interrupt_handler .globl map_kernel .globl map_process @@ -65,13 +66,18 @@ _trap_reboot: ; ----------------------------------------------------------------------------- .area _CODE +; +; The memory banker will deal with the map setting +; init_early: - ld bc, #0x7ffd - xor a - ld (current_map), a - out (c), a ; set page 0 at 0xC000 +; ld bc, #0x7ffd +; xor a +; ld (current_map), a +; out (c), a ; set page 0 at 0xC000 ret + .area _VIDEO + init_hardware: ; set system RAM size ld hl, #128 @@ -79,10 +85,13 @@ init_hardware: ld hl, #(128 - 48) ; 48K for kernel ld (_procmem), hl + ld a, #4 + out (0xfe), a +l2: jr l2 ; screen initialization ; clear - ld hl, #0x4000 - ld de, #0x4001 + ld hl, #0xC000 + ld de, #0xC001 ld bc, #0x1800 ; There should be 0x17FF, but we are going xor a ; to copy additional byte to avoid need of ld (hl), a ; DE and HL increment before attribute @@ -94,7 +103,29 @@ init_hardware: ld (hl), a ldir - im 1 ; set CPU interrupt mode + ; set up the interrupt vectors at 0xFFF4 in each bank we use for + ; kernel. This is a standard spectrum trick from the game world. The + ; interrupt vectors are in ROM and there is no mechanism to make + ; them call your own code. Instead we use IM2 and autovectors. + ; The spectrum bus value is not predictable so IM2 will jump through + ; a vector at I + (random 8bit value). + ; We point I at a chunk of empty ROM that holds 0xFF 0xFF .. for at + ; least 256 bytes. Our IRQ will jump to 0xFFFF which we set to be a + ; JR instruction. 0x0000 is a fixed ROM constant which forms a + ; backward jump to 0xFFF4, where we can finally grab the IRQ. + ; + ; We must keep the Spectrum 48K ROM image mapped at all times. The + ; 128K image hasn't got the 0xFF space we use! + ; + xor a + call setvectors + ld a, #1 + call setvectors + ld a, #7 + call setvectors + ld a, #0x39 + ld i, a +; im 2 ; set CPU interrupt mode ret ;------------------------------------------------------------------------------ @@ -102,12 +133,30 @@ init_hardware: .area _COMMONMEM - ; our vectors are in ROM, so nothing to do here _program_vectors: - ret + pop bc + pop de + pop iy ; task ptr + push iy + push de + push bc + ld a, P_TAB__P_PAGE_OFFSET+1(iy) ; high page of the pair +setvectors: + call switch_bank + ld a, #0x19 + ld (0xffff), a ; JR (plus ROM at 0 gives JR $FFF4) + ld a, #0xC3 ; JP + ld (0xFFF4), a + ld hl, #interrupt_handler + ld (0xFFF5), a ; to IRQ handler + ret + ; bank switching procedure. On entrance: ; A - bank number to set + ; + ; FIXME: we can probably stack BC now we are banking sanely + ; switch_bank: di ; TODO: we need to call di() instead ld (current_map), a @@ -117,13 +166,14 @@ switch_bank: ld (place_for_c), a ld bc, #0x7ffd ld a, (current_map) + or #0x18 ; Spectrum 48K ROM, Screen in Bank 7 out (c), a ld a, (place_for_b) ld b, a ld a, (place_for_c) ld c, a ld a, (place_for_a) - ei +;FIXME ei ret map_kernel: @@ -183,10 +233,15 @@ place_for_c: ; ; Banking helpers ; -; FIXME: use real bank asssignments (0 is not 0 etc) +; Logical Physical +; 0 COMMON (0x4000) +; 1 0 +; 2 1 +; 3 7 +; ; __bank_0_1: - ld a, #1 + xor a ; switch to physical bank 0 (logical 1) bankina0: call switch_bank ; Move to new bank pop hl ; Return address (points to true function address) @@ -196,19 +251,20 @@ bankina0: inc hl push hl ; Restore corrected return pointer ex de, hl - call callhl ; call the function - xor a ; return to bank 0 - jp switch_bank -callhl: jp (hl) + call callhl ; can't optimise - we need the stack depth right + ret +callhl: jp (hl) ; calls from bank 0 are different to the others + ; we started in common so we don't need to map + ; the old state back __bank_0_2: - ld a, #2 + ld a, #1 ; logical 2 -> physical 1 jr bankina0 __bank_0_3: - ld a, #3 + ld a, #7 ; logical 3 -> physical 7 jr bankina0 __bank_1_2: - ld a, #2 + ld a, #1 bankina1: call switch_bank ; Move to new bank pop hl ; Return address (points to true function address) @@ -218,14 +274,22 @@ bankina1: inc hl push hl ; Restore corrected return pointer ex de, hl + call outhl call callhl ; call the function - ld a, #1 ; return to bank 0 - jp switch_bank + xor a ; return to bank 1 (physical 0) + call switch_bank +l1: ld c, #0xfe + ld a, #2 + out (c), a + ld b, #0 + out (c),b + jr l1 + __bank_1_3: - ld a, #3 + ld a, #7 jr bankina1 __bank_2_1: - ld a, #1 + xor a bankina2: call switch_bank ; Move to new bank pop hl ; Return address (points to true function address) @@ -236,13 +300,13 @@ bankina2: push hl ; Restore corrected return pointer ex de, hl call callhl ; call the function - ld a, #2 ; return to bank 0 + ld a, #1 ; return to bank 2 jp switch_bank __bank_2_3: - ld a, #3 + ld a, #7 jr bankina2 __bank_3_1: - ld a, #1 + xor a bankina3: call switch_bank ; Move to new bank pop hl ; Return address (points to true function address) @@ -253,9 +317,9 @@ bankina3: push hl ; Restore corrected return pointer ex de, hl call callhl ; call the function - ld a, #2 ; return to bank 0 + ld a, #7 ; return to bank 0 jp switch_bank __bank_3_2: - ld a, #2 + ld a, #1 jr bankina3 -- 2.34.1