ASRCS = crt0.s tricks.s commonmem.s rc2014.s monitor.s vfdterm.s buffers.s
-CSRCS = devices.c main.c devtty.c vfd-debug.c vfd-term.c extbuffer.c
+CSRCS = devices.c main.c devtty.c vfd-debug.c vfd-term.c
CSRCS += devinput.c
DISCARD_CSRCS = discard.c
DISCARD_DSRCS = ../dev/devide_discard.c ../dev/ds1302_discard.c
DSRCS = ../dev/devfd.c ../dev/devide.c ../dev/mbr.c ../dev/blkdev.c
DSRCS += ../dev/ds1302.c ../dev/ppide_rbc.c
-DASRCS = ../dev/devfd_hw.s ../dev/ds1302_rc2014.s
+DASRCS = ../dev/devfd_hw-banked.s ../dev/ds1302_rc2014-banked.s
NSRCS = ../dev/net/net_native.c
AOBJS = $(ASRCS:.s=.rel)
$(CROSS_AS) $(ASOPTS) $<
$(COBJS): %.rel: %.c
- $(CROSS_CC) $(CROSS_CCOPTS) --codeseg COMMONMEM -c $<
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG1) -c $<
$(DOBJS): %.rel: ../dev/%.c
- $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG1) -c $<
$(DAOBJS): %.rel: ../dev/%.s
$(CROSS_AS) $(ASOPTS) $@ $<
$(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEGDISC) -c $<
$(NOBJS): %.rel: ../dev/net/%.c
- $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+ $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG2) -c $<
clean:
rm -f $(OBJS) $(JUNK) core *~ bootrom.ihx bootrom.bin fuzix.com fuzix.rom
# Compile up the boot block
#
image: bootblock
- cat boot-romwbw.bin ../fuzix.bin >fuzix.romwbw
+# cat boot-romwbw.bin ../fuzix.bin >fuzix.romwbw
bootblock:
sdasz80 -o boot-romwbw.s
PPIDE
+Pending
+
+ TMS99918A VDP as timer
+
+ Sound cards (until Fuzix gets proper audio support)
+
+
Unsupported Hardware
SC108/SC114/Z80SBC64 or other CPU boards with their own banking instead
- of the 512K ROM/RAM
+ of the 512K ROM/RAM. Please see the relevant port instead.
SC114 bit bang serial
Z80 PIO cards (really because I've no idea how to present them)
SC110 CTC/serial (does not appear to be able to chain CTC pairs, as
- is neeed for IM1 mode).
+ is neeed for IM1 mode). You can run a wire by hand.
+
Things To Note When Modifying
* Other tty options - can we autodetect 16550A at 0x80.
- * Probe both 0x10 and 0xE0 for CF ?
-
* How to represent a Z80 PIO usefully to Fuzix (similar issues with Nascom)
both raw and hosting an interface (eg /dev/lp or even floppy)
/* Set this if you have the 8255 IDE adapter (mutually exclusive of RC2014_CF) */
#undef CONFIG_RC2014_PPIDE
/* Set this if you have the floppy interface */
-#undef CONFIG_RC2014_FLOPPY
+#define CONFIG_RC2014_FLOPPY
/* Set this if you have a VFD interface */
#undef CONFIG_RC2014_VFD
+#define OFTSIZE 64
+#define ITABSIZE 48
+#define PTABSIZE 24
+
/*
* Turn selections into system level defines
*/
#define MAX_SWAPS 16 /* We will size if from the partition */
/* Swap will be set up when a suitably labelled partition is seen */
#define CONFIG_DYNAMIC_SWAP
-/* We have lots of RAM so make better use of it for disk buffers. We grab
- a 16K page and use it as our disk cache */
-#define CONFIG_BLKBUF_EXTERNAL
+/* Kept in bank 2 */
+#define CONFIG_DYNAMIC_BUFPOOL
/*
* When the kernel swaps something it needs to map the right page into
* memory using map_for_swap and then turn the user address into a
#define CMDLINE NULL /* Location of root dev name */
#define BOOTDEVICENAMES "hd#,fd,,rd"
-#define NBUFS 32 /* Number of block buffers, keep in line with space reserved in zeta-v2.s */
+#define NBUFS 5 /* Number of block buffers - must match kernel.def */
#define NMOUNTS 4 /* Number of mounts at a time */
#define MAX_BLKDEV 5 /* 1 floppy, 4 IDE */
; 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 _CODE
.area _HOME ; compiler stores __mullong etc in here if you use them
- .area _CODE2
+ .area _STUBS
.area _CONST
.area _INITIALIZED
.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 _INITIALIZER ; binman copies this to the right place for us
.area _GSINIT ; unused
.area _GSFINAL ; unused
+ .area _BUFFERS
.area _DISCARD
+ .area _INITIALIZER ; binman copies this to the right place for us
+ .area _CODE1
+ .area _DISCARD1
+ .area _CODE2
+ .area _DISCARD2
.area _COMMONMEM
; exported symbols
.globl l__COMMONMEM
.globl s__DISCARD
.globl l__DISCARD
+ .globl s__BUFFERS
+ .globl l__BUFFERS
.globl s__DATA
.globl l__DATA
.globl kstack_top
.include "kernel.def"
+ .include "../kernel-z80.def"
- ; Dummy page0 area so binman doesn't pack us
+ ; startup code. Starts _CODE because we want it to be at 0x0100
- .area _PAGE0
-
- ; startup code
.area _CODE
init: ; must be at 0x0100 as we are loaded at that
; setup the memory paging for kernel
ld (hl), #0
ldir
+ ; Zero buffers area
+ ld hl, #s__BUFFERS
+ ld de, #s__BUFFERS + 1
+ ld bc, #l__BUFFERS - 1
+ ld (hl), #0
+ ldir
+
; Hardware setup
+ push af
call init_hardware
+ pop af
; Call the C main routine
+ push af
call _fuzix_main
+ pop af
; fuzix_main() shouldn't return, but if it does...
di
stop: halt
jr stop
+;
+; Call stubs are filled in here by the linker
+;
+
+ .area _STUBS
+ .ds 600
+
+ .area _BUFFERS
+;
+; Buffers (we use asm to set this up as we need them in a special segment
+; so we can grow to fit the space free
+;
+ .globl _bufpool
+ .area _BUFFERS
+
+_bufpool:
+ .ds BUFSIZE * NBUFS
+++ /dev/null
-/* Zeta SBC V2 memory driver
- *
- * 2017-01-03 William R Sowerbutts, based on RAM disk code by Sergey Kiselev
- */
-
-#include <kernel.h>
-#include <kdata.h>
-#include <printf.h>
-#define DEVRD_PRIVATE
-#include "devrd.h"
-
-void rd_page_copy(void); // devrd_zeta2_hw.s
-
-void rd_platform_copy(void)
-{
- uint16_t ocount, count, maxcpy;
-
- ocount = count = rd_cpy_count;
-
- while(true){
- /* ensure transfer will not span a 16KB bank boundary */
- maxcpy = ((uint16_t)rd_src_address) & 0x3FFF;
- if((rd_dst_address & 0x3FFF) > maxcpy)
- maxcpy = rd_dst_address & 0x3FFF;
- maxcpy = 0x4000 - maxcpy;
- if(rd_cpy_count > maxcpy)
- rd_cpy_count = maxcpy;
-#ifdef DEBUG
- kprintf("rd_transfer: src=0x%lx, dst=0x%x(%s) reverse=%d count=%d\n",
- rd_src_address, rd_dst_address, rd_dst_userspace?"user":"kern",
- rd_reverse, rd_cpy_count);
-#endif
- rd_page_copy();
-
- count -= rd_cpy_count;
- if(!count)
- break;
-
- rd_dst_address += rd_cpy_count;
- rd_src_address += rd_cpy_count;
- rd_cpy_count = count;
- }
-
- rd_cpy_count = ocount;
-}
+++ /dev/null
- .module devrd_hw
-
- ; imported symbols
- .globl map_kernel, mpgsel_cache, _kernel_pages
- .globl _rd_platform_copy
- .globl _udata
-
- ; exported symbols
- .globl _rd_page_copy
- .globl _rd_cpy_count
- .globl _rd_reverse
- .globl _rd_dst_userspace
- .globl _rd_dst_address
- .globl _rd_src_address
- .globl _devmem_read
- .globl _devmem_write
-
- .include "kernel.def"
- .include "../kernel-z80.def"
-
- .area _CODE
-_devmem_write:
- ld a, #1
- ld (_rd_reverse), a ; 1 = write
- jr _devmem_go
-
-_devmem_read:
- xor a
- ld (_rd_reverse), a ; 0 = read
- inc a
-_devmem_go:
- ld (_rd_dst_userspace), a ; 1 = userspace
- ; load the other parameters
- ld hl, (_udata + U_DATA__U_BASE)
- ld (_rd_dst_address), hl
- ld hl, (_udata + U_DATA__U_OFFSET)
- ld (_rd_src_address), hl
- ld hl, (_udata + U_DATA__U_OFFSET+2)
- ld (_rd_src_address+2), hl
- ld hl, (_udata + U_DATA__U_COUNT)
- ld (_rd_cpy_count), hl
- ; for single byte transfers we can optimise away the outer loop
- dec l ; test for HL=1
- ld a, h
- or l
- jp nz, _rd_platform_copy ; > 1 byte, do it the hard way
- call _rd_page_copy ; transfer single byte
- ld hl, #1 ; return with HL set appropriately
- ret
-
- .area _COMMONMEM
-;=========================================================================
-; _rd_page_copy - Copy data from one physical page to another
-; See notes in devrd.h for input parameters
-;=========================================================================
-_rd_page_copy:
- ; split rd_src_address into page and offset -- it's limited to 20 bits (max 0xFFFFF)
- ; example address 0x000ABCDE
- ; in memory it is stored: DE BC 0A 00
- ; offset would be 0x0ABCDE & 0x3FFF = 0x3CDE
- ; page would be 0x0ABCDE >> 14 = 0x2A
-
- ; compute source page number
- ld a,(_rd_src_address+1) ; load 0xBC -> B
- ld b, a
- ld a,(_rd_src_address+2) ; load 0x0A -> A
- rl b ; grab the top bit into carry
- rla ; shift accumulator left, load carry bit at the bottom
- rl b ; and again
- rla ; now A is the page number (0x2A)
-
- ; map source page
- ld (mpgsel_cache+1),a ; save the mapping
- out (MPGSEL_1),a ; map source page to bank #1
-
- ; compute source page offset, store in DE
- ld a,(_rd_src_address+1)
- and #0x3F ; mask to 16KB
- or #0x40 ; add offset for bank 1
- ld d, a
- ld a,(_rd_src_address+0)
- ld e, a ; now offset is in DE
-
- ; compute destination page index (addr 0xABCD >> 14 = 0x02)
- ld a,(_rd_dst_address+1) ; load top 8 bits
- and #0xc0 ; mask off top 2 bits
- rlca ; rotate into lower 2 bits
- rlca
- ld b, #0
- ld c, a ; store in l
-
- ; look up page number
- ld a,(_rd_dst_userspace) ; are we loading into userspace memory?
- or a
- jr nz, rd_translate_userspace
- ld hl, #_kernel_pages ; get kernel page table
- jr rd_do_translate
-rd_translate_userspace:
- ld hl, #_udata + U_DATA__U_PAGE ; get user process page table
-rd_do_translate:
- add hl, bc ; add index to base ptr (uint8_t *)
- ld a, (hl) ; load the page number from the page table
-
- ; map destination page
- ld (mpgsel_cache+2),a ; save the mapping
- out (MPGSEL_2),a ; map destination page to bank #2
-
- ; compute destination page offset, store in HL
- ld a,(_rd_dst_address+1)
- and #0x3F ; mask to 16KB
- or #0x80 ; add offset for bank #2
- ld h, a
- ld a, (_rd_dst_address+0)
- ld l, a ; now offset is in HL
-
- ; load byte count
- ld bc,(_rd_cpy_count) ; bytes to copy
-
- ; check if reversed
- ld a, (_rd_reverse)
- or a
- jr nz, go
- ex de,hl ; reverse if necessary
-go:
- ldir ; do the copy
- jp map_kernel ; map back the kernel
-
-; variables
-_rd_cpy_count:
- .dw 0 ; uint16_t
-_rd_reverse:
- .db 0 ; bool
-_rd_dst_userspace:
- .db 0 ; bool
-_rd_dst_address:
- .dw 0 ; uint16_t
-_rd_src_address:
- .db 0 ; uint32_t
- .db 0
- .db 0
- .db 0
-;=========================================================================
* Pages 32-34 are used by the kernel
* Page 35 is the common area for init
* Page 36 is the disk cache
+ * Pages 37 amd 38 are the second kernel bank
*/
- for (i = 32 + 5; i < 64; i++)
+ for (i = 32 + 7; i < 64; i++)
pagemap_add(i);
/* finally add the common area */
void map_init(void)
{
- /* Point the buffers into the 16-32K range that will be used by
- the buffer remap. We offset by 0x4000 if need be in the access
- functions when handling overlaps */
- bufptr bp = bufpool;
- uint8_t *p = (uint8_t *) 0x4000;
- while (bp < bufpool_end) {
- bp++->__bf_data = p;
- p += BLKSIZE;
- }
}
/*
+++ /dev/null
-#include <kernel.h>
-#include <kdata.h>
-#include <printf.h>
-
-extern uint8_t *bdest;
-extern uint16_t blen;
-extern void do_blkzero(uint8_t *ptr) __z88dk_fastcall;
-extern void do_blkcopyk(uint8_t *src) __z88dk_fastcall;
-extern void do_blkcopyul(uint8_t *src) __z88dk_fastcall;
-extern void do_blkcopyuh(uint8_t *src) __z88dk_fastcall;
-
-/*
- * Must live in CODE2
- */
-
-void blktok(void *kaddr, struct blkbuf *buf, uint16_t off, uint16_t len)
-{
- bdest = kaddr;
- blen = len;
- do_blkcopyk(buf->__bf_data + off);
-}
-
-void blkfromk(void *kaddr, struct blkbuf *buf, uint16_t off, uint16_t len)
-{
- bdest = buf->__bf_data + off;
- blen = len;
- do_blkcopyk(kaddr);
-}
-
-/* FIXME: work out a nice way to share the logic */
-void blktou(void *uaddr, struct blkbuf *buf, uint16_t off, uint16_t len)
-{
- uint16_t split;
- bdest = uaddr;
- blen = len;
- /* If it's all below 16K or all over 32K then use the 0x4000 window */
- if ((uint16_t)uaddr + len < 0x4000 || (uint16_t)uaddr > 0x8000) {
- do_blkcopyul(buf->__bf_data + off);
- return;
- }
- /* If it's all below 0x8000 then use the 0x8000 window */
- if ((uint16_t)uaddr + len < 0x8000) {
- do_blkcopyuh(buf->__bf_data + off + 0x4000);
- return;
- }
- /* Split case */
- split = 0x8000 - (uint16_t)uaddr;
- blen = split;
- do_blkcopyuh(buf->__bf_data + off + 0x4000);
- blen = len - split;
- bdest += split;
- do_blkcopyul(buf->__bf_data + off + split);
-}
-
-void blkfromu(void *uaddr, struct blkbuf *buf, uint16_t off, uint16_t len)
-{
- uint16_t split;
- bdest = buf->__bf_data + off;
- blen = len;
- /* If it's all below 16K or all over 32K then use the 0x4000 window */
- if ((uint16_t)uaddr + len < 0x4000 || (uint16_t)uaddr > 0x8000) {
- do_blkcopyul(uaddr);
- return;
- }
- /* If it's all below 0x8000 then use the 0x8000 window */
- if ((uint16_t)uaddr + len < 0x8000) {
- bdest += 0x4000;
- do_blkcopyuh(uaddr);
- return;
- }
- /* Split case */
- split = 0x8000 - uaddr;
- blen = split;
- bdest += 0x4000;
- do_blkcopyuh(uaddr);
- blen = len - split;
- bdest += split- 0x4000;
- do_blkcopyul((uint8_t *)uaddr + split);
-}
-
-static uint8_t scratchbuf[64];
-
-/* Worst case is needing to copy over about 64 bytes */
-void *blkptr(struct blkbuf *buf, uint16_t offset, uint16_t len)
-{
- if (len > 64)
- panic("blkptr");
- bdest = scratchbuf;
- blen = sizeof(scratchbuf);
- do_blkcopyk(buf->__bf_data + offset);
- return scratchbuf;
-}
-
-void blkzero(struct blkbuf *buf)
-{
- do_blkzero(buf->__bf_data);
-}
-
-/*
- * Scratch buffers for syscall arguments - until we can rework
- * execve and realloc to avoid this need
- *
- * The buffers must be valid in both kernel and buffer mappings.
- */
-
-extern uint8_t workbuf[2][BLKSIZE];
-static uint8_t tfree = 3;
-
-void tmpfree(void *p)
-{
- if (p == workbuf[0]) {
- tfree |= 1;
- return;
- }
- if (p == workbuf[1]) {
- tfree |= 2;
- return;
- }
- panic("tmpfree");
-}
-
-void *tmpbuf(void)
-{
- if (tfree & 1) {
- tfree &= ~1;
- return workbuf[0];
- }
- if (tfree & 2) {
- tfree &= ~2;
- return workbuf[1];
- }
- panic("tmpbuf");
-}
--mwxuy
+-rmwxuy
-i fuzix.ihx
-b _CODE=0x0100
--b _COMMONMEM=0xD000
--b _DISCARD=0xC300
+-b _CODE1=0x4000
+-b _CODE2=0x4000
+-b _COMMONMEM=0xF000
+-b _DISCARD=0xC000
-l z80
platform-rc2014/crt0.rel
platform-rc2014/commonmem.rel
platform-rc2014/vfd-term.rel
start.rel
version.rel
-lowlevel-z80.rel
+lowlevel-z80-banked.rel
platform-rc2014/tricks.rel
platform-rc2014/main.rel
-platform-rc2014/extbuffer.rel
platform-rc2014/buffers.rel
timer.rel
kdata.rel
platform-rc2014/devfd.rel
-platform-rc2014/devfd_hw.rel
+platform-rc2014/devfd_hw-banked.rel
platform-rc2014/devices.rel
devio.rel
filesys.rel
devsys.rel
devinput.rel
usermem.rel
-usermem_std-z80.rel
+usermem_std-z80-banked.rel
platform-rc2014/ppide_rbc.rel
platform-rc2014/discard.rel
platform-rc2014/devtty.rel
platform-rc2014/devinput.rel
platform-rc2014/ds1302.rel
platform-rc2014/ds1302_discard.rel
-platform-rc2014/ds1302_rc2014.rel
+platform-rc2014/ds1302_rc2014-banked.rel
platform-rc2014/net_native.rel
-e
MPGSEL_2 .equ 0x7A ; Bank_2 page select register (W/O)
MPGSEL_3 .equ 0x7B ; Bank_3 page select register (W/O)
MPGENA .equ 0x7C ; memory paging enable register, bit 0 (W/O)
+
+NBUFS .equ 5
uint8_t sio_present;
uint8_t sio1_present;
+struct blkbuf *bufpool_end = bufpool + NBUFS;
+
void platform_discard(void)
{
+ uint16_t discard_size = 0x4000 - (uint16_t)bufpool_end;
+ bufptr bp = bufpool_end;
+
+ discard_size /= sizeof(struct blkbuf);
+
+ kprintf("%d buffers added\n", discard_size);
+
+ bufpool_end += discard_size;
+
+ memset( bp, 0, discard_size * sizeof(struct blkbuf) );
+
+ for( bp = bufpool + NBUFS; bp < bufpool_end; ++bp ){
+ bp->bf_dev = NO_DEVICE;
+ bp->bf_busy = BF_FREE;
+ }
}
void platform_idle(void)
}
}
+
+/*
+ * So that we don't suck in a library routine we can't use from
+ * the runtime
+ */
+
+int strlen(const char *p)
+{
+ int len = 0;
+ while(*p++)
+ len++;
+ return len;
+}
+
/*
* Logic for tickless system. If you have an RTC you can ignore this.
*/
; -----------------------------------------------------------------------------
.ifne USE_FANCY_MONITOR ; -----------------------------------------------------
- .area _CODE ; actual monitor lives in kernel bank
+ .area _CODE2 ; actual monitor lives in kernel bank
.include "../lib/monitor-z80.s"
.area _COMMONMEM ; just a stub goes in common memory
.globl init_hardware
.globl _program_vectors
.globl map_kernel
+ .globl map_kernel_restore
.globl map_process
+ .globl map_process_save
.globl map_buffers
.globl map_buffers_user
.globl map_buffers_user_h
ACIA_RTS_LOW_A .EQU 0x96 ; rts low, xmit interrupt disabled
;ACIA_RTS_LOW_A .EQU 0xB6 ; rts low, xmit interrupt enabled
+MAP_BANK1 .equ 0x2221 ; 20 is low, 23 is high
+BANK1 .equ 0x21
+MAP_BANK2 .equ 0x2625 ; for now 24 is buffers - FIXME
+BANK2 .equ 0x25
+
;=========================================================================
; Initialization code
;=========================================================================
;=========================================================================
; Kernel code
;=========================================================================
- .area _CODE
+ .area _COMMONMEM
_platform_reboot:
; We need to map the ROM back in -- ideally into every page.
; install interrupt vectors
_program_vectors:
di
+ pop bc ; bank
pop de ; temporarily store return address
pop hl ; function argument -- base page number
push hl ; put stack back as it was
push de
+ push bc
; At this point the common block has already been copied
call map_process
; Outputs: none; all registers preserved
;=========================================================================
map_process_always:
+map_process_save:
map_process_always_di:
push hl
+ ld hl,#_kernel_pages + 1
+ ; Save the current versions of MPGSEL 1 and 2 for map_kernel_restore
+ ; so we return to the right kernel bank
+ in a,(MPGSEL_1)
+ ld (hl),a
+ inc hl
+ in a,(MPGSEL_2)
+ ld (hl),a
ld hl,#_udata + U_DATA__U_PAGE
jr map_process_2_pophl_ret
+;
+; FIXME: needs to save the old kernel mid mapping
+;
map_buffers_user:
push hl
ld hl,(_udata + U_DATA__U_PAGE)
; Outputs: none; all registers preserved
;=========================================================================
map_kernel:
+map_kernel_restore:
map_kernel_di:
push hl
ld hl,#_kernel_pages
out (MPGSEL_1),a
ret
+;
+; Bank switch functions
+;
+ .globl __bank_0_1
+ .globl __bank_0_2
+ .globl __bank_1_2
+ .globl __bank_2_1
+
+ .globl __stub_0_1
+ .globl __stub_0_2
+
+;
+; We are calling into bank 1 from common. We don't know the current
+; bank but we need to restore it as was.
+;
+__bank_0_1:
+ ld bc,#MAP_BANK1 ; target pairs always a pair
+bank0:
+ pop hl ; in linear order
+ ld e,(hl)
+ inc hl
+ ld d,(hl)
+ inc hl
+ push hl
+ ld (_kernel_pages + 1),bc
+ in a, (MPGSEL_1) ; existing page
+ ld b,a
+ ld a,c ; set new page
+ out (MPGSEL_1),a
+ inc a ; next page follows
+ out (MPGSEL_2),a
+ ex de,hl
+ ld a,b ; old bank
+ cp #BANK1 ; 1 or 2
+ jr z, retbank1
+ call callhl
+ ld bc,#MAP_BANK2
+banksetbc:
+ ld (_kernel_pages + 1), bc
+ ld a,c
+ out (MPGSEL_1),a
+ ld a,b
+ out (MPGSEL_2),a
+ ret
+retbank1:
+ ld bc,#MAP_BANK1
+ jr banksetbc
+__bank_0_2:
+ ld bc,#MAP_BANK2
+ jr bank0
+;
+; These are easier because we have two banks so know the target and
+; return bank in each case
+;
+__bank_1_2:
+ ld bc,#MAP_BANK2
+ pop hl ; in linear order
+ ld e,(hl)
+ inc hl
+ ld d,(hl)
+ inc hl
+ push hl
+ call banksetbc
+ call callhl
+ ld bc,#MAP_BANK1
+ jr banksetbc
+
+__bank_2_1:
+ ld bc,#MAP_BANK1
+ pop hl ; in linear order
+ ld e,(hl)
+ inc hl
+ ld d,(hl)
+ inc hl
+ push hl
+ call banksetbc
+ call callhl
+ ld bc,#MAP_BANK2
+ jr banksetbc
+
+__stub_0_1:
+ ld bc,#MAP_BANK1
+ jr stub_call
+__stub_0_2:
+ ld bc,#MAP_BANK2
+stub_call:
+ pop hl ; return address
+ ex (sp),hl ; bank space now target
+ in a,(MPGSEL_1) ; current bank
+ ld (_kernel_pages+1),bc ; target
+ ld b,a
+ ld a,c
+ out (MPGSEL_1),a
+ inc a
+ out (MPGSEL_2),a
+ ld a,b
+ cp #BANK1
+ jr z, stub_ret_1
+ call callhl
+ ld bc,#MAP_BANK2
+stub_ret:
+ ld (_kernel_pages+1),bc
+ ld a,c
+ out (MPGSEL_1),a
+ inc a
+ out (MPGSEL_2),a
+ pop bc ; return
+ push bc ; correct stack padding
+ push bc
+ ret ; done
+stub_ret_1:
+ call callhl
+ ld bc,#MAP_BANK1
+ jr stub_ret
+
+callhl: jp (hl)
+;
+; Helpers for swap
+;
+
_copy_common:
+ pop bc
pop hl
pop de
push de
push hl
+ push bc
ld a,e
call map_for_swap
ld hl,#0xD300
ld de,#0x4300
ld bc,#0x2D00
ldir
- jr map_kernel
+ jp map_kernel
; MPGSEL registers are read only, so their content is cached here
TOP_PORT .equ MPGSEL_3
- .include "../lib/z80bank16.s"
+ .globl _ptab_alloc
+ .globl _makeproc
+ .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 map_kernel
+ .globl _ramtop
+ .globl _need_resched
+ .globl top_bank
+ .globl _int_disabled
+ .globl _udata
+ .globl _kernel_pages
+ .globl map_kernel_restore
+
+ ; imported debug symbols
+ .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
+
+ .area _COMMONMEM
+
+; ramtop must be in common for single process swapping cases
+; and its a constant for the others from before init forks so it'll be fine
+; here
+_ramtop:
+ .dw 0
+_need_resched:
+ .db 0
+
+; 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().
+_platform_switchout:
+ ; 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
+ ; _udata + U_DATA__U_SP with the following on the stack:
+ push hl ; return code
+ ld hl,(_kernel_pages + 1) ; Save the kernel banks
+ push hl
+ push ix
+ push iy
+ ld (_udata + U_DATA__U_SP), sp ; this is where the SP is restored in _switchin
+
+ ; find another process to run (may select this one again)
+ push af
+ call _getproc
+ pop af
+
+ push hl
+ call _switchin
+
+ ; we should never get here
+ call _platform_monitor
+
+badswitchmsg: .ascii "_switchin: FAIL"
+ .db 13, 10, 0
+
+_switchin:
+ di
+ pop hl ; bank
+ 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 hl ; bank
+
+ ld hl, #P_TAB__P_PAGE_OFFSET+3 ; Common
+ add hl, de ; process ptr
+ ld a, (hl)
+ ld (top_bank),a
+ out (TOP_PORT), a ; *CAUTION* our stack just left the building
+
+ ; ------- No stack -------
+ ; check u_data->u_ptab matches what we wanted
+ ld hl, (_udata + 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 hl, #P_TAB__P_STATUS_OFFSET
+ add hl, de
+ ld (hl), #P_RUNNING
+
+ ; runticks = 0
+ ld hl, #0
+ ld (_runticks), hl
+
+ ; restore machine state -- note we may be returning from either
+ ; _switchout or _dofork
+ ld sp, (_udata + U_DATA__U_SP)
+
+ ; ---- New task stack ----
+
+ pop iy
+ pop ix
+ pop hl
+ ld (_kernel_pages + 1),hl
+ call map_kernel_restore
+ pop hl ; return code
+
+ ; enable interrupts, if the ISR isn't already running
+ ld a, (_udata + U_DATA__U_ININTERRUPT)
+ ld (_int_disabled),a
+ 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
+ ld hl,(_kernel_pages + 1)
+ push hl
+ 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 (_udata + U_DATA__U_SP), sp
+
+ ; now we're in a safe state for _switchin to return in the parent
+ ; process.
+
+ ; --------- we switch stack copies in this call -----------
+ call fork_copy ; copy 0x000 to udata.u_top and the
+ ; uarea and return on the childs
+ ; common
+ ; We are now in the kernel child context
+
+ ; 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
+ pop bc
+
+ ; The child makes its own new process table entry, etc.
+ ld hl, #_udata
+ push hl
+ ld hl, (fork_proc_ptr)
+ push hl
+ push af
+ call _makeproc
+ pop af
+ pop bc
+ pop bc
+
+ ; any calls to map process will now map the childs memory
+
+ ; 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
+
.area _COMMONMEM
void vfd_term_write(char c) {
__asm
- ld hl, #2+0
+ ld hl, #4+0 ; banked
add hl, sp
ld e, (hl)
call VFDTERM_PUTC
vfdLineLen .EQU 40
- .area _CODE
+ .area _CODE1
VFDTERM_PREINIT:
; initialize first two lines
LD A, #0x30