414 lines
9.1 KiB
NASM
414 lines
9.1 KiB
NASM
STACK_TOP = 0xFFFF
|
|
DELAY_LOOPS_PER_MS = 40
|
|
BANK_CONTROL_PORT = 0x00
|
|
BOOT_BANK_MAPPING = 0x06 ; ROM on, banking on, upper 16K at C000-FFFF
|
|
ROM_DISABLE_BIT = 0x01
|
|
BANK_SELECT_BIT = 0x02
|
|
BANK_ENABLE_BIT = 0x04
|
|
BANK_MODE_CLEAR_MASK = 0xF9 ; preserve ROM-disable and auxiliary outputs
|
|
|
|
.include "bios.inc"
|
|
|
|
.globl _main
|
|
.globl _delay_ms
|
|
.globl _io_out
|
|
.globl _io_in
|
|
.globl _enable_interrupts
|
|
.globl _disable_interrupts
|
|
.globl _jump_to
|
|
.globl _bios_putc
|
|
.globl _bios_try_getc
|
|
.globl _bios_getc
|
|
.globl _bios_flush
|
|
.globl _bios_init
|
|
.globl _bios_bank
|
|
.globl _bios_rom_disable
|
|
.globl _bios_rom_enable
|
|
.globl _bank_control_shadow
|
|
.globl resident_image_end
|
|
.globl s__DATA
|
|
.globl l__DATA
|
|
.globl s__INITIALIZER
|
|
.globl s__INITIALIZED
|
|
.globl l__INITIALIZER
|
|
|
|
.area _BOOT (ABS)
|
|
|
|
.org 0x00
|
|
jp boot_copy
|
|
.org 0x08
|
|
boot_copy:
|
|
di
|
|
; Keep ROM enabled while mapping the upper physical 16K at C000-FFFF.
|
|
; The resident copy therefore survives either banked mode after boot.
|
|
ld a, #BOOT_BANK_MAPPING
|
|
out (BANK_CONTROL_PORT), a
|
|
ld sp, #STACK_TOP
|
|
ld hl, #RESIDENT_ROM_OFFSET
|
|
ld de, #RESIDENT_RAM_BASE
|
|
ld bc, #(resident_image_end - RESIDENT_RAM_BASE)
|
|
ldir
|
|
jp init
|
|
|
|
.area _ITABLE (ABS)
|
|
|
|
; The executable vector page is part of the image copied to high RAM.
|
|
.org RESIDENT_RAM_BASE
|
|
jp init
|
|
.org RESIDENT_RAM_BASE + 0x08
|
|
reti
|
|
.org RESIDENT_RAM_BASE + 0x10
|
|
reti
|
|
.org RESIDENT_RAM_BASE + 0x18
|
|
reti
|
|
.org RESIDENT_RAM_BASE + 0x20
|
|
reti
|
|
.org RESIDENT_RAM_BASE + 0x28
|
|
reti
|
|
.org RESIDENT_RAM_BASE + 0x30
|
|
reti
|
|
.org RESIDENT_RAM_BASE + 0x38
|
|
reti
|
|
|
|
; IM 2 table: the CTC supplies vectors 40, 42, 44, and 46.
|
|
.org RESIDENT_RAM_BASE + 0x40
|
|
.dw ctc_isr ; CTC channel 0: vector 0x40
|
|
.dw ctc_isr ; CTC channel 1: vector 0x42
|
|
.dw ctc_isr ; CTC channel 2: vector 0x44
|
|
.dw uart_isr ; CTC channel 3: vector 0x46
|
|
|
|
; Stable BIOS jump table for ROM-resident applications.
|
|
; C050 putc(A), C053 try_getc(), C056 getc(), C059 io_out(A,L),
|
|
; C05C io_in(A), C05F delay_ms(HL), C062 monitor warm start,
|
|
; C065 bank(A), C068 disable ROM, C06B enable ROM, C06E flush output.
|
|
.org BIOS_PUTC
|
|
jp bios_service_putc
|
|
.org BIOS_TRY_GETC
|
|
jp bios_service_try_getc
|
|
.org BIOS_GETC
|
|
jp bios_service_getc
|
|
.org BIOS_IO_OUT
|
|
jp bios_service_io_out
|
|
.org BIOS_IO_IN
|
|
jp bios_service_io_in
|
|
.org BIOS_DELAY_MS
|
|
jp bios_service_delay_ms
|
|
.org BIOS_MONITOR
|
|
jp init
|
|
.org BIOS_BANK
|
|
jp _bios_bank
|
|
.org BIOS_ROM_DISABLE
|
|
jp _bios_rom_disable
|
|
.org BIOS_ROM_ENABLE
|
|
jp _bios_rom_enable
|
|
.org BIOS_FLUSH
|
|
jp bios_service_flush
|
|
|
|
; Port 00 is write-only. Keep its last BIOS-managed value outside the C data
|
|
; areas so a monitor warm start does not reset it. The bootstrap establishes
|
|
; this same value in the hardware before the resident image is copied.
|
|
.org RESIDENT_RAM_BASE + 0x73
|
|
_bank_control_shadow::
|
|
.db BOOT_BANK_MAPPING
|
|
|
|
.area _HOME
|
|
init:
|
|
di
|
|
ld sp, #STACK_TOP
|
|
|
|
; A reset may have interrupted a CTC service routine without executing
|
|
; RETI. Release any stale daisy-chain in-service latches before the
|
|
; channels are reinitialized. RETI acts as RET for each CALL while
|
|
; also signalling end-of-interrupt to daisy-chained peripherals.
|
|
call release_daisy_chain
|
|
call release_daisy_chain
|
|
call release_daisy_chain
|
|
call release_daisy_chain
|
|
|
|
call gsinit
|
|
|
|
; Setup interrupt mode 2
|
|
ld a, #0xC0
|
|
ld i, a
|
|
im 2
|
|
call _bios_init
|
|
call _main
|
|
|
|
hang:
|
|
jr hang
|
|
|
|
release_daisy_chain:
|
|
reti
|
|
|
|
.area _GSINIT
|
|
gsinit:
|
|
ld bc, #l__INITIALIZER
|
|
ld a, b
|
|
or c
|
|
jr z, zero_data
|
|
|
|
ld de, #s__INITIALIZED
|
|
ld hl, #s__INITIALIZER
|
|
ldir
|
|
|
|
zero_data:
|
|
ld bc, #l__DATA
|
|
ld a, b
|
|
or c
|
|
jr z, gsinit_done
|
|
|
|
ld hl, #s__DATA
|
|
xor a
|
|
|
|
clear_loop:
|
|
xor a ; BC loop test below changes A
|
|
ld (hl), a
|
|
inc hl
|
|
dec bc
|
|
ld a, b
|
|
or c
|
|
jr nz, clear_loop
|
|
|
|
gsinit_done:
|
|
.area _GSFINAL
|
|
ret
|
|
resident_image_end:
|
|
|
|
.area _CODE
|
|
|
|
; Select a RAM mapping while retaining the shadowed ROM-disable bit.
|
|
; Input: A=0 selects lower 48K + upper 16K; any other value selects upper 64K.
|
|
; Both modes keep banking enabled. A and flags are preserved.
|
|
_bios_bank::
|
|
push af
|
|
or a
|
|
jr z, bios_bank_split
|
|
|
|
ld a, (_bank_control_shadow)
|
|
and #BANK_MODE_CLEAR_MASK
|
|
or #BANK_ENABLE_BIT
|
|
jr bios_bank_update
|
|
|
|
bios_bank_split:
|
|
ld a, (_bank_control_shadow)
|
|
and #BANK_MODE_CLEAR_MASK
|
|
or #(BANK_ENABLE_BIT | BANK_SELECT_BIT)
|
|
|
|
bios_bank_update:
|
|
ld (_bank_control_shadow), a
|
|
out (BANK_CONTROL_PORT), a
|
|
pop af
|
|
ret
|
|
|
|
; Hide ROM while preserving the selected RAM mapping.
|
|
; A and flags are preserved.
|
|
_bios_rom_disable::
|
|
push af
|
|
ld a, (_bank_control_shadow)
|
|
or #ROM_DISABLE_BIT
|
|
jr bios_bank_update
|
|
|
|
; Make ROM visible again while preserving the selected RAM mapping.
|
|
; A and flags are preserved.
|
|
_bios_rom_enable::
|
|
push af
|
|
ld a, (_bank_control_shadow)
|
|
and #0xFE
|
|
jr bios_bank_update
|
|
|
|
; BIOS ABI services preserve registers except documented return values.
|
|
bios_service_putc:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
push ix
|
|
push iy
|
|
call _bios_putc
|
|
pop iy
|
|
pop ix
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
|
|
bios_service_try_getc:
|
|
push bc
|
|
push de
|
|
push hl
|
|
push ix
|
|
push iy
|
|
call _bios_try_getc
|
|
pop iy
|
|
pop ix
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
ret
|
|
|
|
bios_service_getc:
|
|
push bc
|
|
push de
|
|
push hl
|
|
push ix
|
|
push iy
|
|
call _bios_getc
|
|
pop iy
|
|
pop ix
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
ret
|
|
|
|
bios_service_flush:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
push ix
|
|
push iy
|
|
call _bios_flush
|
|
pop iy
|
|
pop ix
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
|
|
bios_service_io_out:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
push ix
|
|
push iy
|
|
call _io_out
|
|
pop iy
|
|
pop ix
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
|
|
bios_service_io_in:
|
|
push bc
|
|
push de
|
|
push hl
|
|
push ix
|
|
push iy
|
|
call _io_in
|
|
pop iy
|
|
pop ix
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
ret
|
|
|
|
bios_service_delay_ms:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
push ix
|
|
push iy
|
|
call _delay_ms
|
|
pop iy
|
|
pop ix
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
|
|
.macro MAKE_ISR entry, handler
|
|
.globl handler
|
|
entry:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
push ix
|
|
push iy
|
|
|
|
ex af, af'
|
|
push af
|
|
exx
|
|
push bc
|
|
push de
|
|
push hl
|
|
|
|
call handler
|
|
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
exx
|
|
pop af
|
|
ex af, af'
|
|
|
|
pop iy
|
|
pop ix
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ei
|
|
reti
|
|
.endm
|
|
|
|
MAKE_ISR ctc_isr, _ctc_interrupt
|
|
MAKE_ISR uart_isr, _uart_interrupt
|
|
|
|
_enable_interrupts::
|
|
ei
|
|
ret
|
|
|
|
_disable_interrupts::
|
|
di
|
|
ret
|
|
|
|
; Write a byte to an I/O port.
|
|
; Input: A = port address, L = data byte.
|
|
_io_out::
|
|
ld c, a
|
|
ld a, l
|
|
out (c), a
|
|
ret
|
|
|
|
; Read a byte from an I/O port.
|
|
; Input: A = port address.
|
|
; Return: A = data byte.
|
|
_io_in::
|
|
ld c, a
|
|
in a, (c)
|
|
ret
|
|
|
|
; Execute code at HL. Because this routine is entered with CALL, a RET in the
|
|
; target returns to the monitor immediately after jump_to().
|
|
_jump_to::
|
|
jp (hl)
|
|
|
|
; Approximate millisecond delay.
|
|
; Input: HL = delay in milliseconds.
|
|
_delay_ms::
|
|
ld a, h
|
|
or l
|
|
ret z
|
|
delay_ms_outer$:
|
|
push hl
|
|
ld hl, #DELAY_LOOPS_PER_MS
|
|
delay_ms_inner$:
|
|
dec hl
|
|
ld a, h
|
|
or l
|
|
jr nz, delay_ms_inner$
|
|
pop hl
|
|
dec hl
|
|
ld a, h
|
|
or l
|
|
jr nz, delay_ms_outer$
|
|
ret
|