--- /dev/null
+; Based on the Z80 Pack banked code
+;
+; Should unify this somewhat with lib/banked
+;
+
+ .module tricks
+
+ .globl _ptab_alloc
+ .globl _newproc
+ .globl _getproc
+ .globl _platform_monitor
+ .globl trap_illegal
+ .globl _platform_switchout
+ .globl _switchin
+ .globl _low_bank
+ .globl _dup_low_page
+ .globl _dofork
+ .globl _runticks
+ .globl unix_syscall_entry
+ .globl interrupt_handler
+ .globl current_map
+ .globl _ptab
+ .globl _swapper
+ .globl _int_disabled
+ .globl switch_bank
+
+ ; 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().
+_platform_switchout:
+ di
+ ; 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 a,(current_map)
+ push af
+
+ ld (U_DATA__U_SP), sp ; this is where the SP is restored in _switchin
+
+ ;
+ ; We are now running on the sleeping process stack. The switchin
+ ; will simply go back to the saved SP above and discard anything
+ ; here
+ ;
+
+ ; Stash the uarea back into process memory
+ ld hl, (U_DATA__U_PAGE)
+ ld a, l
+ ld bc, #0x7ffd
+ or #BANK_BITS
+ out (c), a
+
+ ; This includes the stacks, so be careful on restore
+ ld hl, #U_DATA
+ ld de, #U_DATA_STASH
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+ ld a, (current_map)
+ or #BANK_BITS
+ ld bc, #0x7ffd
+ out (c), a
+
+ ; find another process to run (may select this one again)
+ push af
+ call _getproc
+ pop af ; we can't optimise this as the linker
+ ; is entitled to patch the 5 bytes here into a
+ ; banked call
+ push hl
+ push af
+ call _switchin
+
+ ; we should never get here
+ call _platform_monitor
+
+badswitchmsg: .ascii "_switchin: FAIL"
+ .db 13, 10, 0
+
+_switchin:
+ di
+ pop hl ; far padding
+ 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 ; far padding
+
+ push de
+ ld hl, #P_TAB__P_PAGE_OFFSET
+ add hl, de ; process ptr
+ pop de
+ ;
+ ; Get ourselves a valid private stack ASAP. We are going to
+ ; copy udata around and our main stacks are in udata
+ ;
+ ld sp, #_swapstack
+
+ ld a, (hl) ; 0 swapped, not zero is the bank for C000
+ or a
+ jr nz, not_swapped
+
+ ; Swap the process in (this may swap something else out first)
+ ; The second pushes are C function arguments. SDCC can trample
+ ; these
+
+ ; Turn this one once we have it all sorted and debugged
+ ; ei
+ ; xor a
+ ; ld (_int_disabled),a
+ push hl ; Save
+ push de
+ push hl ; Arguments
+ push de
+ push af
+ call _swapper
+ pop af
+ pop af
+ pop af
+ pop de ; Restore
+ pop hl
+ ld a,#1
+ ld (_int_disabled),a
+ di
+ ld a, (hl) ; We should now have a page assigned
+not_swapped:
+ ld hl,(U_DATA__U_PTAB)
+ or a
+ sbc hl,de
+; Turn this on once debugged
+; jr z, skip_copyback
+
+ ; We are in DI so we can poke these directly but must not invoke
+ ; any code outside of common
+ or #BANK_BITS ; ROM
+ ; Pages please !
+ ld bc, #0x7ffd
+ out (c), a
+
+ ; Copy the stash from the user page back down into common
+ ; The alternate registers are free - we use them for the
+ ; block copy and for the flipper
+ ;
+ ; FIXME: Add Tormod's optimisation from the 6809 tree
+ ;
+ exx
+ ld hl, #U_DATA_STASH
+ ld de, #U_DATA
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+ exx
+ ;
+ ; Remap the kernel proper
+ ;
+
+ ld a, (current_map)
+ or #BANK_BITS
+ ld bc, #0x7ffd
+ out (c), a
+
+ ; Is our low data in 0x8000 already or do we need to flip
+ ; it with bank 6. _low_bank holds the page pointer of the
+ ; task owning the space
+
+ ld hl, (_low_bank) ; who owns low memory
+ or a
+ sbc hl, de ; preserve DE
+ jr z, nofliplow ; skip the flip if we own low bank
+;
+; Flip low banks over. Preserve DE
+;
+fliplow:
+ exx
+ ld hl, #0x8000
+ ld de, #0xc000
+ ld a, #6 + BANK_BITS
+ ld bc, #0x7ffd
+ out (c), a
+flip2:
+ ld b, #0 ; 256 bytes per outer loop (16K total)
+flip1:
+ ld c, (hl)
+ ld a, (de)
+ ex de, hl
+ ld (hl), c
+ ld (de), a
+ inc hl
+ inc de
+ djnz flip1
+ xor a
+ cp d ; Wrapped to 0x0000 ?
+ jr nz, flip1
+
+ ld a, (current_map)
+ or #BANK_BITS
+ ld bc, #0x7ffd
+ out (c), a
+ exx
+ ld (_low_bank), de ; we own it now
+
+nofliplow:
+
+ ; 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 == HL
+ 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 any 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)
+
+ ;
+ ; We can now use the stack again
+ ;
+
+ pop af
+ pop iy
+ pop ix
+ pop hl ; return code
+
+ ; Make sure we have the right kernel bank to return to
+ call switch_bank
+
+ ; enable interrupts, if the ISR isn't already running
+ ld a, (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
+
+; Interrupts should be off when this is called
+_dup_low_page:
+ di ; FIXME: check callers properly
+ ld a, #0x06 + BANK_BITS ; low page alternate
+ ld bc, #0x7ffd
+ out (c), a
+
+ ld hl, #0x8000 ; fixed
+ ld de, #0xC000 ; page we just mapped in
+ ld bc, #16384
+ ldir
+
+ ld a, (current_map) ; restore mapping
+ or #BANK_BITS ; ROM bits
+ ld bc, #0x7ffd
+ out (c), a
+ ret
+
+
+;
+; 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 bc
+ pop de ; return address
+ pop hl ; new process p_tab*
+ push hl
+ push de
+ push bc
+
+ 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
+
+ ld a,(current_map)
+ push af
+
+ ; 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 16K bank copy here, then copy the live uarea
+ ; into the stash of the new process
+
+ ; --------- copy process ---------
+
+ ld hl, (fork_proc_ptr)
+ ld (_low_bank), hl ; low bank will become the child
+ ld de, #P_TAB__P_PAGE_OFFSET ; bank number
+ add hl, de
+ ; load p_page
+ ld c, (hl)
+ ld hl, (U_DATA__U_PAGE)
+ ld a, l
+
+ ;
+ ; Copy the high page via the bounce buffer
+ ;
+
+ call bankfork ; do the bank to bank copy
+
+ ; FIXME: if we support small apps at C000-FBFF we need to tweak this
+ ; Now copy the 0x8000-0xBFFF area directly
+
+ ld a, #0x06 + BANK_BITS ; low page alternate
+ ld bc, #0x7ffd
+ out (c), a
+
+ ld hl, #0x8000 ; Fixed
+ ld de, #0xC000 ; Page we just mapped in
+ ld bc, #16384
+ ldir
+
+ ; Copy done
+
+ ld a, (U_DATA__U_PAGE) ; parent memory
+ or #BANK_BITS ; get the right ROMs
+ ld bc, #0x7ffd
+ out (c), a ; Switch context to parent in 0xC000+
+
+ ; 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
+
+ ;
+ ; And back into the kernel
+ ;
+ ld bc, #0x7ffd
+ ld a, (current_map)
+ or #BANK_BITS
+ out (c), 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 iy
+ pop ix
+ pop bc
+
+ ; Make a new process table entry, etc.
+ ld hl, (fork_proc_ptr)
+ push hl
+ push af
+ call _newproc
+ pop af
+ 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
+
+;
+; 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
+;
+;
+; Note: this needs reviewing. We now have a lot more program memory
+; we can use with a lazy copying model
+;
+bankfork:
+ or #BANK_BITS ; ROM bits for the bank
+ ld b, #0x3E ; 64 x 256 minus 2 sets for the uarea stash/irqs
+ ld hl, #0xC000 ; base of memory to fork (vectors included)
+bankfork_1:
+ push bc ; Save our counter and also child offset
+ push hl
+ ld bc, #0x7ffd
+ out (c), 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
+ push bc ; save the bank pointers
+ ld bc, #0x7ffd
+ or #BANK_BITS ; ROM bits
+ out (c), a
+ 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
+;
+ .area _COMMONDATA
+bouncebuffer:
+ .ds 256
+; 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. We never use both at once
+; so share with bouncebuffer
+_swapstack:
+_low_bank:
+ .dw _ptab ; Init starts owning this
+
+fork_proc_ptr:
+ .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
--- /dev/null
+;
+; zx128 vt primitives
+;
+ ; exported symbols
+ .globl zx_plot_char
+ .globl zx_scroll_down
+ .globl zx_scroll_up
+ .globl zx_cursor_on
+ .globl zx_cursor_off
+ .globl zx_cursor_disable
+ .globl zx_clear_lines
+ .globl zx_clear_across
+ .globl zx_do_beep
+ .globl _fontdata_8x8
+ .globl _curattr
+ .globl _vtattr
+
+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 ; Standard screen
+ ld d,a
+ ret
+
+videoattr:
+ ; 32 x E + D into HL
+ ld a,e
+ rrca
+ rrca
+ rrca ; A is now 32xE with the top bits overflowed
+ ; into the low 2 bits
+ ld l,a
+ and #3 ; Extract the low 2 bits for the high
+ add #0x58 ; Attributes start 0x5800
+ ld h,a
+ ld a,l
+ and #0xE0 ; mask the bits that are valid
+ add d ; add the low 5 bits from D
+ ld l,a ; and done (the add can't overflow)
+ ret
+
+ .if ZXVID_ONLY
+_plot_char:
+ .endif
+zx_plot_char:
+ pop iy
+ pop hl
+ pop de ; D = x E = y
+ pop bc
+ push bc
+ push de
+ push hl
+ push iy
+
+ push de
+ call videopos
+
+ ld b, #0 ; calculating offset in font table
+ ld a, c
+ or a ; clear carry
+ rla
+ rl b
+ rla
+ rl b
+ rla
+ rl b
+ ld c, a
+
+ ld hl, #_fontdata_8x8-32*8 ; font
+ add hl, bc ; hl points to first byte of char data
+
+ ; We do underline for now - not clear italic or bold are useful
+ ; with the font we have.
+ ld bc,(_vtattr) ; c is vt attributes
+
+ ; printing
+plot_char_loop:
+ ld a, (hl)
+ ld (de), a
+ inc l ; next byte of char data
+ inc d ; next screen line
+
+ ld a, (hl)
+ ld (de), a
+ inc l ; next byte of char data
+ inc d ; next screen line
+
+ ld a, (hl)
+ ld (de), a
+ inc l ; next byte of char data
+ inc d ; next screen line
+
+ ld a, (hl)
+ ld (de), a
+ inc l ; next byte of char data
+ inc d ; next screen line
+
+ ld a, (hl)
+ ld (de), a
+ inc l ; next byte of char data
+ inc d ; next screen line
+
+ ld a, (hl)
+ ld (de), a
+ inc l ; next byte of char data
+ inc d ; next screen line
+
+ ld a, (hl)
+ ld (de), a
+ inc l ; next byte of char data
+ inc d ; next screen line
+
+ ld a, (hl)
+ bit 1,c ; underline ?
+ jr nz, last_ul
+plot_attr:
+ ld (de), a
+
+ pop de
+ call videoattr
+ ld a,(_curattr)
+ ld (hl),a
+ ret
+
+last_ul:
+ ld a,#0xff
+ jr plot_attr
+
+ .if ZXVID_ONLY
+_clear_lines:
+ .endif
+zx_clear_lines:
+ pop bc
+ pop hl
+ pop de ; E = line, D = count
+ push de
+ push hl
+ push bc
+ ; This way we handle 0 correctly
+ inc d
+ jr nextline
+
+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
+ push af
+ call _clear_across
+ pop af
+ pop hl ; clear stack
+ pop hl
+
+ pop de
+ inc e
+nextline:
+ dec d
+ jr nz, clear_next_line
+
+ ret
+
+
+ .if ZXVID_ONLY
+_clear_across:
+ .endif
+zx_clear_across:
+ pop iy
+ pop hl
+ pop de ; DE = coords
+ pop bc ; C = count
+ push bc
+ push de
+ push hl
+ push iy
+ ld a,c
+ or a
+ ret z ; No work to do - bail out
+ push de
+ push bc
+ 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
+ djnz clear_char
+
+ ex de, hl
+ inc de
+ push de
+ pop hl
+
+ dec c
+ jr nz, clear_line
+ pop bc
+ pop de
+ call videoattr
+ ld a,(_curattr)
+ ld b,c
+setattr:
+ ld (hl),a
+ inc hl
+ djnz setattr
+ 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
+ djnz 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
+
+ .if ZXVID_ONLY
+_scroll_down:
+ .endif
+zx_scroll_down:
+ ; set HL = (0,22), DE = (0, 23)
+ xor a
+ ld d, a
+ ld h, a
+ ld l, #22
+ ld e, #23
+ 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
+
+ dec l
+ dec e
+ dec c
+ jr nz, loop_scroll_down
+
+ ; Attributes
+ ld hl,#0x5ADF
+ ld de,#0x5AFF
+ ld bc,#0x02E0
+ lddr
+
+ ret
+
+
+ .if ZXVID_ONLY
+_scroll_up:
+ .endif
+zx_scroll_up:
+ ; set HL = (0,1), DE = (0, 0)
+ xor a
+ ld d, a
+ ld e, a
+ ld h, a
+ ld l, #1
+ 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
+
+ inc l
+ inc e
+ dec c
+ jr nz, loop_scroll_up
+
+ ld hl,#0x5820
+ ld de,#0x5800
+ ld bc,#0x02E0
+ ldir
+ ret
+
+ .if ZXVID_ONLY
+_cursor_on:
+ .endif
+zx_cursor_on:
+ pop bc
+ pop hl
+ pop de
+ push de
+ push hl
+ push bc
+ ld (cursorpos), de
+
+ call videopos
+ ld a, #7
+ add a, d
+ ld d, a
+ ld a, #0xFF
+ ld (de), a
+ ret
+ .if ZXVID_ONLY
+_cursor_disable:
+_cursor_off:
+ .endif
+zx_cursor_disable:
+zx_cursor_off:
+ ld de, (cursorpos)
+ call videopos
+ ld a, #7
+ add a, d
+ ld d, a
+ xor a
+ ld (de), a
+
+ .if ZXVID_ONLY
+_do_beep:
+ .endif
+zx_do_beep:
+ ret
+
+ .area _DATA
+
+cursorpos:
+ .dw 0
+++ /dev/null
-;
-; DivIDE is a bit of a pain as all the firmware is designed around
-; ROM BASIC and snapshots. We trick it by providing a fake BOOT.BIN.
-; Fatware thinks that this is the Fatware code it wants to put in
-; bank3 and then lock. In fact it's our loader which it will load
-; and lock for us.
-;
- .area BOOT (ABS)
-
- .globl null_handler
- .globl unix_syscall_entry
- .globl interrupt_handler
-
- .org 0
-
-;
-; We don't have a JP at 0 as we'd like so our low level code needs
-; to avoid that check
-;
-loader:
- di
- ld a,#0x80
- out (0xE3),a
- ; Page the EEPROM in and control transfers there
- ; not to the jp below
-loader5:
- ; This is where the EEPROM calls us
- jp start
-rst_8:
- .ds 8
-rst_10:
- .ds 8
-rst_18:
- .ds 8
-rst_20:
- .ds 8
-rst_28:
- .ds 8
-rst_30:
- jp unix_syscall_entry
- .ds 5
-rst_38:
- jp interrupt_handler
- .ds 0x66-0x3B
-nmi: ret ; magic...
- retn
-
-;
-; Load and go
-;
-start:
- ld bc,#0x7ffd
-
- ; Map some RAM (must not be RAM2) in 2000-3FFF
- ld a,#0x40
- out (0xE3),a
-
- ; Stack in the first 512 bytes of our data space
- ld sp,#0x2200
-
- ; Select the shadow screen bank
- ld a,#0x17
- out (c), a
-
- ; Clear the shadow screen
- ld hl,#0xc000
- ld de,#0xc001
- ld bc,#6144
- ld (hl),#0
- ldir
- ; Attributes to green writing on black
- ld (hl),#4
- ld bc,#767
- ldir
-
- ; Black border
- xor a
- out (254),a
-
- ; Shadow screen on, bank 0 back at C000-FFFF
- ld bc,#0x7ffd
- ld a,#0x18
- out (c),a
-
- ; Screen is now on bank 7
- ; Memory is on bank 0
-
- ; Put some RAM in 0x2000-3FFF and keep us locked in the low 8K
- ; Do not use #0x42, this is magic for allram mode on later DivIDE
- ld a,#0x40
- out (0xE3),a
- ;
- ; Ensure the master drive is selected
- ;
-wait1:
- in a,(191)
- rla
- jr c, wait1
- ld a,#0xE0
- out (187),a
- nop
-wait2:
- in a,(191)
- and #0xC0
- cp #0x40 ; want busy off, drdy
- jr nz, wait2
-
- ;
- ; Load sectors. We shortcut stuff here because we never
- ; load over 256 sectors
- ;
- ;
- ; We load 112 sectors into 2000-FFFF using bank 0 as the top bank
- ; We then load 32 sectors into the 16K at C000-FFFF bank 1
- ; And finally the same for bank 7
- ; 0x2000 should start ZB then the execution address
- ;
- ; For simplicity we don't bother with a stack, we just use IX
- ;
- xor a ; LBA 0
- out (175),a
- out (179),a
- ld de,#0x7001 ; Load 111 sectors (2200-FFFF)
- ; from sector 1
- ld hl,#0x2200 ; Starting address to load
- call load_loop
-
- ld a,#0x19 ; Select bank 1
- ld bc,#0x7ffd
- ld d,#0x20 ; Load 32 sectors (C000-FFFF)
- ld hl,#0xc000
- call load_loop
-
- ld a,#0x1F ; Select bank 7
- ld bc,#0x7ffd
- out (c),a
- ld d,#0x20 ; Load 32 sectors (C000-FFFF)
- ld hl,#0xC000
- call load_loop
-
- ld a,#0x18
- ld bc,#0x7ffd
- out (c),a ; Switch back to bank 0
- ld hl,#0x2200
- ; 0x2200 should start with a signature of ZB then the execute
- ; address
- ld a,(hl)
- cp #'Z'
- jr nz, failed
- inc hl
- ld a,(hl)
- cp #'B'
- jr nz, failed
- inc hl
- ld a,(hl)
- inc hl
- ld h,(hl)
- ld l,a
- jp (hl)
-
-load_loop:
- ld a,e
- inc e
- out (183),a ; sector number to load
- ld a,#1 ; load one sector
- out (171),a
- ld a,#0x20 ; READ
- out (191),a
- nop
-wait3:
- in a, (191)
- rlca
- jr c, wait3 ; Busy
- bit 4,a ; DRQ ?
- jr z, failed ; Nope - bad
- ld bc,#163 ; Data port
- inir
- inir
- dec d
- jr nz, load_loop
- ret
-
-failed:
- ld hl,#1
- jp OutJPHL ; Into Spectrum ROM and reboot
-
-
- .area BOOT1FF7
-
- ;
- ; For compatibility with FatWare
- ;
-OutEI: ei
-OutRet: ret
-OutJPHL: jp (hl)
-
- .area BOOT1FFE
-RomSign:
- .db 0,14
-; Based on the Z80 Pack banked code
-;
-; Should unify this somewhat with lib/banked
-;
- .module tricks
-
- .globl _ptab_alloc
- .globl _newproc
- .globl _getproc
- .globl _platform_monitor
- .globl trap_illegal
- .globl _platform_switchout
- .globl _switchin
- .globl _low_bank
- .globl _dup_low_page
- .globl _dofork
- .globl _runticks
- .globl unix_syscall_entry
- .globl interrupt_handler
- .globl current_map
- .globl _ptab
- .globl _swapper
- .globl _int_disabled
- .globl switch_bank
-
- ; 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().
-_platform_switchout:
- di
- ; 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 a,(current_map)
- push af
-
- ld (U_DATA__U_SP), sp ; this is where the SP is restored in _switchin
-
- ;
- ; We are now running on the sleeping process stack. The switchin
- ; will simply go back to the saved SP above and discard anything
- ; here
- ;
-
- ; Stash the uarea back into process memory
- ld hl, (U_DATA__U_PAGE)
- ld a, l
- ld bc, #0x7ffd
- or #BANK_BITS
- out (c), a
-
- ; This includes the stacks, so be careful on restore
- ld hl, #U_DATA
- ld de, #U_DATA_STASH
- ld bc, #U_DATA__TOTALSIZE
- ldir
- ld a, (current_map)
- or #BANK_BITS
- ld bc, #0x7ffd
- out (c), a
-
- ; find another process to run (may select this one again)
- push af
- call _getproc
- pop af ; we can't optimise this as the linker
- ; is entitled to patch the 5 bytes here into a
- ; banked call
- push hl
- push af
- call _switchin
-
- ; we should never get here
- call _platform_monitor
-
-badswitchmsg: .ascii "_switchin: FAIL"
- .db 13, 10, 0
-
-_switchin:
- di
- pop hl ; far padding
- 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 ; far padding
-
- push de
- ld hl, #P_TAB__P_PAGE_OFFSET
- add hl, de ; process ptr
- pop de
- ;
- ; Get ourselves a valid private stack ASAP. We are going to
- ; copy udata around and our main stacks are in udata
- ;
- ld sp, #_swapstack
-
- ld a, (hl) ; 0 swapped, not zero is the bank for C000
- or a
- jr nz, not_swapped
-
- ; Swap the process in (this may swap something else out first)
- ; The second pushes are C function arguments. SDCC can trample
- ; these
-
- ; Turn this one once we have it all sorted and debugged
- ; ei
- ; xor a
- ; ld (_int_disabled),a
- push hl ; Save
- push de
- push hl ; Arguments
- push de
- push af
- call _swapper
- pop af
- pop af
- pop af
- pop de ; Restore
- pop hl
- ld a,#1
- ld (_int_disabled),a
- di
- ld a, (hl) ; We should now have a page assigned
-not_swapped:
- ld hl,(U_DATA__U_PTAB)
- or a
- sbc hl,de
-; Turn this on once debugged
-; jr z, skip_copyback
-
- ; We are in DI so we can poke these directly but must not invoke
- ; any code outside of common
- or #BANK_BITS ; ROM
- ; Pages please !
- ld bc, #0x7ffd
- out (c), a
-
- ; Copy the stash from the user page back down into common
- ; The alternate registers are free - we use them for the
- ; block copy and for the flipper
- ;
- ; FIXME: Add Tormod's optimisation from the 6809 tree
- ;
- exx
- ld hl, #U_DATA_STASH
- ld de, #U_DATA
- ld bc, #U_DATA__TOTALSIZE
- ldir
- exx
- ;
- ; Remap the kernel proper
- ;
-
- ld a, (current_map)
- or #BANK_BITS
- ld bc, #0x7ffd
- out (c), a
-
- ; Is our low data in 0x8000 already or do we need to flip
- ; it with bank 6. _low_bank holds the page pointer of the
- ; task owning the space
-
- ld hl, (_low_bank) ; who owns low memory
- or a
- sbc hl, de ; preserve DE
- jr z, nofliplow ; skip the flip if we own low bank
-;
-; Flip low banks over. Preserve DE
-;
-fliplow:
- exx
- ld hl, #0x8000
- ld de, #0xc000
- ld a, #6 + BANK_BITS
- ld bc, #0x7ffd
- out (c), a
-flip2:
- ld b, #0 ; 256 bytes per outer loop (16K total)
-flip1:
- ld c, (hl)
- ld a, (de)
- ex de, hl
- ld (hl), c
- ld (de), a
- inc hl
- inc de
- djnz flip1
- xor a
- cp d ; Wrapped to 0x0000 ?
- jr nz, flip1
-
- ld a, (current_map)
- or #BANK_BITS
- ld bc, #0x7ffd
- out (c), a
- exx
- ld (_low_bank), de ; we own it now
-
-nofliplow:
-
- ; 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 == HL
- 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 any 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)
-
- ;
- ; We can now use the stack again
- ;
-
- pop af
- pop iy
- pop ix
- pop hl ; return code
-
- ; Make sure we have the right kernel bank to return to
- call switch_bank
-
- ; enable interrupts, if the ISR isn't already running
- ld a, (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
-
-; Interrupts should be off when this is called
-_dup_low_page:
- di ; FIXME: check callers properly
- ld a, #0x06 + BANK_BITS ; low page alternate
- ld bc, #0x7ffd
- out (c), a
-
- ld hl, #0x8000 ; fixed
- ld de, #0xC000 ; page we just mapped in
- ld bc, #16384
- ldir
-
- ld a, (current_map) ; restore mapping
- or #BANK_BITS ; ROM bits
- ld bc, #0x7ffd
- out (c), a
- ret
-
-
-;
-; 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 bc
- pop de ; return address
- pop hl ; new process p_tab*
- push hl
- push de
- push bc
-
- 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
-
- ld a,(current_map)
- push af
-
- ; 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 16K bank copy here, then copy the live uarea
- ; into the stash of the new process
-
- ; --------- copy process ---------
-
- ld hl, (fork_proc_ptr)
- ld (_low_bank), hl ; low bank will become the child
- ld de, #P_TAB__P_PAGE_OFFSET ; bank number
- add hl, de
- ; load p_page
- ld c, (hl)
- ld hl, (U_DATA__U_PAGE)
- ld a, l
-
- ;
- ; Copy the high page via the bounce buffer
- ;
-
- call bankfork ; do the bank to bank copy
-
- ; FIXME: if we support small apps at C000-FBFF we need to tweak this
- ; Now copy the 0x8000-0xBFFF area directly
-
- ld a, #0x06 + BANK_BITS ; low page alternate
- ld bc, #0x7ffd
- out (c), a
-
- ld hl, #0x8000 ; Fixed
- ld de, #0xC000 ; Page we just mapped in
- ld bc, #16384
- ldir
-
- ; Copy done
-
- ld a, (U_DATA__U_PAGE) ; parent memory
- or #BANK_BITS ; get the right ROMs
- ld bc, #0x7ffd
- out (c), a ; Switch context to parent in 0xC000+
-
- ; 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
-
- ;
- ; And back into the kernel
- ;
- ld bc, #0x7ffd
- ld a, (current_map)
- or #BANK_BITS
- out (c), 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 iy
- pop ix
- pop bc
-
- ; Make a new process table entry, etc.
- ld hl, (fork_proc_ptr)
- push hl
- push af
- call _newproc
- pop af
- 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
-
-;
-; 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
-;
-;
-; Note: this needs reviewing. We now have a lot more program memory
-; we can use with a lazy copying model
-;
-bankfork:
- or #BANK_BITS ; ROM bits for the bank
- ld b, #0x3E ; 64 x 256 minus 2 sets for the uarea stash/irqs
- ld hl, #0xC000 ; base of memory to fork (vectors included)
-bankfork_1:
- push bc ; Save our counter and also child offset
- push hl
- ld bc, #0x7ffd
- out (c), 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
- push bc ; save the bank pointers
- ld bc, #0x7ffd
- or #BANK_BITS ; ROM bits
- out (c), a
- 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
-;
- .area _COMMONDATA
-bouncebuffer:
- .ds 256
-; 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. We never use both at once
-; so share with bouncebuffer
-_swapstack:
-_low_bank:
- .dw _ptab ; Init starts owning this
-
-fork_proc_ptr:
- .dw 0 ; (C type is struct p_tab *) -- address of child process p_tab entry
+ .include "../dev/zx/tricks.s"
.globl _curattr
.globl _vtattr
- .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 ; Standard screen
- ld d,a
- ret
-
-videoattr:
- ; 32 x E + D into HL
- ld a,e
- rrca
- rrca
- rrca ; A is now 32xE with the top bits overflowed
- ; into the low 2 bits
- ld l,a
- and #3 ; Extract the low 2 bits for the high
- add #0x58 ; Attributes start 0x5800
- ld h,a
- ld a,l
- and #0xE0 ; mask the bits that are valid
- add d ; add the low 5 bits from D
- ld l,a ; and done (the add can't overflow)
- ret
-
-_plot_char:
- pop iy
- pop hl
- pop de ; D = x E = y
- pop bc
- push bc
- push de
- push hl
- push iy
-
- push de
- call videopos
-
- ld b, #0 ; calculating offset in font table
- ld a, c
- or a ; clear carry
- rla
- rl b
- rla
- rl b
- rla
- rl b
- ld c, a
-
- ld hl, #_fontdata_8x8-32*8 ; font
- add hl, bc ; hl points to first byte of char data
-
- ; We do underline for now - not clear italic or bold are useful
- ; with the font we have.
- ld bc,(_vtattr) ; c is vt attributes
-
- ; printing
-plot_char_loop:
- ld a, (hl)
- ld (de), a
- inc l ; next byte of char data
- inc d ; next screen line
-
- ld a, (hl)
- ld (de), a
- inc l ; next byte of char data
- inc d ; next screen line
-
- ld a, (hl)
- ld (de), a
- inc l ; next byte of char data
- inc d ; next screen line
-
- ld a, (hl)
- ld (de), a
- inc l ; next byte of char data
- inc d ; next screen line
-
- ld a, (hl)
- ld (de), a
- inc l ; next byte of char data
- inc d ; next screen line
-
- ld a, (hl)
- ld (de), a
- inc l ; next byte of char data
- inc d ; next screen line
-
- ld a, (hl)
- ld (de), a
- inc l ; next byte of char data
- inc d ; next screen line
-
- ld a, (hl)
- bit 1,c ; underline ?
- jr nz, last_ul
-plot_attr:
- ld (de), a
-
- pop de
- call videoattr
- ld a,(_curattr)
- ld (hl),a
- ret
-
-last_ul:
- ld a,#0xff
- jr plot_attr
-
-_clear_lines:
- pop bc
- pop hl
- pop de ; E = line, D = count
- push de
- push hl
- push bc
- ; This way we handle 0 correctly
- inc d
- jr nextline
-
-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
- push af
- call _clear_across
- pop af
- pop hl ; clear stack
- pop hl
-
- pop de
- inc e
-nextline:
- dec d
- jr nz, clear_next_line
-
- ret
-
-
-_clear_across:
- pop iy
- pop hl
- pop de ; DE = coords
- pop bc ; C = count
- push bc
- push de
- push hl
- push iy
- ld a,c
- or a
- ret z ; No work to do - bail out
- push de
- push bc
- 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
- djnz clear_char
+ ; Build the video library as the only driver
- ex de, hl
- inc de
- push de
- pop hl
+ZXVID_ONLY .equ 1
- dec c
- jr nz, clear_line
- pop bc
- pop de
- call videoattr
- ld a,(_curattr)
- ld b,c
-setattr:
- ld (hl),a
- inc hl
- djnz setattr
- 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
- djnz 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,22), DE = (0, 23)
- xor a
- ld d, a
- ld h, a
- ld l, #22
- ld e, #23
- 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
-
- dec l
- dec e
- dec c
- jr nz, loop_scroll_down
-
- ; Attributes
- ld hl,#0x5ADF
- ld de,#0x5AFF
- ld bc,#0x02E0
- lddr
-
- ret
-
-
-_scroll_up:
- ; set HL = (0,1), DE = (0, 0)
- xor a
- ld d, a
- ld e, a
- ld h, a
- ld l, #1
- 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
-
- inc l
- inc e
- dec c
- jr nz, loop_scroll_up
-
- ld hl,#0x5820
- ld de,#0x5800
- ld bc,#0x02E0
- ldir
- ret
-
-_cursor_on:
- pop bc
- pop hl
- pop de
- push de
- push hl
- push bc
- ld (cursorpos), de
-
- call videopos
- ld a, #7
- add a, d
- ld d, a
- ld a, #0xFF
- ld (de), a
- ret
-_cursor_disable:
-_cursor_off:
- ld de, (cursorpos)
- call videopos
- ld a, #7
- add a, d
- ld d, a
- xor a
- ld (de), a
-
-_do_beep:
- ret
+ .area _VIDEO
- .area _DATA
+ .include "../dev/zx/video.s"
-cursorpos:
- .dw 0