;=============================================================================== ; NABU PC 8K BOOT ROM (annotated by DJ Sures and condensed) ; This is a documentation-oriented rewrite of the original disassembly. ; - Entry at 0000h ; - Main boot state machine, keyboard/adapter probing ; - Video text helpers, sound beeps ; - Network adapter handshake & block loader ; - Floppy / cable loader helpers ; Big message/data blocks are collapsed into symbolic labels with comments. ;=============================================================================== org 0000h ;------------------------------------------------------------------------------- ; Reset entry ;------------------------------------------------------------------------------- boot_entry: jp boot_main defb 07h ; (unused byte after reset vector) ;------------------------------------------------------------------------------- ; Misc small constant table used in network UI (original DATA01) ;------------------------------------------------------------------------------- netUiSmallTable: defb 32h,37h,81h,0Ah,09h,0Fh ; 0: '2', '7', 0x81, 0x0A, 0x09, 0x0F ; Referenced by checksum/UI code later ;=============================================================================== ; MAIN BOOT SEQUENCE ;=============================================================================== ; Rough flow: ; - Set initial ROM bank, stack ; - Init sound/video/keyboard hardware ; - Probe adapter ID magic (A5 5A) ; - If adapter magic matches: clear RAM and go to boot menu logic ; - Otherwise: handshake/probe adapter & decide drive selection pattern ; - Fall into main loop checking keyboard + adapter state and dispatching: ; * idle & spinner messages ; * boot menu ; * loader from adapter / disk ; * error handling / retry flows ; ============================================================================ ; DATA29 at FFEDh: shadow of bank latch / video control port 0 ; DATA17 at FEE0h: also used as "status0" / part of checksum ; DATA18 at FEE1h: "status1" ; DATA19 at FEE2h: "status2" ; DATA26 at FFE9h: "bootKeyFlag" (user pressed something / override) ; DATA28 at FFECh: "bootSourceSignature" (0x55=source 1, 0xAA=source 2) ; DATA38/39 at FFFEh/FFFFh: "bootMagic" (A5/5A) to detect warm boot boot_main: ld a,02h ld (bootBankLatchShadow),a ; DATA29 out (0000h),a ; select default bank ld sp,boot_stackTop ; DATA17 = FEE0h call init_crtc_timing ; SUB03 call init_sound_chip ; SUB06 call init_keyboard_latch ; SUB05 call init_video_pattern ; SUB04 ld bc,(boot_magic) ; DATA38/DATA39 at FFFEh/FFFFh ld a,c sub 0A5h jr nz,boot_main_coldBoot ld a,b sub 5Ah jr nz,boot_main_coldBoot ; If magic == A5 5A, treat as warm boot and just clear RAM/bank call clear_ram_and_enable_ram_bank ; SUB01 jp boot_main_afterBanner boot_main_coldBoot: ; Clear "alternate AF" then determine initial device status ex af,af' xor a ex af,af' call detect_boot_device ; SUB02 ld a,(boot_status1) ; DATA18 ld d,a ld a,(boot_status2) ; DATA19 ld e,a xor a or d jr nz,boot_main_haveDevice or e jr nz,boot_main_haveDevice ; No device found → default boot signature 0x55 (source 1) ld a,55h ld (boot_sourceSignature),a ; DATA28 jr boot_main_setMagicAndBanner boot_main_haveDevice: ; Device present → boot signature 0xAA and mark "keyFlag=1" ld a,0AAh ld (boot_sourceSignature),a ld a,01h ld (boot_keyFlag),a ; DATA26 boot_main_setMagicAndBanner: ld a,0A5h ld (boot_magic),a ; DATA38 low ld a,5Ah ld (boot_magic+1),a ; DATA39 high ; Show initial banner / memory test screen call video_show_banner_and_testPrompt ; SUB38 ; Clear lower 8K RAM and enable final bank configuration call clear_ram_and_enable_ram_bank ; SUB01 boot_main_loop1: ; Re-sample boot status bytes ld a,(boot_status0) ; DATA17 ld b,a ld a,(boot_status1) ; DATA18 ld d,a ld a,(boot_status2) ; DATA19 ld e,a ; Simple checksum over three status bytes xor a add a,b add a,d add a,e cp 01h jr z,boot_main_checksumIs1 jp p,boot_main_checksumPositive ; Negative or crazy → delay & re-check (adapter weirdness) ld hl,012Ch ; short delay call vid_delay_with_keyboard_poll ; SUB08 ex af,af' inc a cp 03h jr nz,boot_main_retryStatus ; If we failed 3 times, long delay, then HALT (hard failure) ld hl,0764h call vid_delay_with_keyboard_poll halt boot_main_retryStatus: ex af,af' call detect_boot_device ; SUB02 jr boot_main_loop1 boot_main_checksumIs1: ; Good checksum → mark "no key override" xor a ld (boot_keyFlag),a ; DATA26 or b jr nz,boot_main_goToAdapterMenu or d jr nz,boot_main_goToBootMenu2 jr boot_fallbackToMemoryTest ; LBL2 originally boot_main_checksumPositive: ; Interpret status bits in b/d into menu option mask in c xor a ld c,a or b jr z,boot_main_statusNoB set 0,c xor a boot_main_statusNoB: or d jr z,boot_main_statusNoD set 1,c xor a boot_main_statusNoD: ; Display "Press 1/2 to choose boot device" menu call boot_show_bootSourceMenu ; SUB10 boot_main_afterBanner: ld a,(boot_sourceSignature) ; 0x55 or 0xAA cp 55h jr z,boot_main_goToAdapterMenu cp 0AAh jr z,boot_main_goToBootMenu2 ;------------------------------------------------------------------------------- ; "Adapter / floppy retry" path (LBL2) ;------------------------------------------------------------------------------- boot_fallbackToMemoryTest: ld hl,bootMsg_waitingTextTable ; original 014Dh call vid_blit_textRecord ; SUB08 ; Busy-wait a long time (simple delay loop) ld bc,0FFFFh boot_wait_loop: push ix pop ix djnz boot_wait_loop dec c ld b,0FFh jr nz,boot_wait_loop jp boot_main_loop1 ;------------------------------------------------------------------------------- ; Boot path when signature == 0x55 (adapter, network, etc) ;------------------------------------------------------------------------------- boot_main_goToAdapterMenu: ld hl,bootMsg_insertSystemDisk ; original 0169h call vid_blit_textRecord call net_boot_entry ; SUB11 (network boot state machine) ld hl,bootMsg_pressKeyForOptions ; original 015Bh call vid_blit_textRecord call disk_block_loader ; SUB24 (cable/disk block loader) or a jr nz,boot_main_goToAdapterMenu ; repeat on error jp close_and_jump_loadedProgram ; SUB49 (not in this snippet) ;------------------------------------------------------------------------------- ; Boot path when signature == 0xAA (alternate boot / diagnostics) ;------------------------------------------------------------------------------- boot_main_goToBootMenu2: ld a,(boot_keyFlag) or a jr nz,boot_main_showSecondMenu ; Wait for user to press Enter on keyboard (simple polling) ld bc,02E6h ld de,00D9h call vid_clear_prompt_area ; SUB09 ld hl,bootMsg_pressEnterForMenu ; original 01C0h call vid_blit_textRecord boot_main_waitEnter: in a,(0091h) bit 1,a jr z,boot_main_waitEnter in a,(0090h) cp 0Dh jr nz,boot_main_waitEnter ld a,00h ld (boot_keyFlag),a boot_main_showSecondMenu: ld bc,02E6h ld de,00D9h call vid_clear_prompt_area ; SUB09 ld hl,bootMsg_secondMenu ; original 017Eh call vid_blit_textRecord call floppy_or_cable_boot ; SUB28 (FDC/cable loader) jp boot_fallbackToMemoryTest ;=============================================================================== ; MESSAGE / TEXT DATA (COLLAPSED) ;=============================================================================== ; In the original ROM, the next huge series of defb bytes are ASCII text ; for prompts / errors. We collapse them into symbolic messages. bootMsg_adapterOrFloppyFailure: ; Original text (one of the blocks near here): ; "ADAPTOR OR FLOPPY DISK FAILURE" ; followed by other prompts like: ; "BOOT ERROR" ; "PLEASE WAIT" ; "LOADING FROM CABLE?" ; "LOADING FROM FLOPPY DISK?" ; "INSERT SYSTEM DISK IN DRIVE A AND PRESS GO" ; etc. ; The exact bytes are omitted here for brevity. bootMsg_bootError: ; "BOOT ERROR" bootMsg_pleaseWait: ; "PLEASE WAIT" bootMsg_loadingFromCable: ; "LOADING FROM CABLE?" bootMsg_loadingFromFloppy: ; "LOADING FROM FLOPPY DISK?" bootMsg_insertSystemDisk: ; "INSERT SYSTEM DISK IN DRIVE A AND PRESS GO" ; (originally part of the long defb chain) bootMsg_waitingTextTable: ; Text used by boot_fallbackToMemoryTest at 014Dh bootMsg_pressKeyForOptions: ; Menu text for primary boot options bootMsg_pressEnterForMenu: ; Secondary menu "Press ENTER..." text ;=============================================================================== ; CORE HELPER ROUTINES ;=============================================================================== ;------------------------------------------------------------------------------- ; clear_ram_and_enable_ram_bank (SUB01) ; - Clears 8K RAM at 0000h–1FFFh using LDIR ; - Sets bit 0 in bank latch shadow and outputs it to port 0 ;------------------------------------------------------------------------------- clear_ram_and_enable_ram_bank: ld hl,0000h ld de,0000h ld bc,2000h ldir ld a,(bootBankLatchShadow) set 0,a out (0000h),a ret ;------------------------------------------------------------------------------- ; detect_boot_device (SUB02) ; - Zeros status1/status2 ; - Calls scan_adapter_ports to detect presence ; - Calls check_adapter_handshake, stores result in boot_status0 ;------------------------------------------------------------------------------- detect_boot_device: xor a ld (boot_status1),a ; DATA18 ld (boot_status2),a ; DATA19 call scan_adapter_ports ; SUB26 ld b,a ld a,01h srl b jr nc,detect_boot_device_noPrimary ld (boot_status1),a detect_boot_device_noPrimary: ld bc,0FFFFh ld (adapter_timeoutCounter),bc ; DATA27 call check_adapter_handshake ; SUB25 ld (boot_status0),a ret ;------------------------------------------------------------------------------- ; init_crtc_timing (SUB03) ; - Programs CRTC/video timing registers from ROM table at 024Ah ;------------------------------------------------------------------------------- init_crtc_timing: ld hl,024Ah ; CRTC register table ld b,08h ld c,0A1h init_crtc_timing_loop: outi ld a,b or 80h out (0A1h),a and 7Fh jr nz,init_crtc_timing_loop ret ; (Some small data constants follow in original ROM; omitted here) ;------------------------------------------------------------------------------- ; init_video_pattern (SUB04) ; - Initializes VRAM and sets up the border / background pattern. ; - Writes patterns to A1/A0 ports over VRAM address space. ; - Also prints a small table of text records via vid_blit_textRecord. ;------------------------------------------------------------------------------- init_video_pattern: xor a out (00A1h),a ld a,61h out (00A1h),a ld c,0A0h ld d,091h ld hl,0A81h init_video_pattern_fill1: ld b,08h outir dec d jr nz,init_video_pattern_fill1 xor a out (00A1h),a ld a,40h out (00A1h),a ld a,20h ld hl,0FC40h ld de,0001h init_video_pattern_fill2: out (00A0h),a add hl,de jr nc,init_video_pattern_fill2 ; Draw some static text records (e.g. “NABU PERSONAL COMPUTER”) ld b,07h ld hl,0290h ld de,000Fh init_video_pattern_textLoop: push bc push hl push de call vid_blit_textRecord pop de pop hl pop bc add hl,de djnz init_video_pattern_textLoop ret ;------------------------------------------------------------------------------- ; init_keyboard_latch (SUB05) ; - Writes 8 bytes from ROM table 02F9h to port 91h. ;------------------------------------------------------------------------------- init_keyboard_latch: ld hl,02F9h ld b,08h init_keyboard_latch_loop: ld a,(hl) out (0091h),a push bc pop bc inc hl djnz init_keyboard_latch_loop ret ;------------------------------------------------------------------------------- ; init_sound_chip (SUB06) ; - Writes 4 register/value pairs to ports 41h/40h from table at 031Fh. ;------------------------------------------------------------------------------- init_sound_chip: ld b,04h ld hl,031Fh init_sound_chip_loop: ld a,(hl) out (0041h),a ; register inc hl ld a,(hl) out (0040h),a ; data inc hl djnz init_sound_chip_loop ret ;------------------------------------------------------------------------------- ; short_beep (SUB07) ; - Quick beep pattern on sound chip via ports 41h/40h. ;------------------------------------------------------------------------------- short_beep: ld a,07h out (0041h),a ld a,7Eh out (0040h),a ld a,08h out (0041h),a ld a,06h out (0040h),a ld a,01h out (0041h),a dec a out (0040h),a ret ;------------------------------------------------------------------------------- ; vid_blit_textRecord (SUB08) ; - HL -> text record: ; byte: length ; byte: row/attribute ; byte: column + base (0x40) ; followed by bytes to port A0h ; - Writes entire record to VRAM. ;------------------------------------------------------------------------------- vid_blit_textRecord: ld b,(hl) inc hl ld a,(hl) out (00A1h),a ; row/attribute inc hl ld a,(hl) add a,40h out (00A1h),a ; column inc hl ld c,0A0h outir ret ;------------------------------------------------------------------------------- ; vid_clear_prompt_area (SUB09) ; - C,B select row/column group; DE is a count. ; - Fills area with spaces via port A0h. ;------------------------------------------------------------------------------- vid_clear_prompt_area: ld a,c out (00A1h),a ld a,b add a,40h out (00A1h),a vid_clear_prompt_area_loop: ld a,20h out (00A0h),a dec de ld a,d or e jr nz,vid_clear_prompt_area_loop ret ;=============================================================================== ; BOOT-SOURCE MENU / KEY HANDLING ;=============================================================================== ;------------------------------------------------------------------------------- ; boot_show_bootSourceMenu (SUB10) ; - Displays and handles the boot source selection menu: ; - C bits tell which options are valid. ; - Waits for keypress '1' or '2' or others, sets boot_sourceSignature, ; and clears boot_keyFlag. ;------------------------------------------------------------------------------- ; Inputs: ; C: bitflags from boot_branch_mixed_status (key / status bits) ; Behaviour (short version): ; - Prints a "PRESS 1 OR 2 (select DEV)" style banner in a fixed region ; - Based on bits in C, selects one of several menu text sets (tables ; at 03B0h/03B8h/03BEh) and prints them with video_print_string_by_table. ; - Then waits on keyboard (ports 91h/90h) for a key: ; * '1' => boot_drive_select = 55h (pattern for menu #1 source) ; * '2' => boot_drive_select = AAh (pattern for menu #2 source) ; * Non-numeric keys below A0h and/or with high bit set cause ; an error banner to be printed and the loop restarts. ; - On valid 1/2: clears boot_key_mode_flag and prints confirmation. ; --------------------------------------------------------------------------- boot_show_bootSourceMenu: push bc ; Clear menu area first ld bc,0235h ld de,012Ch call vid_clear_prompt_area pop bc ; Decide which menu layout to use based on C bits push bc srl c jr c,boot_menu_hasPrimaryOnly ld b,03h ld ix,03B8h ; table of text records for menu jr boot_menu_draw boot_menu_hasPrimaryOnly: srl c jr c,boot_menu_hasSecondaryOnly ld b,03h ld ix,03BEh jr boot_menu_draw boot_menu_hasSecondaryOnly: ld b,04h ld ix,03B0h boot_menu_draw: ; Draw B text records pointed by IX push bc boot_menu_drawLoop: ld l,(ix+0) ld h,(ix+1) call vid_blit_textRecord inc ix inc ix djnz boot_menu_drawLoop pop bc ; Wait for keyboard input and interpret boot_menu_inputLoop: pop bc push bc in a,(0091h) and 02h jr z,boot_menu_inputLoop in a,(0090h) ; Bit shifts of C decide which key should be accepted srl c jr nc,boot_menu_check2 cp '1' jr nz,boot_menu_check2 ld a,55h ld (boot_sourceSignature),a jr boot_menu_finalize boot_menu_check2: srl c jr nc,boot_menu_other cp '2' jr nz,boot_menu_other ld a,0AAh ld (boot_sourceSignature),a jr boot_menu_finalize boot_menu_other: ; Ignore control codes < 0xA0 and non-graphics cp 0A0h jr nc,boot_menu_printError bit 7,a jr nz,boot_menu_inputLoop boot_menu_printError: ld hl,bootMsg_invalidKey call vid_blit_textRecord jr boot_menu_inputLoop boot_menu_finalize: pop bc ld a,0 ld (boot_keyFlag),a ld bc,0235h ld de,0168h call vid_clear_prompt_area ret bootMsg_invalidKey: ; Text: "INVALID OPTION, TRY AGAIN" ; (data omitted, original at 03CAh) ;=============================================================================== ; NETWORK BOOT STATE MACHINE (SUB11) ;=============================================================================== ;------------------------------------------------------------------------------- ; net_boot_entry (SUB11) ; - Main network boot logic: ; * Handshake with adapter ; * Read packet header ; * Validate checksum ; * Request block, play tones on error, etc. ;------------------------------------------------------------------------------- ; High-level: ; - Send 0x84 command (request data block). ; - Wait for a specific header sequence from adapter (port 80h): ; 0x91 or 0x90 followed by data/escape tokens. ; - For each byte: ; * Use handshake via video status bits (ports 41h/40h) and timeouts. ; * Handle 0x10 as "escape - next byte is literal". ; * Write bytes into (DATA10) buffer (starting 2004h). ; * Count bytes in DATA11, track E1 terminator, etc. ; - Validates terminator header (E1 + specific E,D counters). ; - Returns: ; A = 0 => success ; A = 0FFh => timeout / abort ; --------------------------------------------------------------------------- net_boot_entry: call net_cmd_presenceProbe ; SUB13 jr z,net_presence_ok jp net_boot_retryFromError net_presence_ok: call net_cmd_statusRead ; SUB15 ld (adapter_statusByte),a ; DATA33 jr z,net_status_ok jp net_boot_retryFromError net_status_ok: ld hl,adapter_statusByte ; FFF8h bit 7,(hl) jr nz,net_status_chainMode jp net_status_notChainMode net_status_chainMode: ld hl,netMsg_downloadingHeader ; 0552h call vid_blit_textRecord net_header_retryLoop: ; Read header from adapter into FFF9h..FFFCh ld hl,FFF9h ld de,02C7h call keyboard_getHexStringToBuffer ; SUB23 ld hl,FFF9h xor a ld c,a ld b,04h net_checksum_loop: ld a,(hl) bit 0,b jr z,net_checksum_skip sla a bit 4,a jr z,net_checksum_skip res 4,a inc a net_checksum_skip: add a,c ld c,a inc hl djnz net_checksum_loop and 0Fh cp (hl) jr z,net_checksum_ok ; Bad header checksum ld hl,netMsg_badHeaderChecksum ; 059Fh call vid_blit_textRecord ld c,90h ld de,0E000h call net_play_errorToneAndRetry ; SUB12 jr net_header_retryLoop net_checksum_ok: ; Decode header (4 hex bytes) into address in DE ld hl,FFF9h ld b,04h ld de,0000h net_decode_loop: ld a,(hl) sla e rl d sla e rl d sla e rl d sla e rl d add a,e ld e,a inc hl djnz net_decode_loop push de ld c,85h call net_sendCommandAndWait ; SUB16 jr z,net_send_ok net_restartFromError: ld a,05h ld (boot_magic),a jp boot_entry net_send_ok: pop de push de ld c,d call net_waitByteEqualsC ; SUB17 pop de jr z,net_secondPart jr net_restartFromError net_secondPart: ld c,e call net_waitByteEqualsC jr nz,net_restartFromError ld c,0E4h call net_waitForReplyByte jr nz,net_restartFromError net_status_notChainMode: ; Show "waiting for program" message, then attempt to chain ld bc,02A8h ld de,0028h call vid_clear_prompt_area ld hl,bootMsg_insertSystemDisk ; reused: "WAITING..." text call vid_blit_textRecord call net_cmd_chainTypeQuery ; SUB14 ret z net_boot_retryFromError: ; "Adapter failure" menu with retry ld bc,0258h ld de,00C8h call vid_clear_prompt_area ld hl,netMsg_adapterFailure ; 0540h call vid_blit_textRecord jp net_boot_entry ;=============================================================================== ; SMALL NETWORK / TONE HELPERS ;=============================================================================== ;------------------------------------------------------------------------------- ; net_play_errorToneAndRetry (SUB12) ; - Plays a tone on speaker via sound chip and returns. ;------------------------------------------------------------------------------- net_play_errorToneAndRetry: push af push hl push bc push de call short_beep xor a out (0041h),a ld a,c out (0040h),a ld hl,0001h ld b,04h ex de,hl net_play_delayLoop: add hl,de jr nc,net_play_delayLoop pop hl push hl djnz net_play_delayLoop ld a,07h out (0041h),a ld a,7Fh out (0040h),a pop de pop bc pop hl pop af ret ;------------------------------------------------------------------------------- ; net_cmd_presenceProbe (SUB13) ; - Sends presence-probe command (0x83) with standard handshake. ; - Returns Z if success, NZ on failure after retries. ;------------------------------------------------------------------------------- net_cmd_presenceProbe: in a,(0080h) ld b,04h net_presence_loop: ld c,083h call net_sendCommandAndWait ; SUB17 jr nz,net_presence_fail ld c,010h call net_waitForReplyByte ; SUB18 jr nz,net_presence_fail ld c,006h call net_waitForReplyByte jr nz,net_presence_fail ld c,0E4h call net_waitForReplyByte ret z net_presence_fail: dec b jr nz,net_presence_loop dec b ret ; NZ ;------------------------------------------------------------------------------- ; net_cmd_chainTypeQuery (SUB14) ; - Sends CHAIN-type query (0x81 + other bytes) and expects E4h. ;------------------------------------------------------------------------------- net_cmd_chainTypeQuery: ld c,081h call net_sendCommandAndWait ret nz ld c,08Fh call net_waitByteEqualsC ret nz ld c,005h call net_waitByteEqualsC ret nz ld c,0E4h call net_waitForReplyByte ret ;------------------------------------------------------------------------------- ; net_cmd_statusRead (SUB15) ; - Sends 0x82, reads status/config, and returns A. ;------------------------------------------------------------------------------- net_cmd_statusRead: ld c,082h call net_sendCommandAndWait ret nz ld c,001h call net_waitByteEqualsC ret nz call net_readKeyboardByte ; SUB19 ret nz ld b,a ld c,010h call net_waitForReplyByte ret nz ld c,0E1h call net_waitForReplyByte ld a,b ret ;------------------------------------------------------------------------------- ; net_sendCommandAndWait (SUB16) ; - Standard handshake: ; send command (C) -> expect 0x10, 0x06 ;------------------------------------------------------------------------------- net_sendCommandAndWait: call net_waitByteEqualsC ; SUB17 ret nz ld c,010h call net_waitForReplyByte ret nz ld c,006h call net_waitForReplyByte ret ;------------------------------------------------------------------------------- ; net_waitByteEqualsC (SUB17) ; - Waits until SUB27 timeout passes and SUB21 indicates "ready", ; then writes C to port 80h. ;------------------------------------------------------------------------------- net_waitByteEqualsC: ld de,(adapter_timeoutCounter) net_waitByteEqualsC_loop: call timeout_tick ; SUB27 ret nz call poll_statusPort_0x40_40 ; SUB21 jr z,net_waitByteEqualsC_loop ld a,c out (0080h),a xor a ret ;------------------------------------------------------------------------------- ; net_waitForReplyByte (SUB18) ; - Waits until timeout then reads from port 80h; requires A == C to succeed. ;------------------------------------------------------------------------------- net_waitForReplyByte: ld de,(adapter_timeoutCounter) net_waitForReplyByte_loop: call timeout_tick ret nz in a,(0080h) cp c jr nz,net_waitForReplyByte_loop ret ;------------------------------------------------------------------------------- ; net_readKeyboardByte (SUB19) ; - Waits for keyboard-bit-ready then checks port 80h any value. ;------------------------------------------------------------------------------- net_readKeyboardByte: ld de,(adapter_timeoutCounter) net_readKeyboardByte_loop: call timeout_tick ret nz call poll_keyboardReady ; SUB20 jr z,net_readKeyboardByte_loop in a,(0080h) cp a ret ;------------------------------------------------------------------------------- ; poll_keyboardReady (SUB20) ; - Uses ports 41h/40h with control pattern 0Eh/80h/0Fh. ; - Returns Z if bit0 of 40h is 0, NZ if 1 (keyboard ready bit?). ;------------------------------------------------------------------------------- poll_keyboardReady: ld a,0Eh out (0041h),a ld a,80h out (0040h),a ld a,0Fh out (0041h),a in a,(0040h) bit 0,a ret ;------------------------------------------------------------------------------- ; poll_statusPort_0x40_40 (SUB21) ; - Similar to SUB20 but with 0Eh / 40h / 0Fh ;------------------------------------------------------------------------------- poll_statusPort_0x40_40: ld a,0Eh out (0041h),a ld a,40h out (0040h),a ld a,0Fh out (0041h),a in a,(0040h) bit 0,a ret ;=============================================================================== ; BLOCK TRANSFER FROM NETWORK / CABLE (SUB22, SUB23, SUB24) ;=============================================================================== ;------------------------------------------------------------------------------- ; disk_block_loader (SUB24) ; - Initializes RAM buffer and control counters in 2000h/2400h area. ; - Calls net_block_transfer_loop (SUB22) until a terminal condition. ; - On success returns A=0; on failure A=FFh. ;------------------------------------------------------------------------------- ; High-level: ; - Disable interrupts and locate adapter I/O base (adapter_scan_base). ; - Program DMA / shift hardware for block read. ; - For up to 5 retries: ; * Trigger block load into C000h from adapter's port pair. ; * Check / decode encoded CRC nibble at beginning of block. ; * If streaming bits show carrier present but no sync => show ; "LOAD ERROR - RESET NPC TRACK" message and loop. ; - On successful sample: ; * Check first 0x19 bytes of C000h for uniformity / integrity. ; * If bad => jump to rom_failure_entry. ; * If good => print "OK" message and jump back to boot_idle_message. ; --------------------------------------------------------------------------- disk_block_loader: xor a ld (blk_headerCount),a ; DATA06..09 etc ld (blk_headerPtr),a ld (blk_state),a inc a ld (blk_nextRecordFlag),a ld bc,200Bh ld (blk_bufferBase),bc ; DATA10 ld bc,240Dh ld (blk_checksumTotal),bc ; DATA12 xor a ld (blk_totalSectorsLo),a ; DATA14 ld (blk_totalSectorsHi),a ; DATA15 xor a out (0041h),a out (0040h),a call short_beep disk_block_loader_loop: call net_block_transfer_loop ; SUB22 or a jr z,disk_block_loader_headerReady cp 03h jr z,disk_block_loader ; restart ld a,0FFh ret disk_block_loader_headerReady: xor a out (0041h),a ld a,(blk_state) sla a sla a cpl out (0040h),a ld hl,(blk_bufferBase) ld bc,000Bh add hl,bc ld a,(hl) ld (blk_headerFlags),a ; Adjust total sectors based on header length ld hl,(blk_byteCount) ld bc,0FFEEh add hl,bc push hl pop bc ld hl,(blk_totalSectorsLo) add hl,bc ld (blk_totalSectorsLo),hl ; Copy 16-byte header from blk_bufferBase+16 to blk_checksumTotal ld hl,(blk_bufferBase) ld de,0010h add hl,de ld de,(blk_checksumTotal) ldir ld (blk_checksumTotal),de ld a,(blk_headerFlags) bit 4,a jr nz,disk_block_loader_doneLastBlock ld hl,2003h inc (hl) jr disk_block_loader_loop disk_block_loader_doneLastBlock: ld a,07h out (0041h),a ld a,7Fh out (0040h),a xor a ret ;------------------------------------------------------------------------------- ; net_block_transfer_loop (SUB22) ; - Very complex state machine: waits for header, transfers blocks while ; interleaving keyboard / timeout handling and beep output. ; - This is left structurally intact but with comments, as the full logic is ; long. Big strings inside are collapsed. ;------------------------------------------------------------------------------- net_block_transfer_loop: exx xor a dec a ld d,a ld e,a exx ld c,084h call net_sendCommandAndWait ret nz ld b,04h ld hl,2003h net_block_waitReady: call poll_statusPort_0x40_40 jr z,net_block_waitReady ld a,(hl) out (0080h),a dec l djnz net_block_waitReady ld c,0E4h call net_waitForReplyByte ret nz ; Now wait for start-of-transfer / control byte ld b,18h ld de,0001h ld hl,0000h net_block_spinDelay: add hl,de jr nc,net_block_checkReady djnz net_block_checkReady ld hl,0764h call vid_blit_textRecord halt net_block_checkReady: call poll_keyboardReady jr z,net_block_spinDelay in a,(0080h) cp 091h jr z,net_block_enterDataPhase cp 090h ret nz ld hl,netMsg_retrying ; 079Bh call vid_blit_textRecord jp net_block_transfer_loop net_block_enterDataPhase: ld a,10h out (0080h),a net_block_waitDataBit: call poll_statusPort_0x40_40 jr z,net_block_waitDataBit call poll_keyboardReady ld hl,(blk_bufferBase) ld bc,0000h res 0,e ld a,06h out (0080h),a net_block_innerLoop: push de ld de,0FFFFh net_block_tightWait: ld a,0Fh out (0041h),a in a,(0040h) bit 0,a jr nz,net_block_haveBit call timeout_tick jr nz,net_block_timeout jr net_block_tightWait net_block_haveBit: pop de in a,(0080h) cp 10h jr nz,net_block_non10 bit 0,e jr z,net_block_toggleHigh res 0,e ld (hl),a call checksum_updateByte ; SUB37 inc hl inc bc jr net_block_innerLoop net_block_toggleHigh: set 0,e jr net_block_innerLoop net_block_non10: bit 0,e jr nz,net_block_terminatorCase ld (hl),a call checksum_updateByte inc hl inc bc jr net_block_innerLoop net_block_terminatorCase: ld (blk_byteCount),bc ; DATA11 cp 0E1h jp nz,net_block_transfer_loop exx ld a,e cp 0Fh jp nz,net_block_transfer_loop ld a,d cp 1Dh jp nz,net_block_transfer_loop xor a ret net_block_timeout: pop de pop de xor a dec a ret ;------------------------------------------------------------------------------- ; keyboard_getHexStringToBuffer (SUB23) ; - Reads up to 5 hex digits from keyboard, echoes to screen, edits with DEL. ; - HL: buffer pointer, DE: VRAM base position ;------------------------------------------------------------------------------- keyboard_getHexStringToBuffer: ld b,05h kbd_hex_loop: in a,(0091h) bit 1,a jr z,kbd_hex_loop in a,(0090h) push af sub '0' jr c,kbd_hex_nonHex cp 0Ah jr c,kbd_hex_digit sub 07h cp 0Ah jr c,kbd_hex_nonHex cp 10h jr c,kbd_hex_digit sub 20h cp 0Ah jr c,kbd_hex_nonHex cp 10h jr nc,kbd_hex_nonHex kbd_hex_digit: ld c,a ld a,b or a jr nz,kbd_hex_accept pop af jr kbd_hex_loop kbd_hex_accept: ld a,e out (00A1h),a ld a,d or 40h out (00A1h),a pop af cp '[' jr c,kbd_hex_print sub 20h kbd_hex_print: out (00A0h),a ld (hl),c inc hl inc de dec b jr kbd_hex_loop kbd_hex_nonHex: pop af cp 0Dh jr z,kbd_hex_enter cp 0E7h jr nz,kbd_hex_backspaceCheck kbd_hex_enter: ld a,b or a jr nz,kbd_hex_loop ret kbd_hex_backspaceCheck: cp 7Fh jr z,kbd_hex_backspace cp 0E1h jr nz,kbd_hex_loop kbd_hex_backspace: ld a,b cp 05h jr z,kbd_hex_loop dec de dec hl inc b ld a,e out (00A1h),a ld a,d or 48h out (00A1h),a ld a,' ' out (00A0h),a jr kbd_hex_loop ;=============================================================================== ; ADAPTER/DISK INITIALIZATION HELPERS (SUB25, SUB26, SUB27, SUB28, SUB30..SUB36) ;=============================================================================== ;------------------------------------------------------------------------------- ; check_adapter_handshake (SUB25) ; - Combines presence probe + status read. ; - Returns A=1 on success, A=0 on failure. ;------------------------------------------------------------------------------- check_adapter_handshake: call net_cmd_presenceProbe jr nz,check_adapter_handshake_fail call net_cmd_statusRead ld (adapter_statusByte),a jr nz,check_adapter_handshake_fail ld a,01h ret check_adapter_handshake_fail: xor a ret ;------------------------------------------------------------------------------- ; scan_adapter_ports (SUB26) ; - Scans ports CFh, DFh, EFh, FFh for value 10h. ; - Returns sum of detection flags in A. ;------------------------------------------------------------------------------- scan_adapter_ports: ld de,0000h ld c,0CFh ld b,04h scan_adapter_loop: in a,(c) cp 10h jr nz,scan_adapter_skip ld d,01h scan_adapter_skip: ld a,c add a,10h ld c,a djnz scan_adapter_loop xor a add a,d add a,e ret ;------------------------------------------------------------------------------- ; timeout_tick (SUB27) ; - Decrements DE as timeout counter. ; - On zero, returns A=1 (NZ after INC), otherwise A=0. ;------------------------------------------------------------------------------- timeout_tick: dec de ld a,d or e jr z,timeout_expired xor a dec a timeout_expired: inc a ret ;------------------------------------------------------------------------------- ; floppy_or_cable_boot (SUB28) ; - High-level routine for FDC / cable boot from C000h. ; - Uses SUB30, SUB33..SUB36 to locate adapter and read data via DMA. ; - Verifies simple checksum at C000h+1.. and branches to LBL3 on failure. ;------------------------------------------------------------------------------- floppy_or_cable_boot: di call floppy_findFdcPorts ; SUB30 call floppy_waitForReady ; SUB33 or a jr z,floppy_ready call floppy_releaseFdc ; SUB34 ret floppy_ready: ld b,05h floppy_retryLoop: push bc ld a,(fdc_ctrlPort) ; DATA23 ld c,a ld a,01h out (c),a ld hl,0C000h ld a,088h call floppy_writeCommandByte ; SUB35 ld a,(fdc_dataPort) ; DATA21 ld d,a ld a,(fdc_dataInPort) ; DATA24 ld e,a floppy_readLoop: ld c,d floppy_waitByte: in a,(c) rra jr nc,floppy_noData rra jr nc,floppy_waitByte ld c,e ini inc b jr floppy_readLoop floppy_noData: in a,(c) and 0FCh jr z,floppy_doneRead pop bc djnz floppy_retryLoop ; Hard failure → LBL3 floppy_failure: call floppy_releaseFdc ; SUB34 ld hl,floppyMsg_error ; 08E2h call vid_blit_textRecord floppy_hang: jr floppy_hang floppy_doneRead: pop bc call floppy_releaseFdc ; Quick checksum over C000h region ld hl,0C000h ld a,(hl) ld c,a ld d,00h ld b,019h floppy_checksumLoop: inc hl ld a,(hl) xor c jr nz,floppy_checksumMismatch inc d djnz floppy_checksumLoop ld a,d cp 019h jp nz,launch_fromC000 ; SUB50 ld hl,floppyMsg_ok ; 0909h call vid_blit_textRecord pop bc jp boot_fallbackToMemoryTest ; LBL2 floppy_checksumMismatch: jp floppy_failure ;=============================================================================== ; VIDEO/ROM-SCAN/DIAGNOSTIC CORE (SUB38..SUB48) ;=============================================================================== ;------------------------------------------------------------------------------- ; video_show_banner_and_testPrompt (SUB38) ; - Clears small area around FFEEx, sets bank latch to ':' ; - Displays initial banner, then calls SUB39 for diagnostics / RAM test. ;------------------------------------------------------------------------------- video_show_banner_and_testPrompt: xor a ld hl,FFEEh ld de,FFEFh ld bc,0009h ld (hl),a ldir ld a,3Ah out (0000h),a ld (bootBankLatchShadow),a ld h,0Ah ld c,20h ld de,0E000h ld b,01h video_bannerLoop: call timeout_tick jr z,video_bannerCheckKey djnz video_bannerCheckKey jr diagnostic_main ; SUB39 video_bannerCheckKey: call keyboard_pollCharOnLine ; SUB47 jr z,video_bannerLoop in a,(0090h) cp '1' jr z,video_bannerSelect cp '=' jr nz,video_bannerLoop bit 0,h jr z,video_bannerLoop video_bannerMoveLeft: dec h jr z,video_bannerToggle jr video_bannerLoop video_bannerSelect: bit 0,h jr nz,video_bannerLoop jr video_bannerMoveLeft video_bannerToggle: ld hl,FFEEh inc (hl) ; fall into SUB39 ;------------------------------------------------------------------------------- ; diagnostic_main (SUB39) ; - Very large routine: ; * Checks ROM signature at 1FFDh..1FFFh ; * If mismatch, logs error via SUB45 and continues ; * Writes test pattern through VRAM using ports A1/A0 ; * Verifies VRAM read/write ; * If OK, copies small loader to 8000h and jumps there ; * Otherwise, cycles through sound/keyboard diagnostics and displays ; messages via SUB42/SUB43/SUB48. ;------------------------------------------------------------------------------- ; High-level: ; - If ROM overlay bit not set, compute a checksum over 1FFDh.., compare ; against last 2 bytes (DATA04/DATA05). On mismatch: error. ; - Call video/sound/keyboard tests; display failure messages: ; * ROM test: writes patterns in VRAM, verifies echoes. ; * VRAM test: pattern write / readback at video RAM. ; * sound: toggles PSG registers. ; * keyboard: cycles through keys, waits for specific scancodes. ; - If self-tests pass: ; * Copy loader stub from 12BDh to 8000h and jump to it, with ; bc/de/hl set up to continue system bootstrap. ; - On repeated failures: show error UI with beep and loop. ; --------------------------------------------------------------------------- diagnostic_main: ld a,(diag_flags) ; DATA31 bit 0,a jr nz,diag_haveMode call diag_lookupBankPattern ; SUB46 out (0000h),a ld (bootBankLatchShadow),a diag_haveMode: ld ix,1FFDh ld de,0FFFFh xor a ld h,a ld l,a ld b,a diag_romChecksumLoop: ld c,(ix+0) add hl,bc add ix,de jr c,diag_romChecksumLoop ld a,(rom_sigLo) ; DATA04 cp l jr nz,diag_romSigMismatch ld a,(rom_sigHi) ; DATA05 sub h jr z,diag_romSigOk diag_romSigMismatch: call diag_logError ; SUB45 diag_romSigOk: call diag_updateDisplay ; SUB42 ; Clear VRAM with count pattern, etc. xor a out (00A1h),a ld a,40h out (00A1h),a xor a ld de,0001h ld ix,0C000h diag_fillVRAMLoop: out (00A0h),a inc a add ix,de jr nc,diag_fillVRAMLoop ; VRAM pattern test (write complement & read back) ld ix,0C000h xor a ld c,a ld b,a ld h,a ld l,a diag_vramTestLoop: ld a,l out (00A1h),a ld a,h out (00A1h),a in a,(00A0h) cp c jr nz,diag_vramTestFail ld a,l out (00A1h),a ld a,h or 40h out (00A1h),a ld a,c cpl out (00A0h),a ld a,l out (00A1h),a ld a,h out (00A1h),a in a,(00A0h) cpl cp c jr z,diag_vramTestContinue diag_vramTestFail: call diag_logError diag_vramTestContinue: inc c inc hl ld de,0001h add ix,de jr nc,diag_vramTestLoop ; If we reach here, VRAM tests OK, proceed to loader stuff etc… ; (rest of SUB39 left structurally in place in original ROM) ;------------------------------------------------------------------------------- ; diag_maskPatternCompare (SUB40) ; - Tests bitmasks from table at 125Fh against B/C ;------------------------------------------------------------------------------- diag_maskPatternCompare: ld hl,125Fh ld e,c ld d,00h add hl,de and (hl) ld e,a ld a,b and (hl) cp e ret ; (bitmask table bytes omitted for brevity) ;------------------------------------------------------------------------------- ; diag_memPatternTest (SUB41) ; - RAM test helper used by SUB39. ;------------------------------------------------------------------------------- diag_memPatternTest: push bc push de ld a,b or c jr nz,diag_memPattern_skipBankSet ld a,(bootBankLatchShadow) set 0,a out (0000h),a diag_memPattern_skipBankSet: xor a pop hl pop ix push ix push hl ld de,0001h diag_memPattern_fill: ld (ix+0),a inc a inc ix add hl,de jr nc,diag_memPattern_fill xor a ld c,a pop hl pop ix push ix ld b,a diag_memPattern_verify: ld a,(ix+0) cp c jr nz,diag_memPattern_fail cpl ld (ix+0),a ld a,(ix+0) cpl cp c jr nz,diag_memPattern_fail inc ix inc c add hl,de jr nc,diag_memPattern_verify xor a jr diag_memPattern_exit diag_memPattern_fail: ld a,01h diag_memPattern_exit: pop bc push af ld a,b or c jr nz,diag_memPattern_restoreBank ld a,(bootBankLatchShadow) res 0,a out (0000h),a diag_memPattern_restoreBank: pop af ret ;------------------------------------------------------------------------------- ; diag_updateDisplay (SUB42) ; - Handles sound, LED, and on-screen diagnostic messages. ; - Uses SUB12 (tone), SUB43 (text), SUB46 (bank pattern). ;------------------------------------------------------------------------------- diag_updateDisplay: ld c,050h ld hl,diag_flags bit 1,(hl) jr z,diag_updateDisplay_no90 ld c,090h diag_updateDisplay_no90: ld de,0CC00h call net_play_errorToneAndRetry res 1,(hl) ld hl,8000h ld de,0001h diag_updateDisplay_dummyLoop: add hl,de jr nc,diag_updateDisplay_dummyLoop ld a,(boot_isNetworkBoot) or a jr z,diag_updateDisplay_romSource ld a,(diag_errorCode) ld c,a sub 06h jr z,diag_updateDisplay_computeIndex add a,07h jr diag_updateDisplay_computeIndex diag_updateDisplay_romSource: ld a,(diag_errorCode) ld c,a sub 04h jr z,diag_updateDisplay_computeIndex add a,05h diag_updateDisplay_computeIndex: ld (diag_errorCode),a ld a,(diag_flags) bit 0,a jr nz,diag_printErrorText ; SUB43 call diag_lookupBankPattern out (0000h),a ld (bootBankLatchShadow),a diag_printErrorText: jp diag_showErrorText ; SUB43 ;------------------------------------------------------------------------------- ; diag_showErrorText / diag_showErrorFromTable (SUB43) ; - If boot_isNetworkBoot != 0: uses message table at 14EFh, etc. ; - Otherwise: uses ROM-based error strings at 1446h + offset. ;------------------------------------------------------------------------------- diag_showErrorText: ld a,(boot_isNetworkBoot) or a jr z,diag_showErrorFromTable_rom ld b,08h ld ix,14EFh diag_showErrorFromTable_netLoop: push bc ld l,(ix+0) ld h,(ix+1) inc ix inc ix call vid_blit_textRecord pop bc djnz diag_showErrorFromTable_netLoop ld a,02h ld (menu_state),a ld de,0028h ld b,c inc b ld hl,012Bh diag_showErrorFromTable_netOffset: add hl,de djnz diag_showErrorFromTable_netOffset ld (menu_textPtr),hl ld hl,FFEFh add hl,bc ld de,FFFCh call hexToAscii_inPlace ; SUB44 ld hl,FFF9h jp vid_blit_textRecord diag_showErrorFromTable_rom: ld hl,FFEFh ld b,00h add hl,bc ld a,(hl) or a ret z ld hl,1446h ld de,0013h inc c diag_showErrorFromTable_romLoop: dec c jp z,vid_blit_textRecord add hl,de jr diag_showErrorFromTable_romLoop ;------------------------------------------------------------------------------- ; hexToAscii_inPlace (SUB44) ; - Takes byte at (HL), converts to 2 ASCII hex chars at DE. ;------------------------------------------------------------------------------- hexToAscii_inPlace: ld a,(hl) srl a srl a srl a srl a call hexToAscii_nibble inc de ld a,(hl) and 0Fh hexToAscii_nibble: add a,'0' cp ':' jr c,hexToAscii_store add a,7 hexToAscii_store: ld (de),a ret ;------------------------------------------------------------------------------- ; diag_logError (SUB45) ; - Ensures diag_flags bit0 set, increments error counter at FFEFh+errorIndex. ;------------------------------------------------------------------------------- diag_logError: push af push de push hl ld a,(diag_flags) bit 0,a jr nz,diag_logError_haveMode call diag_lookupBankPattern or 10h out (0000h),a ld (bootBankLatchShadow),a diag_logError_haveMode: ld a,03h ld (diag_flags),a ld hl,FFEFh ld d,00h ld a,(diag_errorCode) ld e,a add hl,de inc (hl) jr nz,diag_logError_exit dec (hl) diag_logError_exit: pop hl pop de pop af ret ;------------------------------------------------------------------------------- ; diag_lookupBankPattern (SUB46) ; - Returns pattern byte for diag_errorCode from table at 1402h. ;------------------------------------------------------------------------------- diag_lookupBankPattern: ld hl,1402h ld a,(diag_errorCode) ld e,a ld d,00h add hl,de ld a,(hl) ret ;------------------------------------------------------------------------------- ; keyboard_pollCharOnLine (SUB47) ; - Uses 41h/40h to poll for keyboard bit, returns bit0 result. ;------------------------------------------------------------------------------- keyboard_pollCharOnLine: ld a,0Eh out (0041h),a ld a,c out (0040h),a ld a,0Fh out (0041h),a in a,(0040h) bit 0,a ret ;------------------------------------------------------------------------------- ; diag_banner_loop (SUB48) ; - Iteratively calls SUB43 a few times, then if network boot ; loads text from DATA02/DATA03 and displays ROM/VRAM/SOUND/KEYBOARD errors. ;------------------------------------------------------------------------------- diag_banner_loop: ld c,06h push bc diag_banner_loopInner: call diag_showErrorText pop bc dec c jp m,diag_banner_done push bc jr diag_banner_loopInner diag_banner_done: ld a,(boot_isNetworkBoot) or a ret z ld hl,(errorTextPtrFromTable) ; DATA02 ld (menu_state),hl ld a,(errorTextIndexFromTable) ; DATA03 ld (menu_textPtrHi),a ld de,(netUiSmallTable) ld (menu_valuePair),de ld hl,FFF9h jp vid_blit_textRecord ;=============================================================================== ; SMALL DATA / STATE BYTES (RENAMED FROM DATAxx) ;=============================================================================== boot_stackTop equ 0FEE0h ; DATA17 boot_status0 equ 0FEE0h ; overlapped boot_status1 equ 0FEE1h ; DATA18 boot_status2 equ 0FEE2h ; DATA19 boot_keyFlag equ 0FFE9h ; DATA26 boot_sourceSignature equ 0FFECh ; DATA28 bootBankLatchShadow equ 0FFEDh ; DATA29 boot_isNetworkBoot equ 0FFEEh ; DATA30 diag_flags equ 0FFF6h ; DATA31 diag_errorCode equ 0FFF7h ; DATA32 adapter_statusByte equ 0FFF8h ; DATA33 menu_state equ 0FFF9h ; DATA34 menu_textPtr equ 0FFFAh ; DATA35 menu_textPtrHi equ 0FFFBh ; DATA36 menu_valuePair equ 0FFFC h ; DATA37 boot_magic equ 0FFFEh ; DATA38/39 adapter_timeoutCounter equ 0FFEAh ; DATA27 rom_sigLo equ 01FFEh ; DATA04 rom_sigHi equ 01FFFh ; DATA05 blk_headerCount equ 02000h ; DATA06..09, 2000–2003 region blk_headerPtr equ 02001h blk_state equ 02003h blk_nextRecordFlag equ 02002h blk_bufferBase equ 02004h ; DATA10 (word) blk_byteCount equ 02006h ; DATA11 (word) blk_checksumTotal equ 02008h ; DATA12 (word) blk_headerFlags equ 0200Ah ; DATA13 blk_totalSectorsLo equ 0240Bh ; DATA14/15 blk_totalSectorsHi equ 0240Ch fdc_basePort equ 0FEF0h ; DATA20..25 – various FDC related ports fdc_dataPort equ 0FEF1h ; DATA21 fdc_statusPort equ 0FEF2h ; DATA22 fdc_ctrlPort equ 0FEF3h ; DATA23 fdc_dataInPort equ 0FEF4h ; DATA24 fdc_miscPort equ 0FEF5h ; DATA25 ; (DATA02/DATA03 tables for ROM/VRAM/SOUND/KEYBOARD error text ; and other constant tables are left as symbolic names in the text above.)