# PTC-701 Startup Boot Trace — Annotated

Raw trace: `ptc701_startup_trace.txt` (39,817 lines)

---

## 1802 Native Code Boot Sequence

### Phase 1: Reset & Register Initialization (0x0000–0x003E)

```
; === RESET VECTOR — DIS sets P=R0, X=R4 ===
0000: DIS       P=R0, X=R4

; === Set up HD44780 command register pointer (R7=0x3F80) ===
0002: LDI  3F   D=3F
0004: PHI  R7   R7=3F80
0005: LDI  80   D=80
0007: PLO  R7   R7=3F80

; === Write 0x20 (Function Set: 4-bit mode) to HD44780 ===
0008: LDI  20   D=20
000A: STR  R7   M(3F80)=20

; === Set R4 to init data block at 0x04E2 ===
000B: LDI  E2   D=E2
000D: PLO  R4   R4=04E2
000E: LDI  04   D=04
0010: PHI  R4   R4=04E2

; === R6.1 = 0x60 (RAM page), R5.0 = 0x00 ===
0011: LDI  60   D=60
0013: PHI  R6   R6=6011
0014: GHI  R0   D=00
0015: PLO  R5   R5=0000

; === Load registers from init data at 0x04E2 via LDXA ===
0016: LDXA      D=M(04E2)=62    ; R2.1 = 0x62 (stack page)
0017: PHI  R2   R2=6260
0018: LDXA      D=M(04E3)=61    ; RD.1 = 0x61 (data stack page)
0019: PHI  RD   RD=6158
001A: LDXA      D=M(04E4)=02    ; R3.1 = 0x02 (entry point page)
001B: PHI  R3   R3=0200
001C: LDXA      D=M(04E5)=1A    ; R6.0 = 0x1A (system var offset)
001D: PLO  R6   R6=601A
001E: LDXA      D=M(04E6)=01    ; RE.1 = 0x01 (dispatch table page)
001F: PHI  RE   RE=0100
0020: LDXA      D=M(04E7)=0D    ; R1.1 = 0x0D (ISR page)
0021: PHI  R1   R1=0D00
0022: LDXA      D=M(04E8)=00    ; R1.0 = 0x00 → R1=0x0D00 (interrupt handler)
0023: PLO  R1   R1=0D00
0024: LDXA      D=M(04E9)=21    ; RC.1 = 0x21
0025: PHI  RC   RC=2100
0026: LDXA      D=M(04EA)=02    ; RC.0 = 0x02 → RC=0x2102 (warm boot entry)
0027: PLO  RC   RC=2102

; === Clear Q line ===
0028: REQ

; === Warm boot check: read M(601A), compare with 0xAA ===
0029: LDA  R6   D=M(601A)=00    ; RAM signature byte
002A: SMI  AA   D=56, DF=0      ; 0x00 ≠ 0xAA → COLD BOOT
002C: BZ   40                    ; not taken (would skip to warm boot at 0x0040)

; === Cold boot: override RC to 0x2100 ===
002E: LDXA      D=M(04EB)=21
002F: PHI  RC   RC=2100
0030: LDXA      D=M(04EC)=00
0031: PLO  RC   RC=2100          ; RC=0x2100 (cold boot bytecode entry)

; === Clear M(6007) status byte ===
0032: LDI  07   D=07
0034: PLO  R6   R6=6007
0035: GHI  R0   D=00
0036: STR  R6   M(6007)=00

; === Continue loading init data ===
0037: LDI  1B   D=1B
0039: PLO  R6   R6=601B
003A: LDXA      D=M(04ED)=60    ; R2.0 = 0x60
003B: PLO  R2   R2=6260
003C: LDXA      D=M(04EE)=E0    ; RD.0 = 0xE0 → RD=0x61E0 (data stack top)
003D: PLO  RD   RD=61E0

; === Branch to cold boot continuation ===
003E: BR   6B
```

### Phase 2: Status Check & Busy Poll (0x006B–0x0095)

```
; === Read AMI status register, check bit 1 ===
006B: LDI  07   D=07
006D: PLO  R6   R6=6007
006E: LDI  A0   D=A0
0070: PLO  R7   R7=3FA0
0071: LDN  R7   D=M(3FA0)=00    ; AMI status = 0 (normal)
0072: ANI  02   D=00             ; bit 1 = 0 → OK
0074: BZ   7A                    ; taken (normal path)

; === Store 0x20 to M(6066) display state variable ===
007A: LDI  66   D=66
007C: PLO  R6   R6=6066
007D: LDI  20   D=20
007F: BQ   82                    ; Q=0, not taken
0081: STR  R6   M(6066)=20

; === Switch to X=R6, enable interrupts via RET ===
0082: SEX  R0
0083: RET       P=R0, X=R6       ; now P=R0 (PC at 0x0085), X=R6

; === Poll busy flag at M(3FA0) bit 0 ===
0085: LDI  A0   D=A0
0087: PLO  R7   R7=3FA0
0088: LDN  R7   D=M(3FA0)=00    ; status = 0
0089: SHR       D=00, DF=0       ; bit 0 → DF = 0 (not busy)
008A: BDF  85                    ; not taken (not busy, continue)

; === Set up EF4 timeout: R8.1=0x0E, RF trampoline ===
008C: LDI  0E   D=0E
008E: PHI  R8   R8=0E00
008F: LDI  00   D=00
0091: PHI  RF   RF=0000
0092: LDI  96   D=96
0094: PLO  RF   RF=0096
0095: SEP  RF                    ; jump to trampoline at 0x0096

; === Trampoline: set R0 = 0x00FB (main loop return point) ===
0096: LDI  00   D=00
0098: PHI  R0   R0=0000
0099: LDI  FB   D=FB
009B: PLO  R0   R0=00FB
```

### Phase 3: EF4 Startup Wait Loop (0x009C–0x00A3)

```
; === ON/OFF button timeout loop ===
; Waits for EF4=0 (button press) OR R8 timeout.
; EF4=1 (not pressed) → loop counts down R8 from 0x0E54 to 0x00FF.
; Periodic AMI timer interrupts (cause=0x00) fire every ~256 iterations.
; Loop exits when R8.1 reaches 0x00 (GHI R8 = 0).

009C: BN4  A1        ; EF4=1 (not pressed) → branch to 00A1
00A1: DEC  R8        ; R8-- (countdown timer)
00A2: GHI  R8        ; D = R8 high byte
00A3: BNZ  9C        ; if R8.1 ≠ 0, loop back

; ... loops ~3,300 iterations (R8: 0x0E54 → 0x00FF)
; ... 10 AMI timer interrupts handled during this time (see ISR below)
; ... exits when R8 = 0x00FF → GHI R8 = 0x00 → falls through
```

**ISR summary** (fires ~10 times during EF4 loop):
- Entry at 0x0D00: saves T, D, DF/Q state, R6, R7 to return stack
- Reads M(0x3F81) = 0x00 (interrupt cause), adds 0x1D → dispatches to 0x0D9E
- Handler at 0x0D9E: prepares ACK byte 0x01
- Exit at 0x0E55: writes ACK 0x01 to M(0x3F81), restores all registers, RET

### Phase 4: HD44780 Display Initialization (0x00A5–0x00B8)

```
; === Copy HD44780 init commands from ROM (0x04EF) to RAM (0x6074) ===
00A5: BQ   B2              ; Q=0, not taken
00A7: LDI  74   D=74
00A9: PLO  R6   R6=6074    ; dest pointer = 0x6074
00AA: SEX  R4              ; X = R4 (init data pointer at 0x04EF)

; Loop: copy 9 bytes from ROM init data to RAM 0x6074–0x607C
00AB: LDXA      D=M(04EF)=30    ; → M(6074)=30  HD44780: Function Set 8-bit
00AC: STR  R6   M(6074)=30
00AD: INC  R6
00AE: BNZ  AB               ; loop while D≠0 (0x00 terminates)
; ... copies: 30, 30, 38, 01, 08, 06, 80, 0C, 00 (terminator)
; Commands: 0x30×2 (reset), 0x38 (8-bit/2-line), 0x01 (clear),
;           0x08 (display off), 0x06 (entry mode), 0x80 (DDRAM addr 0),
;           0x0C (display on), 0x00 (terminator)

; === Write 0x00 to M(3F00) — RTC test register / AMI master control ===
00B0: PLO  R7   R7=3F00
00B1: STR  R7   M(3F00)=00

; === Call display write subroutine at 0x1833 via SEP RB ===
00B2: LDI  33   D=33
00B4: PLO  RB   RB=1833
00B5: LDI  18   D=18
00B7: PHI  RB   RB=1833
00B8: SEP  RB              ; call HD44780 output routine

; --- Subroutine at 0x1833 (display init writer) ---
; Entry 0xCA: loads R6.0=0x74 (buffer ptr→0x6074), R7.0=0x4F (→0x3F4F data reg)
; Loop at 0x1837: reads byte from M(R6), exits on 0x00
;   For each byte: writes to M(0x3F4F), then 256-cycle delay (SMI 01 loop)
;   Writes: 0x30, 0x30, 0x38, 0x01, 0x08, 0x06, 0x80, 0x0C
;   Terminates on 0x00 at M(607C)

; ... (256-cycle delay loop between each HD44780 command omitted)
; ... HD44780 init sequence runs twice (trace shows two passes)
```

### Phase 5: AMI Peripheral Setup (0x00B9–0x00F5)

```
; === Display timing registers via subroutine calls ===
00B9: BQ   CB              ; Q=0, not taken
00BB: LDI  22   D=22
00BD: PLO  R6   R6=6022    ; RAM buffer for timing values
00BE: SEX  R6

; Store timing values to RAM: M(6022)=10, M(6021)=02, M(6020)=75, M(601F)=40
00BF: LDI  10   →  M(6022)=10
00C2: LDI  02   →  M(6021)=02
00C5: LDI  75   →  M(6020)=75
00C8: LDI  40   →  M(601F)=40

; === Call subroutine at 0x17B6 (AMI timing register writer) ===
00CB: LDI  17
00CD: PHI  RB   RB=17B6
00CE: LDI  B6
00D0: PLO  RB   RB=17B6
00D1: SEP  R0              ; trampoline → entry 0xCA dispatch

; Entry 0xCA: R6.0=0x22 (→0x6022), R7.0=0x60 (→0x3F60)
; Writes: M(3F60) = M(6022) = 0x10  (display timing reg 1)
;         M(3F62) = M(6021) = 0x02  (display timing reg 2)
; ... (one ISR fires during this sequence, omitted)

; === Bank window 2 setup: M(604E)=0, write 3FC0-3FC3 ===
00D2: BQ   D9              ; Q=0, not taken
00D4: LDI  4E
00D6: PLO  R6   R6=604E
00D7: GHI  R0   D=00
00D8: STR  R6   M(604E)=00      ; bank counter = 0

; Call bank register update subroutine at 0x1955
00D9: LDI  19
00DB: PHI  RB   RB=1955
00DC: LDI  55
00DE: PLO  RB   RB=1955
00DF: SEP  RB

; Subroutine dispatches to entry 0xF4 → 0x0451 (bank reg writer):
;   Reads M(604E)=0, SHL×2 (×4) = 0
;   M(3FC0) = 0×4+0 = 0x00  (slot 0, page 0)
;   M(3FC1) = 0×4+1 = 0x01  (slot 1, page 1)
;   M(3FC2) = 0×4+2 = 0x02  (slot 2, page 2)
;   M(3FC3) = 0×4+3 = 0x03  (slot 3, page 3)

; === Bank window 1 setup: M(604F)=0x80, M(3FA2)=0x00 ===
00E0: LDI  4F
00E2: PLO  R6   R6=604F
00E3: LDI  A2
00E5: PLO  R7   R7=3FA2
00E6: LDN  R6   D=M(604F)=00
00E7: LSQ                        ; skip (Q=0)
00E8: LDI  80   D=80
00EA: STR  R6   M(604F)=80      ; bank shadow = 0x80 (enable bit set)
00EB: ANI  7F   D=00             ; strip enable bit
00ED: STR  R7   M(3FA2)=00      ; write bank 0 to hardware

; === Clear M(6000) and M(6001) ===
00EE: BQ   F5                    ; Q=0, not taken
00F0: GHI  R0   D=00
00F1: PLO  R6   R6=6000
00F2: STR  R6   M(6000)=00
00F3: INC  R6   R6=6001
00F4: STR  R6   M(6001)=00

; === Enter bytecode interpreter ===
00F5: LBR  0500                  ; jump to interpreter loop
```

---

## Pseudo Code Boot Trace (from 0x0500 / RC=0x2100)

The interpreter loop at 0x0500 (copied to RAM 0x6100 later) fetches the first
bytecode at RC=0x2100: `SYS 3E22` (cold boot CALL).

### Cold Boot Init (SYS 3E22)

```
2100 SYS   3E22           ; cold boot initialization handler

; --- SYS 2270: System setup ---
3E23 SYS   2270
  2271 SYS   221A         ; clear status variables (6063-60E3)
    221B PUSH  0000
    221C LD    [6065], TOS.0   ; = 0x00
    221E PUSH  0000
    221F LD    [6064], TOS.0   ; = 0x00
    2221 PUSH  0000
    2222 LD    [6063], TOS.0   ; = 0x00
    2224 PUSH  0000
    2225 LD    [60E3], TOS.0   ; = 0x00
    2227 RET
  2273 PUSH  0000
  2274 LD    [60E0], TOS.0     ; = 0x00
  2276 PUSH  0000
  2277 LD    [6007], TOS.0     ; = 0x00
  2279 PUSH  0000
  227A SYS   180C              ; (unknown init)
  227C PUSH  0000
  227D SYS   1B82              ; (unknown init)
  227F SYS   225A              ; character translation table init

; --- SYS 225A: Build 256-byte char translation table at 0x6800 ---
;     Loop 256 times: M(0x6800+i) = i & 0x7F
  225B PUSH  6800              ; table base
  225E PUSH  0100              ; count = 256
  2261 PUSH  0000              ; start index = 0
  2262 LOOP  FRAME             ; push loop frame (counter=0, limit=256)
  ; ... (256 iterations of: DUP, AND 7F, SWAP, store, INC — omitted)
  226D DROP
  226E RET
  2281 RET
```

### A5A5 Signature Validation & Inventory Setup

```
; --- Validate app ROM signature ---
3E25 PUSH  2184
3E28 SYS   10B6              ; read signature from ROM
3E2A SYS   2142              ; push 0xA5A5 (expected signature)
3E2C ISEQ                    ; compare
3E2D JZ    3E3B              ; if not equal → skip (but it matches here)

; --- First inventory record setup (SYS 2974) ---
3E2F PUSH  2155
3E32 LD    VC, TOS           ; VC = 0x2155 (ROM address table)
3E33 PUSH  09
3E35 LD    VD, TOS           ; VD = 9 (record count)
3E36 SYS   2974              ; inventory record setup from banked ROM
  ; ... reads banked ROM data, finds matching records via COMP,
  ;     loads 14 bytes from banked ROM to RAM accumulators
  2985 RET

; --- Daughter card RAM test (SYS 3DB6) ---
3E38 ISZ   TOS
3E39 JZ    3E3F              ; skip if no daughter card result
3E3F SYS   3DB6
  3DB7 SYS   21C6            ; RAM test routine
    ; Writes 0xA500+counter patterns to 0x8000, reads back, verifies
    ; Tests 16 pages (0x0F iterations), 2 sub-iterations each
    ; ... (RAM test loop iterations omitted — ~48 iterations)
    21C5 RET
  ; ... computes RAM size, stores to 604B, sets up bank pointers
  ; ... STBNK writes data to daughter card RAM
  3DF2 RET

; --- Second inventory record pass (SYS 2974) ---
3E41 PUSH  214A
3E44 LDVCD [TOS]             ; load VC/VD from table at 0x214A
3E45 SYS   2974              ; second pass — same routine
  2985 RET

; --- Main app structure setup (SYS 38E2) ---
3E47 DROP
3E48 PUSH  80
3E4A LD    [6055], TOS.0     ; M(6055) = 0x80
3E4C PUSH  59
3E4E SYS   38E2
  ; Sets up record pointers, calls SYS 2962 (record search),
  ; initializes inventory accumulators (62B2, 60C4, 63C2, 606A),
  ; copies ROM pointers to working area (60B7, 60C3)
  3933 RET
```

### Copy Interpreter to RAM & Write AA Signature (SYS 3DF4)

```
3E50 SYS   3DF4
  3DF5 PUSH  0500
  3DF8 PUSH  6100
  3DFB PUSH  19              ; count = 25 bytes
  3DFD MCPY  [NOS], [3OS]    ; copy interpreter loop (0x0500→0x6100, 25 bytes)
  3DFE PUSH  244A
  3E01 PUSH  600A
  3E04 PUSH  10              ; count = 16 bytes
  3E06 MCPY  [NOS], [3OS]    ; copy dispatch data (0x244A→0x600A, 16 bytes)
  3E07 PUSH  AA
  3E09 LD    [601A], TOS.0   ; *** WRITE 0xAA SIGNATURE *** (warm boot marker)
  3E0B PUSH  0100
  3E0E LD    [62B0], TOS     ; M(62B0) = 0x0100
  3E11 PUSH  60E0
  3E14 LD    [601B], TOS     ; M(601B) = 0x60E0
  3E16 PUSH  2040
  3E19 LD    [6390], TOS     ; M(6390) = 0x2040
  3E1C PUSH  0000
  3E1D LD    [6394], TOS.0   ; M(6394) = 0x00
  3E20 RET
```

### RTC Init (SYS 2282)

```
3E52 SYS   2282
  2283 PUSH  3F0F
  2286 LD    [TOS], 0        ; M(3F0F) = 0x00 (RTC interrupt disable)
  2287 PUSH  0001
  2288 PUSH  60A2
  228B PUSH  08
  228D FILL                  ; fill 8 bytes at 0x60A2 with 0x01 (default time)
  228E SYS   19E0            ; RTC write handler (sets clock registers)
  2290 RET
```

### Serial/EF4 Check (SYS 1988)

```
3E54 SYS   1988
  1EB4 LDAND 6007, 40       ; check bit 6 of status byte
  1EB8 ISZ   TOS            ; bit 6 clear → ISZ returns 1 (true)
  1EB9 JZ    1F05           ; taken (bit 6 was clear, skip serial)
  ; (serial RX poll at 1EBB skipped)
  1F05 SYS   1F14           ; set R5.0 = 0x04 (mode configuration)
  1F07 RET
```

### Record Interpreter & Data Loading (SYS 124E)

```
; === 7 record interpreter iterations load app ROM data ===
3E56 SYS   124E           ; (×7 — processes structured records from app ROM)

; === ROMCPY: Load key mapping table (45 bytes → 0x62DA) ===
237F LD    TOS, [RP]      ; read 16-bit from app ROM pointer
2380 LD    [63C2], TOS    ; store block length
2383 SYS   2370
  2371 LD    TOS, [63C2]
  2374 DUPNZ
  2375 JZ    237C          ; skip if zero
  2377 PUSH  62DA          ; destination RAM
  237A SYS   131C          ; ROMCPY: copy from banked ROM → RAM
  237C RET
2385 RET

; === BANKPTR: Set up ROM read pointer at M(60C0/C1/C2) ===
3E56 SYS   124E
2369 LD    TOS, [RP]      ; read offset from app ROM
236A LD    [63C4], TOS    ; store offset
236D SYS   2360
  2361 LD    TOS, [63C4]
  2364 SYS   1382          ; BANKPTR: compute & store banked ROM pointer
  2366 RET
236F RET

; === 4 more record interpreter iterations ===
3E56 SYS   124E           ; (×4 — load display configuration data)
```

### Clear Display & LCD Refresh

```
3857 PUSH  20              ; fill byte = 0x20 (space)
3859 PUSH  6080            ; dest = display buffer
385C PUSH  20              ; count = 32
385E FILL                  ; fill 32 bytes with spaces
385F LCD                   ; refresh HD44780 (blank screen)
3860 RET
```

### Display Pipeline (SYS 3822)

```
3E56 SYS   124E           ; record interpreter → dispatches to display code
3845 LD    TOS.0, [RP]    ; read display control byte from app ROM
3846 SYS   3822           ; === DISPLAY PIPELINE ===

; --- Stage 1: Primary content load (SYS 37BC) ---
3823 SYS   37BC
  37BD SYS   12D6          ; bank/pointer setup
  37BF LDAND 6280, 04      ; check display flags bit 2
  37C3 JZ    37CD          ; bit 2 clear → skip load
  37CD LD    TOS, [6282]   ; content length
  37D0 DUPNZ
  37D1 JZ    37DC          ; zero → skip
  37D3 PUSH  6080          ; buffer base
  37D6 LD    TOS.0, [6286] ; column offset
  37D9 ADD   TOS, NOS      ; dest = base + offset
  37DA SYS   131C          ; ROMCPY: copy display text to buffer
  37DC RET

; --- Stage 2: Secondary data load (SYS 379A) ---
3825 SYS   379A
  379B LD    TOS, [6284]   ; secondary data pointer
  379E LDAND 6281, 04      ; check extended flags
  37A2 JZ    37A9          ; clear → skip
  37A4 PUSH  6260          ; secondary buffer
  37A7 JP    37B0
  37B0 SYS   131C          ; ROMCPY: copy to secondary buffer
  37B2 PUSH  605A          ; copy var A → var C (pass source info)
  37B5 PUSH  6056
  37B8 PUSH  04
  37BA MCPY  [NOS], [3OS]
  37BB RET

; --- Stage 3: Character formatting (SYS 300E) ---
3827 SYS   300E
  300F LD    TOS, [628D]   ; source pointer
  3012 LD    VC, TOS
  3013 LD    TOS.0, [628F] ; source count
  3016 LD    VD, TOS
  3017 LDAND 6281, 08      ; mode bit 3
  301B ISZ   TOS
  301C LDAND 6281, 10      ; mode bit 4
  3020 ISZ   TOS
  3021 SYS   0640          ; XOR mode bits
  3023 JZ    3050          ; same → skip formatting
  ; (formatting path: hex conversion via CALLI 2812)
  3025-304F ...            ; save/restore vars, convert, copy back
  3050 RET

; --- Stage 4: Insert into display buffer (SYS 37DE) ---
3829 SYS   37DE
  37DF LDAND 6281, 04      ; display active?
  37E3 JZ    37EF          ; no → skip
  37E5 LD    TOS.0, [6287] ; cursor offset
  37E8 PUSH  6080          ; buffer base
  37EB ADD   TOS, NOS      ; insertion point
  37EC DISP                ; insert chars at cursor positions
  37ED JP    3821
  3821 RET

; --- Stage 5: Bit strip & LCD refresh ---
382B LDAND 6281, 04        ; display active?
382F JZ    3833            ; no → skip strip
3831 SYS   07BA           ; strip bit 7 from buffer chars
3833 LD    TOS.0, [6288]  ; next record indicator
3836 DUPNZ
3837 ISZ   TOS
3838 JZ    3823            ; nonzero → loop back to stage 1
383A LCD                   ; final LCD refresh
383B RET
3848 RET
```

### Additional Display Load & Character Table

```
3E56 SYS   124E           ; record interpreter continues
384B LD    TOS.0, [RP]    ; read offset
384C PUSH  6080            ; buffer base
384F ADD   TOS, NOS        ; dest = base + offset
3850 LD    TOS, [RP]      ; read source/length
3851 SWAP
3852 SYS   131C           ; ROMCPY: copy display data
3854 LCD                   ; refresh display
3855 RET

3E56 SYS   124E           ; record interpreter continues
3D4D LD    TOS.0, [6080]  ; read first display char
3D50 LD    [6390], TOS.0  ; save to char table base
3D53 PUSH  0000           ; index = 0
3D54 SYS   3B30           ; character table setup
  3B31 DUP
  3B32 PUSH  6390
  3B35 ADD   TOS, NOS     ; 0x6390 + index
  3B36 LD    TOS.0, [TOS] ; load byte
  3B37 LD    [6080], TOS.0
  3B3A LD    [62D9], TOS.0
  3B3D PUSH  62DA
  3B40 LD    TOS.0, [62D9]
  3B43 JZ    3B4A          ; if zero → skip
  3B4A PUSH  62D7
  3B4D LD    [TOS], NOS   ; save pointer
  3B4E RET
```

### Main Idle Loop — Keyboard Scan

```
3D56 SYS   1BA0           ; clear bit 0 of HW reg 0x3FF1
3D58 PUSH  23ED           ; keyboard handler address
3D5B LD    [62D3], TOS    ; store to dispatch variable
3D5E SYS   2408           ; main dispatch setup
  2409 PUSH  4B
  240B LD    [63AA], TOS.0 ; timeout/mode = 0x4B
  240E LD    TOS, [62D1]  ; dispatch state
  2411 LD    [6012], TOS  ; copy to working variable

; === KEYBOARD SCAN LOOP (repeats until key press) ===
  2413 LD    TOS, [62D3]  ; load handler address (0x23ED)  ← loop entry
  2416 EXEC  TOS          ; indirect call to keyboard handler
    23ED SYS   1898       ; KEY: scan 6 ports 0x3F20-0x3F25
    23EF DUP              ; duplicate result
    23F0 JZ    2406       ; no key → jump to 2406
    2406 RET              ; return (no key)
  2417 DUPNZ              ; dup if nonzero
  2418 ISZ   TOS          ; test zero → 1=no key, 0=key found
  2419 JZ    2422         ; key found → jump to handler (not taken here)
  241B LD    TOS, [6012]  ; reload dispatch state
  241D ISZ   TOS          ; test if zero
  241E JZ    2413         ; state=0 → loop back (rescan keyboard)
```

**End of trace** — PTC-701 is now in its idle loop, waiting for a key press.

---

## Summary

| Phase | Address Range | Purpose |
|-------|--------------|---------|
| 1 | 0x0000–0x003E | Reset, DIS, register init from ROM data, warm/cold boot check |
| 2 | 0x006B–0x0095 | AMI status check, busy poll, trampoline setup |
| 3 | 0x009C–0x00A3 | EF4 ON/OFF button timeout (~3300 iterations + ISRs) |
| 4 | 0x00A5–0x00B8 | HD44780 init commands (30,30,38,01,08,06,80,0C) |
| 5 | 0x00B9–0x00F5 | AMI timing regs, bank registers, LBR 0x0500 |
| 6 | Pseudo 0x2100 | Cold boot CALL → clear vars, char table, A5A5 check |
| 7 | Pseudo 0x3E2F | Inventory record setup, daughter card RAM test |
| 8 | Pseudo 0x3E50 | Copy interpreter to RAM, write 0xAA signature |
| 9 | Pseudo 0x3E52 | RTC init, serial check |
| 10 | Pseudo 0x3E56 | Record interpreter → display pipeline → LCD refresh |
| 11 | Pseudo 0x3D56 | Main idle loop: keyboard scan until key press |
