; =============================================================================
; SVI-609 Bootstrap Card — ROM Disassembly
; =============================================================================
;
; ROM chip:       4096 bytes (4K EPROM)
; Active code:    745 bytes (0x000–0x2E8)
; Padding:        0xFF from 0x2F9 to 0xFEF
; Signature:      "NETROM 1.0 CWJ" at offset 0xFF0 (last 16 bytes)
;
; The ROM is mapped at 0000h on power-up, taking priority over the
; standard BIOS ROM.  A 19-byte bootstrap stub copies the main code
; (743 bytes) to RAM at 8000h, then jumps there.  The main code
; unmaps the SVI-609 ROM (restoring the BIOS ROM) and proceeds to
; initialize the system and boot from either floppy or hard disk.
;
; Boot priority:
;   1. SASI hard disk must be present (required)
;   2. If floppy drive has a disk → boot from floppy (track 0, sector 1)
;   3. If no floppy disk → boot from HDD (SASI Read LBA 0, 2 sectors)
;   4. Both paths load code to C100h and jump there
;   5. If no SASI target responds → JP 0000h (restart)
;
; =============================================================================

; ---------------------------------------------------------------------------
; FDC Ports (WD1793 Floppy Disk Controller)
; ---------------------------------------------------------------------------
FDC_CMD_STATUS  equ 30h         ; W: Command Register / R: Status Register
FDC_TRACK       equ 31h         ; R/W: Track Register
FDC_SECTOR      equ 32h         ; R/W: Sector Register
FDC_DATA        equ 33h         ; R/W: Data Register (used by INI/OUTI)
FDC_CTRL        equ 34h         ; W: Drive select/motor/side latch
                                ; R: b7=INTRQ, b6=DRQ
FDC_DENSITY     equ 38h         ; W: Density select (b0: 0=Double, 1=Single)

; FDC_CTRL write bits:
;   Bit 0: Drive 0 select
;   Bit 1: Drive 1 select
;   Bit 2: Side select (0=side 0, 1=side 1)
;   Bit 3: Motor enable

; FDC_CTRL read bits:
;   Bit 6: DRQ  — Data Request (ready for byte transfer)
;   Bit 7: INTRQ — Interrupt Request (command complete)

; ---------------------------------------------------------------------------
; SASI Host Adapter Ports (directly mapped to SASI bus)
; ---------------------------------------------------------------------------
SASI_RESET      equ 40h         ; W: SASI bus reset (active pulse)
SASI_DATA_OUT   equ 41h         ; W: Write data to SASI bus (auto-ACK)
SASI_DATA_IN    equ 42h         ; R: Read data from SASI bus (auto-ACK)
SASI_SEL        equ 43h         ; W: Assert SASI SEL signal

SASI_STATUS     equ 46h         ; R: SASI bus status register
; Status register bit map:
;   Bit 0: BSY
;   Bit 1: REQ
;   Bit 2: C/D   (1=Command/Status, 0=Data)
;   Bit 3: I/O   (1=Input to host, 0=Output from host)
;   Bit 4: MSG   (1=Message phase)
;   Bit 5: (possibly direct BSY — used for target detection)
;
; Standard SASI phase decode (AND 1Fh):
;   00h = Bus Free
;   03h = Data Out     (BSY+REQ)
;   0Bh = Command      (BSY+REQ+C/D)
;   13h = Data In       (BSY+REQ+I/O)
;   1Bh = Status        (BSY+REQ+I/O+C/D)
;   1Fh = Message In    (BSY+REQ+I/O+C/D+MSG)

; ---------------------------------------------------------------------------
; Other Ports
; ---------------------------------------------------------------------------
SVI609_CTRL     equ 6Fh         ; W: SVI-609 ROM bank control
                                ;    Writing here unmaps the SVI-609 ROM,
                                ;    restoring the standard BIOS ROM at 0000h
PSG_ADDR        equ 88h         ; W: AY-3-8910 PSG address latch
PSG_DATA_W      equ 8Ch         ; W: AY-3-8910 PSG data write

; ---------------------------------------------------------------------------
; External BIOS / BASIC ROM Addresses (in standard SVI-328 ROM)
; ---------------------------------------------------------------------------
WARM_BOOT       equ 0000h       ; System restart / warm boot
BIOS_INIT_IO    equ 34D9h       ; BIOS hardware initialization
BIOS_INIT_TXT   equ 4066h       ; Text mode screen initialization
BIOS_INIT_SCR   equ 40BEh       ; Screen setup
BIOS_INIT_BAS1  equ 6557h       ; BASIC interpreter init (part 1)
BIOS_INIT_BAS2  equ 65B5h       ; BASIC interpreter init (part 2)
BIOS_INIT_HOOK  equ 7CDAh       ; Hook / interrupt vector init
BIOS_HOOK_TABLE equ 084Ch       ; Source data: 78-byte function table in ROM
BIOS_ISR_TABLE  equ 7A66h       ; Source data: 222-byte ISR dispatch table in ROM
HOOK_ENTRY      equ FAC0h       ; Hook entry point (in RAM hook area)
BOOT_ENTRY      equ C100h       ; Boot sector load address / entry point

; ---------------------------------------------------------------------------
; RAM Work Area Addresses
; ---------------------------------------------------------------------------
WORK_FUNC_PTR   equ F500h       ; Function table destination (78 bytes)
WORK_ISR_BASE   equ FA00h       ; ISR dispatch table destination (222 bytes)
HOOK_VECTOR_BASE equ FE79h      ; Interrupt hook vector table (105 × 3 bytes)
CART_SIGNATURE  equ FFFEh       ; Cartridge signature ("MS" = valid cartridge)


; =============================================================================
;  PART 1: BOOTSTRAP STUB  (executes from ROM at 0000h)
; =============================================================================
; This code runs directly from the SVI-609 EPROM, which is mapped at 0000h
; on power-up.  It copies the main code to RAM and transfers control there.
; =============================================================================

            org 0000h

BOOTSTRAP:
0000        di                          ; Disable interrupts
0001        ld   a,00h
0003        out  (FDC_CTRL),a           ; Deselect all drives, motor off
0005        ld   hl,MAIN_CODE_ROM       ; Source: ROM offset 0013h
0008        ld   de,MAIN_CODE_RAM       ; Destination: RAM 8000h
000B        ld   bc,MAIN_CODE_LEN       ; Length: 743 bytes (02E7h)
000E        ldir                        ; Copy main code to RAM
0010        jp   MAIN_CODE_RAM          ; Jump to code in RAM at 8000h

MAIN_CODE_ROM   equ 0013h              ; Start of main code in ROM
MAIN_CODE_RAM   equ 8000h              ; RAM execution address
MAIN_CODE_LEN   equ 02E7h              ; 743 bytes


; =============================================================================
;  PART 2: MAIN CODE  (executes from RAM at 8000h)
; =============================================================================
; After the LDIR above, ROM bytes 0013h–02F9h are at RAM 8000h–82E6h.
; =============================================================================

            org 8000h

; =============================================================================
; INIT — System initialization and boot decision
; =============================================================================

INIT:
8000        di
8001        ld   sp,0F4F6h              ; Set stack pointer

            ; --- Reset FDC and unmap SVI-609 ROM ---
8004        call FDC_FORCE_INT          ; Send Force Interrupt to FDC
8007        out  (SVI609_CTRL),a        ; Unmap SVI-609 ROM → BIOS ROM restored
            ;       From this point on, 0000h–7FFFh is standard BIOS/BASIC ROM

            ; --- Initialize PSG ---
8009        ld   a,0Fh
800B        out  (PSG_ADDR),a           ; Select PSG register 15 (I/O port B)
800D        ld   a,0DFh
800F        out  (PSG_DATA_W),a         ; Set PSG port B = DFh (control lines)

            ; --- Call BIOS init routines ---
8011        call BIOS_INIT_IO           ; 34D9h — hardware initialization

            ; --- Set cartridge signature ---
8014        ld   hl,534Dh               ; 'M','S' (little-endian)
8017        ld   (CART_SIGNATURE),hl     ; Store "MS" at FFFEh (warm-boot marker)

            ; --- Re-set stack pointer ---
801A        ld   sp,0F4F6h
801D        im   1                      ; Interrupt mode 1

            ; --- Clear work area FA00h–FFB3h (1460 bytes) ---
801F        ld   bc,05B4h               ; 1460 bytes
8022        ld   hl,WORK_ISR_BASE       ; FA00h
8025        ld   d,h
8026        ld   e,l
8027        inc  de
8028        ld   (hl),00h
802A        ldir                        ; Fill FA00h–FFB3h with 00h

            ; --- Initialize hook vectors (105 × 3-byte slots with RET) ---
802C        ld   b,69h                  ; 105 entries
802E        ld   hl,HOOK_VECTOR_BASE    ; FE79h
.init_hooks:
8031        ld   (hl),0C9h              ; C9h = RET opcode (hook disabled)
8033        inc  hl
8034        inc  hl
8035        inc  hl                     ; Advance 3 bytes per slot
8036        djnz .init_hooks

            ; --- Copy function table from BIOS ROM to work area ---
8038        ld   hl,WORK_FUNC_PTR       ; F500h
803B        ld   (0FDE6h),hl            ; Store function table pointer
803E        ld   hl,8000h
8041        ld   (0FDE4h),hl            ; Store code base pointer
8044        ld   bc,004Eh               ; 78 bytes
8047        ld   de,WORK_FUNC_PTR       ; Destination: F500h
804A        ld   hl,BIOS_HOOK_TABLE     ; Source: 084Ch (BIOS ROM)
804D        ldir

            ; --- Copy ISR dispatch table from BIOS ROM to work area ---
804F        ld   bc,00DEh               ; 222 bytes
8052        ld   de,WORK_ISR_BASE       ; Destination: FA00h
8055        ld   hl,BIOS_ISR_TABLE      ; Source: 7A66h (BIOS ROM)
8058        ldir

            ; --- Screen and BASIC initialization (BIOS calls) ---
805A        call BIOS_INIT_TXT          ; 4066h — text mode screen init
805D        call BIOS_INIT_SCR          ; 40BEh — screen setup

8060        di
8061        xor  a
8062        ld   (0F790h),a             ; Clear system variable
8065        ld   (0F9B2h),a             ; Clear system variable
8068        ld   a,3Ah                  ; ':'
806A        ld   (0F54Eh),a             ; Set default drive separator ':'
806D        ld   hl,0F810h
8070        ld   (0F878h),hl            ; Set workspace pointer
8073        ld   (0F546h),hl            ; Set workspace pointer
8076        ld   bc,00C8h               ; 200
8079        add  hl,bc
807A        ld   (0F7A2h),hl            ; Set workspace end pointer
807D        ld   a,01h
807F        ld   (0F7EFh),a             ; Set flag
8082        ld   (0FE78h),a             ; Set flag

            ; --- More BIOS / BASIC initialization ---
8085        call BIOS_INIT_HOOK         ; 7CDAh — hook/vector init
8088        call BIOS_INIT_BAS2         ; 65B5h — BASIC init (part 2)
808B        ld   hl,(0FDE4h)            ; Retrieve code base pointer
808E        xor  a
808F        ld   (hl),a                 ; Clear first byte
8090        inc  hl
8091        ld   (0F54Ah),hl            ; Store pointer
8094        call BIOS_INIT_BAS1         ; 6557h — BASIC init (part 1)
8097        call HOOK_ENTRY             ; FAC0h — execute installed hook

            ; ===========================================================
            ; BOOT DECISION — detect HDD, check floppy, choose boot path
            ; ===========================================================

809A        di
809B        ld   sp,0C000h              ; New stack below boot area
809E        call FDC_FORCE_INT          ; Reset FDC

            ; --- Step 1: Detect SASI hard disk ---
80A1        ld   hl,2800h              ; Timeout = 10240 iterations
80A4        call SASI_DETECT           ; Try to select SASI target
80A7        ld   a,h
80A8        cp   00h
80AA        jp   nz,.hdd_present       ; HL != 0 → HDD responded (timeout remaining)
80AD        ld   a,l
80AE        cp   00h
80B0        jp   z,WARM_BOOT           ; HL == 0 → timeout, no SASI device → restart

            ; --- Step 2: HDD present — check for floppy disk ---
.hdd_present:
80B3        call FDC_RESTORE           ; Try to home floppy drive 0
80B6        cp   00h
80B8        jp   nz,.boot_floppy       ; A != 0 → floppy drive found → boot floppy

            ; --- Step 3: No floppy → boot from hard disk ---
80BB        jp   .boot_hdd

.boot_hdd:
80BE        call SASI_BOOT_LOADER      ; SASI Read 2 sectors from LBA 0 → C100h
80C1        jp   BOOT_ENTRY            ; Jump to loaded code at C100h


; =============================================================================
; FDC_RESTORE — Try to restore floppy drive to track 0
; =============================================================================
; Sends the WD1793 Restore command and checks if the drive responds.
; Retries up to 10 times with a long timeout per attempt.
;
; Returns:  A = 1 if floppy drive found (track 0 reached)
;           A = 0 if no floppy drive or error
; =============================================================================

FDC_RESTORE:
80C4        push bc
80C5        push de
80C6        ld   e,0Ah                 ; 10 retries

.restore_retry:
80C8        call FDC_SELECT_DRV0       ; Select drive 0, side 1, motor on
80CB        call FDC_WAIT_READY        ; Wait for FDC not busy
80CE        ld   a,01h
80D0        out  (FDC_DENSITY),a       ; Set density (single density)
80D2        call FDC_WAIT_READY
80D5        ld   a,07h                 ; Restore command (step rate = 3)
80D7        out  (FDC_CMD_STATUS),a    ; Issue Restore

            ; --- Timeout loop waiting for INTRQ ---
80D9        ld   d,04h                 ; Outer loop counter
.restore_outer:
80DB        ld   c,0FFh
.restore_mid:
80DD        ld   b,0FFh
.restore_inner:
80DF        nop
80E0        dec  b
80E1        jp   nz,.restore_inner     ; Inner delay: 255 NOPs
80E4        dec  c
80E5        jp   nz,.restore_mid       ; Mid loop: 255 × 255
80E8        dec  d
80E9        jp   nz,.restore_outer     ; Outer loop: 4 × 255 × 255

            ; --- Check INTRQ (command complete?) ---
80EC        in   a,(FDC_CTRL)          ; Read INTRQ/DRQ status
80EE        and  80h                   ; Test bit 7 = INTRQ
80F0        cp   80h
80F2        jp   z,.restore_check      ; INTRQ set → command completed

            ; --- No INTRQ — drive not responding ---
80F5        call FDC_FORCE_INT         ; Abort
80F8        jp   .restore_fail

.restore_check:
80FB        call FDC_WAIT_READY
80FE        ld   a,00h
8100        out  (FDC_TRACK),a         ; Set track register to 0
8102        call FDC_WAIT_READY
8105        in   a,(FDC_CMD_STATUS)    ; Read FDC status
8107        and  14h                   ; Bits 4,2: Seek Error + Track 0
8109        cp   04h                   ; Only Track 0 set (no errors)?
810B        jp   z,.restore_ok

            ; --- Track 0 not reached — retry ---
810E        dec  e
810F        jp   nz,.restore_retry

.restore_fail:
8112        ld   a,00h                 ; A = 0 → no floppy
8114        jp   .restore_ret

.restore_ok:
8117        ld   a,01h                 ; A = 1 → floppy found
8119        jp   .restore_ret

.restore_ret:
811C        pop  de
811D        pop  bc
811E        ret


; =============================================================================
; FDC_SELECT_DRV1 — Select floppy drive 1 (not called from ROM)
; =============================================================================
; Dead code: this subroutine exists in the ROM but is never referenced.
; Possibly used via external hook or was part of drive B support.
; Writes 0Eh to FDC_CTRL: drive 1 + side 1 + motor on.
; =============================================================================

FDC_SELECT_DRV1:
811F        push af
8120        ld   a,0Eh                 ; Bit 1=Drive 1, Bit 2=Side 1, Bit 3=Motor
8122        out  (FDC_CTRL),a
8124        pop  af
8125        ret


; =============================================================================
; BOOT_FLOPPY — Boot from floppy disk
; =============================================================================

.boot_floppy:
8126        call FDC_READ_BOOT         ; Read boot sector from floppy → C100h
8129        jp   BOOT_ENTRY            ; Jump to loaded code at C100h


; =============================================================================
; FDC_FORCE_INT — Send Force Interrupt command to FDC
; =============================================================================
; Issues WD1793 Force Interrupt (D0h) to abort any pending operation.
; Preserves AF and BC.
; =============================================================================

FDC_FORCE_INT:
812C        push af
812D        push bc
812E        ld   a,0D0h               ; Force Interrupt command
8130        out  (FDC_CMD_STATUS),a
8132        ld   b,0Ch                 ; Short delay for command to take effect
.fdc_fi_delay:
8134        nop
8135        dec  b
8136        jp   nz,.fdc_fi_delay
8139        pop  bc
813A        pop  af
813B        ret


; =============================================================================
; FDC_READ_BOOT — Read boot sector from floppy to C100h
; =============================================================================
; Reads track 0, sector 1 from floppy drive 0 into memory at C100h.
; Uses INI (port 33h) to transfer data byte-by-byte.
; Retries until successful.
; =============================================================================

FDC_READ_BOOT:
813C        push af
813D        push bc
813E        push hl
813F        call FDC_HOME              ; Home drive first (restore + verify)

.fdc_read_retry:
8142        call FDC_SELECT_DRV0       ; Select drive 0
8145        call FDC_WAIT_READY
8148        ld   a,01h
814A        out  (FDC_DENSITY),a       ; Set density
814C        call FDC_WAIT_READY
814F        ld   a,01h
8151        out  (FDC_SECTOR),a        ; Sector 1
8153        call FDC_WAIT_READY
8156        ld   a,80h                 ; Read Sector command
8158        out  (FDC_CMD_STATUS),a

815A        ld   c,FDC_DATA            ; C = port 33h for INI
815C        ld   hl,BOOT_ENTRY         ; Destination: C100h

.fdc_read_poll:
815F        in   a,(FDC_CTRL)          ; Read INTRQ/DRQ from port 34h
8161        add  a,a                   ; Bit 7 → Carry (INTRQ)
8162        jp   c,.fdc_read_done      ; INTRQ set → command complete
8165        jp   p,.fdc_read_poll      ; Bit 6 was 0 → DRQ not set, keep waiting
8168        ini                        ; DRQ set → read byte: (port 33h) → (HL++)
816A        jp   .fdc_read_poll

.fdc_read_done:
816D        call FDC_WAIT_READY
8170        in   a,(FDC_CMD_STATUS)    ; Read FDC status
8172        and  1Ch                   ; Bits 4,3,2: Record Not Found, CRC, Lost Data
8174        jp   nz,.fdc_read_retry    ; Any error → retry

8177        pop  hl
8178        pop  bc
8179        pop  af
817A        ret


; =============================================================================
; FDC_SELECT_DRV0 — Select floppy drive 0
; =============================================================================
; Writes 0Dh to FDC_CTRL: drive 0 + side 1 + motor on.
; =============================================================================

FDC_SELECT_DRV0:
817B        push af
817C        ld   a,0Dh                 ; Bit 0=Drive 0, Bit 2=Side 1, Bit 3=Motor
817E        out  (FDC_CTRL),a
8180        pop  af
8181        ret


; =============================================================================
; FDC_HOME — Restore drive to track 0 with retry (blocking)
; =============================================================================
; Keeps retrying the Restore command until track 0 is confirmed.
; =============================================================================

FDC_HOME:
8182        push af

.home_retry:
8183        call FDC_SELECT_DRV0
8186        call FDC_WAIT_READY
8189        ld   a,01h
818B        out  (FDC_DENSITY),a       ; Set density
818D        call FDC_WAIT_READY
8190        ld   a,07h                 ; Restore command (step rate = 3)
8192        out  (FDC_CMD_STATUS),a
8194        call FDC_WAIT_INTRQ        ; Wait for INTRQ (command complete)
8197        call FDC_WAIT_READY
819A        ld   a,00h
819C        out  (FDC_TRACK),a         ; Set track register to 0
819E        call FDC_WAIT_READY
81A1        in   a,(FDC_CMD_STATUS)    ; Read FDC status
81A3        and  14h                   ; Seek Error + Track 0
81A5        cp   04h                   ; Only Track 0, no error?
81A7        jp   nz,.home_retry        ; Retry if not at track 0

81AA        pop  af
81AB        ret


; =============================================================================
; FDC_WAIT_INTRQ — Wait for FDC INTRQ (command complete)
; =============================================================================
; Polls port 34h bit 7 until INTRQ is asserted.
; Blocks indefinitely.
; =============================================================================

FDC_WAIT_INTRQ:
81AC        push af
.wait_intrq_loop:
81AD        in   a,(FDC_CTRL)          ; Read INTRQ/DRQ
81AF        and  80h                   ; Test bit 7 = INTRQ
81B1        cp   80h
81B3        jp   nz,.wait_intrq_loop   ; Wait until INTRQ set
81B6        pop  af
81B7        ret


; =============================================================================
; FDC_WAIT_READY — Wait for FDC not busy
; =============================================================================
; Polls port 30h bit 0 until the FDC is no longer busy.
; =============================================================================

FDC_WAIT_READY:
81B8        push af
.wait_ready_loop:
81B9        in   a,(FDC_CMD_STATUS)    ; Read FDC status
81BB        and  01h                   ; Bit 0 = Busy
81BD        cp   00h
81BF        jp   nz,.wait_ready_loop   ; Wait until not busy
81C2        pop  af
81C3        ret


; =============================================================================
; SASI_BOOT_LOADER — Read boot sectors from HDD via SASI
; =============================================================================
; Resets SASI bus, then reads 2 sectors (512 bytes) from LBA 0
; into C100h.  This loads the hard disk boot sector / IPL.
; =============================================================================

SASI_BOOT_LOADER:
81C4        call SASI_RESET            ; Reset SASI bus
81C7        call SASI_READ_BOOT        ; Full SASI Read to C100h
81CA        ret


; =============================================================================
; SASI_DETECT — Detect SASI target and send Test Unit Ready
; =============================================================================
; Attempts to detect a SASI device by resetting the bus, checking for
; BSY, then performing target selection and Test Unit Ready.
;
; Input:   HL = timeout counter (decremented each iteration)
; Output:  HL = remaining count (0 = timeout, no device found)
; =============================================================================

SASI_DETECT:
81CB        push af

            ; --- Phase 1: Bus reset and check for target presence ---
.detect_poll_bsy:
81CC        call SASI_RESET            ; Reset SASI bus
81CF        in   a,(SASI_STATUS)       ; Read SASI status
81D1        and  20h                   ; Test bit 5 (BSY / presence indicator)
81D3        cp   20h
81D5        jp   z,.detect_select      ; Bit 5 set → target present, try selection
81D8        dec  hl                    ; Decrement timeout
81D9        ld   a,h
81DA        cp   00h
81DC        jp   nz,.detect_poll_bsy
81DF        ld   a,l
81E0        cp   00h
81E2        jp   nz,.detect_poll_bsy
81E5        jp   .detect_done          ; Timeout → exit (HL = 0)

            ; --- Phase 2: Target selection (assert SEL with device ID) ---
.detect_select:
81E8        call SASI_RESET
81EB        ld   a,01h                 ; Target ID = 1 (bit 0 = SCSI/SASI ID 0)
81ED        out  (SASI_DATA_OUT),a     ; Put device ID on data bus
81EF        out  (SASI_SEL),a          ; Assert SEL signal
81F1        nop
81F2        nop
81F3        in   a,(SASI_STATUS)       ; Read status
81F5        and  02h                   ; Test bit 1 (REQ — target responds)
81F7        cp   02h
81F9        jp   z,.detect_tst_rdy     ; Target responding → send Test Unit Ready
81FC        dec  hl
81FD        ld   a,h
81FE        cp   00h
8200        jp   nz,.detect_select     ; Keep trying selection
8203        ld   a,l
8204        cp   00h
8206        jp   nz,.detect_select
8209        jp   .detect_done          ; Timeout

            ; --- Phase 3: Test Unit Ready ---
.detect_tst_rdy:
820C        call SASI_RESET
820F .detect_tur_loop:
820F        call SASI_TEST_READY       ; Send Test Unit Ready command
8212        and  02h                   ; Check status byte bit 1
8214        cp   02h                   ; (check error indicator)
8216        jp   nz,.detect_done       ; Not ready → exit
8219        dec  hl
821A        ld   a,h
821B        cp   00h
821D        jp   nz,.detect_tur_loop   ; Keep retrying
8220        ld   a,l
8221        cp   00h
8223        jp   nz,.detect_tur_loop

.detect_done:
8226        call SASI_RESET
8229        pop  af
822A        ret


; =============================================================================
; SASI_TEST_READY — Send Test Unit Ready command (opcode 00h)
; =============================================================================
; SASI CDB: 00h 00h 00h 00h 00h 00h  (6 bytes, all zeros)
;
; Returns:  A = status byte from target
; =============================================================================

SASI_TEST_READY:
822B        push bc
822C        call SASI_SELECT_TARGET    ; Wait for bus free + select target

822F        ld   b,06h                 ; 6 command bytes
.tur_cmd_loop:
8231        ld   a,00h                 ; All bytes are 00h
8233        call SASI_SEND_CMD_BYTE
8236        dec  b
8237        jp   nz,.tur_cmd_loop

            ; --- Wait for Status phase, read status ---
.tur_wait_status:
823A        in   a,(SASI_STATUS)
823C        and  1Fh
823E        cp   1Bh                   ; Status phase? (BSY+REQ+I/O+C/D)
8240        jp   nz,.tur_wait_status
8243        in   a,(SASI_DATA_IN)      ; Read status byte

            ; --- Consume Message In phase ---
8245        push af
8246        call SASI_READ_MSG_IN
8249        pop  af                    ; Restore status byte
824A        pop  bc
824B        ret


; =============================================================================
; SASI_READ_BOOT — Full SASI Read(6): 2 sectors from LBA 0 → C100h
; =============================================================================
; Executes SASI Read(6) command: 08h 00h 00h 00h 02h 47h
;   Opcode:           08h (Read)
;   LBA:              000000h (block 0)
;   Transfer length:  02h (2 blocks × 256 bytes = 512 bytes)
;   Control byte:     47h (vendor-specific: retries, ECC, step option)
;
; Data is transferred to C100h using INIR (256 bytes at a time).
; =============================================================================

SASI_READ_BOOT:
824C        call SASI_SELECT_TARGET    ; Wait bus free + select target
824F        call SASI_SEND_READ_CMD    ; Send Read(6) CDB
8252        call SASI_DATA_IN_PHASE    ; Receive data → C100h
8255        call SASI_READ_STATUS      ; Read Status phase
8258        call SASI_READ_MSG_IN      ; Read Message In phase
825B        ret


; =============================================================================
; SASI_SEND_READ_CMD — Send SASI Read(6) CDB
; =============================================================================
; Sends 6-byte CDB: 08h 00h 00h 00h 02h 47h
; =============================================================================

SASI_SEND_READ_CMD:
825C        push af
825D        ld   a,08h                 ; Read(6) opcode
825F        call SASI_SEND_CMD_BYTE
8262        ld   a,00h                 ; LUN 0, LBA MSB = 0
8264        call SASI_SEND_CMD_BYTE
8267        ld   a,00h                 ; LBA middle = 0
8269        call SASI_SEND_CMD_BYTE
826C        ld   a,00h                 ; LBA LSB = 0
826E        call SASI_SEND_CMD_BYTE
8271        ld   a,02h                 ; Transfer length = 2 sectors
8273        call SASI_SEND_CMD_BYTE
8276        ld   a,47h                 ; Control byte (vendor-specific)
8278        call SASI_SEND_CMD_BYTE
827B        pop  af
827C        ret


; =============================================================================
; SASI_DATA_IN_PHASE — Receive data blocks from SASI target
; =============================================================================
; Reads 256-byte blocks using INIR (port 42h) during Data In phases.
; Continues until Status phase (1Bh) is detected.
; Data is stored starting at C100h.
; =============================================================================

SASI_DATA_IN_PHASE:
827D        push hl
827E        push bc
827F        push af
8280        ld   hl,BOOT_ENTRY         ; Destination: C100h

.data_in_poll:
8283        in   a,(SASI_STATUS)
8285        and  1Fh
8287        cp   1Bh                   ; Status phase?
8289        jp   z,.data_in_done       ; Yes → all data received
828C        cp   13h                   ; Data In phase?
828E        jp   nz,.data_in_poll      ; No → keep polling
8291        ld   c,SASI_DATA_IN        ; Port 42h
8293        ld   b,00h                 ; 256 bytes (B=0 wraps in INIR)
8295        inir                       ; Read 256 bytes: (port 42h) → (HL++)
8297        jp   .data_in_poll         ; Check for more data or Status

.data_in_done:
829A        pop  af
829B        pop  bc
829C        pop  hl
829D        ret


; =============================================================================
; SASI_SEND_CMD_BYTE — Send one command byte during Command phase
; =============================================================================
; Waits for Command phase (0Bh), then writes byte to SASI data bus.
;
; Input:   A = byte to send
; =============================================================================

SASI_SEND_CMD_BYTE:
829E        push af
.send_cmd_poll:
829F        in   a,(SASI_STATUS)
82A1        and  1Fh
82A3        cp   0Bh                   ; Command phase? (BSY+REQ+C/D)
82A5        jp   nz,.send_cmd_poll     ; Wait for Command phase
82A8        pop  af
82A9        out  (SASI_DATA_OUT),a     ; Write command byte (auto-ACK)
82AB        ret


; =============================================================================
; SASI_READ_STATUS — Wait for and read Status phase byte
; =============================================================================
; Waits for Status phase (1Bh), reads status byte from data bus.
;
; Returns:  A = status byte (discarded by caller)
; =============================================================================

SASI_READ_STATUS:
82AC        push af
.read_status_poll:
82AD        in   a,(SASI_STATUS)
82AF        and  1Fh
82B1        cp   1Bh                   ; Status phase?
82B3        jp   nz,.read_status_poll
82B6        in   a,(SASI_DATA_IN)      ; Read status byte (auto-ACK)
82B8        pop  af
82B9        ret


; =============================================================================
; SASI_READ_MSG_IN — Wait for and read Message In phase byte
; =============================================================================
; Waits for Message In phase (1Fh), reads message byte from data bus.
;
; Returns:  A = message byte (discarded by caller)
; =============================================================================

SASI_READ_MSG_IN:
82BA        push af
.read_msg_poll:
82BB        in   a,(SASI_STATUS)
82BD        and  1Fh
82BF        cp   1Fh                   ; Message In phase?
82C1        jp   nz,.read_msg_poll
82C4        in   a,(SASI_DATA_IN)      ; Read message byte (auto-ACK)
82C6        pop  af
82C7        ret


; =============================================================================
; SASI_SELECT_TARGET — Wait for Bus Free, then select target
; =============================================================================
; Waits until SASI bus is free (status = 00h), then places device ID
; on the data bus and asserts SEL to select the target.
; =============================================================================

SASI_SELECT_TARGET:
82C8        push af
.bus_free_poll:
82C9        in   a,(SASI_STATUS)
82CB        and  1Fh
82CD        cp   00h                   ; Bus Free?
82CF        jp   nz,.bus_free_poll     ; Wait for bus free
82D2        ld   a,01h                 ; Target ID bit 0 (SASI device 0)
82D4        out  (SASI_DATA_OUT),a
82D6        out  (SASI_SEL),a          ; Assert SEL
82D8        pop  af
82D9        ret


; =============================================================================
; SASI_RESET — Reset SASI bus with delay
; =============================================================================
; Pulses the SASI reset line and waits for the bus to settle.
; =============================================================================

SASI_RESET:
82DA        out  (SASI_RESET),a        ; Assert SASI bus reset
82DC        push bc
82DD        ld   b,3Ch                 ; Delay: 60 NOPs
.reset_delay:
82DF        nop
82E0        dec  b
82E1        jp   nz,.reset_delay
82E4        pop  bc
82E5        ret


; =============================================================================
; End of code — remainder of EPROM is 0FFh padding
; =============================================================================
82E6        defb 0FFh                  ; Padding

; ...
; (0xFF padding from ROM offset 02FAh to 0FEFh)
; ...

; =============================================================================
; ROM Signature at offset 0FF0h (last 16 bytes of EPROM)
; =============================================================================
; "NETROM 1.0 CWJ" — ROM identifier and author initials
; Final two bytes: 15h, E7h (possibly checksum or version tag)
; =============================================================================
