--- /dev/null
+
+CSRCS = devtty.c
+CSRCS += devices.c main.c
+
+ASRCS = crt0.s zx128.s zxvideo.s
+ASRCS += tricks.s commonmem.s
+
+COBJS = $(CSRCS:.c=.rel)
+AOBJS = $(ASRCS:.s=.rel)
+OBJS = $(COBJS) $(AOBJS)
+
+JUNK = $(CSRCS:.c=.lst) $(CSRCS:.c=.asm) $(CSRCS:.c=.sym) $(ASRCS:.s=.lst) $(ASRCS:.s=.sym) $(CSRCS:.c=.rst) $(ASRCS:.s=.rst)
+
+all: $(OBJS)
+
+$(COBJS): %.rel: %.c
+ $(CROSS_CC) $(CROSS_CCOPTS) -c $<
+
+$(AOBJS): %.rel: %.s
+ $(CROSS_AS) $(ASOPTS) $<
+
+clean:
+ rm -f $(OBJS) $(JUNK) core *~
+
+image:
--- /dev/null
+An FUZIX target for ZX Spectrum 128.
+
+Big part of the code was taken from z80pack and msx1 ports.
+
+ZX Spectrum has a memory layout like follows:
+
+0000-3FFF ROM
+4000-57FF Screen pixel data
+5800-5AFF Screen attributes data
+5B00-FFFF RAM
+
+1 memory bank exists at 0xC000, one of 6 16384-byte pages can be mapped there.
+
+So the fuzix port is limited to those features:
+
+1) Code1 segment should be flashed into ROM (instead of BASIC-128 in a simplest case).
+2) We have memory "hole" at screen area (4000-5AFF), which we can not use for code or data.
+ So we can not allow Code1 to be larger than 0x4000 bytes.
+3) We need to store some bootloader procedure in Code1, which should read fuzix image
+ data from somewhere (for now this is done via emulator hack) and place it in RAM at
+ correct addresses.
+4) Common area can not be at F000 as usual because F000 belongs to banking area.
+5) Maximum user program size is 16384 bytes.
+6) ZX Spectrum 128 had not any official floppy disk controller. Third-party hardware
+ (like popular in Eastern Europe Betadisk Interface) was designed to be compatible
+ with old software, so contains some terrible features like FDC port visibility
+ limited to 256-bytes long area of ROM. Outside this area any requests to FDC ports
+ are ignored. This makes disk driver implementation very tricky until we have more
+ smart linker.
\ No newline at end of file
--- /dev/null
+;
+; We need to put commonmem below the 0xC000 for multi tasking.
+;
+ .module commonmem
+
+ ; exported symbols
+ .globl _ub
+ .globl _udata
+ .globl kstack_top
+ .globl istack_top
+ .globl istack_switched_sp
+
+ .area _COMMONMEM
+
+_ub: ; first 512 bytes: starts with struct u_block, with the kernel stack working down from above
+_udata:
+kstack_base:
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+kstack_top:
+
+ ; next 256 bytes: 254 byte interrupt stack, then 2 byte saved stack pointer
+istack_base:
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+istack_top:
+istack_switched_sp: .dw 0
+;
+; Padding so we can read/write uarea easily. We have tons of common
+; free so can be quite relaxed about it all
+;
+ .ds 0x100
--- /dev/null
+/* Enable to make ^Z dump the inode table for debug */
+#undef CONFIG_IDUMP
+/* Enable to make ^A drop back into the monitor */
+#undef CONFIG_MONITOR
+/* Profil syscall support (not yet complete) */
+#define CONFIG_PROFIL
+/* Multiple processes in memory at once */
+#undef CONFIG_MULTI
+/* Single tasking */
+#define CONFIG_SINGLETASK
+/* CP/M emulation */
+#undef CONFIG_CPM_EMU
+
+/* Video terminal, not a serial tty */
+#define CONFIG_VT
+/* We want the 8x8 font */
+#define CONFIG_FONT8X8
+#define CONFIG_FONT8X8SMALL
+
+/* We have 1 bank at C000 with 6 possible pages to map, but I'm not sure if CONFIG_BANK_FIXED is our choise. */
+/* Fixed banking */
+#define CONFIG_BANK_FIXED
+/* 6 16K banks, 1 is for kernel needs */
+#define MAX_MAPS 5
+#define MAP_SIZE 0x4000U
+
+/* Banks as reported to user space */
+#define CONFIG_BANKS 1
+
+/* Vt definitions */
+#define VT_WIDTH 32
+#define VT_HEIGHT 24
+#define VT_RIGHT 31
+#define VT_BOTTOM 23
+
+#define TICKSPERSEC 50 /* Ticks per second */
+#define PROGBASE ((char *)(0xC000)) /* also data base */
+#define PROGTOP ((char *)(0xFFFF)) /* Top of program, base of U_DATA copy */
+#define PROC_SIZE 16 /* Memory needed per process */
+
+#define UDATA_BLOCKS 0 /* We swap the stash not the uarea */
+#define UDATA_SWAPSIZE 0
+
+#define BOOT_TTY (1) /* Set this to default device for stdio, stderr */
+ /* In this case, the default is the first TTY device */
+
+/* We need a tidier way to do this from the loader */
+#define CMDLINE NULL /* Location of root dev name */
+
+/* Device parameters */
+#define NUM_DEV_TTY 1
+
+#define TTYDEV BOOT_TTY /* Device used by kernel for messages, panics */
+#undef SWAPDEV /* Do not use swap */
+#define NBUFS 10 /* Number of block buffers */
+#define NMOUNTS 4 /* Number of mounts at a time */
--- /dev/null
+; 2013-12-18 William R Sowerbutts
+
+ .module crt0
+
+ ; Ordering of segments for the linker.
+ ; 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
+ .area _VIDEO
+ .area _DISCARD ; not discarded yet
+ .area _CONST
+ .area _FONT
+ .area _DATA
+ .area _INITIALIZED
+ .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
+ .area _GSINIT
+ .area _GSFINAL
+ .area _COMMONMEM
+
+ ; imported symbols
+ .globl _fuzix_main
+ .globl init_early
+ .globl init_hardware
+ .globl s__INITIALIZER
+ .globl s__COMMONMEM
+ .globl l__COMMONMEM
+ .globl s__DATA
+ .globl l__DATA
+ .globl kstack_top
+
+
+ .globl unix_syscall_entry
+ .globl nmi_handler
+ .globl interrupt_handler
+
+ ; startup code
+ .area _CODE
+init:
+ 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 0x0E
+
+ ; .org 0x0030 ; syscall entry
+ jp unix_syscall_entry
+
+ .ds 0x05 ; spacer
+
+ ; .org 0x0038 ; interrupt handler
+ jp interrupt_handler
+ .ds 0x2B
+
+ ; .org 0x0066 ; nmi handler
+ jp nmi_handler
+
+init_continue:
+ ld sp, #kstack_top
+
+ ; hack for emulator. Read remaining fuzix part to RAM from fuzix.bin
+ ld bc, #0x1ee7
+ in a, (c)
+
+ ; Configure memory map
+ call init_early
+
+ ; move the common memory where it belongs
+ ld hl, #s__INITIALIZER
+ ld de, #s__COMMONMEM
+ ld bc, #l__COMMONMEM
+ 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
+ call init_hardware
+
+ ; Call the C main routine
+ call _fuzix_main
+
+ ; main shouldn't return, but if it does...
+ di
+stop: halt
+ jr stop
+
--- /dev/null
+#include <kernel.h>
+#include <version.h>
+#include <kdata.h>
+#include <tty.h>
+#include <devsys.h>
+#include <devtty.h>
+
+struct devsw dev_tab[] = /* The device driver switch table */
+{
+// minor open close read write ioctl
+// -----------------------------------------------------------------
+ /* 0: /dev/tty TTY devices */
+ { tty_open, tty_close, tty_read, tty_write, tty_ioctl },
+ /* 1: /dev/mem etc System devices (one offs) */
+ { no_open, no_close, sys_read, sys_write, sys_ioctl },
+ /* Pack to 7 with nxio if adding private devices and start at 8 */
+};
+
+bool validdev(uint16_t dev)
+{
+ /* This is a bit uglier than needed but the right hand side is
+ a constant this way */
+ if(dev > ((sizeof(dev_tab)/sizeof(struct devsw)) << 8) + 255)
+ return false;
+ else
+ return true;
+}
+
+void device_init(void)
+{
+
+}
--- /dev/null
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <devtty.h>
+#include <vt.h>
+#include <tty.h>
+
+char tbuf1[TTYSIZ];
+
+struct s_queue ttyinq[NUM_DEV_TTY + 1] = { /* ttyinq[0] is never used */
+ {NULL, NULL, NULL, 0, 0, 0},
+ {tbuf1, tbuf1, tbuf1, TTYSIZ, 0, TTYSIZ / 2},
+};
+
+/* tty1 is the screen */
+
+/* Output for the system console (kprintf etc) */
+void kputchar(char c)
+{
+ if (c == '\n')
+ tty_putc(0, '\r');
+ tty_putc(0, c);
+}
+
+/* Both console and debug port are always ready */
+bool tty_writeready(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+void tty_putc(uint8_t minor, unsigned char c)
+{
+ minor;
+ vtoutput(&c, 1);
+}
+
+int tty_carrier(uint8_t minor)
+{
+ minor;
+ return 1;
+}
+
+void tty_setup(uint8_t minor)
+{
+ minor;
+}
+
+void tty_pollirq(void)
+{
+
+}
+
+void tty_interrupt(void)
+{
+}
+
+/* This is used by the vt asm code, but needs to live in the kernel */
+uint16_t cursorpos;
+
--- /dev/null
+#ifndef __DEVTTY_DOT_H__
+#define __DEVTTY_DOT_H__
+
+void tty_pollirq(void);
+
+#endif
--- /dev/null
+; UZI mnemonics for memory addresses etc
+
+U_DATA .equ 0xF000 ; (this is struct u_data from kernel.h)
+U_DATA__TOTALSIZE .equ 0x300 ; 256+256+256 bytes.
+
+U_DATA_STASH .equ 0xED00 ; BD00-BFFF
\ No newline at end of file
--- /dev/null
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+
+uint8_t *ramtop = PROGTOP;
+
+
+void pagemap_init(void)
+{
+ int i;
+ for (i = 1; i < 8; i++)
+ pagemap_add(i);
+}
+
+/* The uarea is already synched to the stash which is written with the
+ process */
+uint8_t *swapout_prepare_uarea(ptptr p)
+{
+ p;
+ return NULL;
+}
+
+/* The switchin code will move the uarea into the process itself, we just
+ need to fix up the u_page pointer */
+uint8_t *swapin_prepare_uarea(ptptr p)
+{
+ p;
+ return NULL;
+}
+
+/* On idle we spin checking for the terminals. Gives us more responsiveness
+ for the polled ports */
+void platform_idle(void)
+{
+ /* We don't want an idle poll and IRQ driven tty poll at the same moment */
+ irqflags_t irq = di();
+ tty_pollirq();
+ irqrestore(irq);
+}
+
+void platform_interrupt(void)
+{
+ tty_pollirq();
+ timer_interrupt();
+}
+
+/* Nothing to do for the map of init */
+void map_init(void)
+{
+}
--- /dev/null
+; 2013-12-21 William R Sowerbutts
+; TODO: this code is copied from z80pack. Need to rewrite for zx128.
+
+ .module tricks
+
+ .globl _ptab_alloc
+ .globl _newproc
+ .globl _chksigs
+ .globl _getproc
+ .globl _trap_monitor
+ .globl trap_illegal
+ .globl _inint
+ .globl _switchout
+ .globl _switchin
+ .globl _doexec
+ .globl _dofork
+ .globl _runticks
+ .globl unix_syscall_entry
+ .globl interrupt_handler
+ .globl dispatch_process_signal
+
+ ; imported debug symbols
+ .globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+ .area _COMMONMEM
+
+; 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().
+;
+; FIXME: make sure we optimise the switch to self case higher up the stack!
+;
+; 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
+
+ ; Stash the uarea back into process memory
+ ld hl, (U_DATA__U_PAGE)
+ ld a, l
+ out (21), a
+ ld hl, #U_DATA
+ ld de, #U_DATA_STASH
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+ xor a
+ out (21), a
+
+ ; find another process to run (may select this one again)
+ call _getproc
+
+ push hl
+ call _switchin
+
+ ; we should never get here
+ call _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
+;
+; FIXME: do we actually *need* to restore the stack !
+;
+ push de ; restore stack
+ push bc ; restore stack
+
+ xor a
+ out (21), a
+
+ push de
+ ld hl, #P_TAB__P_PAGE_OFFSET
+ add hl, de ; process ptr
+ pop de
+
+ ld a, (hl)
+
+ ; Pages please !
+ out (21), 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
+
+ xor a
+ out (21), a
+
+ ; 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
+
+ ; 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:
+ call outhl
+ ld hl, #badswitchmsg
+ call outstring
+ ; something went wrong and we didn't switch in what we asked for
+ jp _trap_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
+ 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 (U_DATA__U_SP), sp
+
+ ; now we're in a safe state for _switchin to return in the parent
+ ; process.
+
+ ; 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)
+ ld hl, (U_DATA__U_PAGE)
+ ld a, l
+
+ call bankfork ; do the bank to bank copy
+
+ ; Copy done
+
+ ld hl, (U_DATA__U_PAGE) ; parent memory
+ ld a, l
+ out (21), a ; Switch context to parent
+
+ ; 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
+ xor a
+ out (21), a
+ ; 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
+;
+; 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.
+;
+ .ds 128
+_swapstack:
+
+;
+; 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
+; (this API will be insufficient once we have chmem and proper use of
+; banks - as well as needing to support fork to disk)
+;
+; 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
+ out (21), a ; switch to parent bank
+ 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 port number
+ push bc
+ ld b, a ; save the parent bank id
+ ld a, c ; switch to the child
+ out (21), 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 back is wanted in a
+ pop bc
+ djnz bankfork_1 ; rinse, repeat
+ ret
+
+;
+; For the moment
+;
+bouncebuffer:
+ .ds 256
--- /dev/null
+;
+; ZX Spectrum 128 hardware support
+;
+;
+; This goes straight after udata for common. Because of that the first
+; 256 bytes get swapped to and from disk with the uarea (512 byte disk
+; blocks). This isn't a problem but don't put any variables in here.
+;
+; If you make this module any shorter, check what follows next
+;
+
+
+ .module zx128
+
+ ; exported symbols
+ .globl init_early
+ .globl init_hardware
+ .globl _program_vectors
+ .globl _system_tick_counter
+ .globl platform_interrupt_all
+
+ .globl map_kernel
+ .globl map_process
+ .globl map_process_always
+ .globl map_save
+ .globl map_restore
+
+ .globl _fd_bankcmd
+
+ ; exported debugging tools
+ .globl _trap_monitor
+ .globl outchar
+
+ ; imported symbols
+ .globl _ramsize
+ .globl _procmem
+
+ .globl outcharhex
+ .globl outhl, outde, outbc
+ .globl outnewline
+ .globl outstring
+ .globl outstringhex
+
+ .include "kernel.def"
+ .include "../kernel.def"
+
+; -----------------------------------------------------------------------------
+; COMMON MEMORY BANK (0xF000 upwards)
+; -----------------------------------------------------------------------------
+ .area _COMMONMEM
+
+_trap_monitor:
+ ld a, #128
+ ; out (29), a ; TODO: go where? BASIC48?
+platform_interrupt_all:
+ ret
+
+_trap_reboot:
+ rst 0
+
+;
+; We need the right bank present when we cause the transfer
+;
+_fd_bankcmd:
+ ret
+ pop de ; return
+ pop bc ; command
+ pop hl ; bank
+ push hl
+ push bc
+ push de ; fix stack
+ ld a, i
+ di
+ push af ; save DI state
+ call map_process ; HL alread holds our bank
+ ld a, c ; issue the command
+ ; out (13), a ;
+ call map_kernel ; return to kernel mapping
+ pop af
+ ret po
+ ei
+ ret
+
+; -----------------------------------------------------------------------------
+; KERNEL MEMORY BANK (below 0xC000, only accessible when the kernel is mapped)
+; -----------------------------------------------------------------------------
+ .area _CODE
+
+init_early:
+ ld bc, #0x7ffd
+ xor a
+ out (c), a ; set page 0 at 0xC000
+ ret
+
+init_hardware:
+ ; set system RAM size
+ ld hl, #128
+ ld (_ramsize), hl
+ ld hl, #(128 - 48) ; 48K for kernel
+ ld (_procmem), hl
+
+ ; screen initialization
+ ; clear
+ ld hl, #0x4000
+ ld de, #0x4001
+ ld bc, #0x1800
+ xor a
+ ld (hl), a
+ ldir
+
+ ; set color attributes
+ ld a, #7 ; black paper, white ink
+ ld bc, #0x300 - #1
+ ld (hl), a
+ ldir
+
+ im 1 ; set CPU interrupt mode
+ ret
+
+;------------------------------------------------------------------------------
+; COMMON MEMORY PROCEDURES FOLLOW
+
+ .area _COMMONMEM
+
+ ; our vectors are in ROM, so nothing to do here
+_program_vectors:
+ ret
+
+ ; below is code which was copied from z80pack, so it's useless for zx128
+map_kernel:
+ push af
+ xor a
+ ; out (21), a
+ pop af
+ ret
+
+map_process:
+ ld a, h
+ or l
+ jr z, map_kernel
+ ld a, (hl)
+ ; out (21), a
+ ret
+
+map_process_always:
+ push af
+ ld a, (U_DATA__U_PAGE)
+ ; out (21), a
+ pop af
+ ret
+
+map_save:
+ push af
+ in a, (21)
+ ld (map_store), a
+ pop af
+ ret
+
+map_restore:
+ push af
+ ld a, (map_store)
+ ; out (21), a
+ pop af
+ ret
+
+map_store:
+ .db 0
+
+; outchar: TODO: add something here (char in A). Current port #15 is emulator stub
+outchar:
+ out (#0x15), A
+ ret
--- /dev/null
+;
+; zx128 vt primitives
+
+ .module zx128
+
+ ; exported symbols
+ .globl _plot_char
+ .globl _scroll_down
+ .globl _scroll_up
+ .globl _cursor_on
+ .globl _cursor_off
+ .globl _clear_lines
+ .globl _clear_across
+ .globl _do_beep
+
+ .globl _fontdata_8x8
+
+ .area _VIDEO
+
+ ; colors are ignored everywhere for now
+
+videopos:
+ ld a,e
+ and #7
+ rrca
+ rrca
+ rrca
+ add a,d
+ ld d,e
+ ld e,a
+ ld a,d
+ and #0x18
+ or #0x40
+ ld d,a
+ ret
+
+_plot_char:
+ pop hl
+ pop de ; D = x E = y
+ pop bc
+ push bc
+ push de
+ push hl
+
+ call videopos
+
+ ld b, #0 ; calculating offset in font table
+ ld a, c
+ rla
+ rl b
+ rla
+ rl b
+ rla
+ rl b
+ ld c, a
+
+ ld hl, #_fontdata_8x8
+ add hl, bc ; hl points to first byte of char data
+
+ ; printing
+ ld c, #8
+plot_char_loop:
+ ld a, (hl)
+ ld (de), a
+ inc hl ; next byte of char data
+ inc d ; next screen line
+ dec c
+ jr nz, plot_char_loop
+ ret
+
+
+_clear_lines:
+ pop hl
+ pop de ; E = line, D = count
+ push de
+ push hl
+
+clear_next_line:
+ push de
+ ld d, #0 ; from the column #0
+ ld b, d ; b = 0
+ ld c, #32 ; clear 32 cols
+ push bc
+ push de
+ call _clear_across
+
+ pop hl ; clear stack
+ pop hl
+
+ pop de
+ inc e
+ dec d
+ jr nz, clear_next_line
+
+ ret
+
+
+_clear_across:
+ pop hl
+ pop de ; DE = coords
+ pop bc ; C = count
+ push bc
+ push de
+ push hl
+ call videopos ; first pixel line of first character in DE
+ push de
+ pop hl ; copy to hl
+ xor a
+
+ ; no boundary checks. Assuming that D + C < SCREEN_WIDTH
+
+clear_line:
+ ld b, #8 ; 8 pixel lines to clear for this char
+clear_char:
+ ld (de), a
+ inc d
+ dec b
+ jr nz, clear_char
+
+ ex de, hl
+ inc de
+ push de
+ pop hl
+
+ dec c
+ jr nz, clear_line
+ ret
+
+copy_line:
+ ; HL - source, DE - destination
+
+ ; convert line coordinates to screen coordinates both for DE and HL
+ push de
+ ex de, hl
+ call videopos
+ ex de, hl
+ pop de
+ call videopos
+
+ ld c, #8
+
+copy_line_nextchar:
+ push hl
+ push de
+
+ ld b, #32
+
+copy_pixel_line:
+ ld a, (hl)
+ ld (de), a
+ inc e
+ inc l
+ dec b
+ jr nz, copy_pixel_line
+
+ pop de
+ pop hl
+ inc d
+ inc h
+ dec c
+ jr nz, copy_line_nextchar
+ ret
+
+ ; TODO: the LDIR way should be much faster
+
+_scroll_down:
+ ; set HL = (0,0), DE = (0, 1)
+ xor a
+ ld d, a
+ ld h, a
+ ld l, a
+ ld e, #1
+ ld c, #23 ; 23 lines to move
+
+loop_scroll_down:
+ push hl
+ push de
+ push bc
+
+ call copy_line
+
+ pop bc
+ pop de
+ pop hl
+
+ inc l
+ inc e
+ dec c
+ jr nz, loop_scroll_down
+
+ ret
+
+
+_scroll_up:
+ ; set HL = (0,23), DE = (0, 22)
+ xor a
+ ld d, a
+ ld h, a
+ ld l, #23
+ ld e, #22
+ ld c, #23 ; 23 lines to move
+
+loop_scroll_up:
+ push hl
+ push de
+ push bc
+
+ call copy_line
+
+ pop bc
+ pop de
+ pop hl
+
+ dec l
+ dec e
+ dec c
+ jr nz, loop_scroll_up
+
+ ret
+
+_cursor_on:
+ pop hl
+ pop de
+ push de
+ push hl
+ ld (cursorpos), de
+
+ call videopos
+ ld a, #7
+ add a, d
+ ld d, a
+ ld a, #0xFF
+ ld (de), a
+ ret
+_cursor_off:
+ ld de, (cursorpos)
+ call videopos
+ ld a, #7
+ add a, d
+ ld d, a
+ xor a
+ ld (de), a
+ ret
+
+ ; FIXME: now this is_do_silent_click actually
+_do_beep:
+ ld e, #0xFF ; length
+ ld c, #0xFE ; beeper port
+ ld l, #0x10 ; beeper bit
+loop_beep:
+ ld a, l
+ out (c), a
+ xor a
+ out (c), a
+ dec bc
+ ld a, b
+ or c
+ jr nz, loop_beep
+ ret
+
+ .area _DATA
+
+cursorpos:
+ .dw 0