; =======================================================================
; UPS-65  —  6502 Development Monitor ROM
; =======================================================================
;
; Hardware:
;   CPU  : 65C02 @ 2 MHz
;   RAM  : 6116  2K SRAM   $0000-$07FF
;   VIA  : 6522  (8522)    $A000-$A00F
;   ROM  : 2K              $F800-$FFFF  (also mirrors $F000-$F7FF)
;   Keys : 24-key matrix   6 rows (7442 BCD decoder) x 4 cols
;   Disp : 6-digit 7-seg LED, multiplexed via VIA Port A/B
;   Addr : 82S129 PROM decoder (A15:A8 -> active-low chip selects)
;
; Address map (from 82S129 PROM decode):
;   $0000-$07FF  RAM   6116 2K   (PROM[$00-$07] -> O3 low)
;   $0800-$9FFF  Open bus (unselected)
;   $A000-$A0FF  VIA   6522      (PROM[$A0]     -> O2 low)
;   $A100-$EFFF  Open bus (unselected)
;   $F000-$FFFF  ROM   2K        (PROM[$F0-$FF] -> O4 low)
;                       NOTE: ROM is 2K so $F000-$F7FF mirrors $F800-$FFFF
;
; Vectors:
;   RESET $FFFC/$FFFD = $F94A  -> COLD_START
;   IRQ   $FFFE/$FFFF = $FAA8  -> IRQ_HANDLER
;   NMI   $FFFA/$FFFB = $FFFF  -> not used
;
; =======================================================================
;
; Zero Page Map:
;   $A0/$A1   PTR_LO/PTR_HI      Current address pointer
;   $A2       DATA_BYTE           Current data byte
;   $A3-$A8   DISP_D1..D6        7-seg display buffer (6 digits)
;                                  D1/D2 = addr high byte (HH HL)
;                                  D3/D4 = addr low byte  (LH LL)
;                                  D5/D6 = data byte      (DH DL)
;   $A9-$AB   SCORE_LO/MID/HI    BCD score / run counter
;   $AD-$AF   SAVE_A/X/Y         Saved CPU registers (BRK/IRQ)
;   $C0       IDLE_FLAG           Non-zero -> call idle vector
;   $C1       IDLE_VEC            JMP for idle callback (3 bytes)
;   $C3       IRQ2_FLAG           Non-zero -> call secondary IRQ vec
;   $C4       IRQ2_VEC            JMP for secondary IRQ (3 bytes)
;   $C6       NMI_FLAG            Non-zero -> call NMI vector
;   $C7       NMI_VEC             JMP for NMI handler (3 bytes)
;   $CC       BRK_FLAG            Non-zero -> call BRK hook
;   $CD       BRK_VEC             JMP for BRK hook (3 bytes)
;   $D3/$D4   PC_HI/PC_LO        Saved PC; $33/$33 = warm-start cookie
;   $D5/$D6   TIMER_LO/HI        BCD countdown timer (IRQ driven)
;   $E3       KEY_BUF             Keyboard buffer (IRQ writes, GET_CHAR reads)
;   $E4       DISP_FREEZE         Non-zero = freeze display (don't scan)
;   $E5       DISP_ENABLE         $FF=display on,  $00=display off
;   $E8/$E9   SAVE_PTR_LO/HI     Saved copy of address pointer
;   $EB/$EC   KEY_RAW/DEBOUNCE   Keyboard debounce state (IRQ use)
;   $FD/$FE   RET_LO/HI          Return address (SAVE/RESTORE_REGS)
;   $FF       SAVE_A2             Temp A save (SAVE/RESTORE_REGS)
;
; =======================================================================
;
; 6522 VIA Port assignments:
;   Port A (VIA_ORA) — DUAL FUNCTION:
;     Write bits 6:4  BCD digit select -> 7442 -> digit transistor
;                     $10=digit1, $20=digit2 ... $60=digit6
;     Read  bits 3:0  Keyboard column sense (active-low via 7400)
;   Port B (VIA_ORB) — 7-segment data output:
;     Bits 6:0 = segments g,f,e,d,c,b,a (written each IRQ tick)
;   Timer 1 — free-run mode, continuous IRQ:
;     Drives display multiplex and keyboard scan (~1 kHz at 2 MHz)
;
; =======================================================================
;
; 7-Segment table at $FFD4 (bit encoding: gfedcba):
;   0=$77  1=$14  2=$5B  3=$5E  4=$3C  5=$6E  6=$6F  7=$54
;   8=$7F  9=$7E  A=$7D  b=$2F  C=$63  d=$1F  E=$6B  F=$69
;   (dash=$08 = segment d only, shown while awaiting input)
;
; Keyboard matrix keycode table at $FFF0:
;   Rows selected by 7442 (Port A bits 6:4 = BCD 1-6)
;   Columns sensed via Port A bits 3:0 (active-low, 7400 NAND)
;   Keycode = (row_BCD << 4) | col_bits_active_high
;   0=$18  1=$28  2=$38  3=$48  4=$14  5=$24  6=$34  7=$44
;   8=$12  9=$22  A=$46  b=$FD  C=$4A  d=$F9  E=$A8  F=$FA
;   GO key = $68 (row 6, col 8)
;
; Startup splash at $FFEA: 'CE5-65' shown on LED display at power-on
;   until GO key pressed.  $08 = dash (segment d only).
;
; =======================================================================

; --- 6522 VIA Register Equates ---
VIA_ORB          = $A000  ; Port B data (7-seg segments out)
VIA_ORA          = $A001  ; Port A data (digit select out / key cols in)
VIA_DDRB         = $A002  ; Port B direction register
VIA_DDRA         = $A003  ; Port A direction register
VIA_T1CL         = $A004  ; Timer 1 counter low  (read clears T1 IRQ flag)
VIA_T1CH         = $A005  ; Timer 1 counter high
VIA_T1LL         = $A006  ; Timer 1 latch low
VIA_T1LH         = $A007  ; Timer 1 latch high
VIA_T2CL         = $A008  ; Timer 2 counter low
VIA_T2CH         = $A009  ; Timer 2 counter high
VIA_SR           = $A00A  ; Shift register
VIA_ACR          = $A00B  ; Auxiliary control register
VIA_PCR          = $A00C  ; Peripheral control register
VIA_IFR          = $A00D  ; Interrupt flag register
VIA_IER          = $A00E  ; Interrupt enable register
VIA_ORA_NH       = $A00F  ; Port A data (no handshake)

; --- ROM Entry Point Equates ---
JMP_TABLE                = $F800
REPORT_ERROR             = $F83A
GET_BYTE_AT_PTR          = $F854
MATCH_COMMAND            = $F862
EXEC_COMMAND             = $F874
SAVE_REGS                = $F8F0
RESTORE_REGS             = $F8F3
BYTE_TO_HEX_ASCII        = $F917
NIBBLE_TO_ASCII          = $F925
DELAY                    = $F931
WARM_START               = $F947
COLD_START               = $F94A
MAIN_LOOP                = $F987
READ_CHAR_LOOP           = $F99C
CMD_DISPLAY              = $FA06
CMD_HEX_INPUT            = $FA0E
CMD_BREAKPOINT           = $FA28
WAIT_FOR_H               = $FA62
GET_CHAR                 = $FA6A
PEEK_CHAR                = $FA6E
LOAD_DEFAULT_REGS        = $FA77
ZERO_REGS                = $FA85
FLASH_LEDS               = $FA92
IRQ_HANDLER              = $FAA8
READ_VIA_ORA_INV         = $FB7D
SET_DISPLAY_ON           = $FB85
SET_DISPLAY_OFF          = $FB8A
LOAD_ADDR_SEGS           = $FB94
LOAD_DATA_SEGS           = $FBA7
BYTE_TO_SEG_PAIR         = $FBB1
GET_HEX_NIBBLE           = $FBC4
INIT_DISPLAY             = $FBE0
CMD_ASSEMBLE             = $FBF6
CMD_EXAMINE_REGS         = $FC14
BRK_HANDLER              = $FC2D
INPUT_ADDRESS            = $FC4A
INPUT_ADDR_HI            = $FC7C
INC_PTR                  = $FCB8
DEC_PTR                  = $FCBF
CMD_GO                   = $FCCC
CMD_RESET_PC             = $FCE1
INIT_VIA                 = $FCE8
INIT_VIA_SOFT            = $FCF8
INIT_VECTORS             = $FD07
CLEAR_RAM                = $FD1A
NMI_HANDLER              = $FD46
INIT_DATA                = $FD53

; =======================================================================
; CODE
; =======================================================================

; =================================================================
; JMP_TABLE
; -----------------------------------------------------------------
; 19-entry jump table providing stable entry points for external
; callers. Each slot is a 3-byte JMP to the real routine.
; Callers should always use these addresses, not the internal ones,
; so the ROM can be revised without breaking compatibility.
; =================================================================
JMP_TABLE:
  F800  4C 6A FA  JMP GET_CHAR
  F803  4C 6E FA  JMP PEEK_CHAR
  F806  4C 85 FB  JMP SET_DISPLAY_ON
  F809  4C 8A FB  JMP SET_DISPLAY_OFF
  F80C  4C F0 F8  JMP SAVE_REGS
  F80F  4C F3 F8  JMP RESTORE_REGS
  F812  4C 7C FC  JMP INPUT_ADDR_HI
  F815  4C 4A FC  JMP INPUT_ADDRESS
  F818  4C 4A F9  JMP COLD_START
  F81B  4C 47 F9  JMP WARM_START
  F81E  4C 31 F9  JMP DELAY
  F821  4C B8 FC  JMP INC_PTR
  F824  4C BF FC  JMP DEC_PTR
  F827  4C 3A F8  JMP REPORT_ERROR
  F82A  4C 17 F9  JMP BYTE_TO_HEX_ASCII
  F82D  4C E0 FB  JMP INIT_DISPLAY
  F830  4C C4 FB  JMP GET_HEX_NIBBLE
  F833  4C CC FC  JMP CMD_GO
  F836  4C E1 FC  JMP CMD_RESET_PC
  F839  26 20     ROL $20
  F83B  E0 FB     CPX #$FB
  F83D  29 0F     AND #$0F
  F83F  A8        TAY
  F840  A2 05     LDX #$05
  F842  B9 D4 FF  LDA $FFD4,Y
  F845  95 A3     STA DISP_D1,X
  F847  BD 64 FD  LDA $FD64,X
  F84A  CA        DEX
  F84B  10 F8     BPL $F845
  F84D  20 62 FA  JSR WAIT_FOR_H
  F850  20 8A FB  JSR SET_DISPLAY_OFF
  F853  60        RTS

; =================================================================
; GET_BYTE_AT_PTR
; -----------------------------------------------------------------
; Read the byte at the current address pointer ($A0/$A1)
; without advancing it.
; Out: A = byte at [$A0/$A1]
; =================================================================
GET_BYTE_AT_PTR:
  F854  A2 A0     LDX #$A0
  F856  20 BF FC  JSR DEC_PTR
  F859  A2 00     LDX #$00
  F85B  A1 A0     LDA ($A0,X)
  F85D  A2 A0     LDX #$A0
  F85F  20 B8 FC  JSR INC_PTR

; =================================================================
; MATCH_COMMAND
; -----------------------------------------------------------------
; Compare A against the 8-entry command keycode table at $F8D1.
; On match, dispatch to EXEC_COMMAND.
; On no match, call REPORT_ERROR (error 4) and loop.
; In:  A = raw keycode from keyboard
; Out: jumps to EXEC_COMMAND (X = index) or REPORT_ERROR
; =================================================================
MATCH_COMMAND:
  F862  A2 07     LDX #$07
  F864  DD D1 F8  CMP $F8D1,X
  F867  F0 0B     BEQ EXEC_COMMAND
  F869  CA        DEX
  F86A  10 F8     BPL $F864
  F86C  A9 04     LDA #$04
  F86E  20 3A F8  JSR REPORT_ERROR
  F871  4C 9C F9  JMP READ_CHAR_LOOP

; =================================================================
; EXEC_COMMAND
; -----------------------------------------------------------------
; Save current pointer ($A0/$A1), call INIT_DISPLAY, read a new
; 4-digit hex address into $A0/$A1 via INPUT_ADDRESS.
; In:  X = command index
; Out: $E8/$E9 = saved old pointer, $A0/$A1 = new address
; =================================================================
EXEC_COMMAND:
  F874  A5 A0     LDA PTR_LO
  F876  48        PHA
  F877  A5 A1     LDA PTR_HI
  F879  48        PHA
  F87A  20 E0 FB  JSR INIT_DISPLAY
  F87D  A9 2F     LDA #$2F
  F87F  85 A7     STA DISP_D5
  F881  A9 09     LDA #$09
  F883  85 A8     STA DISP_D6
  F885  20 4A FC  JSR INPUT_ADDRESS
  F888  68        PLA
  F889  85 E9     STA SAVE_PTR_HI
  F88B  68        PLA
  F88C  85 E8     STA SAVE_PTR_LO
  F88E  A5 A0     LDA PTR_LO
  F890  38        SEC
  F891  E5 E8     SBC SAVE_PTR_LO
  F893  85 EA     STA $EA
  F895  A5 A1     LDA PTR_HI
  F897  E5 E9     SBC SAVE_PTR_HI
  F899  30 11     BMI $F8AC
  F89B  D0 28     BNE $F8C5
  F89D  C6 EA     DEC $EA
  F89F  A5 EA     LDA $EA
  F8A1  C9 80     CMP #$80
  F8A3  B0 24     BCS $F8C9
  F8A5  A2 00     LDX #$00
  F8A7  81 E8     STA ($E8,X)
  F8A9  4C BA F8  JMP $F8BA
  F8AC  C9 FF     CMP #$FF
  F8AE  D0 19     BNE $F8C9
  F8B0  C6 EA     DEC $EA
  F8B2  A5 EA     LDA $EA
  F8B4  10 13     BPL $F8C9
  F8B6  A2 00     LDX #$00
  F8B8  81 E8     STA ($E8,X)
  F8BA  A5 E8     LDA SAVE_PTR_LO
  F8BC  85 A0     STA PTR_LO
  F8BE  A5 E9     LDA SAVE_PTR_HI
  F8C0  85 A1     STA PTR_HI
  F8C2  4C 9C F9  JMP READ_CHAR_LOOP
  F8C5  A9 02     LDA #$02
  F8C7  D0 02     BNE $F8CB
  F8C9  A9 03     LDA #$03
  F8CB  20 3A F8  JSR REPORT_ERROR
  F8CE  4C BA F8  JMP $F8BA
  F8D1  90 B0     BCC $F883
  F8D3  F0 30     BEQ $F905
  F8D5  D0 10     BNE $F8E7
  F8D7  50 70     BVC $F949
  F8D9  A2 FF     LDX #$FF
  F8DB  CA        DEX
  F8DC  BD 00 02  LDA $0200,X
  F8DF  9D 01 02  STA $0201,X
  F8E2  A9 EA     LDA #$EA
  F8E4  9D 00 02  STA $0200,X
  F8E7  E4 A0     CPX PTR_LO
  F8E9  D0 F0     BNE $F8DB
  F8EB  E6 A0     INC PTR_LO
  F8ED  4C 9C F9  JMP READ_CHAR_LOOP

; =================================================================
; SAVE_REGS
; -----------------------------------------------------------------
; Push A, X, Y onto the stack. Save return address in $FD/$FE.
; Entry at F8F0: clears carry (save path).
; Entry at F8F3: sets carry (restore path, see RESTORE_REGS).
; =================================================================
SAVE_REGS:
  F8F0  18        CLC
  F8F1  90 01     BCC $F8F4

; =================================================================
; RESTORE_REGS
; -----------------------------------------------------------------
; Pull Y, X, A from the stack and jump via ($FD).
; Paired with SAVE_REGS — shared code body, carry set on entry.
; Out: A/X/Y restored; PC = [$FD]+1
; =================================================================
RESTORE_REGS:
  F8F3  38        SEC
  F8F4  85 FF     STA SAVE_A2
  F8F6  68        PLA
  F8F7  85 FD     STA RET_LO
  F8F9  68        PLA
  F8FA  85 FE     STA RET_HI
  F8FC  B0 0B     BCS $F909
  F8FE  A5 FF     LDA SAVE_A2
  F900  48        PHA
  F901  8A        TXA
  F902  48        PHA
  F903  98        TYA
  F904  48        PHA
  F905  A5 FF     LDA SAVE_A2
  F907  90 05     BCC $F90E
  F909  68        PLA
  F90A  A8        TAY
  F90B  68        PLA
  F90C  AA        TAX
  F90D  68        PLA
  F90E  E6 FD     INC RET_LO
  F910  D0 02     BNE $F914
  F912  E6 FE     INC RET_HI
  F914  6C FD 00  JMP ($00FD)

; =================================================================
; BYTE_TO_HEX_ASCII
; -----------------------------------------------------------------
; Convert byte in A to two 7-segment display codes.
; Low nibble -> NIBBLE_TO_ASCII -> saved in X.
; High nibble shifted down -> NIBBLE_TO_ASCII -> returned in A.
; In:  A = byte to convert
; Out: A = seg code for high nibble, X = seg code for low nibble
; =================================================================
BYTE_TO_HEX_ASCII:
  F917  48        PHA
  F918  20 25 F9  JSR NIBBLE_TO_ASCII
  F91B  AA        TAX
  F91C  68        PLA
  F91D  6A        ROR A
  F91E  6A        ROR A
  F91F  6A        ROR A
  F920  6A        ROR A
  F921  20 25 F9  JSR NIBBLE_TO_ASCII
  F924  60        RTS

; =================================================================
; NIBBLE_TO_ASCII
; -----------------------------------------------------------------
; Convert low nibble of A to a 7-segment display code.
; Indexes the 16-entry table at $FFD4 (bits = gfedcba).
; In:  A = value (bits 3:0 used)
; Out: A = 7-segment code  e.g. 0->$77, 1->$14 ... F->$69
; =================================================================
NIBBLE_TO_ASCII:
  F925  29 0F     AND #$0F
  F927  C9 0A     CMP #$0A
  F929  B0 03     BCS $F92E
  F92B  09 30     ORA #$30
  F92D  60        RTS
  F92E  69 36     ADC #$36
  F930  60        RTS

; =================================================================
; DELAY
; -----------------------------------------------------------------
; Software delay loop. Three nested counters:
;   Outer:  A counts down to 0
;   Middle: Y counts $4B..0  (~75 iterations)
;   Inner:  X counts $FF..0  (~255 iterations)
; Total iterations ≈ A x 75 x 255  (at 2 MHz, A=1 ≈ 10 ms).
; In:  A = delay multiplier
; Preserves: A, X, Y
; =================================================================
DELAY:
  F931  20 F0 F8  JSR SAVE_REGS
  F934  A0 4B     LDY #$4B
  F936  A2 FF     LDX #$FF
  F938  CA        DEX
  F939  D0 FD     BNE $F938
  F93B  88        DEY
  F93C  D0 F8     BNE $F936
  F93E  38        SEC
  F93F  E9 01     SBC #$01
  F941  D0 F1     BNE $F934
  F943  20 F3 F8  JSR RESTORE_REGS
  F946  60        RTS

; =================================================================
; WARM_START
; -----------------------------------------------------------------
; Warm start entry. Calls GET_CHAR to sync, then falls into
; COLD_START. Because $D3/$D4 will still hold the magic cookie
; ($33/$33) the cold-start check will skip straight to MAIN_LOOP.
; =================================================================
WARM_START:
  F947  20 62 FA  JSR WAIT_FOR_H

; =================================================================
; COLD_START
; -----------------------------------------------------------------
; RESET vector target ($FFFC = $4A $F9).
; 1. Initialise stack pointer ($FF).
; 2. Call INIT_VIA (full hardware init of 6522).
; 3. Warm-start check: if $D3=$D4=$33 (magic cookie) jump to
;    MAIN_LOOP immediately, skipping all initialisation.
; 4. Otherwise (genuine power-on):
;    a. Store cookie $33 in $D3 and $D4 for future warm starts.
;    b. INIT_VECTORS  — set up zero-page indirect vectors.
;    c. CLEAR_RAM     — zero pages $00-$07, zero-page vars.
;    d. SET_DISPLAY_ON + load $FFEA table -> shows 'CE5-65'
;       on the 6-digit LED display (board identity splash).
;    e. WAIT_FOR_H   — spin until the GO key (row6,col8=$68)
;       is pressed. Display holds 'CE5-65' until then.
;    f. SET_DISPLAY_OFF.
;    g. DEC $E003 / INC $E004: reads open bus (no device at
;       $E000 on this hardware). These are DEAD CODE left over
;       from an earlier board revision that had a 6551 ACIA.
;       On real hardware the bus floats to ~$FF so DEC gives
;       $FE (non-zero) -> BNE always taken -> falls into MAIN_LOOP.
;    h. JMP $E005: UNREACHABLE on this hardware. Would crash.
; =================================================================
COLD_START:
  F94A  A2 FF     LDX #$FF
  F94C  9A        TXS
  F94D  20 E8 FC  JSR INIT_VIA
  F950  A9 33     LDA #$33
  F952  C5 D3     CMP PC_HI
  F954  D0 04     BNE $F95A
  F956  C5 D4     CMP PC_LO
  F958  F0 2D     BEQ MAIN_LOOP
  F95A  85 D3     STA PC_HI
  F95C  85 D4     STA PC_LO
  F95E  20 07 FD  JSR INIT_VECTORS
  F961  20 1A FD  JSR CLEAR_RAM
  F964  20 85 FB  JSR SET_DISPLAY_ON
  F967  A2 05     LDX #$05
  F969  BD EA FF  LDA $FFEA,X
  F96C  95 A3     STA DISP_D1,X
  F96E  CA        DEX
  F96F  10 F8     BPL $F969
  F971  20 62 FA  JSR WAIT_FOR_H
  F974  20 8A FB  JSR SET_DISPLAY_OFF
  F977  CE 03 E0  DEC $E003  ; OPEN BUS — no device here on this hardware (dead code)
  F97A  D0 0B     BNE MAIN_LOOP
  F97C  EE 04 E0  INC $E004  ; OPEN BUS — no device here on this hardware (dead code)
  F97F  D0 06     BNE MAIN_LOOP
  F981  20 E0 FB  JSR INIT_DISPLAY
  F984  4C 05 E0  JMP $E005  ; OPEN BUS — no device here on this hardware (dead code)

; =================================================================
; MAIN_LOOP
; -----------------------------------------------------------------
; Main command interpreter loop (re-entered after each command).
; 1. FLASH_LEDS — heartbeat flash of display.
; 2. Check $C0: if non-zero, dispatch via indirect vector ($C1)
;    (user-hookable idle callback).
; 3. Otherwise call INIT_VIA and set pointer to $0200.
; 4. Fall into READ_CHAR_LOOP.
; =================================================================
MAIN_LOOP:
  F987  20 92 FA  JSR FLASH_LEDS
  F98A  A5 C0     LDA IDLE_FLAG
  F98C  D0 03     BNE $F991
  F98E  6C C1 00  JMP ($00C1)
  F991  20 8A FB  JSR SET_DISPLAY_OFF
  F994  A9 02     LDA #$02
  F996  85 A1     STA PTR_HI
  F998  A9 00     LDA #$00
  F99A  85 A0     STA PTR_LO

; =================================================================
; READ_CHAR_LOOP
; -----------------------------------------------------------------
; Fetch next keycode via GET_CHAR ($E3), store in $A2.
; Dispatch on keycode:
;   $58 'X' -> examine memory at current pointer
;   $68 'h' -> store byte (hex input mode)
;   $64 'd' -> CMD_DISPLAY (decrement pointer, redisplay)
;   $62 'b' -> CMD_BREAKPOINT
;   $51 'Q' -> CMD_RESET_PC
;   other   -> CMD_HEX_INPUT (treat as hex digit entry)
; =================================================================
READ_CHAR_LOOP:
  F99C  A2 00     LDX #$00
  F99E  A1 A0     LDA ($A0,X)
  F9A0  85 A2     STA DATA_BYTE
  F9A2  20 6A FA  JSR GET_CHAR
  F9A5  C9 58     CMP #$58
  F9A7  F0 18     BEQ $F9C1
  F9A9  C9 68     CMP #$68
  F9AB  F0 29     BEQ $F9D6
  F9AD  C9 64     CMP #$64
  F9AF  D0 03     BNE $F9B4
  F9B1  4C 06 FA  JMP CMD_DISPLAY
  F9B4  C9 62     CMP #$62
  F9B6  D0 03     BNE $F9BB
  F9B8  4C 28 FA  JMP CMD_BREAKPOINT
  F9BB  C9 51     CMP #$51
  F9BD  F0 2B     BEQ $F9EA
  F9BF  D0 4D     BNE CMD_HEX_INPUT
  F9C1  A9 00     LDA #$00
  F9C3  85 E6     STA $E6
  F9C5  20 E0 FB  JSR INIT_DISPLAY
  F9C8  A9 7D     LDA #$7D
  F9CA  85 A7     STA DISP_D5
  F9CC  A9 1F     LDA #$1F
  F9CE  85 A8     STA DISP_D6
  F9D0  20 4A FC  JSR INPUT_ADDRESS
  F9D3  4C E1 F9  JMP $F9E1
  F9D6  A5 A2     LDA DATA_BYTE
  F9D8  A2 00     LDX #$00
  F9DA  81 A0     STA ($A0,X)
  F9DC  A2 A0     LDX #$A0
  F9DE  20 B8 FC  JSR INC_PTR
  F9E1  A2 00     LDX #$00
  F9E3  A1 A0     LDA ($A0,X)
  F9E5  85 A2     STA DATA_BYTE
  F9E7  4C A2 F9  JMP $F9A2
  F9EA  A5 A0     LDA PTR_LO
  F9EC  85 E8     STA SAVE_PTR_LO
  F9EE  A5 A1     LDA PTR_HI
  F9F0  85 E9     STA SAVE_PTR_HI
  F9F2  A9 00     LDA #$00
  F9F4  85 A0     STA PTR_LO
  F9F6  85 A1     STA PTR_HI
  F9F8  85 A2     STA DATA_BYTE
  F9FA  20 E0 FB  JSR INIT_DISPLAY
  F9FD  A5 AD     LDA SAVE_A
  F9FF  A6 AE     LDX SAVE_X
  FA01  A4 AF     LDY SAVE_Y
  FA03  6C E8 00  JMP ($00E8)

; =================================================================
; CMD_DISPLAY
; -----------------------------------------------------------------
; Display command. Decrements pointer $A0/$A1 then
; loops back to READ_CHAR_LOOP to refresh display.
; =================================================================
CMD_DISPLAY:
  FA06  A2 A0     LDX #$A0
  FA08  20 BF FC  JSR DEC_PTR
  FA0B  4C E1 F9  JMP $F9E1

; =================================================================
; CMD_HEX_INPUT
; -----------------------------------------------------------------
; Hex digit entry. Searches $FFF0 keycode table for match,
; shifts $A2 left 4 bits and ORs in the new nibble.
; In:  A = raw keycode
; Out: $A2 updated; loops to READ_CHAR_LOOP
; =================================================================
CMD_HEX_INPUT:
  FA0E  A2 FF     LDX #$FF
  FA10  86 E6     STX $E6
  FA12  E8        INX
  FA13  DD F0 FF  CMP $FFF0,X
  FA16  D0 FA     BNE $FA12
  FA18  8A        TXA
  FA19  06 A2     ASL DATA_BYTE
  FA1B  06 A2     ASL DATA_BYTE
  FA1D  06 A2     ASL DATA_BYTE
  FA1F  06 A2     ASL DATA_BYTE
  FA21  05 A2     ORA DATA_BYTE
  FA23  85 A2     STA DATA_BYTE
  FA25  4C A2 F9  JMP $F9A2

; =================================================================
; CMD_BREAKPOINT
; -----------------------------------------------------------------
; Breakpoint / multi-function command (key 'b').
; Calls INIT_DISPLAY, reads a 4-digit hex address.
; Then waits for a second keycode and dispatches:
;   'A' -> CMD_ASSEMBLE  (mini assembler)
;   '!' -> CMD_EXAMINE_REGS
;   'B' -> GET_BYTE_AT_PTR
;   '1' -> JMP $E000 (run user code in RAM — only valid
;           if user has loaded a program at $0000-$07FF)
;   ctrl-Q ($11) -> insert NOP sled and run
;   other -> COLD_START
; =================================================================
CMD_BREAKPOINT:
  FA28  20 E0 FB  JSR INIT_DISPLAY
  FA2B  A9 14     LDA #$14
  FA2D  85 A7     STA DISP_D5
  FA2F  A9 0D     LDA #$0D
  FA31  85 A8     STA DISP_D6
  FA33  20 6A FA  JSR GET_CHAR
  FA36  20 8A FB  JSR SET_DISPLAY_OFF
  FA39  C9 41     CMP #$41
  FA3B  D0 03     BNE $FA40
  FA3D  4C F6 FB  JMP CMD_ASSEMBLE
  FA40  C9 21     CMP #$21
  FA42  D0 03     BNE $FA47
  FA44  4C 14 FC  JMP CMD_EXAMINE_REGS
  FA47  C9 42     CMP #$42
  FA49  D0 03     BNE $FA4E
  FA4B  4C 54 F8  JMP GET_BYTE_AT_PTR
  FA4E  C9 31     CMP #$31
  FA50  D0 06     BNE $FA58
  FA52  20 E0 FB  JSR INIT_DISPLAY
  FA55  4C 00 E0  JMP $E000  ; OPEN BUS — no device here on this hardware (dead code)
  FA58  C9 11     CMP #$11
  FA5A  D0 03     BNE $FA5F
  FA5C  4C D9 F8  JMP $F8D9
  FA5F  4C 4A F9  JMP COLD_START

; =================================================================
; WAIT_FOR_H
; -----------------------------------------------------------------
; Spin until the GO key is pressed.
; Calls GET_CHAR repeatedly, comparing against keycode $68
; (row 6, col 8 — the GO/RUN key on the front panel).
; Used at startup to hold the 'CE5-65' splash until dismissed.
; Out: A = $68
; =================================================================
WAIT_FOR_H:
  FA62  20 6A FA  JSR GET_CHAR
  FA65  C9 68     CMP #$68
  FA67  D0 F9     BNE WAIT_FOR_H
  FA69  60        RTS

; =================================================================
; GET_CHAR
; -----------------------------------------------------------------
; Blocking keyboard read.
; Polls zero-page $E3 (written by IRQ_HANDLER on keypress)
; until non-zero, then clears $E3 and returns the keycode.
; Out: A = keycode; $E3 cleared
; =================================================================
GET_CHAR:
  FA6A  A5 E3     LDA KEY_BUF
  FA6C  F0 FC     BEQ GET_CHAR

; =================================================================
; PEEK_CHAR
; -----------------------------------------------------------------
; Non-destructive read of keyboard buffer $E3.
; Returns current value without clearing it.
; Out: A = $E3 (0 if no key pending)
; =================================================================
PEEK_CHAR:
  FA6E  A5 E3     LDA KEY_BUF
  FA70  48        PHA
  FA71  A9 00     LDA #$00
  FA73  85 E3     STA KEY_BUF
  FA75  68        PLA
  FA76  60        RTS

; =================================================================
; LOAD_DEFAULT_REGS
; -----------------------------------------------------------------
; Load the 6-byte default display pattern from $FFE4
; into display buffer $A3-$A8.
; =================================================================
LOAD_DEFAULT_REGS:
  FA77  20 85 FB  JSR SET_DISPLAY_ON
  FA7A  A2 05     LDX #$05
  FA7C  BD E4 FF  LDA $FFE4,X
  FA7F  95 A3     STA DISP_D1,X
  FA81  CA        DEX
  FA82  10 F8     BPL $FA7C
  FA84  60        RTS

; =================================================================
; ZERO_REGS
; -----------------------------------------------------------------
; Clear the 6-byte display buffer $A3-$A8 to $00
; (blanks all six LED digits).
; =================================================================
ZERO_REGS:
  FA85  20 85 FB  JSR SET_DISPLAY_ON
  FA88  A2 05     LDX #$05
  FA8A  A9 00     LDA #$00
  FA8C  95 A3     STA DISP_D1,X
  FA8E  CA        DEX
  FA8F  10 F9     BPL $FA8A
  FA91  60        RTS

; =================================================================
; FLASH_LEDS
; -----------------------------------------------------------------
; Flash the 6-digit display 10 times as a heartbeat indicator.
; Alternates between current display content and blank,
; with a short DELAY between each state.
; Preserves: A, X, Y
; =================================================================
FLASH_LEDS:
  FA92  A0 0A     LDY #$0A
  FA94  20 77 FA  JSR LOAD_DEFAULT_REGS
  FA97  A9 02     LDA #$02
  FA99  20 31 F9  JSR DELAY
  FA9C  20 85 FA  JSR ZERO_REGS
  FA9F  A9 02     LDA #$02
  FAA1  20 31 F9  JSR DELAY
  FAA4  88        DEY
  FAA5  D0 ED     BNE $FA94
  FAA7  60        RTS

; =================================================================
; IRQ_HANDLER
; -----------------------------------------------------------------
; IRQ/BRK handler — wired to $FFFE (IRQ vector).
; Driven by VIA Timer 1 free-run overflow; handles ALL real-time I/O.
;
; STEP 1 — Triage:
;   Save A,X,Y. Check B flag in stacked SR:
;   if set -> BRK_HANDLER (software breakpoint).
;   Check VIA_IFR bit 6 (Timer 1): if set, read VIA_T1CL to clear IRQ.
;   If other IRQ source, dispatch via zero-page vector ($C4).
;
; STEP 2 — Display multiplex (6-digit 7-segment LED):
;   Read VIA_ORA, strip bit 7, invert lower nibble.
;   Add $10 to advance digit scan (BCD 1->2->3->4->5->6->1).
;   Wrap: value >= $70 -> AND $90 resets to $10 (digit 1).
;   Write new value to VIA_ORA:
;     Port A bits 6:4 -> 7442 BCD decoder -> digit transistor ON.
;   If $E4=0 (display enabled):
;     Load segment byte from $A3-$A8 for current digit,
;     write to VIA_ORB -> lights segments a-g on that digit.
;   Display buffer: $A3=dig1(addr HH) $A4=dig2(addr HL)
;                   $A5=dig3(addr LH) $A6=dig4(addr LL)
;                   $A7=dig5(data H)  $A8=dig6(data L)
;
; STEP 3 — Keyboard scan (24-key, 6 rows x 4 cols):
;   Port A bits 3:0 return active-low column sense via 7400.
;   Inverted -> active-high. Keycode = row_value | col_bits.
;   Debounce via $EB/$EC (must be stable for 2 IRQ ticks).
;   Confirmed keypress written to $E3 for GET_CHAR to collect.
;   Key encoding:  upper nibble = row (BCD 1-6)
;                  lower nibble = active column bits
;     e.g. '0'=$18 (row1,col8)  '5'=$24 (row2,col4)
;          '9'=$22 (row2,col2)  GO=$68  (row6,col8)
;
; STEP 4 — BCD timers:
;   Decrement packed-BCD counter $D5/$D6 (SED mode).
;   On underflow: reload and increment score at $A9-$AB.
;
; STEP 5 — Restore A,X,Y and RTI.
; =================================================================
IRQ_HANDLER:
  FAA8  48        PHA
  FAA9  8A        TXA
  FAAA  48        PHA
  FAAB  98        TYA
  FAAC  48        PHA
  FAAD  BA        TSX
  FAAE  BD 04 01  LDA $0104,X
  FAB1  29 10     AND #$10
  FAB3  F0 03     BEQ $FAB8
  FAB5  4C 2D FC  JMP BRK_HANDLER
  FAB8  2C 0D A0  BIT VIA_IFR  ; 6522: VIA_IFR
  FABB  70 07     BVS $FAC4
  FABD  A5 C3     LDA IRQ2_FLAG
  FABF  D0 03     BNE $FAC4
  FAC1  6C C4 00  JMP ($00C4)
  FAC4  A5 C9     LDA RSV_FLAG
  FAC6  D0 09     BNE $FAD1
  FAC8  20 CE FA  JSR $FACE
  FACB  4C D1 FA  JMP $FAD1
  FACE  6C CA 00  JMP ($00CA)
  FAD1  AD 04 A0  LDA VIA_T1CL  ; 6522: VIA_T1CL
  FAD4  58        CLI
  FAD5  A5 E5     LDA DISP_ENABLE
  FAD7  D0 06     BNE $FADF
  FAD9  20 94 FB  JSR LOAD_ADDR_SEGS
  FADC  20 A7 FB  JSR LOAD_DATA_SEGS
  FADF  20 7D FB  JSR READ_VIA_ORA_INV
  FAE2  18        CLC
  FAE3  69 10     ADC #$10
  FAE5  C9 70     CMP #$70
  FAE7  B0 05     BCS $FAEE
  FAE9  8D 01 A0  STA VIA_ORA  ; 6522: VIA_ORA
  FAEC  90 05     BCC $FAF3
  FAEE  29 90     AND #$90
  FAF0  8D 01 A0  STA VIA_ORA  ; 6522: VIA_ORA
  FAF3  A6 E4     LDX DISP_FREEZE
  FAF5  D0 0C     BNE $FB03
  FAF7  29 70     AND #$70
  FAF9  4A        LSR A
  FAFA  4A        LSR A
  FAFB  4A        LSR A
  FAFC  4A        LSR A
  FAFD  AA        TAX
  FAFE  B5 A2     LDA DATA_BYTE,X
  FB00  8D 00 A0  STA VIA_ORB  ; 6522: VIA_ORB
  FB03  20 7D FB  JSR READ_VIA_ORA_INV
  FB06  A6 EC     LDX KEY_DEBOUNCE
  FB08  CA        DEX
  FB09  F0 0D     BEQ $FB18
  FB0B  10 24     BPL $FB31
  FB0D  AA        TAX
  FB0E  29 0F     AND #$0F
  FB10  F0 33     BEQ $FB45
  FB12  86 EB     STX KEY_RAW
  FB14  E6 EC     INC KEY_DEBOUNCE
  FB16  D0 2D     BNE $FB45
  FB18  AA        TAX
  FB19  29 70     AND #$70
  FB1B  85 ED     STA $ED
  FB1D  A5 EB     LDA KEY_RAW
  FB1F  29 70     AND #$70
  FB21  C5 ED     CMP $ED
  FB23  D0 20     BNE $FB45
  FB25  8A        TXA
  FB26  C5 EB     CMP KEY_RAW
  FB28  D0 1B     BNE $FB45
  FB2A  85 E3     STA KEY_BUF
  FB2C  E6 EC     INC KEY_DEBOUNCE
  FB2E  4C 45 FB  JMP $FB45
  FB31  C5 EB     CMP KEY_RAW
  FB33  F0 10     BEQ $FB45
  FB35  29 70     AND #$70
  FB37  85 ED     STA $ED
  FB39  A5 EB     LDA KEY_RAW
  FB3B  29 70     AND #$70
  FB3D  C5 ED     CMP $ED
  FB3F  D0 04     BNE $FB45
  FB41  A9 00     LDA #$00
  FB43  85 EC     STA KEY_DEBOUNCE
  FB45  F8        SED
  FB46  C6 D5     DEC TIMER_LO
  FB48  D0 2C     BNE $FB76
  FB4A  C6 D6     DEC TIMER_HI
  FB4C  10 28     BPL $FB76
  FB4E  A9 90     LDA #$90
  FB50  85 D5     STA TIMER_LO
  FB52  A9 01     LDA #$01
  FB54  85 D6     STA TIMER_HI
  FB56  A0 00     LDY #$00
  FB58  A2 02     LDX #$02
  FB5A  B5 A9     LDA SCORE_LO,X
  FB5C  18        CLC
  FB5D  69 01     ADC #$01
  FB5F  E0 00     CPX #$00
  FB61  D0 06     BNE $FB69
  FB63  C9 24     CMP #$24
  FB65  B0 06     BCS $FB6D
  FB67  90 0B     BCC $FB74
  FB69  C9 60     CMP #$60
  FB6B  90 07     BCC $FB74
  FB6D  94 A9     STY SCORE_LO,X
  FB6F  CA        DEX
  FB70  10 E8     BPL $FB5A
  FB72  30 02     BMI $FB76
  FB74  95 A9     STA SCORE_LO,X
  FB76  D8        CLD
  FB77  68        PLA
  FB78  A8        TAY
  FB79  68        PLA
  FB7A  AA        TAX
  FB7B  68        PLA
  FB7C  40        RTI

; =================================================================
; READ_VIA_ORA_INV
; -----------------------------------------------------------------
; Read VIA Port A and condition for keyboard decode.
;   AND $7F  -> strip bit 7
;   EOR $0F  -> invert lower nibble: active-low -> active-high
; Result: bits 6:4 = current digit scan position (BCD 1-6)
;         bits 3:0 = 1 for each column where a key IS pressed
; Out: A = conditioned Port A value
; =================================================================
READ_VIA_ORA_INV:
  FB7D  A9 7F     LDA #$7F
  FB7F  2D 01 A0  AND VIA_ORA  ; 6522: VIA_ORA
  FB82  49 0F     EOR #$0F
  FB84  60        RTS

; =================================================================
; SET_DISPLAY_ON
; -----------------------------------------------------------------
; Enable display output: set $E5=$FF then call INIT_VIA.
; Shares code body with SET_DISPLAY_OFF (entry point determines
; whether $E5 is set to $FF or $00 before the shared tail).
; Out: $E5=$FF, VIA reinitialised, display scanning active
; =================================================================
SET_DISPLAY_ON:
  FB85  48        PHA
  FB86  A9 FF     LDA #$FF
  FB88  D0 03     BNE $FB8D

; =================================================================
; SET_DISPLAY_OFF
; -----------------------------------------------------------------
; Disable display output: set $E5=$00 then call INIT_VIA.
; Out: $E5=$00, VIA reinitialised, display output suppressed
; =================================================================
SET_DISPLAY_OFF:
  FB8A  48        PHA
  FB8B  A9 00     LDA #$00
  FB8D  85 E5     STA DISP_ENABLE
  FB8F  20 E8 FC  JSR INIT_VIA
  FB92  68        PLA
  FB93  60        RTS

; =================================================================
; LOAD_ADDR_SEGS
; -----------------------------------------------------------------
; Convert the 16-bit address in $A0/$A1 to four 7-segment
; codes and store in display buffer $A3-$A6.
; Calls BYTE_TO_SEG_PAIR twice (high byte then low byte).
; In:  $A0=addr low, $A1=addr high
; Out: $A3-$A6 = segment codes for 4-digit address display
; =================================================================
LOAD_ADDR_SEGS:
  FB94  A5 A1     LDA PTR_HI
  FB96  20 B1 FB  JSR BYTE_TO_SEG_PAIR
  FB99  86 A3     STX DISP_D1
  FB9B  84 A4     STY DISP_D2
  FB9D  A5 A0     LDA PTR_LO
  FB9F  20 B1 FB  JSR BYTE_TO_SEG_PAIR
  FBA2  86 A5     STX DISP_D3
  FBA4  84 A6     STY DISP_D4
  FBA6  60        RTS

; =================================================================
; LOAD_DATA_SEGS
; -----------------------------------------------------------------
; Convert data byte $A2 to two 7-segment codes,
; store in display buffer $A7/$A8.
; In:  $A2 = data byte
; Out: $A7/$A8 = segment codes for 2-digit data display
; =================================================================
LOAD_DATA_SEGS:
  FBA7  A5 A2     LDA DATA_BYTE
  FBA9  20 B1 FB  JSR BYTE_TO_SEG_PAIR
  FBAC  86 A7     STX DISP_D5
  FBAE  84 A8     STY DISP_D6
  FBB0  60        RTS

; =================================================================
; BYTE_TO_SEG_PAIR
; -----------------------------------------------------------------
; Split byte in A into two nibbles, look each up in $FFD4.
; 7-seg table: $77=0 $14=1 $5B=2 $5E=3 $3C=4 $6E=5
;              $6F=6 $54=7 $7F=8 $7E=9 $7D=A $2F=b
;              $63=C $1F=d $6B=E $69=F  (bits = gfedcba)
; In:  A = byte
; Out: X = seg code for high nibble, Y = seg code for low nibble
; =================================================================
BYTE_TO_SEG_PAIR:
  FBB1  A8        TAY
  FBB2  4A        LSR A
  FBB3  4A        LSR A
  FBB4  4A        LSR A
  FBB5  4A        LSR A
  FBB6  AA        TAX
  FBB7  BD D4 FF  LDA $FFD4,X
  FBBA  AA        TAX
  FBBB  98        TYA
  FBBC  29 0F     AND #$0F
  FBBE  A8        TAY
  FBBF  B9 D4 FF  LDA $FFD4,Y
  FBC2  A8        TAY
  FBC3  60        RTS

; =================================================================
; GET_HEX_NIBBLE
; -----------------------------------------------------------------
; Read one hex digit from the keyboard.
; Calls GET_CHAR, searches 16-entry keycode table at $FFF0.
; Keycode table maps matrix scan codes to hex 0-F:
;   0=$18 1=$28 2=$38 3=$48  (rows 1-4, col8)
;   4=$14 5=$24 6=$34 7=$44  (rows 1-4, col4)
;   8=$12 9=$22 A=$46 B=$FD
;   C=$4A D=$F9 E=$A8 F=$FA
; Keycode $64 (delete key) -> carry set, caller restarts entry.
; Out: A = nibble 0-$F, C=0 (valid) or C=1 (delete/cancel)
; =================================================================
GET_HEX_NIBBLE:
  FBC4  20 6A FA  JSR GET_CHAR
  FBC7  C9 64     CMP #$64
  FBC9  F0 13     BEQ $FBDE
  FBCB  A2 00     LDX #$00
  FBCD  DD F0 FF  CMP $FFF0,X
  FBD0  F0 07     BEQ $FBD9
  FBD2  E8        INX
  FBD3  E0 10     CPX #$10
  FBD5  F0 ED     BEQ GET_HEX_NIBBLE
  FBD7  D0 F4     BNE $FBCD
  FBD9  8A        TXA
  FBDA  29 0F     AND #$0F
  FBDC  18        CLC
  FBDD  60        RTS
  FBDE  38        SEC
  FBDF  60        RTS

; =================================================================
; INIT_DISPLAY
; -----------------------------------------------------------------
; Re-initialise display: call INIT_VIA_SOFT, enable display,
; fill buffer $A3-$A8 with $08 (middle segment only = dash).
; Shows '------' on all six digits while awaiting input.
; Preserves: A, X, Y
; =================================================================
INIT_DISPLAY:
  FBE0  20 F0 F8  JSR SAVE_REGS
  FBE3  20 F8 FC  JSR INIT_VIA_SOFT
  FBE6  20 85 FB  JSR SET_DISPLAY_ON
  FBE9  A2 05     LDX #$05
  FBEB  A9 08     LDA #$08
  FBED  95 A3     STA DISP_D1,X
  FBEF  CA        DEX
  FBF0  10 FB     BPL $FBED
  FBF2  20 F3 F8  JSR RESTORE_REGS
  FBF5  60        RTS

; =================================================================
; CMD_ASSEMBLE
; -----------------------------------------------------------------
; Mini assembler command ('A').
; Sets up a 2-byte entry window, reads a target address,
; then loops reading byte values (via GET_HEX_NIBBLE pairs)
; and storing them at successive addresses.
; Exit: press the GO key ($68).
; =================================================================
CMD_ASSEMBLE:
  FBF6  20 E0 FB  JSR INIT_DISPLAY
  FBF9  A9 FD     LDA #$FD
  FBFB  85 A8     STA DISP_D6
  FBFD  A9 3D     LDA #$3D
  FBFF  85 A7     STA DISP_D5
  FC01  20 4A FC  JSR INPUT_ADDRESS
  FC04  A5 A1     LDA PTR_HI
  FC06  85 A9     STA SCORE_LO
  FC08  A5 A0     LDA PTR_LO
  FC0A  85 AA     STA SCORE_MID
  FC0C  A9 00     LDA #$00
  FC0E  85 D5     STA TIMER_LO
  FC10  85 D6     STA TIMER_HI
  FC12  85 AB     STA SCORE_HI

; =================================================================
; CMD_EXAMINE_REGS
; -----------------------------------------------------------------
; Examine/modify saved CPU registers ('!' command).
; Restores saved pointer, calls PEEK_CHAR and loops
; reading keycodes, dispatching to BRK_HANDLER.
; =================================================================
CMD_EXAMINE_REGS:
  FC14  20 8A FB  JSR SET_DISPLAY_OFF
  FC17  A5 A9     LDA SCORE_LO
  FC19  85 A1     STA PTR_HI
  FC1B  A5 AA     LDA SCORE_MID
  FC1D  85 A0     STA PTR_LO
  FC1F  A5 AB     LDA SCORE_HI
  FC21  85 A2     STA DATA_BYTE
  FC23  20 6E FA  JSR PEEK_CHAR
  FC26  C9 68     CMP #$68
  FC28  D0 ED     BNE $FC17
  FC2A  4C 87 F9  JMP MAIN_LOOP

; =================================================================
; BRK_HANDLER
; -----------------------------------------------------------------
; BRK instruction handler.
; Checks $CC: if clear, dispatch via indirect vector ($CD)
; (user BRK hook). Otherwise call INIT_VIA, pull saved
; registers from stack, enter WARM_START.
; In: stacked registers from IRQ_HANDLER
; =================================================================
BRK_HANDLER:
  FC2D  A5 CC     LDA BRK_FLAG
  FC2F  D0 03     BNE $FC34
  FC31  6C CD 00  JMP ($00CD)
  FC34  20 8A FB  JSR SET_DISPLAY_OFF
  FC37  BA        TSX
  FC38  BD 03 01  LDA $0103,X
  FC3B  85 A2     STA DATA_BYTE
  FC3D  BD 01 01  LDA $0101,X
  FC40  85 A0     STA PTR_LO
  FC42  BD 02 01  LDA $0102,X
  FC45  85 A1     STA PTR_HI
  FC47  4C 47 F9  JMP WARM_START

; =================================================================
; INPUT_ADDRESS
; -----------------------------------------------------------------
; Read a 4-digit hex address from the keyboard.
; Calls GET_HEX_NIBBLE four times, packing nibbles into
; $A0 (low byte) and $A1 (high byte).
; Each digit is looked up in $FFD4 and placed in display
; buffer $A3-$A6 as it is typed (live display update).
; Delete key restarts entry from the beginning.
; Out: $A0=addr low, $A1=addr high, $A3-$A6=display codes
; =================================================================
INPUT_ADDRESS:
  FC4A  20 C4 FB  JSR GET_HEX_NIBBLE
  FC4D  B0 FB     BCS INPUT_ADDRESS
  FC4F  0A        ASL A
  FC50  0A        ASL A
  FC51  0A        ASL A
  FC52  0A        ASL A
  FC53  85 E8     STA SAVE_PTR_LO
  FC55  BD D4 FF  LDA $FFD4,X
  FC58  85 A3     STA DISP_D1
  FC5A  A5 A1     LDA PTR_HI
  FC5C  29 0F     AND #$0F
  FC5E  05 E8     ORA SAVE_PTR_LO
  FC60  85 A1     STA PTR_HI
  FC62  20 C4 FB  JSR GET_HEX_NIBBLE
  FC65  90 06     BCC $FC6D
  FC67  A9 08     LDA #$08
  FC69  85 A3     STA DISP_D1
  FC6B  D0 DD     BNE INPUT_ADDRESS
  FC6D  85 E8     STA SAVE_PTR_LO
  FC6F  BD D4 FF  LDA $FFD4,X
  FC72  85 A4     STA DISP_D2
  FC74  A5 A1     LDA PTR_HI
  FC76  29 F0     AND #$F0
  FC78  05 E8     ORA SAVE_PTR_LO
  FC7A  85 A1     STA PTR_HI

; =================================================================
; INPUT_ADDR_HI
; -----------------------------------------------------------------
; Read the high byte (two hex digits) of an address.
; Called mid-way through INPUT_ADDRESS for the upper byte.
; Out: $A1 updated with high byte
; =================================================================
INPUT_ADDR_HI:
  FC7C  20 C4 FB  JSR GET_HEX_NIBBLE
  FC7F  90 06     BCC $FC87
  FC81  A9 08     LDA #$08
  FC83  85 A4     STA DISP_D2
  FC85  D0 DB     BNE $FC62
  FC87  0A        ASL A
  FC88  0A        ASL A
  FC89  0A        ASL A
  FC8A  0A        ASL A
  FC8B  85 E8     STA SAVE_PTR_LO
  FC8D  BD D4 FF  LDA $FFD4,X
  FC90  85 A5     STA DISP_D3
  FC92  A5 A0     LDA PTR_LO
  FC94  29 0F     AND #$0F
  FC96  05 E8     ORA SAVE_PTR_LO
  FC98  85 A0     STA PTR_LO
  FC9A  20 C4 FB  JSR GET_HEX_NIBBLE
  FC9D  90 06     BCC $FCA5
  FC9F  A9 08     LDA #$08
  FCA1  85 A5     STA DISP_D3
  FCA3  D0 D7     BNE INPUT_ADDR_HI
  FCA5  85 E8     STA SAVE_PTR_LO
  FCA7  BD D4 FF  LDA $FFD4,X
  FCAA  85 A6     STA DISP_D4
  FCAC  A5 A0     LDA PTR_LO
  FCAE  29 F0     AND #$F0
  FCB0  05 E8     ORA SAVE_PTR_LO
  FCB2  85 A0     STA PTR_LO
  FCB4  20 8A FB  JSR SET_DISPLAY_OFF
  FCB7  60        RTS

; =================================================================
; INC_PTR
; -----------------------------------------------------------------
; Increment the 16-bit pointer at zero-page $00,X / $01,X.
; Low byte incremented; if it wraps to zero, high byte incremented.
; In:  X = zero-page base of pointer pair
; =================================================================
INC_PTR:
  FCB8  F6 00     INC $00,X
  FCBA  D0 02     BNE $FCBE
  FCBC  F6 01     INC $01,X
  FCBE  60        RTS

; =================================================================
; DEC_PTR
; -----------------------------------------------------------------
; Decrement the 16-bit pointer at zero-page $00,X / $01,X.
; Low byte decremented; if it wraps to $FF, high byte decremented.
; In:  X = zero-page base of pointer pair
; =================================================================
DEC_PTR:
  FCBF  48        PHA
  FCC0  D6 00     DEC $00,X
  FCC2  B5 00     LDA $00,X
  FCC4  C9 FF     CMP #$FF
  FCC6  D0 02     BNE $FCCA
  FCC8  D6 01     DEC $01,X
  FCCA  68        PLA
  FCCB  60        RTS

; =================================================================
; CMD_GO
; -----------------------------------------------------------------
; Go/Execute command.
; Calls INIT_VIA, then loops stepping $A2 and $A0/$A1
; with a 1-unit delay between each step.
; Effectively a slow-run / single-step-with-delay loop.
; =================================================================
CMD_GO:
  FCCC  20 8A FB  JSR SET_DISPLAY_OFF
  FCCF  A9 01     LDA #$01
  FCD1  20 31 F9  JSR DELAY
  FCD4  E6 A2     INC DATA_BYTE
  FCD6  D0 F7     BNE $FCCF
  FCD8  E6 A0     INC PTR_LO
  FCDA  D0 F3     BNE $FCCF
  FCDC  E6 A1     INC PTR_HI
  FCDE  4C CF FC  JMP $FCCF

; =================================================================
; CMD_RESET_PC
; -----------------------------------------------------------------
; Reset PC command ('Q').
; Zeroes $D3 (saved PC high) and jumps to COLD_START.
; Because the warm-start cookie ($33/$33) is intact in
; $D3/$D4, COLD_START will skip straight to MAIN_LOOP.
; =================================================================
CMD_RESET_PC:
  FCE1  A9 00     LDA #$00
  FCE3  85 D3     STA PC_HI
  FCE5  4C 4A F9  JMP COLD_START

; =================================================================
; INIT_VIA
; -----------------------------------------------------------------
; Full hardware initialisation of the 6522 VIA.
; 1. Write $7F to VIA_IER -> disable all interrupts.
; 2. Copy 15 bytes from init table at $FD56 into VIA
;    registers $A000-$A00E, configuring:
;      DDRB = $FF output  (segment data -> LED display)
;      DDRA = $70 partial (bits 6:4 out = 7442 digit select,
;                          bits 3:0 in  = keyboard col sense)
;      T1 latch  = scan period (2 MHz -> ~1 kHz IRQ rate)
;      ACR = Timer 1 free-run, continuous IRQ generation
;      PCR = CA1/CA2/CB1/CB2 passive
;      IER = enable Timer 1 interrupt only
; Clobbers: A, X
; =================================================================
INIT_VIA:
  FCE8  A2 7F     LDX #$7F
  FCEA  8E 0E A0  STX VIA_IER  ; 6522: VIA_IER
  FCED  A2 0E     LDX #$0E
  FCEF  BD 56 FD  LDA $FD56,X
  FCF2  9D 00 A0  STA VIA_ORB,X  ; 6522: VIA_ORB
  FCF5  CA        DEX
  FCF6  10 F7     BPL $FCEF

; =================================================================
; INIT_VIA_SOFT
; -----------------------------------------------------------------
; Software-only VIA reinit (no hardware register writes).
; Clears $E4 (display freeze flag -> scanning enabled),
; clears decimal mode (CLD), enables interrupts (CLI).
; Out: $E4=0, decimal mode off, IRQs enabled
; =================================================================
INIT_VIA_SOFT:
  FCF8  A2 00     LDX #$00
  FCFA  86 E4     STX DISP_FREEZE
  FCFC  D8        CLD
  FCFD  58        CLI
  FCFE  60        RTS
  FCFF  20 07 FD  JSR INIT_VECTORS
  FD02  A9 06     LDA #$06
  FD04  4C 3A F8  JMP REPORT_ERROR

; =================================================================
; INIT_VECTORS
; -----------------------------------------------------------------
; Initialise zero-page indirect jump vectors from ROM table.
; Copies entries from $FD53 into zero-page slots:
;   $C0/$C1/$C2  — idle callback vector
;   $C3/$C4/$C5  — secondary IRQ vector
;   $C6/$C7/$C8  — NMI vector
;   $C9/$CA/$CB  — reserved
;   $CC/$CD/$CE  — BRK hook vector
; All default to JMP MAIN_LOOP or JMP COLD_START.
; =================================================================
INIT_VECTORS:
  FD07  A2 02     LDX #$02
  FD09  BD 53 FD  LDA INIT_DATA,X
  FD0C  95 C0     STA IDLE_FLAG,X
  FD0E  95 C3     STA IRQ2_FLAG,X
  FD10  95 C6     STA NMI_FLAG,X
  FD12  95 C9     STA RSV_FLAG,X
  FD14  95 CC     STA BRK_FLAG,X
  FD16  CA        DEX
  FD17  10 F0     BPL $FD09
  FD19  60        RTS

; =================================================================
; CLEAR_RAM
; -----------------------------------------------------------------
; Clear working RAM on cold start.
; Zeroes: $AD-$AF (saved A/X/Y registers)
;         $E3-$EC (keyboard buffer and display state)
;         $0200-$07FF (RAM pages 2-7, 6 x 256 bytes)
; =================================================================
CLEAR_RAM:
  FD1A  A9 00     LDA #$00
  FD1C  85 AD     STA SAVE_A
  FD1E  85 AE     STA SAVE_X
  FD20  85 AF     STA SAVE_Y
  FD22  A2 09     LDX #$09
  FD24  95 E3     STA KEY_BUF,X
  FD26  CA        DEX
  FD27  10 FB     BPL $FD24
  FD29  AA        TAX
  FD2A  9D 00 02  STA $0200,X
  FD2D  9D 00 03  STA $0300,X
  FD30  9D 00 04  STA $0400,X
  FD33  9D 00 05  STA $0500,X
  FD36  9D 00 06  STA $0600,X
  FD39  9D 00 07  STA $0700,X
  FD3C  E8        INX
  FD3D  D0 EB     BNE $FD2A
  FD3F  85 A9     STA SCORE_LO
  FD41  85 AA     STA SCORE_MID
  FD43  85 AB     STA SCORE_HI
  FD45  60        RTS

; =================================================================
; NMI_HANDLER
; -----------------------------------------------------------------
; NMI handler stub.
; Saves registers, checks $C6: if clear dispatch via ($C7).
; Otherwise jump via ($FFFC) -> COLD_START (warm restart).
; =================================================================
NMI_HANDLER:
  FD46  20 F0 F8  JSR SAVE_REGS
  FD49  A5 C6     LDA NMI_FLAG
  FD4B  D0 03     BNE $FD50
  FD4D  6C C7 00  JMP ($00C7)
  FD50  6C FC FF  JMP ($FFFC)

; =================================================================
; INIT_DATA
; -----------------------------------------------------------------
; Initialisation data block (ROM tables):
;   $FD53        — default vector data (copied by INIT_VECTORS)
;   $FD56        — 15-byte VIA init table (copied by INIT_VIA)
;   $FFD4        — 16-entry 7-seg lookup (digits 0-F, gfedcba)
;   $FFE4        — 6-byte default register display pattern
;   $FFEA        — 6-byte startup splash: 'CE5-65' (7-seg codes)
;   $FFF0        — 16-entry keycode table (matrix codes -> 0-F)
;   $FFFA/$FFFB  — NMI  vector = $FFFF (not used)
;   $FFFC/$FFFD  — RESET vector = $F94A (COLD_START)
;   $FFFE/$FFFF  — IRQ  vector = $FAA8 (IRQ_HANDLER)
; =================================================================
INIT_DATA:
  FD53  FF         .BYTE $FF
  FD54  FF         .BYTE $FF
  FD55  FC         .BYTE $FC
  FD56  00        BRK
  FD57  10 7F     BPL $FDD8
  FD59  70 6B     BVS $FDC6
  FD5B  04         .BYTE $04
  FD5C  00        BRK
  FD5D  00        BRK
  FD5E  00        BRK
  FD5F  00        BRK
  FD60  00        BRK
  FD61  40        RTI
  FD62  00        BRK
  FD63  00        BRK
  FD64  C0 6B     CPY #$6B
  FD66  09 09     ORA #$09
  FD68  0F         .BYTE $0F
  FD69  09 FF     ORA #$FF
  FD6B  FF         .BYTE $FF
  FD6C  FF         .BYTE $FF
  FD6D  FF         .BYTE $FF
  FD6E  FF         .BYTE $FF
  FD6F  FF         .BYTE $FF
  FD70  FF         .BYTE $FF
  FD71  FF         .BYTE $FF
  FD72  FF         .BYTE $FF
  FD73  FF         .BYTE $FF
  FD74  FF         .BYTE $FF
  FD75  FF         .BYTE $FF
  FD76  FF         .BYTE $FF
  FD77  FF         .BYTE $FF
  FD78  FF         .BYTE $FF
  FD79  FF         .BYTE $FF
  FD7A  FF         .BYTE $FF
  FD7B  FF         .BYTE $FF
  FD7C  FF         .BYTE $FF
  FD7D  FF         .BYTE $FF
  FD7E  FF         .BYTE $FF
  FD7F  FF         .BYTE $FF
  FD80  FF         .BYTE $FF
  FD81  FF         .BYTE $FF
  FD82  FF         .BYTE $FF
  FD83  FF         .BYTE $FF
  FD84  FF         .BYTE $FF
  FD85  FF         .BYTE $FF
  FD86  FF         .BYTE $FF
  FD87  FF         .BYTE $FF
  FD88  FF         .BYTE $FF
  FD89  FF         .BYTE $FF
  FD8A  FF         .BYTE $FF
  FD8B  FF         .BYTE $FF
  FD8C  FF         .BYTE $FF
  FD8D  FF         .BYTE $FF
  FD8E  FF         .BYTE $FF
  FD8F  FF         .BYTE $FF
  FD90  FF         .BYTE $FF
  FD91  FF         .BYTE $FF
  FD92  FF         .BYTE $FF
  FD93  FF         .BYTE $FF
  FD94  FF         .BYTE $FF
  FD95  FF         .BYTE $FF
  FD96  FF         .BYTE $FF
  FD97  FF         .BYTE $FF
  FD98  FF         .BYTE $FF
  FD99  FF         .BYTE $FF
  FD9A  FF         .BYTE $FF
  FD9B  FF         .BYTE $FF
  FD9C  FF         .BYTE $FF
  FD9D  FF         .BYTE $FF
  FD9E  FF         .BYTE $FF
  FD9F  FF         .BYTE $FF
  FDA0  FF         .BYTE $FF
  FDA1  FF         .BYTE $FF
  FDA2  FF         .BYTE $FF
  FDA3  FF         .BYTE $FF
  FDA4  FF         .BYTE $FF
  FDA5  FF         .BYTE $FF
  FDA6  FF         .BYTE $FF
  FDA7  FF         .BYTE $FF
  FDA8  FF         .BYTE $FF
  FDA9  FF         .BYTE $FF
  FDAA  FF         .BYTE $FF
  FDAB  FF         .BYTE $FF
  FDAC  FF         .BYTE $FF
  FDAD  FF         .BYTE $FF
  FDAE  FF         .BYTE $FF
  FDAF  FF         .BYTE $FF
  FDB0  FF         .BYTE $FF
  FDB1  FF         .BYTE $FF
  FDB2  FF         .BYTE $FF
  FDB3  FF         .BYTE $FF
  FDB4  FF         .BYTE $FF
  FDB5  FF         .BYTE $FF
  FDB6  FF         .BYTE $FF
  FDB7  FF         .BYTE $FF
  FDB8  FF         .BYTE $FF
  FDB9  FF         .BYTE $FF
  FDBA  FF         .BYTE $FF
  FDBB  FF         .BYTE $FF
  FDBC  FF         .BYTE $FF
  FDBD  FF         .BYTE $FF
  FDBE  FF         .BYTE $FF
  FDBF  FF         .BYTE $FF
  FDC0  FF         .BYTE $FF
  FDC1  FF         .BYTE $FF
  FDC2  FF         .BYTE $FF
  FDC3  FF         .BYTE $FF
  FDC4  FF         .BYTE $FF
  FDC5  FF         .BYTE $FF
  FDC6  FF         .BYTE $FF
  FDC7  FF         .BYTE $FF
  FDC8  FF         .BYTE $FF
  FDC9  FF         .BYTE $FF
  FDCA  FF         .BYTE $FF
  FDCB  FF         .BYTE $FF
  FDCC  FF         .BYTE $FF
  FDCD  FF         .BYTE $FF
  FDCE  FF         .BYTE $FF
  FDCF  FF         .BYTE $FF
  FDD0  FF         .BYTE $FF
  FDD1  FF         .BYTE $FF
  FDD2  FF         .BYTE $FF
  FDD3  FF         .BYTE $FF
  FDD4  FF         .BYTE $FF
  FDD5  FF         .BYTE $FF
  FDD6  FF         .BYTE $FF
  FDD7  FF         .BYTE $FF
  FDD8  FF         .BYTE $FF
  FDD9  FF         .BYTE $FF
  FDDA  FF         .BYTE $FF
  FDDB  FF         .BYTE $FF
  FDDC  FF         .BYTE $FF
  FDDD  FF         .BYTE $FF
  FDDE  FF         .BYTE $FF
  FDDF  FF         .BYTE $FF
  FDE0  FF         .BYTE $FF
  FDE1  FF         .BYTE $FF
  FDE2  FF         .BYTE $FF
  FDE3  FF         .BYTE $FF
  FDE4  FF         .BYTE $FF
  FDE5  FF         .BYTE $FF
  FDE6  FF         .BYTE $FF
  FDE7  FF         .BYTE $FF
  FDE8  FF         .BYTE $FF
  FDE9  FF         .BYTE $FF
  FDEA  FF         .BYTE $FF
  FDEB  FF         .BYTE $FF
  FDEC  FF         .BYTE $FF
  FDED  FF         .BYTE $FF
  FDEE  FF         .BYTE $FF
  FDEF  FF         .BYTE $FF
  FDF0  FF         .BYTE $FF
  FDF1  FF         .BYTE $FF
  FDF2  FF         .BYTE $FF
  FDF3  FF         .BYTE $FF
  FDF4  FF         .BYTE $FF
  FDF5  FF         .BYTE $FF
  FDF6  FF         .BYTE $FF
  FDF7  FF         .BYTE $FF
  FDF8  FF         .BYTE $FF
  FDF9  FF         .BYTE $FF
  FDFA  FF         .BYTE $FF
  FDFB  FF         .BYTE $FF
  FDFC  FF         .BYTE $FF
  FDFD  FF         .BYTE $FF
  FDFE  FF         .BYTE $FF
  FDFF  FF         .BYTE $FF
  FE00  FF         .BYTE $FF
  FE01  FF         .BYTE $FF
  FE02  FF         .BYTE $FF
  FE03  FF         .BYTE $FF
  FE04  FF         .BYTE $FF
  FE05  FF         .BYTE $FF
  FE06  FF         .BYTE $FF
  FE07  FF         .BYTE $FF
  FE08  FF         .BYTE $FF
  FE09  FF         .BYTE $FF
  FE0A  FF         .BYTE $FF
  FE0B  FF         .BYTE $FF
  FE0C  FF         .BYTE $FF
  FE0D  FF         .BYTE $FF
  FE0E  FF         .BYTE $FF
  FE0F  FF         .BYTE $FF
  FE10  FF         .BYTE $FF
  FE11  FF         .BYTE $FF
  FE12  FF         .BYTE $FF
  FE13  FF         .BYTE $FF
  FE14  FF         .BYTE $FF
  FE15  FF         .BYTE $FF
  FE16  FF         .BYTE $FF
  FE17  FF         .BYTE $FF
  FE18  FF         .BYTE $FF
  FE19  FF         .BYTE $FF
  FE1A  FF         .BYTE $FF
  FE1B  FF         .BYTE $FF
  FE1C  FF         .BYTE $FF
  FE1D  FF         .BYTE $FF
  FE1E  FF         .BYTE $FF
  FE1F  FF         .BYTE $FF
  FE20  FF         .BYTE $FF
  FE21  FF         .BYTE $FF
  FE22  FF         .BYTE $FF
  FE23  FF         .BYTE $FF
  FE24  FF         .BYTE $FF
  FE25  FF         .BYTE $FF
  FE26  FF         .BYTE $FF
  FE27  FF         .BYTE $FF
  FE28  FF         .BYTE $FF
  FE29  FF         .BYTE $FF
  FE2A  FF         .BYTE $FF
  FE2B  FF         .BYTE $FF
  FE2C  FF         .BYTE $FF
  FE2D  FF         .BYTE $FF
  FE2E  FF         .BYTE $FF
  FE2F  FF         .BYTE $FF
  FE30  FF         .BYTE $FF
  FE31  FF         .BYTE $FF
  FE32  FF         .BYTE $FF
  FE33  FF         .BYTE $FF
  FE34  FF         .BYTE $FF
  FE35  FF         .BYTE $FF
  FE36  FF         .BYTE $FF
  FE37  FF         .BYTE $FF
  FE38  FF         .BYTE $FF
  FE39  FF         .BYTE $FF
  FE3A  FF         .BYTE $FF
  FE3B  FF         .BYTE $FF
  FE3C  FF         .BYTE $FF
  FE3D  FF         .BYTE $FF
  FE3E  FF         .BYTE $FF
  FE3F  FF         .BYTE $FF
  FE40  FF         .BYTE $FF
  FE41  FF         .BYTE $FF
  FE42  FF         .BYTE $FF
  FE43  FF         .BYTE $FF
  FE44  FF         .BYTE $FF
  FE45  FF         .BYTE $FF
  FE46  FF         .BYTE $FF
  FE47  FF         .BYTE $FF
  FE48  FF         .BYTE $FF
  FE49  FF         .BYTE $FF
  FE4A  FF         .BYTE $FF
  FE4B  FF         .BYTE $FF
  FE4C  FF         .BYTE $FF
  FE4D  FF         .BYTE $FF
  FE4E  FF         .BYTE $FF
  FE4F  FF         .BYTE $FF
  FE50  FF         .BYTE $FF
  FE51  FF         .BYTE $FF
  FE52  FF         .BYTE $FF
  FE53  FF         .BYTE $FF
  FE54  FF         .BYTE $FF
  FE55  FF         .BYTE $FF
  FE56  FF         .BYTE $FF
  FE57  FF         .BYTE $FF
  FE58  FF         .BYTE $FF
  FE59  FF         .BYTE $FF
  FE5A  FF         .BYTE $FF
  FE5B  FF         .BYTE $FF
  FE5C  FF         .BYTE $FF
  FE5D  FF         .BYTE $FF
  FE5E  FF         .BYTE $FF
  FE5F  FF         .BYTE $FF
  FE60  FF         .BYTE $FF
  FE61  FF         .BYTE $FF
  FE62  FF         .BYTE $FF
  FE63  FF         .BYTE $FF
  FE64  FF         .BYTE $FF
  FE65  FF         .BYTE $FF
  FE66  FF         .BYTE $FF
  FE67  FF         .BYTE $FF
  FE68  FF         .BYTE $FF
  FE69  FF         .BYTE $FF
  FE6A  FF         .BYTE $FF
  FE6B  FF         .BYTE $FF
  FE6C  FF         .BYTE $FF
  FE6D  FF         .BYTE $FF
  FE6E  FF         .BYTE $FF
  FE6F  FF         .BYTE $FF
  FE70  FF         .BYTE $FF
  FE71  FF         .BYTE $FF
  FE72  FF         .BYTE $FF
  FE73  FF         .BYTE $FF
  FE74  FF         .BYTE $FF
  FE75  FF         .BYTE $FF
  FE76  FF         .BYTE $FF
  FE77  FF         .BYTE $FF
  FE78  FF         .BYTE $FF
  FE79  FF         .BYTE $FF
  FE7A  FF         .BYTE $FF
  FE7B  FF         .BYTE $FF
  FE7C  FF         .BYTE $FF
  FE7D  FF         .BYTE $FF
  FE7E  FF         .BYTE $FF
  FE7F  FF         .BYTE $FF
  FE80  FF         .BYTE $FF
  FE81  FF         .BYTE $FF
  FE82  FF         .BYTE $FF
  FE83  FF         .BYTE $FF
  FE84  FF         .BYTE $FF
  FE85  FF         .BYTE $FF
  FE86  FF         .BYTE $FF
  FE87  FF         .BYTE $FF
  FE88  FF         .BYTE $FF
  FE89  FF         .BYTE $FF
  FE8A  FF         .BYTE $FF
  FE8B  FF         .BYTE $FF
  FE8C  FF         .BYTE $FF
  FE8D  FF         .BYTE $FF
  FE8E  FF         .BYTE $FF
  FE8F  FF         .BYTE $FF
  FE90  FF         .BYTE $FF
  FE91  FF         .BYTE $FF
  FE92  FF         .BYTE $FF
  FE93  FF         .BYTE $FF
  FE94  FF         .BYTE $FF
  FE95  FF         .BYTE $FF
  FE96  FF         .BYTE $FF
  FE97  FF         .BYTE $FF
  FE98  FF         .BYTE $FF
  FE99  FF         .BYTE $FF
  FE9A  FF         .BYTE $FF
  FE9B  FF         .BYTE $FF
  FE9C  FF         .BYTE $FF
  FE9D  FF         .BYTE $FF
  FE9E  FF         .BYTE $FF
  FE9F  FF         .BYTE $FF
  FEA0  FF         .BYTE $FF
  FEA1  FF         .BYTE $FF
  FEA2  FF         .BYTE $FF
  FEA3  FF         .BYTE $FF
  FEA4  FF         .BYTE $FF
  FEA5  FF         .BYTE $FF
  FEA6  FF         .BYTE $FF
  FEA7  FF         .BYTE $FF
  FEA8  FF         .BYTE $FF
  FEA9  FF         .BYTE $FF
  FEAA  FF         .BYTE $FF
  FEAB  FF         .BYTE $FF
  FEAC  FF         .BYTE $FF
  FEAD  FF         .BYTE $FF
  FEAE  FF         .BYTE $FF
  FEAF  FF         .BYTE $FF
  FEB0  FF         .BYTE $FF
  FEB1  FF         .BYTE $FF
  FEB2  FF         .BYTE $FF
  FEB3  FF         .BYTE $FF
  FEB4  FF         .BYTE $FF
  FEB5  FF         .BYTE $FF
  FEB6  FF         .BYTE $FF
  FEB7  FF         .BYTE $FF
  FEB8  FF         .BYTE $FF
  FEB9  FF         .BYTE $FF
  FEBA  FF         .BYTE $FF
  FEBB  FF         .BYTE $FF
  FEBC  FF         .BYTE $FF
  FEBD  FF         .BYTE $FF
  FEBE  FF         .BYTE $FF
  FEBF  FF         .BYTE $FF
  FEC0  FF         .BYTE $FF
  FEC1  FF         .BYTE $FF
  FEC2  FF         .BYTE $FF
  FEC3  FF         .BYTE $FF
  FEC4  FF         .BYTE $FF
  FEC5  FF         .BYTE $FF
  FEC6  FF         .BYTE $FF
  FEC7  FF         .BYTE $FF
  FEC8  FF         .BYTE $FF
  FEC9  FF         .BYTE $FF
  FECA  FF         .BYTE $FF
  FECB  FF         .BYTE $FF
  FECC  FF         .BYTE $FF
  FECD  FF         .BYTE $FF
  FECE  FF         .BYTE $FF
  FECF  FF         .BYTE $FF
  FED0  FF         .BYTE $FF
  FED1  FF         .BYTE $FF
  FED2  FF         .BYTE $FF
  FED3  FF         .BYTE $FF
  FED4  FF         .BYTE $FF
  FED5  FF         .BYTE $FF
  FED6  FF         .BYTE $FF
  FED7  FF         .BYTE $FF
  FED8  FF         .BYTE $FF
  FED9  FF         .BYTE $FF
  FEDA  FF         .BYTE $FF
  FEDB  FF         .BYTE $FF
  FEDC  FF         .BYTE $FF
  FEDD  FF         .BYTE $FF
  FEDE  FF         .BYTE $FF
  FEDF  FF         .BYTE $FF
  FEE0  FF         .BYTE $FF
  FEE1  FF         .BYTE $FF
  FEE2  FF         .BYTE $FF
  FEE3  FF         .BYTE $FF
  FEE4  FF         .BYTE $FF
  FEE5  FF         .BYTE $FF
  FEE6  FF         .BYTE $FF
  FEE7  FF         .BYTE $FF
  FEE8  FF         .BYTE $FF
  FEE9  FF         .BYTE $FF
  FEEA  FF         .BYTE $FF
  FEEB  FF         .BYTE $FF
  FEEC  FF         .BYTE $FF
  FEED  FF         .BYTE $FF
  FEEE  FF         .BYTE $FF
  FEEF  FF         .BYTE $FF
  FEF0  FF         .BYTE $FF
  FEF1  FF         .BYTE $FF
  FEF2  FF         .BYTE $FF
  FEF3  FF         .BYTE $FF
  FEF4  FF         .BYTE $FF
  FEF5  FF         .BYTE $FF
  FEF6  FF         .BYTE $FF
  FEF7  FF         .BYTE $FF
  FEF8  FF         .BYTE $FF
  FEF9  FF         .BYTE $FF
  FEFA  FF         .BYTE $FF
  FEFB  FF         .BYTE $FF
  FEFC  FF         .BYTE $FF
  FEFD  FF         .BYTE $FF
  FEFE  FF         .BYTE $FF
  FEFF  FF         .BYTE $FF
  FF00  FF         .BYTE $FF
  FF01  FF         .BYTE $FF
  FF02  FF         .BYTE $FF
  FF03  FF         .BYTE $FF
  FF04  FF         .BYTE $FF
  FF05  FF         .BYTE $FF
  FF06  FF         .BYTE $FF
  FF07  FF         .BYTE $FF
  FF08  FF         .BYTE $FF
  FF09  FF         .BYTE $FF
  FF0A  FF         .BYTE $FF
  FF0B  FF         .BYTE $FF
  FF0C  FF         .BYTE $FF
  FF0D  FF         .BYTE $FF
  FF0E  FF         .BYTE $FF
  FF0F  FF         .BYTE $FF
  FF10  FF         .BYTE $FF
  FF11  FF         .BYTE $FF
  FF12  FF         .BYTE $FF
  FF13  FF         .BYTE $FF
  FF14  FF         .BYTE $FF
  FF15  FF         .BYTE $FF
  FF16  FF         .BYTE $FF
  FF17  FF         .BYTE $FF
  FF18  FF         .BYTE $FF
  FF19  FF         .BYTE $FF
  FF1A  FF         .BYTE $FF
  FF1B  FF         .BYTE $FF
  FF1C  FF         .BYTE $FF
  FF1D  FF         .BYTE $FF
  FF1E  FF         .BYTE $FF
  FF1F  FF         .BYTE $FF
  FF20  FF         .BYTE $FF
  FF21  FF         .BYTE $FF
  FF22  FF         .BYTE $FF
  FF23  FF         .BYTE $FF
  FF24  FF         .BYTE $FF
  FF25  FF         .BYTE $FF
  FF26  FF         .BYTE $FF
  FF27  FF         .BYTE $FF
  FF28  FF         .BYTE $FF
  FF29  FF         .BYTE $FF
  FF2A  FF         .BYTE $FF
  FF2B  FF         .BYTE $FF
  FF2C  FF         .BYTE $FF
  FF2D  FF         .BYTE $FF
  FF2E  FF         .BYTE $FF
  FF2F  FF         .BYTE $FF
  FF30  FF         .BYTE $FF
  FF31  FF         .BYTE $FF
  FF32  FF         .BYTE $FF
  FF33  FF         .BYTE $FF
  FF34  FF         .BYTE $FF
  FF35  FF         .BYTE $FF
  FF36  FF         .BYTE $FF
  FF37  FF         .BYTE $FF
  FF38  FF         .BYTE $FF
  FF39  FF         .BYTE $FF
  FF3A  FF         .BYTE $FF
  FF3B  FF         .BYTE $FF
  FF3C  FF         .BYTE $FF
  FF3D  FF         .BYTE $FF
  FF3E  FF         .BYTE $FF
  FF3F  FF         .BYTE $FF
  FF40  FF         .BYTE $FF
  FF41  FF         .BYTE $FF
  FF42  FF         .BYTE $FF
  FF43  FF         .BYTE $FF
  FF44  FF         .BYTE $FF
  FF45  FF         .BYTE $FF
  FF46  FF         .BYTE $FF
  FF47  FF         .BYTE $FF
  FF48  FF         .BYTE $FF
  FF49  FF         .BYTE $FF
  FF4A  FF         .BYTE $FF
  FF4B  FF         .BYTE $FF
  FF4C  FF         .BYTE $FF
  FF4D  FF         .BYTE $FF
  FF4E  FF         .BYTE $FF
  FF4F  FF         .BYTE $FF
  FF50  FF         .BYTE $FF
  FF51  FF         .BYTE $FF
  FF52  FF         .BYTE $FF
  FF53  FF         .BYTE $FF
  FF54  FF         .BYTE $FF
  FF55  FF         .BYTE $FF
  FF56  FF         .BYTE $FF
  FF57  FF         .BYTE $FF
  FF58  FF         .BYTE $FF
  FF59  FF         .BYTE $FF
  FF5A  FF         .BYTE $FF
  FF5B  FF         .BYTE $FF
  FF5C  FF         .BYTE $FF
  FF5D  FF         .BYTE $FF
  FF5E  FF         .BYTE $FF
  FF5F  FF         .BYTE $FF
  FF60  FF         .BYTE $FF
  FF61  FF         .BYTE $FF
  FF62  FF         .BYTE $FF
  FF63  FF         .BYTE $FF
  FF64  FF         .BYTE $FF
  FF65  FF         .BYTE $FF
  FF66  FF         .BYTE $FF
  FF67  FF         .BYTE $FF
  FF68  FF         .BYTE $FF
  FF69  FF         .BYTE $FF
  FF6A  FF         .BYTE $FF
  FF6B  FF         .BYTE $FF
  FF6C  FF         .BYTE $FF
  FF6D  FF         .BYTE $FF
  FF6E  FF         .BYTE $FF
  FF6F  FF         .BYTE $FF
  FF70  FF         .BYTE $FF
  FF71  FF         .BYTE $FF
  FF72  FF         .BYTE $FF
  FF73  FF         .BYTE $FF
  FF74  FF         .BYTE $FF
  FF75  FF         .BYTE $FF
  FF76  FF         .BYTE $FF
  FF77  FF         .BYTE $FF
  FF78  FF         .BYTE $FF
  FF79  FF         .BYTE $FF
  FF7A  FF         .BYTE $FF
  FF7B  FF         .BYTE $FF
  FF7C  FF         .BYTE $FF
  FF7D  FF         .BYTE $FF
  FF7E  FF         .BYTE $FF
  FF7F  FF         .BYTE $FF
  FF80  FF         .BYTE $FF
  FF81  FF         .BYTE $FF
  FF82  FF         .BYTE $FF
  FF83  FF         .BYTE $FF
  FF84  FF         .BYTE $FF
  FF85  FF         .BYTE $FF
  FF86  FF         .BYTE $FF
  FF87  FF         .BYTE $FF
  FF88  FF         .BYTE $FF
  FF89  FF         .BYTE $FF
  FF8A  FF         .BYTE $FF
  FF8B  FF         .BYTE $FF
  FF8C  FF         .BYTE $FF
  FF8D  FF         .BYTE $FF
  FF8E  FF         .BYTE $FF
  FF8F  FF         .BYTE $FF
  FF90  FF         .BYTE $FF
  FF91  FF         .BYTE $FF
  FF92  FF         .BYTE $FF
  FF93  FF         .BYTE $FF
  FF94  FF         .BYTE $FF
  FF95  FF         .BYTE $FF
  FF96  FF         .BYTE $FF
  FF97  FF         .BYTE $FF
  FF98  FF         .BYTE $FF
  FF99  FF         .BYTE $FF
  FF9A  FF         .BYTE $FF
  FF9B  FF         .BYTE $FF
  FF9C  FF         .BYTE $FF
  FF9D  FF         .BYTE $FF
  FF9E  FF         .BYTE $FF
  FF9F  FF         .BYTE $FF
  FFA0  FF         .BYTE $FF
  FFA1  FF         .BYTE $FF
  FFA2  FF         .BYTE $FF
  FFA3  FF         .BYTE $FF
  FFA4  FF         .BYTE $FF
  FFA5  FF         .BYTE $FF
  FFA6  FF         .BYTE $FF
  FFA7  FF         .BYTE $FF
  FFA8  FF         .BYTE $FF
  FFA9  FF         .BYTE $FF
  FFAA  FF         .BYTE $FF
  FFAB  FF         .BYTE $FF
  FFAC  FF         .BYTE $FF
  FFAD  FF         .BYTE $FF
  FFAE  FF         .BYTE $FF
  FFAF  FF         .BYTE $FF
  FFB0  FF         .BYTE $FF
  FFB1  FF         .BYTE $FF
  FFB2  FF         .BYTE $FF
  FFB3  FF         .BYTE $FF
  FFB4  FF         .BYTE $FF
  FFB5  FF         .BYTE $FF
  FFB6  FF         .BYTE $FF
  FFB7  FF         .BYTE $FF
  FFB8  FF         .BYTE $FF
  FFB9  FF         .BYTE $FF
  FFBA  FF         .BYTE $FF
  FFBB  FF         .BYTE $FF
  FFBC  FF         .BYTE $FF
  FFBD  FF         .BYTE $FF
  FFBE  FF         .BYTE $FF
  FFBF  FF         .BYTE $FF
  FFC0  FF         .BYTE $FF
  FFC1  FF         .BYTE $FF
  FFC2  FF         .BYTE $FF
  FFC3  FF         .BYTE $FF
  FFC4  FF         .BYTE $FF
  FFC5  FF         .BYTE $FF
  FFC6  FF         .BYTE $FF
  FFC7  FF         .BYTE $FF
  FFC8  FF         .BYTE $FF
  FFC9  FF         .BYTE $FF
  FFCA  FF         .BYTE $FF
  FFCB  FF         .BYTE $FF
  FFCC  FF         .BYTE $FF
  FFCD  FF         .BYTE $FF
  FFCE  FF         .BYTE $FF
  FFCF  FF         .BYTE $FF
  FFD0  FF         .BYTE $FF
  FFD1  FF         .BYTE $FF
  FFD2  FF         .BYTE $FF
  FFD3  FF         .BYTE $FF
  FFD4  77         .BYTE $77
  FFD5  14         .BYTE $14
  FFD6  5B         .BYTE $5B
  FFD7  5E 3C 6E  LSR $6E3C,X
  FFDA  6F         .BYTE $6F
  FFDB  54         .BYTE $54
  FFDC  7F         .BYTE $7F
  FFDD  7E 7D 2F  ROR $2F7D,X
  FFE0  63         .BYTE $63
  FFE1  1F         .BYTE $1F
  FFE2  6B         .BYTE $6B
  FFE3  69 21     ADC #$21
  FFE5  7D F5 F7  ADC $F7F5,X
  FFE8  7D BE 39  ADC $39BE,X
  FFEB  79 6E 08  ADC $086E,Y
  FFEE  6F         .BYTE $6F
  FFEF  6E 18 28  ROR $2818
  FFF2  38        SEC
  FFF3  48        PHA
  FFF4  14         .BYTE $14
  FFF5  24 34     BIT $34
  FFF7  44         .BYTE $44
  FFF8  12         .BYTE $12
  FFF9  22         .BYTE $22
  FFFA  46 FD     LSR RET_LO
  FFFC  4A        LSR A
  FFFD  F9 A8 FA  SBC IRQ_HANDLER,Y

; --- End of ROM ---
