STACK_TOP = 0xFFFF
; At 500 kHz the CPU has about 500 T-states per millisecond.
; The inner delay loop below costs about 26 T-states per taken iteration:
;   dec hl  =  6T
;   ld a,h  =  4T
;   or l    =  4T
;   jr nz   = 12T
; 500 / 26 = 19.2, so 20 iterations is the nearest simple whole-number choice.
DELAY_LOOPS_PER_MS = 20

        .globl _main
        .globl _delay_ms
        .globl _io_out
        .globl _io_in
        .globl s__DATA
        .globl l__DATA
        .globl s__INITIALIZER
        .globl s__INITIALIZED
        .globl l__INITIALIZER

.area _ITABLE (ABS)

.org 0x00
        jp init
.org 0x08
        reti
.org 0x10
        reti
.org 0x18
        reti
.org 0x20
        reti
.org 0x28
        reti
.org 0x30
        reti
.org 0x38
        reti

.area _HOME
init:
        di
        ld sp, #STACK_TOP

        call gsinit
        call _main

hang:
        jr hang

.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:
        ld (hl), a
        inc hl
        dec bc
        ld a, b
        or c
        jr nz, clear_loop

gsinit_done:
.area _GSFINAL
        ret

.area _CODE
; 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

; Approximate millisecond delay.
; Input: HL = delay in milliseconds.
; For CLK_SPEED = 500000 Hz this uses 20 inner iterations per ms.
_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
