# 02 — Complete Memory Map (ROM + RAM)

*Part of **DiKon** — PCL3 raster firmware for the Kodak Diconix 150, by RND2 Labs / TheRND2 ([thernd2.com/diconix](https://thernd2.com/diconix)) — see [the repository README](../README.md).
This is the single most valuable reference in the package. Every region below is cited to a
code address that reads or writes it. Cross-checked against the authoritative hub notes
(`DICONIX_SESSION_2026-08-14_FINAL.md`, `DICONIX_M7V5_PORTABILITY.md`) — no discrepancies.*

The uPD78C10 has a flat 64 KB space. Decode (74HC10 + 74HC04): **A15=0 → ROM**,
**0x8000 → head/data latch**, **0xC000 → SRAM**. 0xE000–0xFFFF decodes to nothing.

```
0x0000 ┌──────────────────────────────────────┐
       │ boot dispatch (opcode 0xBA @0x0000)   │
0x0080 │ CALT vector table  (0x0080–0x00BF)    │  32 entries × 2 bytes
0x00C0 │ core code: producer/consumer, engine, │
       │ ESC + control dispatch, ESC handlers  │
0x0744 │ self-test (script 0x07F9 / text 0x0820)│
0x2800 │ RESIDENT FONT BITMAPS (~22.5 KB)       │  ← reclaimed by M7 (unreferenced)
       │   └ M7 v5 blob 0x2800–0x2F28          │
0x7FFF └──────────────────────────────────────┘  ROM ends (A15=0)
0x8000    HEAD LATCH (write) / DATA LATCH (read)   single dual-latch address
0xC000 ┌──────────────────────────────────────┐
       │ SRAM 8K (CXK5864)                      │
0xDFFF └──────────────────────────────────────┘
0xE000    (unmapped — nothing decoded)
```

## ROM regions

| Region | Address | What | Evidence |
|---|---|---|---|
| Reset / boot | 0x0000 | Dispatch table led by opcode `0xBA`; 78C10 low-vector boot | boot signature `BA 54 2C 00 …` |
| **CALT vector table** | **0x0080–0x00BF** | 32 two-byte vectors reached by the 78C10 `CALT` instruction | see decoded table below |
| Producer (enqueue) | 0x05E9 | Centronics receive: `SKIT F1` → read 0x8000 → store to ring | `CALT-84` producer |
| Ring-full flag set | **0x0610** | `MVIW VV:C8,$01` — stock buffer-full flag (ring collision) | doc 03 |
| C8 clear (genuine full) | 0x065A–0x0661 | clears VV:C8 only when WP==RP | doc 03 |
| Consume tail | 0x0648 / `CALL 0x067F` | stock dequeue tail | — |
| Self-test setup | 0x0744 | loads script + text pointers (0x075B, 0x075E) | doc 06 |
| Self-test script | **0x07F9** | 39-byte opcode script (`01`=print seg, `03 n c`=repeat, `02 x`=ASCII diag, `FF FF`=end) | — |
| Self-test text | **0x0820** | five `0xFF`-terminated text segments; region ends hard at 0x0881 | — |
| Print engine / motion | 0x0C64, 0x0CE3, 0x0D35/0x0D44 (carriage kick), 0x1915/0x1916 (delay/wait) | line print, carriage & feed motion, timing spins | doc 05 |
| Control-code dispatch | 0x191B → TABLE **0x1928** | entry = `0x1928 + 2×char` for 0x08–0x1B | doc 05 |
| ESC dispatch (guarded) | 0x195D / 0x1960 → TABLE **0x196A** | entry = `0x1928 + 2×char`; ignore handler = **0x1A1E** | doc 05 |
| $8000 data-latch read | **0x1A29** | read Centronics byte (via `CALT $009A`) | hub FINAL note |
| $8000 head-latch write | **0x1A2E** | `MOV ($8000),A` — fire nozzles | hub FINAL note |
| ACK routine | **0x1817** | pulse /ACK on PC0 to release host (via `CALT $0080`) | doc 03 |
| ESC L graphics load | 0x1E1C (also 0x1F1A / flush 0x1F24 / park 0x1F8F) | head-latch column loader | doc 05 |
| ESC J feed | 0x1BAE → converter **0x1B8E** (×2/3, mod-3 remainder VV:28) | paper feed | doc 04 |
| CR / LF / FF handlers | CR 0x1B48, LF 0x1B72, FF 0x1BBE | text control | doc 05 |
| getbyte front-end | **0x2029** (via `CALT $0082`) → dequeue 0x0624 | pull one byte from ring | doc 05 |
| Second producer site | 0x222E | `SKIT F1` count-path producer | doc 03 |
| Resident fonts | **0x2800–0x7FFF** (~22.5 KB) | dot-matrix glyph bitmaps — **nothing in code < 0x2800 references this region** | reclaimed by M7 |

### CALT vector table @0x0080 (decoded entries)

The 78C10 `CALT n` instruction calls the 16-bit vector at `0x0080 + (n & 0x3E)`. Verified entries:

| CALT index | Vector | Routine |
|---|---|---|
| `$0080` | **0x1817** | Centronics /ACK pulse (release host) |
| `$0082` | **0x2029** | getbyte — pull next byte from ring (→ dequeue 0x0624) |
| `$009A` | **0x1A29** | read the 0x8000 Centronics data latch |
| `$00BA` | **0x1B4F** | "print line now" flush (used by CR handler + ESC L pre-load flush) |

*(Cross-check: on the 150 stock image `[0x80]=0x1817`, `[0x82]=0x2029`. On the 150 Plus /
180si / 300w every entry differs — this is why the patch is 150-specific; see doc 09.)*

## RAM map (SRAM 0xC000–0xDFFF)

Stock firmware uses a "working page" at **V = 0xC1**: a variable written `VV:xx` lives at
`0xC1xx`. So `VV:C8` = **0xC1C8**, `VV:A6` = 0xC1A6, `VV:E7` = 0xC1E7, etc.

### Stock regions

| Region | Address | What | Evidence |
|---|---|---|---|
| Stack | base **0xC200**, grows down | CPU stack | hub FINAL note |
| Low variable block | **0xC100–~0xC268** | stock working-page variables (VV:xx) | — |
| — full-flag | 0xC1C8 (`VV:C8`) | Centronics buffer-full / host-ACK gate | 0x0610 sets it |
| — engine motion flags | 0xC1A6 (`VV:A6`, bit7=motion, bit3=pend) | print-in-flight state | 0x1B55 tests it |
| — byte count | 0xC1E7 (`E7:E8:E9`) | received-byte counter | 0x0618 `INRW VV:E7` |
| — ESC L count/pos | 0xC1AB / 0xC1AD | saved line-buffer count & position | 0x1F29 resume |
| — density table | 0xC1AF (from `VV:A9/AA`) | ESC L density | 0x1E1C |
| **High variable blocks** | **0xCD08, 0xCD18, 0xCD7C** | stock state (spacing/positioning) | referenced from 0x036A, 0x0C75, 0x1030, 0x113D, 0x137B, 0x13ED |
| **Input ring buffer** | **0xD30D–0xDE0C** (2816 B) | Centronics FIFO; wrap 0xDE0D → 0xD30D | producer 0x05E9 / consumer 0x0624 |
| — ring control block | 0xD301, 03, 05, 07, **09 (WP)**, **0B (RP)** | pointers; **WP=$D309, RP=$D30B** | LDED/SHLD $D309/$D30B |

**Note on the high blocks:** M7's early full-width attempt (v3b) put its column buffers at
0xC700–0xD000, which **spans 0xCD08–0xCD7C** and clobbered these stock variables → crash.
That is the entire reason the shipping buffers were moved to 0xC300–0xCBFF. See doc 10 (v3b/v4).

### RND2 M7-claimed regions (all proven free of stock references)

| Region | Address | What | Notes |
|---|---|---|---|
| **M7 code blob (ROM)** | **0x2800–0x2F28** (v5, 1833 B; end 0x2F29) | the whole PCL front-end, in reclaimed font space | blob cap asserted < 0x3000; v5t extended a data-only text stream 0x2F29–0x2FFE; **v5f (DiKon) extends the text window to 0x2F29–0x3018** — 0x3000–0x30FF verified free (0 dasm operand refs; runtime read-watch 0) |
| **Column buffers (RAM)** | **COLSA 0xC300, COLSB 0xC780, COLSEND 0xCC00** (2×1152 = 2304 B) | even/odd interleave pass buffers | ends 0x108 B below stock 0xCD08 |
| **16-bit parser scratch** | **0xC269–0xC270** | NPAR16 (C269/6A), NREM16 (C26B/6C), T16 (C26D/6E), PARCNT (C26F), FINF (C270) | verified-free window 0xC269–0xCBFF |
| **v4b trim scratch** | **0xC271–0xC27A** | LGA, LGB, TOTB (2), WB16 (2), GIDX, LGT, PBIN, HDRB | all < COLSA=0xC300 |
| **M7 band scratch** | **0xDE18–0xDE35** | TMP (0xDE1E), ROWK (0xDE19), SUB (0xDE1A), CC (0xDE1D), CBP (0xDE21), CBPLIM (0xDE22)… | above stock ring end 0xDE0D; hardware-proven since v3f |

**Verified-free windows M7 relies on:** ROM `0x2800–0x2FFF` (0 references from code < 0x2800,
confirmed against the 696 KB disassembly) and RAM `0xC269–0xCBFF` (no stock read/write). These
two claims are what make a stock-32K, zero-board-mod PCL printer possible.
