Skip to main content

Chombimon

Click to see it run!

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:

  1. Transparent background where Chombimon's text appears on top of your program's output.
  2. Matte blue background so you can see the console clearly. (The program's io::background_color is not visible in this mode.)
  3. 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:

AddressInstructionNotes
10_0000move fp, $10_1000Set up the FP frame
10_0004move sp, fpSet up the SP stack
10_0005add sp, 16Make room for b:0 and i:4
10_0007move b:0, $10Start with $10 (red) in b:0
10_000apush int $d0_0300Point i:4 at io::background_color
10_000epop i:4
10_0010store [i:4], b:0Store our color in io::background_color
10_0013add b:0, $01Increase color
10_0016compare b:0, $12Did we reach $12?
10_0019if not greater
10_001ajump near $10_0010If 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 typeWhat it does
d 10_0000Disassemble one instruction at $10_0000
d 10_0000 10_001fDisassemble a range
f 10_0800 10_0fff 00Fill the range with the byte $00
gGo: run the program, starting from the current IP
g 10_0000Go from IP=$10_0000
h 10_0000 10_0fff 3c f6Hunt: Search for the byte sequence, printing each match
lLoad 4 KB from the flash chip back to $10_0000
l 10_2000Load 4 KB to $10_2000 instead
m 10_0800Hex dump: eight bytes from $10_0800
m 10_0800 10_08ffHex dump a range
rPrint FP, SP, IP, GP, and the flags
r i:0 i:4 b:8Print the specified framed registers
sSave 4 KB from $10_0000 to the flash chip
s 10_2000Save 4 KB from $10_2000 instead
t 10_0000 10_001f 10_0800Transfer: Copy the bytes from $10_0000$10_001f to 10_0800
wWalk: debug by stepping instructions one at a time
w 10_0000Walk 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 combinationWhat it does
CTRL + \Cycle between Chombimon screen modes (transparent, matte, hidden)
SHIFT + ENTERMove to the start of the next line
HOME ENDMove to the first column of the top/bottom line
FN + LEFT FN + RIGHT (Apple keyboards)
CTRL + HOMEMove to the first column of the current line
CTRL + FN + LEFT (Apple keyboards)
PAGE UP PAGE DOWNPage the whole screen (for d or m scrolling)
SHIFT + UP SHIFT + DOWN
CTRL + C CTRL + DScroll the screen contents up/down making blank lines
CTRL + BACKSPACEClear the screen
CTRL + DELETE (Apple keyboards)
CTRL + SHIFT + BACKSPACEClear the current line
CTRL + SHIFT + DELETE (Apple keyboards)
CTRL + number keyChange HASCII color
CTRL + ,Enable reverse (HASCII {rev})
SHIFT + ESCDisable reverse (HASCII {reset})

Special characters

MarkerMeaning
>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.