Chombimon
Guess what fits in 36 kilobytes of uncompressed ROM!
- a hex editor,
- an assembly language disassembler and assembler,
- a debugger that steps through programs, one instruction at a time, and...
- the full HASCII 8x8 font
Inspired by classic Commodore 64 machine language monitors, the Chombimon demo implements all of the above!
Feature tour
1. Start the ROM cartridge
You will see something like this:
Chombimon 2026-09-04
User RAM: $10_0000 to $13_ebff
fp=0000_0000 sp=0000_0000 swconz
ip=0000_0000 gp=0000_0000 000000
.█
Chombimon is running on the same virtual machine as the program it's debugging, so User RAM: $10_0000 to $13_ebff tells which memory is safe for your program to use. The range $13_ec00 … $13_ffff is reserved for the monitor.
The fp, sp, ip, and gp are the CPU's conventional registers. The swconz bits are the CPU flags.
2. Typing commands
The . prompt is where you type a command. Generally, you can move the cursor anywhere on the screen, and type over any text. When you press ENTER, the current line is processed according to what kind of line it is (generally without considering the lines above or below it).
For example, move the cursor over one of the registers and change its value:
Chombimon 2026-09-04
User RAM: $10_0000 to $13_ebff
fp=1234_abcd█sp=0000_0000 swconz
ip=0000_0000 gp=0000_0000 000000
.
Then press ENTER. The change is applied. To see the result, you can type the r command, which prints all the registers again:
Chombimon 2026-09-04
User RAM: $10_0000 to $13_ebff
fp=1234_abcd sp=0000_0000 swconz
ip=0000_0000 gp=0000_0000 000000
.
.r
fp=1234_abcd sp=0000_0000 swconz
ip=0000_0000 gp=0000_0000 000000
.█
What is the point of these registers? When you resume running your program (w or g commands), the CPU registers will have these values.
3. Disassembling
We didn't make a program to disassemble yet, so let's experiment by disassembling Chombimon itself! Type d c0_0000 to disassemble the first instruction at the start of the ROM region. It is always a jump instruction, that skips over the HASCII font and some other data, in this build to jump fixed $c0_4840.
Chombimon 2026-09-04
User RAM: $10_0000 to $13_ebff
fp=0000_0000 sp=0000_0000 swconz
ip=0000_0000 gp=0000_0000 000000
.d c0_0000
c0_0000; jump fixed $c0_4840
b5 c0 48 40
Type d c0_4840 c0_4850 to disassemble that location and some subsequent instructions:
.d c0_4840 c0_4850
c0_4840; move fp, $13_ed00
b4 13 ed 00
c0_4844; move i:140, $13_ee80
e2 23 13 ee 80
c0_4849; move sp, i:140
26 23
c0_484b; move i:136, $d0_0000
e2 22 d0 00 00
c0_4850; move i:132, $13_ec00
e2 21 13 ec 00
Below each instruction like move i:132, $13_ec00 we see the corresponding machine code bytes e2 21 13 ec 00. If you press the up/down arrow keys, moving the cursor to the top or bottom of the screen, more assembly will scroll into view.
4. Hex dumps
The m command displays memory as a hex dump. For example, m c0_1900 c0_1970 means show the memory from $c0_1900 to $c0_1970:
.m c0_1900 c0_1970
c0_1900:3e 30 30 30 30 3e 00 00 >0000>▒▒
c0_1908:3e 30 30 3e 30 30 3e 00 >00>00>▒
c0_1910:3e 30 30 3e 3e 30 30 3e >00>>00>
c0_1918:3e 30 30 3e 3e 30 30 3e >00>>00>
c0_1920:3e 30 30 3e 3e 30 30 3e >00>>00>
c0_1928:3e 30 30 3e 30 30 3e 00 >00>00>▒
c0_1930:3e 30 30 30 30 3e 00 00 >0000>▒▒
c0_1938:00 00 00 00 00 00 00 00 ▒▒▒▒▒▒▒▒
c0_1940:3e 30 30 30 30 30 30 3e >000000>
c0_1948:3e 30 30 3e 00 00 00 00 >00>▒▒▒▒
c0_1950:3e 30 30 3e 00 00 00 00 >00>▒▒▒▒
c0_1958:3e 30 30 30 30 3e 00 00 >0000>▒▒
c0_1960:3e 30 30 3e 00 00 00 00 >00>▒▒▒▒
c0_1968:3e 30 30 3e 00 00 00 00 >00>▒▒▒▒
c0_1970:3e 30 30 30 30 30 30 3e >000000>
These are the 8x8 glyphs for the font letters D and E. Each byte is a theme color index, where $30 is Theme Color A (allowing the text to be recolored) and $3e is an optional outline.
Normally you can type over the hex bytes and press ENTER to modify them, and normally you can type over the disassembly and press ENTER to assemble new instructions, but it won't work here. Why? Because ROM means read-only memory.
5. Screen modes
You may recall that the Palix background color is controlled by a byte at $d0_0300 which selects the color (a system palette index):
# Palix video system
var background_color: byte located at $d0_0300
In Chombimon, try this:
.m d0_0300
d0_0300:00█00 18 93 00 00 00 00 ▒▒▒╭▒▒▒▒
d0_0308:
.
Replace the 00 with 10 (16 decimal) and press ENTER; the screen's background color should change to red. 11 gives orange. Try some other numbers. This is a good way to see immediate feedback from a program.
Screen modes
Chombimon renders its text using Tile Layer A, and supports three screen modes:
- Transparent background where Chombimon's text appears on top of your program's output.
- Matte blue background so you can see the console clearly. (The program's
io::background_coloris not visible in this mode.)- Hidden so that the debugged program's output is completely visible.
Pressing
CTRL+\cycles between these three modes.
6. Assembling a program
Now let's write a small program that increments io::background_color at $d0_0300. Type d 10_0000:
.d 10_0000
10_0000; ---█
00
Then write move fp, $10_1000 over the --- that you see there, and press ENTER to assemble. (You don't need to type the hex bytes b4 10 10 00; they appear automatically.) Input this program that way:
.d 10_0000
10_0000; move fp, $10_1000
b4 10 10 00
10_0004; move sp, fp
15
10_0005; add sp, 16
3b 10
10_0007; move b:0, $10
40 00 10
10_000a; push int $d0_0300
cd d0 03 00
10_000e; pop i:4
32 01
10_0010; store [i:4], b:0
63 01 00
10_0013; add b:0, $01
88 00 01
10_0016; compare b:0, $12
95 00 12
10_0019; if not greater
0f
10_001a; jump near $10_0010
3c f6
10_001c; █
Here's an annotated listing:
| Address | Instruction | Notes |
|---|---|---|
10_0000 | move fp, $10_1000 | Set up the FP frame |
10_0004 | move sp, fp | Set up the SP stack |
10_0005 | add sp, 16 | Make room for b:0 and i:4 |
10_0007 | move b:0, $10 | Start with $10 (red) in b:0 |
10_000a | push int $d0_0300 | Point i:4 at io::background_color |
10_000e | pop i:4 | |
10_0010 | store [i:4], b:0 | Store our color in io::background_color |
10_0013 | add b:0, $01 | Increase color |
10_0016 | compare b:0, $12 | Did we reach $12? |
10_0019 | if not greater | |
10_001a | jump near $10_0010 | If not, jump to 10_0010 |
10_001c | --- | The program ends with an invalid instruction fault. |
7. Saving your work
When you are done assembling, press ENTER to get a . prompt, then type s and ENTER, then y for "yes" to save your work:
10_001a; jump near $10_0010
3c f6
10_001c;
.s
save 10_0000-10_0fff? y
.█
Since there is no hard disk yet for the virtual machine, your work is saved in the small flash memory area. If you want to load it again, for example after resetting the computer, use l:
.l
load 10_0000-10_0fff? y
.█
8. Debugging
To run the program, you could write g 10_0000 to "go" (jump) to the start of your code. Instead, let's use w to "walk" one instruction at a time, which is more fun:
.w 10_0000
10_0000> move fp, $10_1000
[ENTER=step ESC=exit]
The program starts with IP=$10_0000, the first line of our program. Each time you press ENTER, one instruction will execute:
.w 10_0000
10_0000> move fp, $10_1000
fp=$0010_1000 ......
10_0004> move sp, fp
sp=$0010_1000 ......
10_0005> add sp, 16
sp=$0010_1010 ......
10_0007> move b:0, $10
b:0=$10 ......
10_000a> push int $d0_0300 ......
10_000e> pop i:4
i:4=$00d0_0300 ......
10_0010> store [i:4], b:0
[ENTER=step ESC=exit]
When a register is modified, its new value is printed (fp=$0010_1000). The CPU flags appear as ....... For example, if the skip flag (SF) and negative flag (NF) were set, you would see s...n..
Stepping through the program line by line, you should eventually see the screen's background color change. The program ends with an "invalid instruction" fault when the --- byte is reached (RAM starts out as all zeros).
10_0013> add b:0, $01
b:0=$13 ......
10_0016> compare b:0, $12 ..c...
10_0019> if not greater s.c...
10_001a/ jump near $10_0010 ..c...
10_001c> ---
FAIL: #0 (invalid instruction)
[ENTER=step ESC=exit]
Cheat sheet
Hexadecimal syntax: Hex numbers require the
$prefix for inputs that can also be decimal. For hex-only inputs, the$prefix must be omitted._is an optional digit separator.
Commands
| What you type | What it does |
|---|---|
d 10_0000 | Disassemble one instruction at $10_0000 |
d 10_0000 10_001f | Disassemble a range |
f 10_0800 10_0fff 00 | Fill the range with the byte $00 |
g | Go: run the program, starting from the current IP |
g 10_0000 | Go from IP=$10_0000 |
h 10_0000 10_0fff 3c f6 | Hunt: Search for the byte sequence, printing each match |
l | Load 4 KB from the flash chip back to $10_0000 |
l 10_2000 | Load 4 KB to $10_2000 instead |
m 10_0800 | Hex dump: eight bytes from $10_0800 |
m 10_0800 10_08ff | Hex dump a range |
r | Print FP, SP, IP, GP, and the flags |
r i:0 i:4 b:8 | Print the specified framed registers |
s | Save 4 KB from $10_0000 to the flash chip |
s 10_2000 | Save 4 KB from $10_2000 instead |
t 10_0000 10_001f 10_0800 | Transfer: Copy the bytes from $10_0000…$10_001f to 10_0800 |
w | Walk: debug by stepping instructions one at a time |
w 10_0000 | Walk from from IP=$10_0000 |
Input line types
When you press ENTER, the cursor's line is matched against an input pattern.
Example input patterns and what ENTER does |
|---|
.d 10_0000 Executes a command such as d, m, etc. |
10_0800:48 45 4c 4c 4f 20 00 00 HELLO ▒▒ Accepts the edited bytes (the HASCII field is ignored) |
10_0000; move fp, $10_1000 Assembles the edited instruction |
i:4 [10_1004]=$00d0_0300 13632256 Accepts the edited register value (the decimal field is ignored) |
fp=0010_1000 sp=0010_1010 swconz Accepts both edited registers (the legend is ignored) |
ip=0010_0010 gp=0000_0000 000000 Accepts both edited registers and the edited flag bits |
10_0010! store [i:4], b:0 Does nothing; walking output is not an input, but retyping ! as ; makes it one |
b4 10 10 00 Error; indented rows are never inputs |
[d0_0300]=$11 ....n. Error; indented rows are never inputs |
Keyboard shortcuts
| Key combination | What it does |
|---|---|
CTRL + \ | Cycle between Chombimon screen modes (transparent, matte, hidden) |
SHIFT + ENTER | Move to the start of the next line |
HOME END | Move to the first column of the top/bottom line |
FN + LEFT FN + RIGHT (Apple keyboards) | |
CTRL + HOME | Move to the first column of the current line |
CTRL + FN + LEFT (Apple keyboards) | |
PAGE UP PAGE DOWN | Page the whole screen (for d or m scrolling) |
SHIFT + UP SHIFT + DOWN | |
CTRL + C CTRL + D | Scroll the screen contents up/down making blank lines |
CTRL + BACKSPACE | Clear the screen |
CTRL + DELETE (Apple keyboards) | |
CTRL + SHIFT + BACKSPACE | Clear the current line |
CTRL + SHIFT + DELETE (Apple keyboards) | |
CTRL + number key | Change HASCII color |
CTRL + , | Enable reverse (HASCII {rev}) |
SHIFT + ESC | Disable reverse (HASCII {reset}) |
Special characters
| Marker | Meaning |
|---|---|
> | In w mode, the IP has advanced normally to the next instruction |
! | In w mode, the IP moved elsewhere due to jump, pop ip, etc |
/ | In w mode, this instruction is being skipped due to an if or SF=1 |
? | Indicates an input error |
**, *** | A byte or instruction is in unreadable memory |
--- | Chombit's spacer instruction (opcode $00); triggers an "invalid instruction" fault |
AI Usage Statement
The Chombimon ROM cartridge was made using AI. Its binary embeds a 14 kilobyte HASCII font that is hand-made, as are this documentation page and the main Hybrix product.
Why AI? Chombimon is a development tool that demands correctness across a complex CPU specification, and we needed it early to expose machine flaws that would be expensive to fix if postponed. The compromise was to focus human energy on design and correctness mechanisms, while using AI to generate the 7,000+ individual assembly instructions. One day, with less demanding time constraints, it would be a delight to reimplement Chombimon as a fully hand-made work.