Wildbits PC Resource Page - by Roger Taylor
Kits, memory maps and other resources for WildBits NitrOS-9 on the K2 and Jr2.
| Core / NitrOS-9 Parity | K2 StockLongview_rc24core built 2026-10-09 stock kit built 2026-10-09 14:18 | Jr2 StockLongview_rc24core built 2026-10-09 stock kit built 2026-10-09 14:19 | K2 FutureLongview_rc24core built 2026-10-09 future kit built 2026-10-09 14:18 | Jr2 FutureLongview_rc24core built 2026-10-09 future kit built 2026-10-09 14:19 |
|---|---|---|---|---|
| 2,016 KB SRAM with FLASHDIS; VICKY / fonts + CLUT / text / color pages at $FC / $FD / $FE / $FF | ✓ | ✓ | ✓ | ✓ |
| Memory / bus: 2,016 KB RAM, shaped flash/cart writes and RTC write-strobe handling, DIP-switchable turbo mode | ✓ | ✓ | ✓ | ✓ |
| Video / Graphics: hardware lines, sprite engine fixes, 640 × 240 × 16-color bitmaps, and readable text color LUT registers; OS-9 friendly font and palette are built in (saving over 2K of bootfile space if needed) | ✓ | ✓ | ✓ | ✓ |
| Sound / Music: WM8776 codec chip; FPGA OPL3 FM synthesis on K2 and Jr2; MIDI control register with FIFO-empty flags and FIFO-reset bit (like WizFi); VS1053b mp3/sound chip wiring fixed; $FF9x sound registers | ✓ | ✓ | ✓ | ✓ |
| Mach-speed: DMA with logic operations, FPU, math coprocessor | ✓ | ✓ | ✓ | ✓ |
| Communications: DriveWire fractional baud-enable generator (BAUDCE) - WizFi RX/TX interrupts | ✓ | ✓ | ✓ | ✓ |
| UART baud timing and reset-sequencing improvements | ✓ | ✓ | ✓ | ✓ |
| RP2040 supervisor: RPDrv/rp mailbox driver and fpga command for status, image listing, SD upload and flash programming | ✓ | — | ✓ | — |
| Bells and whistles: Hardware mouse pointer with software-controlled visibility | ✓ | ✓ | ✓ | ✓ |
| Hardware key-repeat (typematic) engine | — | — | ✓ | — |
| 20 Mhz turbo mode: 10-tick RAM reads, 14-tick RAM writes, 25 Mhz internal cycles | ✓ | ✓ | ✓ | ✓ |
| WizFi360: all 17 baud rates; chip, FIFO and UART reset register ($FF21) | — | — | ✓ | ✓ |
| W6100 ethernet port | ✓ | — | ✓ | — |
| Overlaid now on the Future disks: wb/drivewire_stability, wb/k2_core_typematic_support, wb/rp2040, wb/wizfi, wb/rtaylor — not in main; open pull requests in bold | ||||
| K2 hardware-typematic keyboard: interrupt-driven keydrv with core key repeat wb/k2_core_typematic_support · overlaid, 3 patches ahead of main · tip 4943eaadc 2026-09-01 | — | — | ✓ | — |
| DriveWire stability rework: dwio/rbdw error correction, bounded transmit wait, abort handshake, stream resync wb/drivewire_stability · overlaid, 1 patch ahead of main · tip 49b77e1bd 2026-08-28 | — | — | ✓ | ✓ |
| RP2040 supervisor: RPDrv + rp in the K2 Level 1/FEU bootfile; fpga command with flash-slot wipe (fwipe/pwipe); a latched supervisor error no longer fails every request wb/rp2040 · PR #439 open · overlaid, 8 patches ahead of main · tip f94b00eb2 2026-10-02 | — | — | ✓ | — |
| Play improvements; Play and View move to Level 2 CMDS; wildspeed ed.5 with an internal-cycle (mul) class in the perceived blend; MIDrv/mi read and write (MIDI OUT); wmset testing mode removed wb/rtaylor · overlaid, 7 patches ahead of main · PR #441 closed · tip 62934ba10 2026-10-09 | — | — | ✓ | ✓ |
| WizFi360: all 17 baud rates, chip/FIFO/UART reset register at $FF21, data at $FF22; wizupdate firmware updater; internal SD ready timer wb/wizfi · overlaid, 1 patch ahead of main · tip 1b7fe960f 2026-10-07 | — | — | ✓ | ✓ |
Memory Atlas
How the Wildbits (FNX6809, B0C cores) translates the 6809’s 64K into physical RAM, flash, and I/O under NitrOS-9 Level 2 — the MMU hardware, the FEU boot path, the trampolines, and the runtime windows the OS actually uses. Region colors are consistent across every diagram. Ladders are not to scale.
1The translation pipeline
Every CPU access goes through one of eight 8K slots. The slot’s LUT entry supplies the physical block; the entry’s top bits pick which bus answers.
CPU address
A15..A13 selects one of 8 slots; A12..A0 is the 8K offset. Fixed I/O at $FDxx–$FFxx bypasses translation.
MMU LUT entry
4 LUTs × 8 slots × 8 bits. Active LUT = $FFA0[1:0]. Slot registers at $FFA8–$FFAF read/write the LUT selected by $FFA0[5:4].
Physical bus
Entry prefix routes to RAM, the flash window, expansion RAM, or a sectored I/O page. Block number becomes the high address bits.
| entry bits | region | blocks | size | physical |
|---|---|---|---|---|
| 00xxxxxx | RAM | $00–$3F | 512 KB | SRAM (also the VKY video port) |
| 01xxxxxx | Flash window | $40–$7F | 512 KB | $08_0000–$0F_FFFF on the system bus; chip offset $00000–$7FFFF |
| 100xxxxx | Expansion RAM | $80–$9F | 256 KB | $10_0000–$13_FFFF |
| $A0–$FB | RAM | $A0–$FB | 736 KB | $14_0000–$1F_7FFF (block × $2000) |
| 111111xx | Sectored device pages | $FC–$FF | 4×8 KB | VICKY / fonts + CLUTs / text / attributes |
| $00–$FB | RAM with FLASHDIS set | $00–$FB | 2,016 KB | $00_0000–$1F_7FFF; replaces flash/expansion at $40–$9F |
$FFA0 control byte. The kernel keeps edit == active in system state; code that pokes $FFA8–AF (rbmem, mousedrv) silently assumes this. $FFA1 bit 0 enables the $FD00–$FDFF RAM zone; bit 1 enables $FFF0–$FFFF.
2Block decode at reset · FLASHDIS clear
⋮
$3F
⋮
$77
⋮
$7F
⋮
$9F
At reset: 156 RAM blocks (1,248 KB). Block numbers are what you write into a slot register; fixed CPU register addresses are separate.
| Code | slot | maps in | Notes |
|---|---|---|---|
| vtio | 7 / 2 | $FC–$FF | video and CLUT pages; sound uses fixed $FF9x registers; bootlist warns it must init early to claim $E000+ safely |
| mousedrv | 2 | $FC | draws the pointer sprite via $4C00 |
| rbmem | 2 | $40–$7F | flash sector reads/writes through $4000; rbmem temporarily clears FLASHDIS for flash access and restores it afterward |
| wildspeed, probes | — | FE-page | fixed I/O needs no mapping |
3System-state 64K (Level 2)
What the kernel’s own map looks like once NitrOS-9 is up.
⋮
$DFFF
User processes get their own LUT: block 0’s trampoline page is common, the process’s data/code blocks fill the middle, and the fixed I/O pages ride on top. The kernel switches maps by writing $FFA0[1:0] from the LowSub trampolines.
4Fixed-System I/O Page, $FE00–$FFFF
Fixed peripheral registers, the MMU, VICKY’s master registers and CPU vectors: one block stack on the left, register tables on the right, with bits listed under each register.
fixed decode range$FFF0–$FFFF
fixed decode range$FFE0–$FFEF
fixed decode range$FFC0–$FFDF
fixed decode range$FFB0–$FFBF
fixed decode range$FFA0–$FFAF
fixed decode range$FF91–$FF99
fixed decode range$FF90
fixed decode range$FF80–$FF8F
fixed decode range$FF70–$FF74
fixed decode range$FF60–$FF6F
fixed decode range$FF50–$FF5F
fixed decode range$FF40–$FF48
fixed decode range$FF30–$FF39
fixed decode range$FF20–$FF2A
fixed decode range$FF10–$FF18
fixed decode range$FF00–$FF01
fixed decode range$FEE0–$FEFB
fixed decode range$FEC0–$FED7
fixed decode range$FEB0–$FEBF
fixed decode range$FEA0–$FEA8
16 registers · rc18$FE78–$FE7F
$FE88–$FE8F
fixed decode range$FE90–$FE91
1 register; K2 rc18 upper half is mailbox$FE80–$FE87 K2
$FE80–$FE8F Jr2
fixed decode range$FE70–$FE72
fixed decode range$FE60–$FE67
fixed decode range$FE50–$FE54
fixed decode range$FE40–$FE4F
fixed decode range$FE30–$FE3F
fixed decode range$FE20–$FE2F
fixed decode range$FE10–$FE16
fixed decode range$FE00–$FE0F
$FE00–$FFFF has fixed decoding in every task map. Blocks are shown from high addresses at the top to low addresses at the bottom.
$FE00–$FE0F · System control
used by: krn scrub, wbreset, wbinfo, feu
| Register | Bits, reset values and notes |
|---|---|
| $FE00 | SYS0 write: b7 hardware reset (requires the $DE/$AD sentinels) b6 network LED b5 caps-lock LED enable b4 buzzer b3/b2 LED1/LED0 b1 SD LED b0 power LED |
| $FE00 (read) | SYS0 read: b7 SD write-protect, b6 card-detect, b5:0 stored SYS0 control bits. SYS0 resets to $01; SYS1 resets to $40. |
| $FE01 | SYS1: b7:6 LED1 blink rate b5:4 LED0 blink rate b3 SID stereo b2 PSG stereo b1/b0 LED1/LED0 manual mode |
| $FE02/$FE03 | RST0/RST1 reset sentinels: write $DE, $AD to arm the SYS0 b7 hardware reset (what wbreset does) |
| $FE04/$FE05 | Random-number value low / high on read; seed low / high on write. |
| $FE06 | Random-number generator control: b0 enable, b1 seed-valid; b7 reads the generator’s done flag. Not keyboard-LED enable. |
| $FE07 | Read: machine ID, $16 K2 / $1A Jr2. |
| $FE07–$FE0F | Read: $FE08/$FE09 board revision characters; $FE0A/$FE0B subversion, $FE0C/$FE0D version and $FE0E/$FE0F chip number, low byte first. K2 writes to this range set RGB keyboard-LED colors; read and write meanings differ. |
$FE10–$FE16 · K2 optical keyboard
used by: keydrv_k2 (F$IRQ on INT_PENDING_3 b2)
| Register | Bits, reset values and notes |
|---|---|
| $FE10 | key data (FIFO head) |
| $FE11 | status: b7 = 1 mechanical / 0 optical b0 = 1 FIFO empty |
| $FE12/$FE13 | FIFO count lo / hi |
| $FE14 | typematic initial delay in 60 Hz frames (reset 30 = 500 ms) |
| $FE15 | typematic repeat period in frames (reset 5 = 12 cps) |
| $FE16 | b0 = 1 enables hardware key repeat (reset 0 = off, so older disks see stock behavior) |
| — | all three typematic registers read back; write-then-readback of $FE14 is keydrv’s core probe; a hardware reset clears $FE16, a software restart does not |
$FE20–$FE2F · Interrupt controller
used by: krn, clock (SOF), keydrv, wizfi, mousedrv
| Register | Bits, reset values and notes |
|---|---|
| $FE20–$FE23 | pending, groups 0–3 (write 1 to clear) |
| $FE24–$FE27 | polarity, groups 0–3 |
| $FE28–$FE2B | edge select, groups 0–3 |
| $FE2C–$FE2F | mask, groups 0–3 (1 = masked; reset = all masked) |
| group 0 | b0 VICKY start-of-frame b1 start-of-line b2 PS/2 keyboard b3 PS/2 mouse b4 timer 0 b5 timer 1 b6 DMA b7 cartridge |
| group 1 | b0 UART b4 RTC b5 VIA0 b6 VIA1 b7 SD insert |
| group 2 | b0 IEC data b1 IEC clock b2 IEC ATN b3 IEC SREQ |
| group 3 | b0 WiFi RX b1 MIDI RX b2 K2 keyboard FIFO b3 WizNet FIFO route (output tied to 0 in RC24) b4 reserved MIDI-VS input (not connected on K2/Jr2) b5 WiFi TX empty (all edge) |
| — | not reset by a software restart: krn cold start writes $FF to all four masks and pendings |
$FE30–$FE3F · Timers 0 / 1
used by: wizfi Timer0 polling
| Register | Bits, reset values and notes |
|---|---|
| $FE30 / $FE38 | write: counter control (clear, load, count up/down); read: status |
| $FE31–$FE33 / $FE39–$FE3B | 24-bit counter value |
| $FE34 / $FE3C | compare control (reload on match) |
| $FE35–$FE37 / $FE3D–$FE3F | 24-bit compare value; a match raises INT group 0 b4 / b5 |
$FE40–$FE4F · Real-time clock
used by: clock2, wildspeed timebase
| Register | Bits, reset values and notes |
|---|---|
| $FE40 / $FE41 | seconds / seconds alarm (BCD) |
| $FE42 / $FE43 | minutes / minutes alarm |
| $FE44 / $FE45 | hours / hours alarm |
| $FE46 / $FE47 | day / day alarm |
| $FE48 | day of week |
| $FE49 / $FE4A | month / year |
| $FE4B | rates |
| $FE4C | enables |
| $FE4D | flags |
| $FE4E | control: b1 24-hour mode b2 run on battery b3 UTI update-transfer inhibit (latch before reading) |
| $FE4F | century |
$FE50–$FE54 · PS/2 ports
used by: mousedrv, keydrv_ps2 (Jr2)
| Register | Bits, reset values and notes |
|---|---|
| $FE50 | control: b5 mouse FIFO clear b4 keyboard FIFO clear b3 mouse write b1 keyboard write |
| $FE51 | byte to send |
| $FE52 | keyboard input |
| $FE53 | mouse input |
| $FE54 | status: b7/b6 keyboard ack/nak b5/b4 mouse ack/nak b1 mouse FIFO empty b0 keyboard FIFO empty |
$FE60–$FE67 · UART (DriveWire)
used by: dwinit / dwread / dwwrite
| Register | Bits, reset values and notes |
|---|---|
| $FE60 | RX / TX data (divisor low when DLAB set) |
| $FE61 | interrupt enable (divisor high when DLAB set) |
| $FE62 | interrupt ID (read) / FIFO control (write) |
| $FE63 | line control (b7 = DLAB) |
| $FE64 | modem control |
| $FE65 | line status |
| $FE66 | modem status |
| $FE67 | scratch |
| — | baud = 22.1184 Mhz / (16 × (divisor + 1)) on BAUDCE-fixed cores: 230400 = divisor 5, 115200 = 11 |
$FE70–$FE72 · CODEC
used by: audio setup
| Register | Bits, reset values and notes |
|---|---|
| $FE70 / $FE71 | command lo / hi |
| $FE72 | Read: b0 busy. Write any byte while idle to start transmission of the command stored at $FE70/$FE71. |
$FE80–$FE87 · IEC serial bus (K2)
| Register | Bits, reset values and notes |
|---|---|
| $FE80–$FE87 | data / clock / ATN / SREQ line control and sense. Jr2 retains the $FE80–$FE8F decode; K2 uses $FE88–$FE8F for the RP2040 mailbox. |
| — | input edges arrive on INT group 2 b0–b3 |
$FE78–$FE7F / $FE88–$FE8F · K2 RP2040 mailbox
K2 rc18 and later; used by RPDrv/rp and the fpga command. Jr2 has no onboard RP2040 supervisor. Register names follow defs/wildbits.d on wb/rp2040.
| Register | Bits, reset values and notes |
|---|---|
| $FE78 | RP.Control — Read/write; reset $01.b0 enable mailbox polling and command transfer b1 clear last error when written as 1 b7 clear TX and RX FIFOs when written as 1 FIFO clear does not abort a supervisor operation. |
| $FE79 | RP.Status — Read-only; reset $20.b7 error present b6 RX FIFO has data b5 TX FIFO empty b4 TX FIFO full b3 command pending or in flight b2 supervisor SD-present flag b1 supervisor upload-active flag b0 supervisor seen online |
| $FE7A | RP.Command — Write a command byte to submit the TX payload; read back the most recently accepted command. Submit only while status b3 is clear. Reset $00. |
| $FE7B | RP.Remote — Read-only raw status byte from the latest valid supervisor response. Reset $00. |
| $FE7C / $FE7D | RP.TxCount_H / RP.TxCount_L — Read-only TX FIFO byte count, high byte first; range 0–256. Reset 0. |
| $FE7E / $FE7F | RP.RxCount_H / RP.RxCount_L — Read-only RX FIFO byte count, high byte first; range 0–256. Reset 0. |
| $FE88 | RP.TxData — Write one payload byte into the TX FIFO. Writes while full are ignored; reads return $00. |
| $FE89 | RP.RxData — Read and remove one byte from the RX FIFO; empty reads return $00. Writes have no effect. |
| $FE8A | RP.ADC0 — Read-only supervisor ADC channel 0 sample; reset $00. |
| $FE8B | RP.ADC1 — Read-only supervisor ADC channel 1 sample; reset $00. |
| $FE8C | RP.ADC2 — Read-only supervisor ADC channel 2 sample; reset $00. |
| $FE8D | RP.ADC3 — Read-only supervisor ADC channel 3 sample; reset $00. |
| $FE8E | RP.Error — Read-only last error; $00 = none. Local errors: $E1 command submitted while busy, $E2 TX payload too long, $E3 RX FIFO overflow. Valid supervisor responses also update this byte. |
| $FE8F | RP.FwMajor — Read-only supervisor firmware major version from its response; reset $00. |
The 6809 count registers are big endian; multibyte command payload fields use the supervisor protocol’s little-endian order. Each FIFO holds 256 bytes; a command payload is at most 240 bytes. The current RPDrv waits one 60 Hz tick after busy clears before copying the response, because RX FIFO filling can still be in progress.
$FE90–$FE91 · SD card 0 (SPI)
used by: llwbsd (/s0, /s1)
| Register | Bits, reset values and notes |
|---|---|
| $FE90 | status / control: b7 SPI busy b1 slow SPI clock select (1 = slow initialization rate; 0 = fast) b0 chip-select enable |
| $FE91 | data: write a byte to shift it out, read the byte shifted in |
$FEA0–$FEA8 · Mouse pointer
used by: mousedrv
| Register | Bits, reset values and notes |
|---|---|
| $FEA0 | b0 show pointer b1 mode (0 = OS computes X/Y, 1 = hardware decodes PS/2 packets) |
| $FEA2 / $FEA3 | X position: write high / low; read low / high. Do not use the same byte order for both. |
| $FEA4 / $FEA5 | Y position: write high / low; read low / high. |
| $FEA6–$FEA8 | Raw PS/2 packet bytes 0–2, supplied by software for the hardware packet decoder. |
$FEB0–$FEBF · VIA0 (W65C22)
used by: joystick readers
| Register | Bits, reset values and notes |
|---|---|
| $FEB0 / $FEB1 | port B / port A data |
| $FEB2 / $FEB3 | DDRB / DDRA |
| $FEB4–$FEB7 | timer 1 counter lo/hi, latch lo/hi |
| $FEB8 / $FEB9 | timer 2 counter lo / hi |
| $FEBA | shift register |
| $FEBB | auxiliary control (ACR) |
| $FEBC | peripheral control (PCR) |
| $FEBD | interrupt flags (IFR) |
| $FEBE | interrupt enable (IER) |
| $FEBF | port A, no handshake |
| — | the joystick ports hang off this VIA |
$FEC0–$FED7 · DMA engine
| Register | Bits, reset values and notes |
|---|---|
| $FEC0 | control: b0 enable b1 2D mode b2 fill b3 interrupt enable b5:4 byte-lane mask — either bit set and the transfer completes writing nothing b6 double speed, and fill takes the 16-bit word b7 start transfer |
| $FEC1 | status (read: b7 transfer in progress, b6:0 hardwired 0) / 8-bit fill byte (write) |
| $FEC2 / $FEC3 | 16-bit fill word, high / low — used instead of $FEC1 when control b6 is set |
| $FEC5–$FEC7 | source address hi / mid / lo |
| $FEC9–$FECB | destination address hi / mid / lo |
| $FECC / $FECD | X size hi / lo |
| $FECE / $FECF | Y size hi / lo — 2D only |
| $FED0–$FED3 | source stride hi/lo, destination stride hi/lo — 2D only |
| $FED4 | DMA_OP_REG (rc17): b2:0 operation — DMA_OP_COPY 0, DMA_OP_OR 1, DMA_OP_AND 2, DMA_OP_XOR 3, DMA_OP_MASK 4 (per nibble: a source nibble of 0 keeps the destination’s)b3 DMA_OP_NOT — the result inverted (NOR, NAND, XNOR with the others)b7 DMA_OP_Implemented, read-only: 1 on a core that has the ops (probe it first)reset 0 = a plain copy |
| $FED5–$FED7 | read and write as ordinary bytes but drive nothing |
| the 1D length is not contiguous: bits 23:16 come from $FECF, bits 15:8 from $FECC and bits 7:0 from $FECD. $FECE is ignored in 1D mode | |
| DMA_RDY holds the CPU while a transfer runs; the bus grant / DMA active / drain handshake keeps a transfer and the CPU out of the same SRAM slot, and the $FED4 logic ops apply to 1D and 2D copies and to fills alike | |
| Read-back. Those are the write addresses, and they are not the read addresses. Every multi-byte field is big endian, high byte at the low address. Writes land directly, but the read decode hands back a different register for sixteen of the thirty-two bytes: both address quads come back reversed, and all four size and stride pairs come back swapped. A driver that reads a register back to check what it wrote will not recognise its own values unless that permutation is built in. The tests/dma probe writes each address’s own low byte into itself and prints what comes back, which identifies whether the fitted core matches this decode or a later one. | |
| The map above was corrected on 2026-09-09 against the core RTL. What was documented before that was wrong from $FEC4 down: both address quads were one byte low, and the whole size and stride block was four bytes high. | |
Driving it (read out of the core RTL):
| |
RC24 uses the scheduler bus-grant and pipeline-drain handshake described above. dmaxfer exercises 1D/2D copies, fills and logic operations. CPU polling is supported; sleep is optional. Hardware completion depends on continued scheduler grants. | |
$FEE0–$FEFB · Math coprocessor
used by: wild (sprites, CLUT math)
| Register | Bits, reset values and notes |
|---|---|
| $FEE0 / $FEE1 | unsigned multiply operand A (16-bit) |
| $FEE2 / $FEE3 | multiply operand B |
| $FEE4 / $FEE5 | divisor |
| $FEE6 / $FEE7 | dividend |
| $FEE8–$FEEB | 32-bit add operand A |
| $FEEC–$FEEF | 32-bit add operand B |
| $FEF0–$FEF3 | 32-bit product (read) |
| $FEF4 / $FEF5 | quotient (read) |
| $FEF6 / $FEF7 | remainder (read) |
| $FEF8–$FEFB | 32-bit sum (read) |
| F256 math-block layout at base $FEE0; wild also parks scratch pointers in the add registers | |
Fixed I/O on both boards, so it needs no MMU work from any task. Everything is big endian, which suits the 6809: a 16-bit std or ldd lands the right way round. The XYMATH_* block once documented at $D300 does not exist in these cores — nothing decodes that address. | |
| Write the operands, then read the result, and only write below $FEF0. The write decode keys on address bit 3 alone and ignores bit 4, so a write to $FEF0–$FEF7 lands in the multiply and divide operands, and a write to $FEF8–$FEFF lands in the adder’s inputs. Storing to a result address silently destroys an operand. Operands read back at their own addresses. | |
| The multiply is combinational and needs no wait. It is unsigned, 16×16, and the full 32-bit product survives, because 32 bits is the exact width a 16×16 product needs — nothing is truncated. FFFF×FFFF returns FFFE0001; a signed multiplier would return 00000001. The divider is a different matter: it is pipelined with a latency of 12 clocks and both its valid inputs tied high, so it re-divides every clock. Allow at least 12 math-block clocks after writing the operands before reading; these are the approximately 25 Mhz I/O clocks, not 6809 instruction or E-clock cycles. | |
$FF00–$FF01 · SD card 1 (SPI)
used by: second SD slot (SDC1); /s0 uses $FE90
| Register | Bits, reset values and notes |
|---|---|
| $FF00 | status / control: b7 SPI busy b1 slow SPI clock select (1 = slow initialization rate; 0 = fast) b0 chip-select enable |
| $FF01 | data: write a byte to shift it out, read the byte shifted in |
$FF10–$FF18 · Splash / SPI flash DMA
used by: splash loader, flash utilities
| Register | Bits, reset values and notes |
|---|---|
| $FF10 | control: read b7 busy b6 FIFO empty b5:0 control bits |
| $FF11 | command byte for the flash |
| $FF12/$FF13 | Read: receive FIFO count low / high (12-bit). Write: transfer length high / low (16-bit). Read and write orders differ. |
| $FF14–$FF16 | $FF14 is a stored control byte, not part of the source address. The 24-bit source address is at $FF15–$FF17, high / middle / low. |
| $FF17 | Low byte of the 24-bit flash source address (see $FF15–$FF17). |
| $FF18 | FIFO data port (read pops one byte) |
$FF20–$FF2A · WizFi360 WiFi UART
used by: wizfi (WizCon4), wizi, ntptime, wizupdate, modem -R
| Register | Bits, reset values and notes |
|---|---|
| $FF20 | control/status, reset $0A: b4–b0 rate code 0–16: the WizFi360's actual rates, 600.006 to 2,000,000 baud (10 = 115,273.8 default, 13 = 930,232.6; nominal names remain 115200 and 921600) b5 reserved (reads 0) b6 RX FIFO empty (read) b7 TX FIFO empty (read) |
| $FF21 | reset, reset $80 (each bit holds its reset while set): b0 WizFi360 chip reset b1 TX and RX FIFO reset b2 TX and RX UART reset b7 reads 1: the register exists |
| $FF22 | data: write pushes to the 2K TX FIFO, read pops the 2K RX FIFO |
| $FF23/$FF24 | RX FIFO read count (16-bit) |
| $FF25/$FF26 | RX FIFO write count |
| $FF27/$FF28 | TX FIFO read count |
| $FF29/$FF2A | TX FIFO write count |
| — | RX non-empty and TX drained edges arrive on INT group 3 b0 / b5 |
RC24 uses the same 24 Mhz serial clock and fractional bit-period settings for WizFi TX and RX. These rates follow the W600 divisor values; nominal names do not state the exact baud.
| Code | Nominal baud name | Implemented baud |
|---|---|---|
| 0 | 600 | 600.006 |
| 1 | 1200 | 1,200.01 |
| 2 | 1800 | 1,800.02 |
| 3 | 2400 | 2,400.1 |
| 4 | 4800 | 4,800.2 |
| 5 | 9600 | 9,601.5 |
| 6 | 14400 | 14,404.0 |
| 7 | 19200 | 19,203.1 |
| 8 | 38400 | 38,424.6 |
| 9 | 57600 | 57,636.9 |
| 10 | 115200 | 115,273.8 |
| 11 | 230400 | 231,213.9 |
| 12 | 460800 | 465,116.3 |
| 13 | 921600 | 930,232.6 |
| 14 | 1000000 | 1,000,000 |
| 15 | 1500000 | 1,538,461.5 |
| 16 | 2000000 | 2,000,000 |
$FF30–$FF39 · MIDI FIFO + SAM2695 Synth
used by: MIDI tools
| Register | Bits, reset values and notes |
|---|---|
| $FF30 | control / status: read: b3 TX FIFO empty · b2 RX FIFO empty · b7:4 and b1:0 read back the control byte write: b1 = 1 holds both FIFOs in reset (FIFO reset only, as on the WizFi; the synth chip is untouched) · b0 unused (no rate select) |
| $FF31 | FIFO data port: write = next byte to the synth (TX FIFO), read = next received byte (RX FIFO) |
| $FF32/$FF33 | RX FIFO count hi / lo — bytes waiting to be read (hi = bits 10:8, 11-bit count) |
| $FF34/$FF35 | RX FIFO write-side count hi / lo (the UART’s view of the same FIFO; normally equal to $FF32/$FF33) |
| $FF36/$FF37 | TX FIFO read-side count hi / lo (bytes the UART still has to send, as it drains) |
| $FF38/$FF39 | TX FIFO write-side count hi / lo — bytes queued by the CPU (hi = bits 10:8) |
| — | MIDI RX events: INT group 3 b1: a received-byte event pulse while the RX FIFO has data. b4 is not connected on either board. |
$FF40–$FF48 · WizNet W6100 ethernet
used by: wiznet drivers (K2 only)
| Register | Bits, reset values and notes |
|---|---|
| $FF40 | control: b0 enable b3:1 command b4 reset FIFOs b5 start transfer b6 stored control bit; no FIFO interrupt output is implemented in RC24 b7 transfer in progress (read) |
| $FF41 | MR register value (read / write) |
| $FF42 | TX FIFO count low byte (read); not a full 11-bit count. |
| $FF43 | single-access port: write a byte to / read a byte from the W6100 |
| $FF44/$FF45 | Write: W6100 address high / low. Read: stored address low / high. The read multiplexer reverses this pair. |
| $FF46/$FF47 | Read: RX FIFO count high / low (11-bit). Write: transfer size high / low (16-bit). |
| $FF48 | FIFO data port; $FF49–$FF4F mirror this port in the RTL. Reads remove a byte and writes enqueue a byte. |
| — | Group 3 b3 is the reserved FIFO IRQ route, but WizNet_FIFO_IRQn_o is tied to 0 in RC24. Poll the bridge; the module IRQ pin has a separate group-2 route. |
$FF50–$FF5F · VS1053b audio codec
used by: vs (in main); MP3 / audio streaming (K2 and Jr2)
| Register | Bits, reset values and notes |
|---|---|
| $FF50 | control: b0 START: a 0→1 edge starts one SCI transaction; it does not self-clear, so write 0 then 1 b1 READ: 1 = SCI read, 0 = SCI write b2 FAST: 1 = SPI clock IO_Clk/4 (6.29 Mhz) for streaming once CLOCKF has raised the chip clock; 0 = IO_Clk/16 (1.57 Mhz), within the chip’s boot-time limits b3 RESET: 1 holds the chip’s XRESET low with the bit engine idle and the SDI FIFO flushed; write 1, wait a tick, write 0 b6:4 unused, read back as written b7 BUSY (read): transfer in progress, or waiting for DREQ |
| $FF51 | SCI register select: b3:0 SCI register number: 0 MODE, 1 STATUS, 2 BASS, 3 CLOCKF, 4 DECODE_TIME, 5 AUDATA, 6 WRAM, 7 WRAMADDR, 8 HDAT0, 9 HDAT1, A AIADDR, B VOL, C–F AICTRL0–3 b7:4 unused |
| $FF52 | SCI data high: b7:0 bits 15–8 of the value to write, or of the last read result (high byte first, so one std / ldd covers both) |
| $FF53 | SCI data low: b7:0 bits 7–0 of the value to write, or of the last read result |
| $FF54 | SDI FIFO status (read): b7 FIFO empty b6 FIFO full b5:3 read as 0 b2:0 FIFO count bits 10–8; the read also snapshots the whole count for $FF55 |
| $FF55 | SDI FIFO count (read): b7:0 FIFO count bits 7–0 from the $FF54 snapshot: ldd $FF54 then anda #7 gives the 11-bit count in one consistent read |
| $FF56 | b7:0 read as $00 |
| $FF57 | SDI FIFO data port (write): b7:0 next stream byte for the chip; 2048-byte FIFO, sent over XDCS as DREQ permits |
| $FF58–$FF5F | mirror $FF50–$FF57 |
| — | DREQ flow control in hardware: BUSY stays set until the chip is ready. Frames are exactly 32 clocks per SCI command and 8 per SDI byte, and DREQ is synchronized in the FPGA. The chip runs from 12.288 Mhz on both machines; its reset follows the board cold reset or CTRL bit 3. Equates in wildbits.d; the vs command (in main) tests and plays through it |
| — | K2 drives the VS1053 boot strap from the FPGA; Jr2 does not. Decoder/synth startup therefore depends on board straps and software initialization. This table specifies the FPGA bridge, not a guarantee of the chip’s current playback mode. Consult the installed vs command and board documentation before changing straps. |
$FF60–$FF6F · I2C master (HDMI)
used by: video init, i2c tools
| Register | Bits, reset values and notes |
|---|---|
| $FF60 | Control: b0 request write, b1 request read; clear the request bits before issuing another command. Read b7 = receive FIFO empty. |
| $FF61 | I2C slave address (reset $72 for the HDMI transmitter). |
| $FF62 | Register address within the target device. |
| $FF63 | Write data / receive FIFO read port (read removes a byte). |
| $FF64 | Transfer count (reset $02). |
| $FF65–$FF67 | Stored extra command bytes. |
| $FF68–$FF6F | Mirrors $FF60–$FF67. K2 only; the automatic controller initializes HDMI before user commands. |
$FF70–$FF74 · Logo LCD (SPI)
used by: case LCD tools
| Register | Bits, reset values and notes |
|---|---|
| $FF70 | data byte |
| $FF71 | command byte |
| $FF72/$FF73 | pixel lo / hi (16-bit color) |
| $FF74 | control: reset value $30; read back b7 busy b6 tearing-effect input b5:0 control (backlight, reset, DC) |
$FF80–$FF8F · NES / SNES pads
used by: game pads
| Register | Bits, reset values and notes |
|---|---|
| $FF80 | control: b0 enable the serial pad scanner b2 pad type (0 = SNES, 1 = NES) b6 fetch complete (read) b7 start fetch (self-clearing) |
| $FF81–$FF8F | Button read ports: $FF84/$FF85, $FF86/$FF87, $FF88/$FF89 and $FF8A/$FF8B for pads 0–3. The first byte depends on NES/SNES mode; the second contains the remaining low nibble. Other offsets in this range return $A5, not button words. |
| — | the plain joystick ports are connected through VIA0 ($FEB0–$FEBF) |
$FF90 · DIP switches
used by: wildspeed, wbinfo, feu
| Register | Bits, reset values and notes |
|---|---|
| $FF90 | read-only: b7 gamma on b6:4 user 2..0 b3:0 boot mode 3..0 b0 (SW_BOOT_MODE0) is the turbo gate on RC24: 1 selects the faster SRAM schedule, live-flippable; actual CPU rate depends on read/write/I/O cycles and video arbitration |
$FF91–$FF99 · PSG, OPL3 and SID
Fixed sound registers on K2 and Jr2; no sectored sound page is needed.
| Register | Access and meaning |
|---|---|
| $FF91 / $FF92 / $FF93 | Write PSG left / both / right. |
| $FF94 / $FF95 | Write OPL3 bank 0 address / data. |
| $FF96 / $FF97 | Write OPL3 bank 1 address / data. |
| $FF98 | SID selector, read/write, reset $00: bits 6:5 select left (00), right (01), both (10), or neither (11); bits 4:0 select the SID register. Bit 7 reads 0. |
| $FF99 | Write SID data to the selected chip(s) and register. A 6809 std $FF98 writes selector then data; protect that pair if another writer can interleave. |
| Reads | Only $FF98 reads the selector back; the other sound ports return $FF. These are FPGA bridge ports, not nine independent chip registers. |
$FFA0–$FFAF · MMU (TinyVicky)
used by: krn, F$Link/VModul (slots 0–1 window), vtio/rbmem/mousedrv (slot 2), bootos9
| Register | Bits, reset values and notes |
|---|---|
| $FFA0 | memory control — MMU_MEM_CTRL / DAT.Task (defs/wildbits.d):b5:4 which LUT edits write b1:0 active LUT (reset 0) |
| $FFA1 | I/O control — MMU_IO_CTRL (defs/wildbits.d):b0 internal RAM at $FD00–$FDFF b1 internal RAM at $FFF0–$FFFF (vectors) b2 FLASHDIS: 1 = LUT blocks $40–$9F are RAM (768 KB, chip bytes $08_0000–$13_FFFF); 0 = flash at $40–$7F and the expansion connector at $80–$9Fb7 FLASHDIS.OK, read-only: 1 when FLASHDIS is implemented (older cores read back 0)All bits cleared by reset, so the machine always boots from flash. Read-modify-write only once a kernel runs: a bare store clears b2 and pulls 768 KB of live RAM away. See Documentation · FLASHDIS. |
| $FFA8–$FFAF | slot 0–7 block registers of the LUT selected for editing (8K slots at $0000, $2000 … $E000); reset = identity 0–7 with slot 7 on flash block $7F in boot mode; writes are captured on the 25 Mhz side during the CPU’s data-valid window |
| — | reads of $FFA0–$FFAF return the register copies (used by pmap, the crash dump, F$SetTsk) |
$FFB0–$FFBF · VIA1 (W65C22)
used by: joystick / expansion
| Register | Bits, reset values and notes |
|---|---|
| $FFB0–$FFBF | same layout as VIA0: port B, port A, DDRB, DDRA, T1 counter/latch, T2, SR, ACR, PCR, IFR, IER, port A no-handshake |
| — | external device on the K2; its interrupt is INT group 1 b6 |
$FFC0–$FFDF · VICKY master control
used by: vtio (banner, cursor, palette), SOLdrv, scfg
| Register | Bits, reset values and notes |
|---|---|
| $FFC0/$FFC1 | $FFC0 master control: b0 text, b1 text overlay, b2 graphics, b3 bitmap, b4 tiles, b5 sprites, b6 gamma, b7 video disable. $FFC1 is a different register: b2:0 video mode, b3 sync control, b4 font background in overlay, b5 font bank, b6 MemText enable, b7 MemText background. |
| $FFC2/$FFC3 | layer control 0 / 1 (which bitmap/tile layers draw where) |
| $FFC4 | border: b0 enable b6:4 X scroll offset |
| $FFC5–$FFC7 | border color B / G / R |
| $FFC8/$FFC9 | Border X / Y size: six-bit fields (0–63), reset $10 = 16. |
| $FFCA | VKY_DRAWLINE_REG: b0 VKY_DRAWLINE_EN — queued line-draw pixels reach SRAM only while it is set |
| $FFCB | GFX MODE (VKY_GFX_MODE):b0 HIRES4 — every bitmap plane 640 × 240 at four bits per dot b3:1 GROUP — the 16-entry CLUT slice the nibbles index A plane alone: bit 4 of its bitmap control byte. Sprites, tiles and text stay 320-wide. Details: Documentation · 640 × 240 × 16-color mode. Reset 0 = the 320-wide modes. |
| $FFCD–$FFCF | background color B / G / R (graphics mode, pixel value 0) |
| $FFD0 | cursor control: b0 text-cursor enable |
| $FFD1 | text buffer start offset |
| $FFD2/$FFD3 | cursor character / color |
| $FFD4–$FFD7 | Cursor X and Y: write X high/low, Y high/low; read X low/high, Y low/high. |
| $FFD8 | Write: line-interrupt control, b0 enable. Read: current horizontal pixel count high nibble (bits 11:8). |
| $FFDB | Read: raster-line count low byte. No line-compare control field here. |
| $FFD9/$FFDA | Write: 12-bit line compare high nibble / low byte. Read: $FFD9 horizontal pixel count low byte; $FFDA raster-line count high nibble. |
| $FFDC–$FFDF | read-only core version: VKY_VERSION_LO, VKY_VERSION_HI, VKY_SUBVER_LO, VKY_SUBVER_HI |
| — | SOL line interrupt arrives on INT group 0 b1 |
$FFE0–$FFEF · Floating-point unit
used by: float-math demos
| Register | Bits, reset values and notes |
|---|---|
| $FFE0 | control 0: b0/b1 route input A/B through the fixed→float converter b3 add/subtract select b5:4 and b7:6 operand routing |
| $FFE1 | control 1: b1:0 result select |
| $FFE2/$FFE3 | $FFE2 valid controls: b0 convert input A, b1 direct input A valid, b2 convert input B, b3 direct input B valid. $FFE3 is stored but unused by this RTL. |
| $FFE8–$FFEB | input A (32-bit, big-endian; 20.12 fixed or IEEE float) |
| $FFEC–$FFEF | input B |
| $FFE4–$FFE7 (read) | status: multiply, divide (b3 divide-by-zero), add/sub, float→int converter; each has a result-valid bit plus zero/underflow/overflow flags |
| $FFE8–$FFEB (read) | FPMATH_OUT: the result selected by control 1 |
| $FFEC–$FFEF (read) | fixed-point converter result |
IEEE-754 single precision, 32 bits, big endian, in fixed I/O on both boards, so like the integer block it needs no MMU work. Inside are Xilinx AXI cores: multiply, divide, add/subtract, and two converters between 20.12 fixed point and float. Everything is pipelined — poll the valid bit in the matching status register before reading a result. Latencies are 6 clocks for the multiply and both converters, 14 for the divide.
Operands are written to the same sixteen bytes the results are read from. Writes always land in the control file; reads return control, status or a result depending on the address. So an operand at $FFE8 or $FFEC can never be read back, only the result that replaced it. The input valid signals are levels taken from control 2, not pulses: write the operands, then set control 2, then wait for the pipe. Both of the adder’s input muxes default to input 0, so adding input 0 to input 1 needs control 0 bits 7:6 set to 01 — $40 adds, $48 subtracts.
$FFF0–$FFFF · 6809 vectors
used by: krn (DisTable), feu
| Register | Bits, reset values and notes |
|---|---|
| $FFF0 | reserved (6309 illegal-opcode trap) |
| $FFF2 | SWI3 |
| $FFF4 | SWI2 — every OS-9 system call |
| $FFF6 | FIRQ |
| $FFF8 | IRQ |
| $FFFA | SWI |
| $FFFC | NMI |
| $FFFE | RESET |
| — | fetched from flash block $7F (the FEU) in boot mode; once $FFA1 b1 is set they come from the internal RAM overlay, which is how krn installs its own vectors (D.XSWI2 etc.) |
Both pages retain their fixed addresses in every task map. The vector area at $FFF0–$FFFF switches between boot flash and internal vector RAM under $FFA1 control.
5Boot: from reset to shell
Reset — boot mode. The MMU comes up with slots 0–6 identity-mapped to RAM and slot 7 pointed at flash block $7F (verified in the LUT reset values), so the 6809’s vectors at $FFFE fetch from the top 8K of the flash window: the FEU.
FEU runs from flash. The Flash Environment Utility (living in blocks $78–$7F, with its file area visible to OS-9 later as /f0) initializes hardware, shows its menu, and executes its startup script: bootos9 /s0/OS9Boot.
bootos9 loads the bootfile. OS9Boot is read from the SD (or DriveWire) volume into RAM. The loader requires krn to be the final module, anchored exactly 4096 bytes before end-of-file — break that and you get “can’t locate the kernel in the bootfile.”
Kernel takes over. krn (at Bt.Start = $EE00) copies DisTable to D.Clock, plants the LowSub trampolines at $0160, shadows the constant page at $FD00, clears boot mode so slot 7 becomes RAM, and walks the module chain: ioman, file managers, drivers, clock.
sysgo → startup → shell. sysgo forks the shell (loading the merged SHELLMODS pack), startup runs (load utilpak1 / link shell / wbinfo; other commands depend on the installed startup file), and the console is yours.
WildBits Interrupt Groups: Hardware vs. wildbits.d
Hardware truth from fpga-6809-cores-staging/source/IRQ_Controller_Jr.v
(the lirq0 concatenation, line 153–156) vs. the OS definitions in
nitros9/defs/wildbits.d (lines 206–251). Registers per group:
PENDING $FE20+n, POLARITY $FE24+n,
EDGE $FE28+n, MASK $FE2C+n.
Report date 2026-09-01.
$FE20 / $FE24 / $FE28 / $FE2Cbits 7:0
$FE21 / $FE25 / $FE29 / $FE2Dbits 15:8
$FE22 / $FE26 / $FE2A / $FE2Ebits 23:16
$FE23 / $FE27 / $FE2B / $FE2Fbits 31:24
write 1 to clear · latches unmasked too$FE20–$FE23
0 falling / 1 rising$FE24–$FE27
1 edge / 0 level (reset $FF)$FE28–$FE2B
1 masked (reset $FF)$FE2C–$FE2F
Low bits at the bottom. The four 8-bit groups make one 32-bit lirq word; each group owns one byte in each of the four register quads, at $FE20+n, $FE24+n, $FE28+n, $FE2C+n.
RC24 interrupt routing
SD insertion is group 1 bit 7; VIA1 is bit 6. DMA is group 0 bit 6. Groups 2 and 3 have definitions in the current wildbits.d; board wiring determines which inputs are connected.
Group 0 — core system (bits 7:0 of lirq)
PENDING $FE20 · POLARITY $FE24 · EDGE $FE28 · MASK $FE2C
| Bit | Hardware signal (RTL) | What it is | wildbits.d | Status |
|---|---|---|---|---|
| 0 | VICKY_INT_Sync[0] | TinyVicky start of frame (60 Hz) — the OS clock tick | INT_VKY_SOF %00000001 | MATCH |
| 1 | VICKY_INT_Sync[1] | TinyVicky start of line (per raster line) | INT_VKY_SOL %00000010 | MATCH |
| 2 | Keyboard_int_PulSe[3] | PS/2 keyboard event pulse | INT_PS2_KBD %00000100 | MATCH |
| 3 | Mouse_int_PulSe[3] | PS/2 mouse event pulse | INT_PS2_MOUSE %00001000 | MATCH |
| 4 | ~Timer0_i | Timer 0 reached target | INT_TIMER_0 %00010000 | MATCH |
| 5 | ~Timer1_i | Timer 1 reached target | INT_TIMER_1 %00100000 | MATCH |
| 6 | DMA_INT_i | DMA engine interrupt | INT_DMA %01000000 | MATCH |
| 7 | CRT_IRQn_Sync | Cartridge (“CRT”) IRQ line from the expansion port | INT_CARTRIDGE %10000000 | MATCH |
Group 1 — peripherals (bits 15:8)
PENDING $FE21 · POLARITY $FE25 · EDGE $FE29 · MASK $FE2D
| Bit | Hardware signal (RTL) | What it is | wildbits.d | Status |
|---|---|---|---|---|
| 0 | !COM1_int_PulSe[3] | UART (COM1 / 16550) ready | INT_UART %00000001 | MATCH |
| 1 | VICKY_INT_Sync[2] | Unused VICKY input (tied off by the board wrapper) | — | Unused |
| 2 | VICKY_INT_Sync[3] | Unused VICKY input (tied off by the board wrapper) | — | Unused |
| 3 | VICKY_INT_Sync[4] | Unused VICKY input (tied off by the board wrapper) | — | Unused |
| 4 | RTC_IRQ[2] | Real-time clock chip event | INT_RTC %00010000 | MATCH |
| 5 | VIA0_INT_i | 65C22 VIA #0 event | INT_VIA0 %00100000 | MATCH |
| 6 | VIA1_INT_i | VIA1 event on K2; no external VIA1 interrupt on Jr2 | INT_VIA1 %01000000 | MATCH |
| 7 | SDC_IRQ[2] | SD card inserted | INT_SDC_INS %01000000 | WRONG VALUE |
SD insertion and VIA1 are separate
The current definitions use INT_SDC_INS = %10000000 and INT_VIA1 = %01000000. The optical keyboard is group 3 bit 2 on the K2.
Group 2 — IEC bus + module IRQ pins (bits 23:16)
PENDING $FE22 · POLARITY $FE26 · EDGE $FE2A · MASK $FE2E
| Bit | Hardware signal (RTL) | What it is | wildbits.d | Status |
|---|---|---|---|---|
| 0 | IEC_DATA_i_Sync | IEC serial bus DATA in | IEC_DATA_i %00000001 | MATCH |
| 1 | IEC_CLK_i_Sync | IEC serial bus CLK in | IEC_CLK_i %00000010 | MATCH |
| 2 | IEC_ATN_i_Sync | IEC serial bus ATN in | IEC_ATN_i %00000100 | MATCH |
| 3 | IEC_SREQ_i_Sync | IEC serial bus SREQ in | IEC_SREQ_i %00001000 | MATCH |
| 4 | NET_IRQn_Sync | Ethernet (WizNet module) IRQ pin | INT_NET_PIN %00010000 | MATCH |
| 5 | WIFI_IRQn_Sync | WiFi module IRQ pin (the module’s own line, not the FIFOs) | INT_WIFI_PIN %00100000 | MATCH |
| 6 | HDMI_IRQn_Sync | HDMI encoder IRQ pin | INT_HDMI_PIN %01000000 | MATCH |
| 7 | constant 1'b0 | unused | — | n/a |
The IEC bits also drive NMI
The four IEC bits (group 2 bits 0–3, Interrupt[16..19]) have a second life: when the
IEC_NMI_IRQn_i input is high they also drive the CPU’s NMI
through the same pending/mask terms as IRQ (IRQ_Controller_Jr.v).
Group 3 — FIFO events: WiFi / keyboard / MIDI / WizNet (bits 31:24)
PENDING $FE23 · POLARITY $FE27 · EDGE $FE2B · MASK $FE2F
| Bit | Hardware signal (RTL) | What it is | wildbits.d | Status |
|---|---|---|---|---|
| 0 | NEW_Rx_FIFO_WIFI_Sync | WiFi RX FIFO went non-empty | INT_WIZFI_RX %00000001 | MATCH |
| 1 | NEW_Rx_FIFO_MIDI_Sync | MIDI received-byte event pulse (while RX data is available) | INT_MIDI_RX %00000010 | MATCH |
| 2 | NEW_Optical_Kbd_Sync | K2 optical keyboard FIFO went non-empty (latches on every keystroke; Jr2 never wires this) | INT_OPT_KBD %00000100 | MATCH |
| 3 | NEW_WizNet_FIFO_Sync | WizNet FIFO route; bridge output is tied to 0 in RC24, so it does not generate FIFO events | INT_WIZNET %00001000 | MATCH |
| 4 | NEW_Rx_FIFO_MIDI_VS_Sync | Reserved MIDI-VS input; tied inactive on both boards | INT_MIDI_VS_RX %00010000 | MATCH |
| 5 | NEW_Tx_FIFO_WIFI_Sync | WiFi TX FIFO drained to empty | INT_WIZFI_TX %00100000 | MATCH |
| 6–7 | constant 2'b00 | unused | — | n/a |
Shared group, separate masks and handlers
WiFi, MIDI and the K2 keyboard share PENDING_3/MASK_3. Each driver must clear and unmask only its own bits. The overlaid K2 typematic keyboard driver installs an F$IRQ handler for bit 2; it does not rely solely on 60 Hz polling.
Controller behavior (applies to all four groups)
- Reset defaults: POLARITY = $00 (falling edge), EDGE = $FF (edge mode), MASK = $FF (all masked), PENDING = $00. These reset only on FPGA reset — a soft reboot preserves them, so mask bits a driver unmasked (e.g. wizfi’s group-3 bits) survive into the next OS boot.
- PENDING latches unconditionally:
pending <= pending | irq_event— the mask gates only the CPU IRQ output (Interrupt[n] = pending[n] & ~mask[n]), never the latch. - PENDING is write-1-to-clear; reads are side-effect-free. MASK is a plain read/write register.
- OS cold start: the Level 2 kernel writes $FF to all four MASK and PENDING registers, masking and clearing every group before driver initialization.
Definitions and board wiring
The current definitions include DMA, SD insertion and group 2/3 sources. An equate does not imply a connected peripheral: the board wrappers tie off unused VICKY inputs and MIDI-VS RX. K2-only keyboard and Ethernet inputs do not become Jr2 hardware.
Where this comes from
RC24: IRQ_Controller_Jr.v, the K2/Jr2 IO_Page0 device wrappers, OpticalKeyboardScanner.v, F256K2_MIDI_Interface.v, defs/wildbits.d, Level 2 krn.asm and the K2 typematic-overlay driver. Register decoding and board connections were reviewed together.
WildBits Physical Memory Map
The Longview map with FLASHDIS enabled: 252 allocatable RAM blocks (2,016 KB), four device pages, and one address rule for CPU, VICKY and DMA. Boot and runtime allocations reduce the memory shown as free.
One address rule
block × $2000 + offset
2,016 KB mapped from a 2,048 KB SRAM chip. The final four block entries select 32 KB of device address space instead of SRAM.
System RAM — who owns what at run time
| Blocks | Owner | Contents |
|---|---|---|
| $00 | Kernel (permanent) | The system block: D.* globals page, the 8K-block allocation map at $0200 (one byte per physical block, up to 2MB), system/user dispatch tables, kernel data — and the boot trampoline at $0600 (below). Marked used by the memory-sizing routine itself. |
| $01–$03 | Boot-size-dependent | May hold boot modules or be available for general allocation. (Block 1 doubles as bootos9’s temporary MMU copy window during a re-boot, then returns to the pool.) |
| $04–$07 | Boot modules | OS9Boot is loaded into the blocks just below block 8 — blocks (8−n)…7 for an n-block bootfile (for n=4 this is $04–$07; larger bootfiles occupy more lower blocks). The kernel (last module in the merge) lands in the top block and is marked used; the rest hold the boot module directory contents. |
| $08–$3F | General allocation (448 KB span) | Process address spaces, RBF/SCF buffers, bitmap framebuffers (VICKY fetches these over its RAM port — the layers you see in view/shellbg live here), and rbmem’s 8K flash-write cache (one block via F$AllRAM at Init). |
| $40–$9F | General RAM · 768 KB | FLASHDIS replaces the flash and expansion selects with SRAM at $08_0000–$13_FFFF. |
| $A0–$FB | General RAM · 736 KB | SRAM at $14_0000–$1F_7FFF, including former device and undecoded gaps. No separate RAM windows. |
| $FC–$FF | Device pages · 32 KB | VICKY, fonts/CLUTs, text and attributes; excluded from the RAM allocator. |
Memory sizing starts with the boot map. The Longview krnp2 expands the block table to 256 entries, checks FLASHDIS.OK, and sets FLASHDIS when supported. The pool has 252 blocks (2,016 KB); only $FC–$FF are marked NotRAM. With FLASHDIS clear, $40–$9F are also excluded, leaving 156 blocks (1,248 KB). mfree reports what remains after allocations, not the full pool. Longview needs its matching kernel, disk and FEU.
The trampoline
When bootos9 (re)boots the system, running code is about to yank the MMU out from under itself. The escape: it copies a tiny relocatable stub to $0600 in block 0 (RELOC_ADDR), jumps to it, and the stub — running from an address that stays mapped — resets MMU slots 0–7 to identity (blocks 0–7), then jumps to the kernel entry point found in the freshly-loaded bootfile. The same staging is used by the FEU’s os9boot. The staging allocation depends on the bootfile size; $04–$07 describes a four-block bootfile, not every current build.
The boot chain, block by block
- Power-on → Booter (flash $7D–$7F, the top 24 KB of the addressable flash window — the three
booter_*.binpieces fnxmgr installs). - Booter → FEU OS-9: loads the mini system from /f0 (flash $78–$7C, the five
f0_*.binpieces, 40K). FEU = “First Executable Unit” — a Level 1-style OS-9 system with boot modules in flash and working data in RAM. - FEU startup →
bootos9 /s0/OS9Boot: the FEU’s startup script chains to the real system — bootos9 reads the SD card’s OS9Boot into RAM blocks (8−n)…7 through the block-1 copy window. - Trampoline: stub at $0600, MMU identity map, jump to kernel — full OS-9 running from SD-loaded RAM. The FEU’s flash blocks return to being just the /f0 drive.
Flash at reset · FLASHDIS clear
| Blocks | Size | Role |
|---|---|---|
| $40–$77 | 448K | /f1 — the user flash drive (formattable, writable; rbmem erase/program with DQ6 polling). |
| $78–$7C | 40K | /f0 — the FEU system image; mounted read-only at run time. fnxmgr’s bulk.csv flashes these as chip sectors $38–$3C. |
| $7D–$7F | 24K | Booter — first code after reset; chip sectors $3D–$3F. |
Expansion window · FLASHDIS clear
One external-bus decode, with board-dependent attached hardware: on the K2 this is the 256 KB expansion region
(physical $10_0000–$13_FFFF). On the Jr2 the same select reaches the
cartridge port: /c0 is based at block $80 and /c1 at $90
(cartridge flash identification must use the actual cartridge chip; it is not necessarily the onboard flash type). This region shares the external
bus — and its single write strobe — with the flash chip and the RTC, which is why the v8_rc6 core’s
strobe policy covers all three.
Sectored device pages ($FC–$FF)
| Block | Contents |
|---|---|
| $FC | VICKY registers and tables, by page offset: $0000–$0BFF gamma windows B / G / R (each has a 256-byte table mirrored through a 1 KB decode window, readable) $0C00–$0FFF mouse pointer graphic $1000–$10FF bitmap layer registers (three 8-byte sets at $1000 / $1008 / $1010; the line-draw accelerator at $1080–$1087, see the sprite chapter) · $1100–$11FF tile registers · $1200–$12FF memtext registers $1300–$16FF sprite attribute records (128 × 8 bytes, see the sprite chapter) $1700–$173F text foreground color LUT · $1740–$177F text background color LUT (16 entries × 4 bytes each, B G R A; loaded with the OS-9 palette at power-up; readable through shadow copies) |
| $FD | Font memory at $0000–$0FFF (FONT_BLK); the four graphics CLUTs at $1000–$1FFF (256 entries × 4 bytes each, LUTn at $1000 + $400×n; readable; all zero at power-up until a program loads them) |
| $FE | Text screen memory; the device decode exposes offsets $0000–$12BF (4,800 bytes) in this 8 KB page. |
| $FF | Text color attribute memory; offsets $0000–$12BF (4,800 bytes). One byte per character cell: foreground index in the high nibble, background in the low. |
These are VICKY-internal pages, not RAM — mapping one into a slot windows the device, and the
MMU entry pattern 1111 11xx routes there instead of to memory.
Longview system RAM
2,016 KB of SRAM in one run (K2 and Jr2): with FLASHDIS set, LUT entries $00–$FB select SRAM at $00_0000–$1F_7FFF. The former $C0–$CF device gap and $F0–$FB undecoded blocks are now RAM. Devices occupy only $FC–$FF. Every RAM block follows block × $2000 + offset; the 16-bit SRAM chip’s word-address pins carry the byte address divided by two. The kernel excludes the four device pages from its 256-entry allocation map. Fixed CPU register and vector overlays remain unchanged.
Memory and external-bus writes
With the max-RAM kernel, FLASHDIS maps 2,016 KB of SRAM on Longview. Flash and cartridge accesses use the shaped write pulse when FLASHDIS is clear; when set, those MMU blocks select SRAM instead. RTC accesses retain the raw CPU write signal for their wait-stretched cycles. DMA remains a separate physical-address engine; the DMA sequencing and limitations above still apply.
Source: shared TyVKy2K2turbo_MMU_FNX6809.v write-strobe logic and TyVKy2K2x1_MMU_Register.v FLASHDIS decode, used by both projects.
FLASHDIS — the flash window as RAM: MMU_IO_CTRL ($FFA1) bit 2
768 KB more RAM (K2 and Jr2). The SRAM is 2 MB, but the flash chip and the expansion connector sat in front of its middle third: LUT entries $40–$7F selected the flash and $80–$9F the expansion, so chip bytes $08_0000–$13_FFFF were never reachable. One bit swaps them out.
FLASHDIS = 0 — Longview reset map
156 RAM blocks · 1,248 KB in all
FLASHDIS = 1 — Longview RAM map
252 RAM blocks · 2,016 KB in all · free memory depends on allocations
| Register | Bit | Name (defs/wildbits.d) | Meaning |
|---|---|---|---|
$FFA1 MMU_IO_CTRL | 7 | FLASHDIS.OK | Read-only. 1 on a core that implements FLASHDIS, 0 on one that does not — they hand back what was stored, and nothing stores a 1 there. The kernel tests this bit before touching bit 2. |
| 2 | FLASHDIS | 1 = LUT blocks $40–$9F are RAM. 0 = flash at $40–$7F and the expansion connector at $80–$9F, as always. Cleared by reset, so the machine boots from flash every time; the FEU trampoline runs from flash and stores $00 / $02 here, and is unaffected. | |
| 1 | — | Internal RAM at $FFF0–$FFFF (the vector page); unchanged. | |
| 0 | — | Internal RAM at $FD00–$FDFF; unchanged. |
The register is readable; names as in defs/wildbits.d (equates FLASHDIS = %00000100, FLASHDIS.OK = %10000000).
What the kernel does at boot (Longview krnp2, both machines, before anything is forked):
1 · read
lda >MMU_IO_CTRL — one read of $FFA1.
2 · FLASHDIS.OK set
ORs FLASHDIS in (read-modify-write, bits 0/1 kept). Only $FC–$FF are excluded: 252 RAM blocks, 2,016 KB.
2′ · FLASHDIS.OK clear
Leaves the register unchanged and also excludes $40–$9F. On the Longview reset map, 156 RAM blocks remain (1,248 KB). This flag does not identify or make a V8 device map compatible.
WildBits 64K Memory Map
Which CPU addresses pass through the MMU, and which are hard-wired — including the two constant-RAM pages that sit outside MMU translation entirely. Derived from the shipping RTL decode logic, not documentation.
top carved out →$E000–$FFFF
switchable · $FFA1 bit 0$FD00–$FDFF
switchable · $FFA1 bit 1$FFF0–$FFFF
Low addresses at the bottom, datasheet-style. The right column expands the top 768 bytes of slot 7; $E000–$FCFF below the carve-outs is ordinary MMU-translated space.
How the split works
The CPU’s 64K space is divided into eight 8K slots, each translated through an MMU look-up entry. A single inhibit term overrides that translation for the top pages:
| Region | Class | Behavior |
|---|---|---|
| $0000–$FCFF | MMU | Translated through the active LUT entry for its slot. The entry’s high bits pick the physical target (see encoding below). |
| $FD00–$FDFF | Constant RAM | When $FFA1 bit 0 = 1, a dedicated internal RAM page supersedes whatever the MMU maps there. Bit 0 = 0 (the reset state): ordinary MMU space. |
| $FE00–$FEFF | Fixed I/O | Always the fixed I/O page — never RAM, regardless of MMU contents. |
| $FF00–$FF9F | Fixed I/O | Always fixed I/O. |
| $FFA0–$FFAF | MMU regs | The MMU’s own control and slot registers (decoded as $FFAx). |
| $FFB0–$FFEF | Fixed I/O | Always fixed I/O (includes the text controller at $FFC0). |
| $FFF0–$FFFF | Constant RAM | The 6809 interrupt-vector page. When $FFA1 bit 1 = 1, a dedicated internal RAM page supersedes MMU RAM/flash. Bit 1 = 0 (reset state): vectors come from MMU space (RAM or flash). |
The constant-RAM pages — what makes them special
Both switchable pages ($FD00–$FDFF and $FFF0–$FFFF) are physically
separate internal RAM, not part of the MMU-managed block RAM. Three properties follow:
• While enabled, they are visible at the same addresses in every task and every LUT —
remapping slots never moves or hides them. That is what makes the vector page usable for interrupt
dispatch across task switches.
• RESET clears both enable bits, so the pages disappear from the map after a reset until
software sets $FFA1 again — but their contents are retained until power-off.
Values written before a reset are still there when the page is re-enabled.
• They supersede reads and writes: with a page enabled, nothing behind it (MMU RAM or flash)
can be touched at those addresses.
MMU machinery
| Register | Address | Function |
|---|---|---|
| MMU_MEM_CTRL | $FFA0 | Bits 1:0 select the active LUT (0–3); bits 5:4 select the LUT being edited through the slot registers. |
| MMU_IO_CTRL | $FFA1 | Bit 0 enables constant RAM at $FD00–$FDFF; bit 1 enables constant RAM at $FFF0–$FFFF; bit 2 FLASHDIS turns LUT blocks $40–$9F from flash/expansion into 768 KB of RAM; bit 7 FLASHDIS.OK (read-only) reads 1 when FLASHDIS exists. Readable; read-modify-write it. Names as in defs/wildbits.d. |
| MMU_SLOT_0–7 | $FFA8–$FFAF | One LUT entry per 8K slot ($FFA8 → $0000–$1FFF … $FFAF → $E000–$FFFF). |
Slot-entry encoding (physical target select)
| Entry bits | Target | Notes |
|---|---|---|
| 00xx xxxx | System RAM | 8K block number in the low bits. |
| 01xx xxxx | Flash / SRAM | Blocks $40–$7F: flash with FLASHDIS clear; SRAM with it set. |
| 100x xxxx | Expansion / SRAM | Blocks $80–$9F: expansion with FLASHDIS clear; SRAM with it set. |
| $A0–$FB | System RAM | 736 KB at $14_0000–$1F_7FFF, regardless of FLASHDIS. |
| $00–$FB | System RAM with FLASHDIS set | 252 blocks (2,016 KB) at $00_0000–$1F_7FFF. |
| 1111 11xx | Sectored I/O page | The four relocatable device pages ($FC–$FF). |
Selected fixed I/O groups
| Address | Device |
|---|---|
| $FE00–$FE03 | SYS0/SYS1 system control, RST0/RST1 |
| $FE20–$FE2F | Interrupt controller (pending / polarity / edge / mask) |
| $FE50–$FE54 | PS/2 keyboard and mouse |
| $FE60–$FE67 | 16750 UART (DriveWire) |
| $FE70 | WM8776 codec control |
| $FE90 | SD card controller |
| $FEB0 | VIA0 |
| $FEC0 | DMA controller |
| $FF20–$FF2A | WizFi UART and reset controls |
| $FF30–$FF39 | MIDI FIFO + SAM2695 Synth |
| $FF91–$FF99 | PSG / OPL3 / SID sound ports |
| $FE78–$FE7F / $FE88–$FE8F | K2 RP2040 mailbox |
| $FFC0 | Text controller (TXT.Base) |
Where this comes from
Decode logic traced in the shipping cores (both machines share these files):
• TyVKy2K2turbo_MMU_FNX6809.v — page decodes ($FDxx / $FExx / refined $FF00–$FF9F + $FFB0–$FFEF / $FFFx) and the $FFAx MMU-register select.
• TyVKy2K2x1_MMU_Register.v — RAM_Access_Inhibit (the master “not subject to the MMU” term), slot-entry target encoding, and the two $FFA1 enable bits.
• defs/wildbits.d — register names, device addresses, and the constant-RAM retention note.
One caveat: all fixed decodes are qualified by !Dbg_Mode_i — when the hardware debugger owns the bus, these carve-outs are bypassed.
WildBits Sprite Engine
Hardware reference for the 128-sprite engine in the 6809 cores: register map, attribute format, coordinate system, the minimum programming recipe for putting a sprite on screen — and the mapping required by Longview RC24.
3 × 256 bytes$0000–$0BFF
future$1200–$12FF
128 × 8 bytes, 4 banks of 32 · record →$1300–$16FF
foreground $1700, background $1740$1700–$177F
b0 enable · b2:1 LUT · b4:3 depth · b6:5 size+0
pixel data, 24-bit, big-endian+1–+3
Low offsets at the bottom. Page $FC reaches the CPU through an MMU slot (slot 2 puts record n at $5300 + 8n); the pixel data the record points to lives in system RAM, not in the page.
| At a glance | Detail |
|---|---|
| 128 | hardware sprites, scanned 127 → 0 each line pair (sprite 0 composites on top) |
| 4 sizes | 32×32, 24×24, 16×16, 8×8 — per-sprite, 2 control bits |
| 8 bpp | indexed color through one of 4 graphics CLUTs; index 0 = transparent |
| 320×240 | coordinate space, origin offset +32: screen top-left is (32,32) |
How the engine works
Sprites are line-buffered, not framebuffered. The display runs 640×480 with every 320×240 pixel doubled, so the engine only needs each sprite line once per pair of scanlines. On every odd scanline, during horizontal blanking, the master video scheduler enters its sprite phase (DoSpriteHere), hands VRAM channel 3 to the sprite state machine, and fires it.
The sprite state machine then walks all 128 attribute records, from sprite 127 down to sprite 0. For each record it checks two things in two clocks: the per-sprite enable bit, and a line hit — whether the upcoming pixel row falls inside the sprite’s vertical span. Disabled records take the probe/next path; enabled records also perform the line-hit check. Rendering throughput depends on the number and width of sprites hitting that row. On a hit, the engine multiplies the row-within-sprite by the sprite’s line width, adds the pixel base address, and streams that one line of pixels (8–32 bytes, fetched 16 bits at a time) from video RAM into the sprite line buffer at horizontal position X. Because sprite 0 is written last, it wins overlaps.
During the following even scanline, the compositor reads the line buffer back at pixel rate and merges it with text, bitmap, and tiles according to the master control register. A non-zero pixel index is looked up in the sprite’s selected CLUT; index 0 lets the layers behind show through.
The scan pipeline, per line pair
| Stage | State(s) | What happens |
|---|---|---|
| Arm | SP_IDLE → SP_PRESTATE | Sprite_Effect_On raised by the master scheduler on odd-line blanking; channel select preloaded to sprite 127. |
| Probe | SP_STATE0 → SP_STATE1 | One clock of BRAM latency, then the 64-bit attribute record is valid. Disabled → NEXTCHANNEL. |
| Line hit | ENABLED | Hit when Y ≤ line/2+32 < Y+height. Miss → NEXTCHANNEL. |
| Fetch | SP_GET_DATA_VRAM0..1, SP_WRITE_ATTRIB0..2 | Line-buffer write pointer set to X+64; VRAM counter loaded with start/stop = base + row×width (+width); pixels stream on each VRAM_Data_Valid until the counter reaches stop. |
| Next / done | NEXTCHANNEL → … → SP_TRF_DONE | Decrement sprite select; after sprite 0, signal done — the master scheduler moves on to bitmap/tile slots. |
Register map
Master control — $FFC0 (fixed I/O, visible in every map)
| Bit | Mask | Name | Function |
|---|---|---|---|
| 0 | $01 | Text_Mode_En | Text layer on |
| 1 | $02 | Text_Overlay | Text floats over graphics (text background transparent) |
| 2 | $04 | Graph_Mode_En | Graphics pipeline on — required for sprites |
| 3 | $08 | Bitmap_En | Bitmap layer on |
| 4 | $10 | TileMap_En | Tile layer on |
| 5 | $20 | Sprite_En | Sprite layer on |
| 6 | $40 | GAMMA_En | Gamma correction |
| 7 | $80 | Disable_Vid | Disable video; available CPU bandwidth still depends on other bus users |
Text + overlay + graphics + sprites = $27. Add bitmap for $2F.
Sprite attribute block — VICKY page $FC, offsets $1300–$16FF
The 128 attribute records live in a dual-port BRAM inside I/O page $FC (the same page as the bitmap/gamma registers). Map the page into a CPU slot to reach it — e.g. write $FC to MMU_SLOT_2 ($FFAA) and the block appears at $4000–$5FFF. Each sprite owns 8 bytes:
| Sprites | Page offset | CPU addr (slot 2) | Record n at |
|---|---|---|---|
| 0 – 31 | $1300–$13FF | $5300–$53FF | $5300 + 8×n |
| 32 – 63 | $1400–$14FF | $5400–$54FF | $5400 + 8×(n−32) |
| 64 – 95 | $1500–$15FF | $5500–$55FF | $5500 + 8×(n−64) |
| 96 – 127 | $1600–$16FF | $5600–$56FF | $5600 + 8×(n−96) |
The RTL rotates the page index (RecodedAddy) so that page $13 lands on BRAM rows 0–31 — the table above is the net effect; sprite 0 really is the first 8 bytes of $1300.
The 8-byte attribute record
Multi-byte fields are big-endian (high byte first, 6809-friendly), so a single 16-bit
STD at the high offset stores X or Y correctly in one instruction:
Worth knowing if you are reading other documentation: pre-rework cores and the official 65C02 material describe these fields as little-endian. The 6809 rework changed it, confirmed on the hardware, and the NitrOS-9 equates were corrected to match on 2026-08-31.
| Offset | Field | Contents |
|---|---|---|
| +0 | CTRL | bit 0 enable · bits 2:1 LUT · bits 4:3 depth · bits 6:5 size |
| +1 | ADDR hi | pixel data address bits 23:16 |
| +2 | ADDR mid | pixel data address bits 15:8 |
| +3 | ADDR lo | pixel data address bits 7:0 |
| +4 | X hi | position bits 15:8 |
| +5 | X lo | position bits 7:0 |
| +6 | Y hi | position bits 15:8 |
| +7 | Y lo | position bits 7:0 |
| CTRL bits 6:5 | Size | Example CTRL (enable, LUT0) |
|---|---|---|
| 00 | 32 × 32 | $01 |
| 01 | 24 × 24 | $21 |
| 10 | 16 × 16 | $41 |
| 11 | 8 × 8 | $61 |
The pixel-data address is a physical address in system RAM (block × $2000 + offset, for every SRAM block through $FB), pixels stored row-major, one byte per pixel, rows packed at the sprite’s width. Keep depth = 00 (8 bpp).
Graphics CLUTs — VICKY page $FD, offsets $1000–$1FFF
Sprites pick one of four CLUTs with CTRL bits 2:1. The CLUTs share page $FD with the font (font at $0000–$0FFF, CLUTs above it). Each CLUT is 256 entries × 4 bytes, ordered B, G, R, A. All four read back (the CPU port of a true dual-port RAM) and hold zeros at power-up: the shell’s colors come from the text color LUTs below, so nothing loads these until a bitmap viewer or a sprite program does.
| CLUT | Page offset | CPU addr (slot 2) | Entry i at |
|---|---|---|---|
| LUT0 | $1000–$13FF | $5000–$53FF | $5000 + 4×i |
| LUT1 | $1400–$17FF | $5400–$57FF | $5400 + 4×i |
| LUT2 | $1800–$1BFF | $5800–$5BFF | $5800 + 4×i |
| LUT3 | $1C00–$1FFF | $5C00–$5FFF | $5C00 + 4×i |
Text color LUTs — VICKY page $FC, offsets $1700 (foreground) and $1740 (background)
Text and the banner font take their colors from two 16-entry tables in page $FC, the sprite-record page: foreground at $1700–$173F, background at $1740–$177F, 4 bytes per entry ordered B, G, R, A, indexed by the two nibbles of the character’s attribute byte. The core loads both with the OS-9 palette when the bitstream comes up, so they survive a reset; a power cycle restores them. The CPU reads them back (each table has a shadow copy written on the same strobe), so a program can save the tables it is about to change and put them back on exit. lutrd on the kit disks dumps both, plus the graphics CLUTs and the gamma tables.
| Table | Page offset | Entries | Access |
|---|---|---|---|
| text FG | $1700–$173F | 16 × B G R A | read / write |
| text BG | $1740–$177F | 16 × B G R A | read / write |
| gamma B / G / R | $0000 / $0400 / $0800 | 3 × 256 bytes | read / write |
| graphics LUT0–3 | page $FD $1000 + $400×n | 256 × B G R A each | read / write |
Line-draw accelerator — VICKY page $FC, offsets $1080–$1087 (wildbits.d: TyVKY_LD_*)
A Bresenham line engine sits at the top of the bitmap register block (TyVKY_BM0_CTRL_REG is $F000 with page $FC in slot 7, so these are $F080–$F087). Write the two end points and a color, raise the go bit, and the engine walks the line at 100 Mhz, queueing line writes into an 8,192-entry × 34-bit FIFO (34 KB of queue data; 36 KB of allocated block RAM) that the memory manager drains into the chosen bitmap. It writes one byte per pixel at bitmap start + y×320 + x in the 320-wide 8-bit modes; in HIRES4 (GFX_HIRES4) X runs to 639 (TyVKY_LD_XMAX4) and the low nibble of the color is the dot. Pixels reach SRAM only while VKY_DRAWLINE_REG ($FFCA) bit 0 is set.
| Offset | wildbits.d | Write | Read |
|---|---|---|---|
| $1080 | TyVKY_LD_CTRL | bit 0 TyVKY_LD_ENABLE (legacy; the write gate is $FFCA bit 0) · bit 1 TyVKY_LD_GO · bits 3:2 target bitmap (TyVKY_LD_BM0 $00, TyVKY_LD_BM1 $04, TyVKY_LD_BM2 $08; $0C falls back to base $001000) · bit 4 TyVKY_LD_RESET | bit 7 TyVKY_LD_DONE (1 = the generator is done, not busy), bits 6:0 the control bits |
| $1081 | TyVKY_LD_COLOR | 8-bit CLUT index (HIRES4: bits 3:0) | the color |
| $1082 | TyVKY_LD_X0_H | X0 bits 9:8 | TyVKY_LD_COUNT_H: FIFO entries still queued, bits 13:8 |
| $1083 | TyVKY_LD_X0_L | X0 bits 7:0 | TyVKY_LD_COUNT_L: FIFO entries still queued, bits 7:0 |
| $1084 | TyVKY_LD_X1_H | X1 bits 9:8 | X1 bits 7:0 (the read order is swapped) |
| $1085 | TyVKY_LD_X1_L | X1 bits 7:0 | X1 bits 9:8 |
| $1086 | TyVKY_LD_Y0 | Y0 (8 bits) | Y1 (swapped) |
| $1087 | TyVKY_LD_Y1 | Y1 (8 bits) | Y0 (swapped) |
- Accepted range: X0 and X1 below 320 (640 in HIRES4), Y0 and Y1 below 240 (
TyVKY_LD_XMAX8,TyVKY_LD_XMAX4,TyVKY_LD_YMAX) — a go with any end point outside is ignored (the engine stays idle, done never rises). - Handshake: asserting the go bit while idle starts the line and the engine parks in its done state until the go bit is written back to 0; drop it before the next line. Done (bit 7 on read) says the walk has finished; the FIFO count at $1082/$1083 says how many entries are still queued. Wait for the queue to drain before treating the bitmap as complete. In HIRES4, shallow lines can combine two adjacent pixels into one entry; unpaired pixels preserve the other nibble.
- Read-back quirks: X0 cannot be read (its two bytes return the FIFO count), and the X1 and Y read order is the reverse of the write order — both are in TinyVicky_BM_Registers.v’s read multiplexer. Bit 7 on read is
TyVKY_LD_DONE. - Target address: bits 3:2 pick which bitmap layer’s start address ($1001–$1003 / $1009–$100B / $1011–$1013) the pixel addresses are built on; the layer does not have to be displayed.
- Cost: the line FIFO uses 8 × RAMB36 (288 Kbit physical), with 8,192 × 34 bits (272 Kbit) of queue data. The sprite engine has a separate 512 × 18-bit line buffer; it does not use this FIFO.
Sources: TinyVicky_BM_Registers.v (LINE_DRAWING_REG, the write decode on address bit 7, the read multiplexer), LineDraw.v (X0_Ok/Y0_Ok limits, the IDLE/RUN/DONE machine, Addy_Addyx_Offset), TinyVickyCoreModule.v (the LINEDRAWING_Reg64 wiring), TinyVKY2K2_IO_Page0_Devices.v (CS_VICKY_BITMAP: page $FC, offsets $1000–$10FF; absolute $1F_9000–$1F_90FF; the Jr2 decode is identical).
640 × 240 × 16-color bitmap mode — GFX MODE $FFCB
The bitmap engine’s native picture is 320 × 240 with one byte per dot, each dot shown twice across. HIRES4 keeps every byte where it is and reads it as two dots of four bits, the high nibble on the left, so a 320-byte row becomes 640 dots and the frame stays 76,800 bytes: same RAM, same fetch bandwidth, same raster, nothing for an old monitor to notice. Two ways to switch it on, OR’d together:
| Where | Bits |
|---|---|
| $FFCB (fixed I/O) | bit 0 GFX_HIRES4: every bitmap plane · bits 3:1 GFX_GROUP: which 16-entry slice of the plane’s CLUT the nibbles index. Pokeable from any task, no page $FC mapping needed; reset value 0 = the 320-wide behaviour. |
| bitmap control byte ($F000 / $F008 / $F010 with page $FC in slot 7) | bit 4 BM0_HIRES4: this plane alone · bits 7:5 BM0_GROUP: its CLUT slice. Bits 3:0 (enable, LUT number) unchanged. |
Colors. A dot’s CLUT entry is group × 16 + nibble, in the CLUT the plane’s LUT bits select; the sixteen entries of the slice are the whole palette of that plane. Nibble 0 is transparent, as byte 0 is in the 320 mode, so it shows the layer behind or VICKY’s background color ($FFCD–$FFCF). Transparency is per 640 dot: the two halves of one byte can show through to different things.
What it does not change. Sprites, tiles and text are not part of it. They fetch and draw exactly as before, at 320 dots doubled, and are composited with the 640-wide bitmap dots in the usual layer order ($FFC2/$FFC3): a sprite or tile dot always covers a pair of bitmap dots, and its X position is still a 320-column unit, so it cannot sit on an odd 640 column. Each plane decides for itself, so a 640-wide picture and a 320-wide one can share the frame. The dot order inside a pair is fixed by the design (a blanking-aligned phase, not a sampled clock), so it is the same on every build.
On the Jr2 the mode costs a second CLUT copy and a second color line in block RAM (see the block-RAM budget below); both cores carry it.
Coordinates
Sprite positions live in a space offset 32 pixels beyond the visible screen on the top and left, so sprites can slide off any edge. The visible 320×240 area spans sprite coordinates (32,32) to (351,271):
Upper-right corner for an N-wide sprite: X = 32 + 320 − N, Y = 32. For 8×8: X = 344 ($0158), Y = 32 ($0020).
Minimum recipe — a green 8×8 sprite at screen center
Read this first
Five register groups, one protected mapping window. This example describes direct MLUT access: the kernel restores maps during task switches, so a temporary direct mapping must remain protected until it is restored. Use kernel mapping services where the installed kernel supports the required device mapping.
- Prepare pixel data (no masking needed) — fill a 64-byte buffer in RAM you own with
$01(pixel index 1) and compute its physical address: block# × $2000 + offset. The buffer must outlive the sprite — a process’s pages are recycled at exit, so stay resident or use memory you keep. - Enter the masked window — save CC first, then
orcc #IntMasks. Everything touching $FFA0, the slot registers, or the mapped window sits inside one masked stretch. - MLUT setup — read
$FFA0, save it, write it back with bits [5:4] (EDIT map) copied from bits [1:0] (ACTIVE map) — otherwise slot writes edit somebody else’s map. Save slot 5 ($FFAD, window at $A000) and write$FDinto it. - Palette (page $FD) — LUT0 entry 1 at window+$1004: write
$00, $FF, $00, $00(B, G, R, A) — pure green. - Sprite record (page $FC) — write
$FCto $FFAD; sprite 0’s record is at window+$1300, eight bytes, big-endian fields:If the other 127 records can’t be trusted (power-on garbage can carry stray enable bits), zero $1300–$16FF first.Offset Value Meaning +0 $61 enable + 8×8 (size 11) + LUT0 + depth 0 +1..+3 addr hi, mid, lo physical address of the pixel buffer +4, +5 $00, $BC X = 188 (center: 32 + (320−8)/2) +6, +7 $00, $94 Y = 148 (center: 32 + (240−8)/2) - Leave the window — restore $FFAD, restore $FFA0, then restore the saved CC (including the original interrupt masks).
- Enable the layer —
$FFC0is fixed I/O: no mapping, no masking, any map, any time. Read it, OR in$26(graphics $04 + overlay $02 + sprite $20, preserving the text bit), write it back. The green square appears at center.
To hide the sprite later: clear bit 5 of $FFC0 (no mapping needed), or clear the record’s enable bit (needs the mapped window again). The complete working implementation of this recipe is the sprtest command source: level1/wildbits/cmds/sprtest.asm.
Where this comes from
RTL and definitions read for this reference:
• Sprite_State_Machine.v — the per-line-pair scan, line-hit test and VRAM fetch.
• TinyVickyCoreModule.v — master video scheduler and compositor.
• TinyVKY2K2_IO_Page0_Devices.v — attribute BRAM, page rotation (RecodedAddy), CLUTs.
• defs/wildbits.d — register names and addresses.
How the cores spend their block RAM
Routed RAMB36/RAMB18 totals for both RC24 builds, followed by a source/IP inventory of the main memories. Per-purpose grouping follows those instances; logical capacity differs from physical allocation. The 2 MB external SRAM (the CPU's memory and the video RAM) is not FPGA memory and is outside this ledger.
Source: RC24 routed utilization reports (K2 and Jr2, October 9) and memory IP configurations (.xci). Physical primitive totals are report figures; logical capacities come from the IP configuration and RTL arrays. Kbit = 1,024 bits; a RAMB36 tile is 36 Kbit, a RAMB18 half-tile 18 Kbit.
K2 (xc7a200t)
| At a glance | Detail |
|---|---|
| 44 / 365 | block RAM tiles used (12.05%) |
| 1,584 Kbit | 198 KB physical: 29 × 36K + 30 × 18K (27 RAMB36E1, 2 FIFO36E1, 28 RAMB18E1, 2 FIFO18E1) |
| 272 Kbit | line FIFO queue data; physical allocation includes primitive granularity |
| 584 LUTs | as distributed RAM (1.26% of the LUTs that can be memory), plus 196 as shift registers |
By purpose
| Purpose | Kbit | Share · Of the 1,584 Kbit | What it is | |
|---|---|---|---|---|
| Graphics engine (VICKY) | 396 | 25.0% | CLUTs, three layer line buffers, sprite line, final RGB lines, tile-map capture, mouse pointer, sprite registers, gamma | |
| Serial and bus I/O FIFOs | 270 | 17.0% | MIDI, WiFi, WizNet, VS1053, PS/2, optical keyboard, LCD, I²C | |
| Text display | 252 | 15.9% | character matrix, color matrix, font, foreground and background palettes | |
| MemText (VRAM text mode) | 180 | 11.4% | its own font, two line memories, two greyscale LUTs | |
| Line-draw accelerator FIFO | 288 | 18.2% | one 8,192-entry × 34-bit line-write FIFO | |
| Sound | 90 | 5.7% | PSG/SID command FIFOs, SID and OPL3 emulation tables | |
| Debug serial port | 90 | 5.7% | the Gavin debug channel's command, reply and receive buffers | |
| System page RAM | 18 | 1.1% | $FD00 page, palette read-back shadows, the $FFF0 vector copy | |
| Total | 1,584 | 100% | 44 tiles | |
Block by block
Logical = what the IP was configured to hold; physical = the primitives it occupies. A true-dual-port memory with a 32- or 64-bit second port is forced into whole 36 Kbit tiles however small its contents, which is where most of the gap lives.
| Instance | Holds | Configured | Logical Kbit | Physical | Kbit |
|---|---|---|---|---|---|
| Text display | |||||
| TEXT_BLOCK | the text screen's character matrix (page $FE) | 8192 × 8, true dual port | 64 | 2 × RAMB36 | 72 |
| COLOR_BLOCK | the text screen's color matrix (page $FF) | 8192 × 8, true dual port | 64 | 2 × RAMB36 | 72 |
| FONT | the two text font sets, initialized in the FPGA image from Font_OS9_bannerfont.coe | 4096 × 8, true dual port | 32 | 1 × RAMB36 | 36 |
| FOREGROUND_LUT | 16-entry text foreground palette (B,G,R,A); the video side reads it 32 bits wide | 64 × 8 / 32 | 0.5 | 1 × RAMB36 | 36 |
| BACKGROUND_LUT | 16-entry text background palette | 64 × 8 / 32 | 0.5 | 1 × RAMB36 | 36 |
| Graphics engine | |||||
| LUT_4Tables_H | the four graphics CLUTs (4 × 256 entries × 4 bytes) | 4096 × 8 / 32 | 32 | 1 × RAMB36 | 36 |
| LUT_4Tables_L | a second copy of the CLUTs so HIRES4 looks up both dots of a byte in one cycle | 4096 × 8 / 32 | 32 | 1 × RAMB36 | 36 |
| Layer0/1/2_DPMem | one scan line of pixels for each of the three bitmap/tile layers | 3 × (256 × 32 / 16) | 24 | 3 × RAMB36 | 108 |
| Sprite_DPMem | the composed sprite line | 512 × 18 | 9 | 1 × RAMB18 | 18 |
| OUTPUT_RGB_H | the final RGB pixel line handed to the output | 512 × 32, simple dual port | 16 | 1 × RAMB18 | 18 |
| OUTPUT_RGB_L | the second dot of each HIRES4 pair, one color line of its own | 512 × 32, simple dual port | 16 | 1 × RAMB18 | 18 |
| TileMapCapture | two tile-map lines captured as 16-bit entries | 512 × 16, simple dual port | 8 | 1 × RAMB18 | 18 |
| MousePointerMem | the hardware mouse pointer image | 1024 × 8, true dual port | 8 | 1 × RAMB18 | 18 |
| Sprite_Reg_Block_inst | the 128 sprite attribute records (page $FC, $1300–$16FF); the engine reads a whole 64-bit record per cycle | 1024 × 8 / 64 | 8 | 2 × RAMB36 | 72 |
| GAMMA_R / G / B | gamma tables, boot-loaded from init_hex_GAMMA.coe | 3 × (256 × 8) | 6 | 3 × RAMB18 | 54 |
| Line-draw accelerator | |||||
| LD_AddyPixel_FIFO | the Bresenham unit's queue: mode/nibble flags, color and 24-bit address | FIFO 8,192 × 34, common clock | 272 | 8 × RAMB36 | 288 |
| MemText — the text mode whose matrices live in video RAM (master control register 1, bit 6) | |||||
| MEM_TEXT_FONT | two 8×8 font sets and one 8×16 set, from FontSet2x8x8Set_1x8x16Set.coe | 8192 × 8, true dual port | 64 | 2 × RAMB36 | 72 |
| TEXT_MEMORY, COLOR_MEMORY | one line of characters and one of colors fetched from VRAM | 2 × (128 × 16) | 4 | 2 × RAMB18 | 36 |
| FG_LUT, BG_LUT | MemText palettes, greyscale at boot (LUT_GreyScale.coe) | 2 × (2048 × 8 / 32) | 32 | 2 × RAMB36 | 72 |
| System page RAM | |||||
| FC00_Block_RAM | the $FD00–$FDFF page, read-back shadows of the text palettes at $FF00–$FF7F, and the $FFF0–$FFFF vector copy that lets any block sit in slot 7 | 1024 × 8, single port | 8 | 1 × RAMB18 | 18 |
| Serial and bus I/O FIFOs | |||||
| MIDI_RxD_FIFO, MIDI_TxD_FIFO | the MIDI UART (SAM2695 and the DIN port) | 2 × (FIFO 2048 × 8) | 32 | 2 × RAMB18 | 36 |
| WIFI_RxD_FIFO, WIFI_TxD_FIFO | the WizFi serial channel | 2 × (FIFO 2048 × 8) | 32 | 2 × RAMB18 | 36 |
| Wiz5100S Rx_FIFO, Tx_FIFO | K2 only: the WizNet Ethernet bus interface | 2 × (FIFO 2048 × 8) | 32 | 2 × RAMB18 | 36 |
| VS1053B_My_FIFO | data stream to the VS1053 codec ($FF50 bridge) | FIFO 2048 × 8 | 16 | 1 × RAMB18 | 18 |
| FIFOKeyboard, FIFOMouse | PS/2 receive queues | 2 × (FIFO 256 × 8) | 4 | 2 × RAMB18 | 36 |
| FIFO_OPTKBD | K2 only: the optical keyboard scanner | FIFO 1024 × 16 | 16 | 1 × RAMB18 | 18 |
| LCD_FIFO_2K | K2 only: SPI LCD command/data stream | FIFO 2048 × 12, built-in | 24 | 1 × FIFO36 | 36 |
| I2C_FIFO_CMD, I2C_FIFO_READ | K2 only: the I²C master's command and read queues | FIFO 1024 × 36; FIFO 2048 × 8 | 52 | 1 × RAMB36 + 1 × RAMB18 | 54 |
| Sound | |||||
| PSG_CMD_Write_FIFO_inst | register writes queued to the sound chips | FIFO 256 × 17 | 4.3 | 1 × RAMB18 | 18 |
| SID_FIFO_inst | the SID/OPL3 bridge's access FIFO | FIFO 1024 × 16, built-in | 16 | 1 × FIFO18 | 18 |
| sid_6581_Left | tables inferred inside the SID emulation | RTL arrays | — | 1 × RAMB18 | 18 |
| opl/channels | channel state of the OPL3 emulation | RTL arrays | — | 2 × RAMB18 | 36 |
| Debug serial port (GavinDebug) | |||||
| Serial2CPU | 48-bit debug commands from the host | FIFO 512 × 48, built-in | 24 | 1 × FIFO36 | 36 |
| CPU2Serial | replies to the host | FIFO 512 × 8, built-in | 4 | 1 × FIFO18 | 18 |
| DEBUG_SERIAL_BUF | the debug data buffer (what k2dump reads out) | 2048 × 8, true dual port | 16 | 1 × RAMB18 | 18 |
| SerialRxD_FIFO | raw receive queue of the debug UART | FIFO 512 × 8 | 4 | 1 × RAMB18 | 18 |
| Total block RAM | — | 44 tiles | 1,584 | ||
Distributed (LUT) RAM
The RC24 placed report counts 584 LUTs as distributed RAM and 196 as shift registers. LUT RAM serves small peripheral queues, emulation storage and palette shadows; shift registers provide pipeline delays and synchronization storage.
Jr2 (xc7a35t)
| At a glance | Detail |
|---|---|
| 40 / 50 | block RAM tiles used (80.0%) |
| 1,440 Kbit | 180 KB physical: 27 × 36K + 26 × 18K (26 RAMB36E1, 1 FIFO36E1, 24 RAMB18E1, 2 FIFO18E1) |
| 272 Kbit | line FIFO queue data; physical allocation includes primitive granularity |
| 488 LUTs | as distributed RAM (5.1% of the 9,600 LUTs that can be memory), plus 180 as shift registers |
By purpose
| Purpose | Kbit | Share · Of the 1,440 Kbit | Difference from the K2 | |
|---|---|---|---|---|
| Graphics engine (VICKY) | 396 | 27.5% | same blocks: the HIRES4 pair (second CLUT copy, second RGB line, +54 Kbit) | |
| Text display | 252 | 17.5% | same blocks | |
| MemText (VRAM text mode) | 180 | 12.5% | same blocks | |
| Line-draw accelerator FIFO | 288 | 20.0% | same block | |
| Serial and bus I/O FIFOs | 126 | 8.8% | no WizNet, optical keyboard, LCD or I²C queues (−144 Kbit) | |
| Sound | 90 | 6.2% | same blocks | |
| Debug serial port | 90 | 6.2% | same blocks | |
| System page RAM | 18 | 1.2% | same block | |
| Total | 1,440 | 100% | 40 tiles | |
Block by block
| Instance | Holds | Configured | Logical Kbit | Physical | Kbit |
|---|---|---|---|---|---|
| Text display — as the K2 | |||||
| TEXT_BLOCK, COLOR_BLOCK | character and color matrices | 2 × (8192 × 8) | 128 | 4 × RAMB36 | 144 |
| FONT | the two text font sets (FONT_CPU_Memory here, the same contents) | 4096 × 8 | 32 | 1 × RAMB36 | 36 |
| FOREGROUND_LUT, BACKGROUND_LUT | text palettes | 2 × (64 × 8 / 32) | 1 | 2 × RAMB36 | 72 |
| Graphics engine | |||||
| LUT_4Tables_H | the four graphics CLUTs (4 × 256 entries × 4 bytes) | 4096 × 8 / 32 | 32 | 1 × RAMB36 | 36 |
| LUT_4Tables_L | the second copy of the CLUTs for HIRES4’s two dots per byte (as the K2) | 4096 × 8 / 32 | 32 | 1 × RAMB36 | 36 |
| Layer0/1/2_DPMem | three layer line buffers | 3 × (256 × 32 / 16) | 24 | 3 × RAMB36 | 108 |
| Sprite_DPMem | the composed sprite line | 512 × 18 | 9 | 1 × RAMB18 | 18 |
| OUTPUT_RGB_H | the final RGB pixel line handed to the output | 512 × 32 | 16 | 1 × RAMB18 | 18 |
| OUTPUT_RGB_L | the second dot of each HIRES4 pair, one color line of its own | 512 × 32 | 16 | 1 × RAMB18 | 18 |
| TileMapCapture, MousePointerMem | tile-map line capture; mouse pointer image | 512 × 16; 1024 × 8 | 16 | 2 × RAMB18 | 36 |
| Sprite_Reg_Block_inst | the 128 sprite attribute records | 1024 × 8 / 64 | 8 | 2 × RAMB36 | 72 |
| GAMMA_R / G / B | gamma tables | 3 × (256 × 8) | 6 | 3 × RAMB18 | 54 |
| Line-draw accelerator, MemText, system page — as the K2 | |||||
| LD_AddyPixel_FIFO | the line-draw pixel queue | FIFO 8,192 × 34 | 272 | 8 × RAMB36 | 288 |
| MEM_TEXT_FONT, TEXT_MEMORY, COLOR_MEMORY, FG_LUT, BG_LUT | the MemText font, line memories and palettes | 8192 × 8; 2 × (128 × 16); 2 × (2048 × 8 / 32) | 100 | 4 × RAMB36 + 2 × RAMB18 | 180 |
| FC00_Block_RAM | the $FD00 page, palette shadows and vector copy | 1024 × 8 | 8 | 1 × RAMB18 | 18 |
| Serial and bus I/O FIFOs | |||||
| MIDI_RxD_FIFO, MIDI_TxD_FIFO | the MIDI UART | 2 × (FIFO 2048 × 8) | 32 | 2 × RAMB18 | 36 |
| WIFI_RxD_FIFO, WIFI_TxD_FIFO | the WizFi serial channel | 2 × (FIFO 2048 × 8) | 32 | 2 × RAMB18 | 36 |
| VS1053B_My_FIFO | data stream to the VS1053 codec | FIFO 2048 × 8 | 16 | 1 × RAMB18 | 18 |
| FIFOKeyboard, FIFOMouse | PS/2 receive queues | 2 × (FIFO 256 × 8) | 4 | 2 × RAMB18 | 36 |
| Sound and debug port — as the K2 | |||||
| PSG_CMD_Write_FIFO_inst, SID_FIFO_inst, sid_6581_Left, opl/channels | sound-chip command queues and emulation tables | FIFO 256 × 17; FIFO 1024 × 16; RTL arrays | ≈ 20 | 1 × FIFO18 + 4 × RAMB18 | 90 |
| Serial2CPU, CPU2Serial, DEBUG_SERIAL_BUF, SerialRxD_FIFO | the debug channel's queues and buffer | FIFO 512 × 48; FIFO 512 × 8; 2048 × 8; FIFO 512 × 8 | 48 | 1 × FIFO36 + 1 × FIFO18 + 2 × RAMB18 | 90 |
| Total block RAM | — | 40 tiles | 1,440 | ||
The Jr2 RC24 placed report counts 488 LUTs as distributed RAM (5.1% of the 9,600 memory-capable LUTs) and 181 as shift registers.
What this means
- The Jr2 is the constrained board. RC24 uses 40 of 50 block RAM tiles, leaving 10 tiles (360 Kbit). The line FIFO alone occupies eight tiles.
- The K2 has 321 tiles free (11,556 Kbit). Block RAM is nowhere near a limit there; the K2 core's timing closure (the 200 Mhz scheduler, the SRAM strobe constraints) is the constraint, not memory.
- Physical allocation exceeds logical contents because of primitive granularity. The four palette-style LUTs (text foreground/background, MemText foreground/background) occupy 144 Kbit for 33 Kbit of contents because their video-side ports are 32 bits wide; the sprite register block takes 72 Kbit for 8 because its engine port is 64 bits wide; the three gamma tables take 54 Kbit for 6. These are allocation costs, not unused externally addressable RAM; changing them requires redesigning the video ports.
- No ILA or VIO debug cores are in either routed build. The
MC6809_ILA,Chipscope144_3x12andvio_0IPs exist in both projects but occupy no block RAM in these bitstreams. - The text screen lives in the FPGA, the graphics do not. The 8 KB character matrix, 8 KB color matrix and 4 KB font are block RAM (mapped through device pages $FD–$FF), while bitmaps, tiles and sprite images stream from the external 2 MB SRAM through the line buffers listed above.