From: Will Sowerbutts Date: Sat, 27 Dec 2014 01:02:49 +0000 (+0000) Subject: Kernel support for Z180 CPUs X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=fa1a543a313af203914e16c4430a4ea5db0810b5;p=FUZIX.git Kernel support for Z180 CPUs --- diff --git a/Kernel/Makefile b/Kernel/Makefile index a5fb69ed..562aa1ef 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -11,13 +11,13 @@ export SUBVERSION = "ac1" UNAME_S := $(shell uname -s) -ifeq ($(CPU),z80) +ifneq (,$(filter $(CPU),z80 z180)) # matches CPU = z80 or z180 export CROSS_AS=sdasz80 export CROSS_LD=sdldz80 export CROSS_CC=sdcc #export CROSS_CCOPTS=-c --std-sdcc99 --no-std-crt0 -mz80 -I$(ROOT_DIR)/cpu-z80 -I$(ROOT_DIR)/platform-$(TARGET) -I$(ROOT_DIR)/include --max-allocs-per-node 1000000 --opt-code-size --Werror --stack-auto --constseg CONST #export CROSS_CCOPTS=-c --std-sdcc99 --no-std-crt0 -mz80 -I$(ROOT_DIR)/cpu-z80 -I$(ROOT_DIR)/platform-$(TARGET) -I$(ROOT_DIR)/include --max-allocs-per-node 200000 --opt-code-size --Werror --stack-auto --constseg CONST -export CROSS_CCOPTS=-c --std-sdcc99 --no-std-crt0 -mz80 -I$(ROOT_DIR)/cpu-z80 -I$(ROOT_DIR)/platform-$(TARGET) -I$(ROOT_DIR)/include --max-allocs-per-node 30000 --opt-code-size --Werror --stack-auto --constseg CONST +export CROSS_CCOPTS=-c --std-sdcc99 --no-std-crt0 -m$(CPU) -I$(ROOT_DIR)/cpu-$(CPU) -I$(ROOT_DIR)/platform-$(TARGET) -I$(ROOT_DIR)/include --max-allocs-per-node 30000 --opt-code-size --Werror --stack-auto --constseg CONST #export CROSS_CCOPTS+=--nostdlib --nostdinc -Isdcclib/include export CROSS_CC_SEG2=--codeseg CODE2 export CROSS_CC_SEGDISC=--codeseg DISCARD --constseg DISCARD @@ -30,9 +30,9 @@ export BINEXT = .rel # to look in the usual places !) # ifeq ($(UNAME_S),Darwin) -export LIBZ80=/usr/local/share/sdcc/lib/z80 +export LIBZ80=/usr/local/share/sdcc/lib/$(CPU) else -export LIBZ80=/usr/share/sdcc/lib/z80 +export LIBZ80=/usr/share/sdcc/lib/$(CPU) endif else ifeq ($(CPU),6502) export CROSS_AS=ca65 @@ -110,10 +110,14 @@ all: fuzix.bin .SUFFIXES: # delete the default suffixes .SUFFIXES: .c .s .rel +usermem_std-z180.rel: usermem_std-z180.s usermem_std-z80.s + +lowlevel-z180.rel: lowlevel-z180.s lowlevel-z80.s + target: - -rm platform + -rm -f platform ln -sf platform-$(TARGET) platform - $(MAKE) -C platform-$(TARGET) + +make -C platform-$(TARGET) $(C1OBJS): %$(BINEXT): %.c $(CROSS_CC) $(CROSS_CCOPTS) $(CROSS_CC_SEG1) $< @@ -156,7 +160,7 @@ tools/bintomdv: tools/bintomdv.c tools/makejv3: tools/makejv3.c -ifeq ($(CPU), z80) +ifneq (,$(filter $(CPU),z80 z180)) # matches CPU = z80 or z180 uzi.ihx: target $(OBJS) platform-$(TARGET)/uzi.lnk $(CROSS_LD) -n -k $(LIBZ80) -f platform-$(TARGET)/uzi.lnk @@ -167,10 +171,10 @@ fuzix.bin: uzi.ihx tools/analysemap tools/memhogs tools/binman tools/bintomdv head -5 hogs.txt makebin -s 65536 -p uzi.ihx >uzi.tmp tools/binman uzi.tmp uzi.map fuzix.bin - $(MAKE) -C platform-$(TARGET) image + +make -C platform-$(TARGET) image else fuzix.bin: target $(OBJS) tools/decbdragon - $(MAKE) -C platform-$(TARGET) image + +make -C platform-$(TARGET) image endif clean: diff --git a/Kernel/cpu-z180/cpu.h b/Kernel/cpu-z180/cpu.h new file mode 100644 index 00000000..b9fa3a29 --- /dev/null +++ b/Kernel/cpu-z180/cpu.h @@ -0,0 +1 @@ +#include "../cpu-z80/cpu.h" diff --git a/Kernel/cpu-z180/z180.def b/Kernel/cpu-z180/z180.def new file mode 100644 index 00000000..73d33b64 --- /dev/null +++ b/Kernel/cpu-z180/z180.def @@ -0,0 +1,71 @@ +; ASCI serial ports +ASCI_CNTLA0 .equ (Z180_IO_BASE+0x00) ; ASCI control register A channel 0 +ASCI_CNTLA1 .equ (Z180_IO_BASE+0x01) ; ASCI control register A channel 1 +ASCI_CNTLB0 .equ (Z180_IO_BASE+0x02) ; ASCI control register B channel 0 +ASCI_CNTLB1 .equ (Z180_IO_BASE+0x03) ; ASCI control register B channel 0 +ASCI_STAT0 .equ (Z180_IO_BASE+0x04) ; ASCI status register channel 0 +ASCI_STAT1 .equ (Z180_IO_BASE+0x05) ; ASCI status register channel 1 +ASCI_TDR0 .equ (Z180_IO_BASE+0x06) ; ASCI transmit data reg, channel 0 +ASCI_TDR1 .equ (Z180_IO_BASE+0x07) ; ASCI transmit data reg, channel 1 +ASCI_RDR0 .equ (Z180_IO_BASE+0x08) ; ASCI receive data reg, channel 0 +ASCI_RDR1 .equ (Z180_IO_BASE+0x09) ; ASCI receive data reg, channel 0 +ASCI_ASEXT0 .equ (Z180_IO_BASE+0x12) ; ASCI extension register channel 0 +ASCI_ASEXT1 .equ (Z180_IO_BASE+0x13) ; ASCI extension register channel 1 +ASCI_ASTC0L .equ (Z180_IO_BASE+0x1A) ; ASCI time constant register channel 0 low +ASCI_ASTC0H .equ (Z180_IO_BASE+0x1B) ; ASCI time constant register channel 0 high +ASCI_ASTC1L .equ (Z180_IO_BASE+0x1C) ; ASCI time constant register channel 1 low +ASCI_ASTC1H .equ (Z180_IO_BASE+0x1D) ; ASCI time constant register channel 1 high + +; Z180 MMU +MMU_CBR .equ (Z180_IO_BASE+0x38) ; common1 base register +MMU_BBR .equ (Z180_IO_BASE+0x39) ; bank base register +MMU_CBAR .equ (Z180_IO_BASE+0x3A) ; common/bank area register + +; Z180 DMA engine +DMA_SAR0L .equ (Z180_IO_BASE+0x20) ; DMA source address reg, channel 0L +DMA_SAR0H .equ (Z180_IO_BASE+0x21) ; DMA source address reg, channel 0H +DMA_SAR0B .equ (Z180_IO_BASE+0x22) ; DMA source address reg, channel 0B +DMA_DAR0L .equ (Z180_IO_BASE+0x23) ; DMA dest address reg, channel 0L +DMA_DAR0H .equ (Z180_IO_BASE+0x24) ; DMA dest address reg, channel 0H +DMA_DAR0B .equ (Z180_IO_BASE+0x25) ; DMA dest address reg, channel 0B +DMA_BCR0L .equ (Z180_IO_BASE+0x26) ; DMA byte count reg, channel 0L +DMA_BCR0H .equ (Z180_IO_BASE+0x27) ; DMA byte count reg, channel 0H +DMA_MAR1L .equ (Z180_IO_BASE+0x28) ; DMA memory address reg, channel 1L +DMA_MAR1H .equ (Z180_IO_BASE+0x29) ; DMA memory address reg, channel 1H +DMA_MAR1B .equ (Z180_IO_BASE+0x2A) ; DMA memory address reg, channel 1B +DMA_IAR1L .equ (Z180_IO_BASE+0x2B) ; DMA I/O address reg, channel 1L +DMA_IAR1H .equ (Z180_IO_BASE+0x2C) ; DMA I/O address reg, channel 1H +DMA_BCR1L .equ (Z180_IO_BASE+0x2E) ; DMA byte count reg, channel 1L +DMA_BCR1H .equ (Z180_IO_BASE+0x2F) ; DMA byte count reg, channel 1H +DMA_DSTAT .equ (Z180_IO_BASE+0x30) ; DMA status register +DMA_DMODE .equ (Z180_IO_BASE+0x31) ; DMA mode register +DMA_DCNTL .equ (Z180_IO_BASE+0x32) ; DMA/WAIT control register + +; Z180 Timer +TIME_TMDR0L .equ (Z180_IO_BASE+0x0C) ; Timer data register, channel 0L +TIME_TMDR0H .equ (Z180_IO_BASE+0x0D) ; Timer data register, channel 0H +TIME_RLDR0L .equ (Z180_IO_BASE+0x0E) ; Timer reload register, channel 0L +TIME_RLDR0H .equ (Z180_IO_BASE+0x0F) ; Timer reload register, channel 0H +TIME_TCR .equ (Z180_IO_BASE+0x10) ; Timer control register +TIME_TMDR1L .equ (Z180_IO_BASE+0x14) ; Timer data register, channel 1L +TIME_TMDR1H .equ (Z180_IO_BASE+0x15) ; Timer data register, channel 1H +TIME_RLDR1L .equ (Z180_IO_BASE+0x16) ; Timer reload register, channel 1L +TIME_RLDR1H .equ (Z180_IO_BASE+0x17) ; Timer reload register, channel 1H +TIME_FRC .equ (Z180_IO_BASE+0x18) ; Timer Free running counter + +; Z180 Interrupts +INT_IL .equ (Z180_IO_BASE+0x33) ; Interrupt vector low register +INT_ITC .equ (Z180_IO_BASE+0x34) ; Interrupt vector low register + +; ESCC serial ports (Z80182) +ESCC_CTRL_A .equ 0xE0 ; ESCC Channel A control register +ESCC_DATA_A .equ 0xE1 ; ESCC Channel A data register +ESCC_CTRL_B .equ 0xE2 ; ESCC Channel B control register +ESCC_DATA_B .equ 0xE3 ; ESCC Channel B data register + +Z182_SYSCONFIG .equ 0xEF ; System Configuration Register +Z182_ROMBR .equ 0xE8 ; ROMBR register + +; Debugging +DEBUGBANK .equ 0 +DEBUGCOMMON .equ 0 diff --git a/Kernel/cpu-z180/z180.h b/Kernel/cpu-z180/z180.h new file mode 100644 index 00000000..591b896e --- /dev/null +++ b/Kernel/cpu-z180/z180.h @@ -0,0 +1,54 @@ +#ifndef __Z180_DOT_H__ +#define __Z180_DOT_H__ + +void copy_and_map_process(uint16_t *pageptr); + +/* irqvector values */ +#define Z180_INT_UNUSED 0xFF +#define Z180_INT0 0 +#define Z180_INT1 1 +#define Z180_INT2 2 +#define Z180_INT_TIMER0 3 +#define Z180_INT_TIMER1 4 +#define Z180_INT_DMA0 5 +#define Z180_INT_DMA1 6 +#define Z180_INT_CSIO 7 +#define Z180_INT_ASCI0 8 +#define Z180_INT_ASCI1 9 + +#pragma portmode z180 /* use in0/out0 */ + +__sfr __at (Z180_IO_BASE + 0x0C) TIME_TMDR0L; /* Timer data register, channel 0L */ +__sfr __at (Z180_IO_BASE + 0x0D) TIME_TMDR0H; /* Timer data register, channel 0H */ +__sfr __at (Z180_IO_BASE + 0x0E) TIME_RLDR0L; /* Timer reload register, channel 0L */ +__sfr __at (Z180_IO_BASE + 0x0F) TIME_RLDR0H; /* Timer reload register, channel 0H */ +__sfr __at (Z180_IO_BASE + 0x10) TIME_TCR; /* Timer control register */ +__sfr __at (Z180_IO_BASE + 0x14) TIME_TMDR1L; /* Timer data register, channel 1L */ +__sfr __at (Z180_IO_BASE + 0x15) TIME_TMDR1H; /* Timer data register, channel 1H */ +__sfr __at (Z180_IO_BASE + 0x16) TIME_RLDR1L; /* Timer reload register, channel 1L */ +__sfr __at (Z180_IO_BASE + 0x17) TIME_RLDR1H; /* Timer reload register, channel 1H */ +__sfr __at (Z180_IO_BASE + 0x18) TIME_FRC; /* Timer Free running counter */ + +__sfr __at (Z180_IO_BASE + 0x10) ASCI_CNTLA0; /* ASCI control register A channel 0 */ +__sfr __at (Z180_IO_BASE + 0x01) ASCI_CNTLA1; /* ASCI control register A channel 1 */ +__sfr __at (Z180_IO_BASE + 0x02) ASCI_CNTLB0; /* ASCI control register B channel 0 */ +__sfr __at (Z180_IO_BASE + 0x03) ASCI_CNTLB1; /* ASCI control register B channel 0 */ +__sfr __at (Z180_IO_BASE + 0x04) ASCI_STAT0; /* ASCI status register channel 0 */ +__sfr __at (Z180_IO_BASE + 0x05) ASCI_STAT1; /* ASCI status register channel 1 */ +__sfr __at (Z180_IO_BASE + 0x06) ASCI_TDR0; /* ASCI transmit data reg, channel 0 */ +__sfr __at (Z180_IO_BASE + 0x07) ASCI_TDR1; /* ASCI transmit data reg, channel 1 */ +__sfr __at (Z180_IO_BASE + 0x08) ASCI_RDR0; /* ASCI receive data reg, channel 0 */ +__sfr __at (Z180_IO_BASE + 0x09) ASCI_RDR1; /* ASCI receive data reg, channel 0 */ +__sfr __at (Z180_IO_BASE + 0x12) ASCI_ASEXT0; /* ASCI extension register channel 0 */ +__sfr __at (Z180_IO_BASE + 0x13) ASCI_ASEXT1; /* ASCI extension register channel 1 */ +__sfr __at (Z180_IO_BASE + 0x1A) ASCI_ASTC0L; /* ASCI time constant register channel 0 low */ +__sfr __at (Z180_IO_BASE + 0x1B) ASCI_ASTC0H; /* ASCI time constant register channel 0 high */ +__sfr __at (Z180_IO_BASE + 0x1C) ASCI_ASTC1L; /* ASCI time constant register channel 1 low */ +__sfr __at (Z180_IO_BASE + 0x1D) ASCI_ASTC1H; /* ASCI time constant register channel 1 high */ + +__sfr __at (0xE0) ESCC_CTRL_A; /* ESCC Channel A control register */ +__sfr __at (0xE1) ESCC_DATA_A; /* ESCC Channel A data register */ +__sfr __at (0xE2) ESCC_CTRL_B; /* ESCC Channel B control register */ +__sfr __at (0xE3) ESCC_DATA_B; /* ESCC Channel B data register */ + +#endif diff --git a/Kernel/cpu-z180/z180.s b/Kernel/cpu-z180/z180.s new file mode 100644 index 00000000..10bbd20d --- /dev/null +++ b/Kernel/cpu-z180/z180.s @@ -0,0 +1,722 @@ +; 2014-12-19 William R Sowerbutts +; An attempt at generic Z180 support code, based on N8VEM Mark IV and DX-Design P112 targets + + .module z180 + .z180 + + ; exported symbols + .globl z180_init_hardware + .globl z180_init_early + .globl _program_vectors + .globl _copy_and_map_process + .globl interrupt_table ; not used elsewhere but useful to check correct alignment + .globl _irqvector + .globl _kernel_flag + + ; imported symbols + .globl _ramsize + .globl _procmem + .globl _newproc + .globl _runticks + .globl _chksigs + .globl _inint + .globl _getproc + .globl _trap_monitor + .globl _switchin + .globl _switchout + .globl _dofork + .globl map_kernel + .globl unix_syscall_entry + .globl null_handler + .globl nmi_handler + .globl interrupt_handler + .globl outchar + .globl outcharhex + .globl outhl, outde, outbc + .globl outnewline + .globl outstring + .globl outstringhex + + .include "kernel.def" + .include "../cpu-z180/z180.def" + .include "../kernel.def" + +; ----------------------------------------------------------------------------- +; Constant used for timer configuration +; ----------------------------------------------------------------------------- +TIMER_TICK_RATE = 100 ; Hz + +; ----------------------------------------------------------------------------- +; Initialisation code +; ----------------------------------------------------------------------------- + .area _DISCARD + +z180_init_early: + ; Assumes we enter with BBR=CBR and CBAR=x0 where x>0. + ; If we are running in the first 64K we need to first copy ourselves elsewhere + in0 a, (MMU_BBR) + cp #(OS_BANK + FIRST_RAM_BANK) + jr z, dommu ; we're in position already + cp #15 + jr nc, finalcopy ; we're running above 64K + ; we are running at least in part below 64K -- copy us up to 128KB temporarily + ld a, #0x02 + call copykernel +.if DEBUGCOMMON + ld a, #'<' + call outchar + ld a, #'C' + call outchar + ld a, #'=' + call outchar +.endif + ld a, #0x20 ; reprogram the MMU to use the copy at 128KB + out0 (MMU_BBR), a + out0 (MMU_CBR), a +.if DEBUGCOMMON + call outcharhex + ld a, #'>' + call outchar +.endif + +finalcopy: + ; copy us into position + ld a, #((OS_BANK+FIRST_RAM_BANK) >> 4) + call copykernel + +dommu: +.if DEBUGCOMMON + ld a, #'<' + call outchar + ld a, #'C' + call outchar + ld a, #'=' + call outchar +.endif + ld a, #(OS_BANK + FIRST_RAM_BANK) ; reprogram MMU to use copy at physical + out0 (MMU_BBR), a ; low 60K + out0 (MMU_CBR), a ; upper 4K (including our stack) +.if DEBUGCOMMON + call outcharhex + ld a, #'>' + call outchar +.endif + ; program MMU for 60KB/4KB bank/common1 split. + ld a, #0xF0 + out0 (MMU_CBAR), a + + ret + +copykernel: + out0 (DMA_DAR0B), a + + ; HL = BBR << 4 + xor a + ld h, a + in0 l, (MMU_BBR) ; read BBR value + add hl, hl ; shift left 4 bits + add hl, hl + add hl, hl + add hl, hl + + ; load the DMA engine registers with source (HL<<8), destination (bank + ; register programmed already), and count (64KB) + out0 (DMA_BCR0L), a ; A=0 still + out0 (DMA_BCR0H), a + out0 (DMA_DAR0L), a + out0 (DMA_DAR0H), a + out0 (DMA_SAR0L), a + out0 (DMA_SAR0H), l + out0 (DMA_SAR0B), h + ld bc, #0x0240 + out0 (DMA_DMODE), b ; 0x02 - memory to memory, burst mode + out0 (DMA_DSTAT), c ; 0x40 - enable DMA channel 0 + ; in burst mode the Z180 CPU stops until the DMA completes + ret + +z180_init_hardware: + ; setup interrupt vectors for the kernel bank + + ; (this code used to be in _program_vectors but now we do it once and copy) + ; write HALT across all vectors + ld hl, #0 + ld de, #1 + ld bc, #0x007f ; program first 0x80 bytes only + ld (hl), #0x76 ; HALT instruction + ldir + + ; now install the interrupt vector at 0x0038 + ld a, #0xC3 ; JP instruction + ld (0x0038), a + ld hl, #z80_irq + 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 + + ; program Z180 interrupt table registers + ld hl, #interrupt_table ; note table MUST be 32-byte aligned! + out0 (INT_IL), l + ld a, h + ld i, a + im 1 ; set CPU interrupt mode for INT0 + + ; set up system tick timer + xor a + out0 (TIME_TCR), a + ld hl, #(CPU_CLOCK_KHZ * (1000/20) / TIMER_TICK_RATE) ; timer ticks at PHI/20 + out0 (TIME_RLDR0L), l + out0 (TIME_RLDR0H), h + ld a, #0x11 ; enable downcounting and interrupts for timer 0 only + out0 (TIME_TCR), a + + ; Enable illegal instruction trap (vector at 0x0000) + ; Enable external interrupts (INT0/INT1/INT2) + ld a, #0x87 + out0 (INT_ITC), a + ret + +; ----------------------------------------------------------------------------- +; KERNEL MEMORY BANK (below 0xC000, only accessible when the kernel is mapped) +; ----------------------------------------------------------------------------- + .area _CODE + +_kernel_flag: + .db 1 + +_copy_and_map_process: + di ; just to be sure + pop bc ; temporarily store return address + pop de ; function argument -- pointer to base page number + push de ; put stack back as it was + push bc + + ; overwrites the full 64KB of target process memory space + + ; WARNING: + ; assumes processes page numbers are only 8-bits wide + ; assumes kernel is physically 64K aligned + ; assumes processes have a full 64K allocated to them + + ld bc, #0x0240 + out0 (DMA_DMODE), b ; 0x02 - memory to memory, burst mode + + ; load destination page number into HL + ld a, (de) + ld b, a ; stash copy in B -- BC remains unmodified hereafter + ld l, a + ld h, #0 + + ; shift left 4 bits + add hl, hl + add hl, hl + add hl, hl + add hl, hl + ; now bottom four bits of H holds the top four bits of physical address, + ; while the top four bits of L hold the next four bits. + out0 (DMA_DAR0B), h + + ; source bank -- kernel is always 64K aligned + ld a, #((OS_BANK + FIRST_RAM_BANK) >> 4) + out0 (DMA_SAR0B), a + + ; Copy vectors -- virtual 0000 to 0080 + ld de, #0x0080 + out0 (DMA_BCR0H), d ; 0x0080 bytes to copy + out0 (DMA_BCR0L), e + out0 (DMA_DAR0H), l ; computed destination page + out0 (DMA_DAR0L), d + out0 (DMA_SAR0H), d ; source is kernel, always 64K aligned + out0 (DMA_SAR0L), d + ; call dump_dma_state + out0 (DMA_DSTAT), c ; 0x40 - enable DMA channel 0 + ; CPU stalled until DMA completes + + ; Clone 0x7F into virtual 0080 through 0100 (kernel has code here, reserved in userspace) + ; In the future interrupt stubs may go in here for processes with less than 64K allocated + out0 (DMA_BCR0H), d ; 0x80 bytes + out0 (DMA_BCR0L), e + dec e ; 0x80 -> 0x7F + out0 (DMA_SAR0B), h + out0 (DMA_SAR0H), l + out0 (DMA_SAR0L), e + ; no need to set DAR0B, DAR0H, DAR0L since they naturally ends up there after the above copy + ; call dump_dma_state + out0 (DMA_DSTAT), c ; 0x40 - enable DMA channel 0 + ; CPU stalled until DMA completes + + ; Copy common memory code from kernel bank (from end of U_DATA to end of memory) + ld de, #(0x10000 - U_DATA__TOTALSIZE - U_DATA) ; copy to end of memory + out0 (DMA_BCR0H), d ; set byte count + out0 (DMA_BCR0L), e + ld de, #(U_DATA+U_DATA__TOTALSIZE) + ld a, #((OS_BANK + FIRST_RAM_BANK) >> 4) ; source bank -- kernel is always 64K aligned + out0 (DMA_SAR0B), a + out0 (DMA_SAR0H), d ; source is kernel, always 64K aligned + out0 (DMA_SAR0L), e + ; compute dest address; (HL << 8) + DE + out0 (DMA_DAR0L), e + ld a, l + add d + out0 (DMA_DAR0H), a + ld a, h + jr nc, bankok + inc a +bankok: out0 (DMA_DAR0B), a + ; call dump_dma_state + out0 (DMA_DSTAT), c ; 0x40 - enable DMA channel 0 + ; CPU stalled until DMA completes + ; note we just overflowed at least one, possibly both DMA bank registers + + ; Copy user code (ie fill in the middle, 0x100 up to end of U_DATA) from the current process + ; compute dest address; (HL << 8) + 0x0100 + xor a + out0 (DMA_DAR0L), a + ld a, l + inc a + out0 (DMA_DAR0H), a + ld a, h + jr nc, bankok2 + inc a +bankok2:out0 (DMA_DAR0B), a + ; compute source address from current process + in0 l, (MMU_CBR) ; get current process memory address + ld h, #0 + add hl, hl ; shift left 4 bits + add hl, hl + add hl, hl + add hl, hl + inc hl ; add in 0x100 start offset + xor a + out0 (DMA_SAR0L), a + out0 (DMA_SAR0H), l + out0 (DMA_SAR0B), h + ld de, #(U_DATA + U_DATA__TOTALSIZE - 0x100) ; byte count + out0 (DMA_BCR0H), d ; set byte count + out0 (DMA_BCR0L), e + ; call dump_dma_state + out0 (DMA_DSTAT), c ; 0x40 - enable DMA channel 0 + ; CPU stalled until DMA completes + + ; finally reprogram the MMU to bring the new process common memory into context + ; note this replaces the stack, but we just copied it over. +.if DEBUGCOMMON + ld a, #'<' + call outchar + ld a, #'C' + call outchar + ld a, #'=' + call outchar + ld a, b + call outcharhex + ld a, #'>' + call outchar +.endif + out0 (MMU_CBR), b + + ret ; was jp map_kernel but we never change MMU_BBR + +_program_vectors: + ; copy_and_map_process has all the fun now + ret + +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. + ; Hooray. + ld (U_DATA__U_SP), sp + + ; now we're in a safe state for _switchin to return in the parent + ; process. + + ; --------- copy process --------- + ld hl, (fork_proc_ptr) + ld de, #P_TAB__P_PAGE_OFFSET + add hl, de + push hl + call _copy_and_map_process + 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 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; DEBUGGING +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; mmu_cbar_msg: .ascii "MMU: CBAR=" +;; .db 0 +;; mmu_cbr_msg: .ascii ", CBR=" +;; .db 0 +;; mmu_bbr_msg: .ascii ", BBR=" +;; .db 0 +;; +;; mmu_state_dump: +;; ld hl, #mmu_cbar_msg +;; call outstring +;; in0 a, (MMU_CBAR) +;; call outcharhex +;; ld hl, #mmu_cbr_msg +;; call outstring +;; in0 a, (MMU_CBR) +;; call outcharhex +;; ld hl, #mmu_bbr_msg +;; call outstring +;; in0 a, (MMU_BBR) +;; call outcharhex +;; call outnewline +;; ret +;; ;------------------------------------------------------------------------------ +;; dumpbuf: .ds 16 +;; +;; dump_process_memory: +;; ; enter with the 64K bank to dump in A (low 4 bits only) +;; out0 (DMA_SAR0B), a +;; +;; ld hl, #0 +;; ld a, #0x02 +;; out0 (DMA_DMODE), a ; 0x02 - memory to memory, burst mode +;; xor a +;; out0 (DMA_SAR0H), a +;; out0 (DMA_SAR0L), a +;; ld a, #(OS_BANK + MARK4_RAM_BANK_OFFSET) +;; out0 (DMA_DAR0B), a +;; +;; nextblock: +;; ; set dest to our target buffer +;; ld a, #dumpbuf +;; out0 (DMA_DAR0H), a +;; ; 16 bytes +;; xor a +;; out0 (DMA_BCR0H), a +;; ld a, #0x10 +;; out0 (DMA_BCR0L), a +;; ld a, #0x40 +;; out0 (DMA_DSTAT), a ; 0x40 - enable DMA channel 0 +;; ; DMA does the copy +;; +;; ; print address +;; call outhl +;; ld a, #':' +;; call outchar +;; ld a, #' ' +;; call outchar +;; +;; ; print data +;; ex de, hl +;; ld hl, #dumpbuf +;; ld b, #0x10 +;; nextbyte: +;; ld a, (hl) +;; call outcharhex +;; ld a, #' ' +;; call outchar +;; inc hl +;; djnz nextbyte +;; ex de, hl +;; +;; call outnewline +;; +;; ld de, #0x10 +;; add hl, de +;; ld a, h +;; or l +;; jr nz, nextblock +;; ret +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; ----------------------------------------------------------------------------- +; COMMON MEMORY BANK (0xF000 upwards) +; ----------------------------------------------------------------------------- + .area _COMMONMEM + + ; MUST arrange for this table to be 32-byte aligned + ; linked immediately after commonmem.s +interrupt_table: + .dw z180_irq_unused ; 1 INT1 external interrupt - ? disconnected + .dw z180_irq_unused ; 2 INT2 external interrupt - SD card socket event + .dw z180_irq3 ; 3 Timer 0 + .dw z180_irq_unused ; 4 Timer 1 + .dw z180_irq_unused ; 5 DMA 0 + .dw z180_irq_unused ; 6 DMA 1 + .dw z180_irq_unused ; 7 CSI/O + .dw z180_irq8 ; 8 ASCI 0 + .dw z180_irq9 ; 9 ASCI 1 + .dw z180_irq_unused ; 10 + .dw z180_irq_unused ; 11 + .dw z180_irq_unused ; 12 + .dw z180_irq_unused ; 13 + .dw z180_irq_unused ; 14 + .dw z180_irq_unused ; 15 + .dw z180_irq_unused ; 16 + +_irqvector: .db 0 + +z80_irq: + push af + xor a + jr z180_irqgo + +; z180_irq1: +; push af +; ld a, #1 +; jr z180_irqgo +; +; z180_irq2: +; push af +; ld a, #2 +; jr z180_irqgo + +z180_irq3: + push af + ld a, #3 + ; fall through -- timer is likely to be the most common, we'll save it the jr +z180_irqgo: + ld (_irqvector), a + ; quick and dirty way to debug which interrupt is jamming us up ... + ; add #0x30 + ; .globl outchar + ; call outchar + pop af + jp interrupt_handler + ; this isn't perfect -- interrupt_handler always ends with RETI while only + ; INT0 should end with RETI, the others ending with a normal RET. unless + ; there are Z80 interrupt peripherals on the bus we'll be OK. + +; z180_irq4: +; push af +; ld a, #4 +; jr z180_irqgo +; +; z180_irq5: +; push af +; ld a, #5 +; jr z180_irqgo +; +; z180_irq6: +; push af +; ld a, #6 +; jr z180_irqgo +; +; z180_irq7: +; push af +; ld a, #7 +; jr z180_irqgo + +z180_irq8: + push af + ld a, #8 + jr z180_irqgo + +z180_irq9: + push af + ld a, #9 + jr z180_irqgo + +; z180_irq10: +; push af +; ld a, #10 +; jr z180_irqgo +; +; z180_irq11: +; push af +; ld a, #11 +; jr z180_irqgo +; +; z180_irq12: +; push af +; ld a, #12 +; jr z180_irqgo +; +; z180_irq13: +; push af +; ld a, #13 +; jr z180_irqgo +; +; z180_irq14: +; push af +; ld a, #14 +; jr z180_irqgo +; +; z180_irq15: +; push af +; ld a, #15 +; jr z180_irqgo +; +; z180_irq16: +; push af +; ld a, #16 +; jr z180_irqgo +z180_irq_unused: + push af + ld a, #0xFF + jr z180_irqgo + +; 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. +_switchout: + di + call _chksigs + ; 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 + + ; set inint to false + xor a + ld (_inint), a + + ; no need to stash udata on this platform since common memory is dedicated + ; to each process. + + ; find another process to run (may select this one again) + call _getproc + + push hl + call _switchin + + ; we should never get here + jp _trap_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 (struct p_tab *) + push de ; restore stack (WRS: AC thinks this may not be required -- he's probably right!) + push bc ; restore stack + + ; probably not reqired since we're only called from kernel code ... + ; call map_kernel + + ld hl, #P_TAB__P_PAGE_OFFSET + add hl, de ; now HL points at the p_page value for the next process + + ; when we add support for swap we should check here that (hl) is non-zero + + ; map in the common memory for the new process -- this swaps common + ; memory and the stack under our feet so let's hope that common memory + ; contains a copy of this code, eh? + ld a, (hl) + ; out0 (MMU_BBR), a -- WRS: leave the kernel mapped in + out0 (MMU_CBR), a + + ; sanity 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 + + ; WRS -- we can skip this for now until we start re-arranging processes in + ; memory and/or support swapping to disk? + ;; ; 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, (_inint) + or a + ret z ; in ISR, leave interrupts off + ei + ret ; return with interrupts on + +switchinfail: + ; something went wrong and we didn't switch in what we asked for + call outhl + ld hl, #badswitchmsg + call outstring + jp _trap_monitor diff --git a/Kernel/lowlevel-z180.s b/Kernel/lowlevel-z180.s new file mode 100644 index 00000000..3f25435a --- /dev/null +++ b/Kernel/lowlevel-z180.s @@ -0,0 +1 @@ +.include "lowlevel-z80.s" diff --git a/Kernel/usermem_std-z180.s b/Kernel/usermem_std-z180.s new file mode 100644 index 00000000..f5c949f1 --- /dev/null +++ b/Kernel/usermem_std-z180.s @@ -0,0 +1 @@ +.include "usermem_std-z80.s"