Code, maps, text and arbitrary bytes
Compact pair v5.2, combined state-flow v5.3/v5.4 and the v4.3 baseline, with matching RAM decrunchers.
Drop any binary file, BMP, PNG, or Screen 5–8 image. Every compression route is automatically tested and verified byte-for-byte in your browser, complete with ready-to-assemble Z80 unpackers and a one-click MSX ROM builder.
See the Help & format guide for format header IDs and bitwise specifications.
; =====================================================================
; CBS Cruncher v5.5 - Screen 5-8 reusable-buffer decruncher v1.2
; =====================================================================
; Assembled size:
; - 321 bytes (fixed Screen 5, standard VDP port 99h)
; - Category range: 319 to 358 bytes (321 B fixed Screen 5/7, 358 B dynamic)
;
; Decrunches a C3h v5.5 image or a C4h sequence of independent v5.5 chunks.
; Every EOS uploads the completed RAM chunk with HMMC. C4h then advances Y and
; reuses the same caller-selected buffer until the complete image is restored.
;
; Interrupt & Stack Safety:
; - 100% safe with MSX BIOS interrupts (EI) enabled during token parsing.
; - Never uses SP as a data pointer or reads below SP.
; - Uses DI during atomic VDP port setup and HMMC transfer.
; - Preserves alternate register set (BC', DE', HL' are untouched).
;
; Input:
; HL = CBS image container (C3h or C4h)
; DE = packed destination coordinate (D = packed X, E = Y)
; IX = start of the selected 1/2/4/8/16 KB reusable RAM buffer
; A = call route (bits 3-2 = VRAM command page 0-3, bits 1-0 = source)
;
; Public entry points:
; CBS_DECRUNCH normal image-decompression entry
; =====================================================================
CBS_DECRUNCH:
CBS_V52_SCREEN58_RAM_TO_VRAM:
AND 0Ch
RRCA
RRCA
LD B,D ; B = packed X
LD D,A ; DE = (page << 8) | Y
LD (CBS_V52_VRAM_DY),DE
EX DE,HL ; DE = container
LD L,B
LD H,00h
ADD HL,HL
LD (CBS_V52_VRAM_DX),HL
EX DE,HL ; HL = container
LD A,(HL) ; C3h or C4h
INC HL
CP C4h
LD A,(HL) ; packed width (0 = 256)
INC HL
LD C,01h ; 1 chunk for C3h
JR NZ,CBS_V52_VRAM_HEADER_READY
INC HL ; skip C4h total height
LD C,(HL) ; C4h chunk count
INC HL
CBS_V52_VRAM_HEADER_READY:
EX DE,HL
DEC A
LD L,A
LD H,00h
INC HL ; HL = 16-bit byte width (1..256)
LD (CBS_V52_VRAM_ROW_WIDTH+1),HL
ADD HL,HL
LD A,H
AND 01h ; command NX wraps 512 dots to 000h
LD H,A
LD (CBS_V52_VRAM_NX),HL
EX DE,HL
LD A,C
LD (CBS_V52_VRAM_CHUNKS+1),A
CBS_V52_VRAM_CHUNK_START:
LD A,(HL) ; chunk line count (0 = 256)
INC HL
DEC A
LD C,A
LD B,00h
INC BC
LD (CBS_V52_VRAM_NY),BC
PUSH IX
POP DE ; DE = start of reusable RAM buffer
LD A,80h ; sentinel control-bit reservoir
LD BC,FFFFh ; default negative offset = -1
PUSH BC ; persistent last offset on stack
CBS_V52_VRAM_ROW_START:
CALL CBS_V52_VRAM_GET_GAMMA
JR CBS_V52_VRAM_COPY_LITERALS
CBS_V52_VRAM_AFTER_LITERAL:
CALL CBS_V52_VRAM_GET_BIT
JR C,CBS_V52_VRAM_NEW_OFFSET
CALL CBS_V52_VRAM_GET_GAMMA_EOS
CBS_V52_VRAM_COPY_MATCH:
EX (SP),HL
PUSH HL
ADD HL,DE
LDIR
POP HL
EX (SP),HL
CBS_V52_VRAM_AFTER_MATCH:
CALL CBS_V52_VRAM_GET_BIT
JR C,CBS_V52_VRAM_NEW_OFFSET
CALL CBS_V52_VRAM_GET_GAMMA_EOS
CBS_V52_VRAM_COPY_LITERALS:
LDIR
JR CBS_V52_VRAM_AFTER_LITERAL
CBS_V52_VRAM_NEW_OFFSET:
CALL CBS_V52_VRAM_GET_BIT
JR C,CBS_V52_VRAM_GENERAL_OFFSET
CBS_V52_VRAM_ROW_OFFSET:
CALL CBS_V52_VRAM_GET_NEG_GAMMA
INC BC
PUSH DE
PUSH HL
CBS_V52_VRAM_ROW_WIDTH:
LD DE,0000h
LD HL,0000h
CBS_V52_VRAM_MULTIPLY:
OR A
SBC HL,DE
INC C
JR NZ,CBS_V52_VRAM_MULTIPLY
LD B,H
LD C,L
POP HL
POP DE
CALL CBS_V52_VRAM_GET_BIT
JR CBS_V52_VRAM_STORE_OFFSET
CBS_V52_VRAM_GENERAL_OFFSET:
CALL CBS_V52_VRAM_GET_NEG_GAMMA
INC BC
RR B
LD B,C
LD C,(HL)
INC HL
RR B
RR C
CBS_V52_VRAM_STORE_OFFSET:
EX (SP),HL
POP HL
PUSH BC
LD BC,0001h
CALL NC,CBS_V52_VRAM_GAMMA_LOOP
INC BC
JR CBS_V52_VRAM_COPY_MATCH
CBS_V52_VRAM_FINISHED:
POP BC
PUSH HL
PUSH IX
POP HL
LD A,(HL)
INC HL
LD (CBS_V52_VRAM_CLR),A
EX DE,HL
DI
LD HL,CBS_V52_VRAM_COMMAND_DATA
CBS_V52_PORT_CONTROL:
LD C,99h
LD A,24h
OUT (C),A
LD A,91h
OUT (C),A
INC C
INC C
LD B,0Bh
OTIR
DEC C
DEC C
LD A,44 + 80h
OUT (C),A
LD A,91h
OUT (C),A
INC C
INC C
EX DE,HL
LD A,(CBS_V52_VRAM_NY)
LD D,A
JR CBS_V52_VRAM_COPY_FIRST
CBS_V52_VRAM_COPY:
LD A,(CBS_V52_VRAM_ROW_WIDTH+1)
LD B,A
CBS_V52_VRAM_COPY_L:
LD A,(HL)
INC HL
OUT (C),A
CBS_V52_VRAM_COPY_FIRST:
DJNZ CBS_V52_VRAM_COPY_L
DEC D
JR NZ,CBS_V52_VRAM_COPY
DEC C
DEC C
LD A,02h
OUT (C),A
LD A,8Fh
OUT (C),A
CBS_V52_VRAM_EXIT_WAIT:
IN A,(C)
RRCA
JR C,CBS_V52_VRAM_EXIT_WAIT
XOR A
OUT (C),A
LD A,8Fh
OUT (C),A
LD HL,CBS_V52_VRAM_DY
LD A,(CBS_V52_VRAM_NY)
ADD A,(HL)
LD (HL),A
POP HL
CBS_V52_VRAM_CHUNKS:
LD A,00h
DEC A
RET Z
LD (CBS_V52_VRAM_CHUNKS+1),A
EI
JP CBS_V52_VRAM_CHUNK_START
CBS_V52_VRAM_GET_GAMMA_EOS:
LD BC,0001h
CALL CBS_V52_VRAM_GET_BIT
JR NC,CBS_V52_VRAM_GAMMA_LOOP
CALL CBS_V52_VRAM_GET_BIT
RET NC
POP BC
JR CBS_V52_VRAM_FINISHED
CBS_V52_VRAM_GET_GAMMA:
LD BC,0001h
JR CBS_V52_VRAM_GAMMA_ENTRY
CBS_V52_VRAM_GET_NEG_GAMMA:
LD BC,FFFEh
CBS_V52_VRAM_GAMMA_ENTRY:
CALL CBS_V52_VRAM_GET_BIT
RET C
CBS_V52_VRAM_GAMMA_LOOP:
CALL CBS_V52_VRAM_GET_BIT
RL C
RL B
JR CBS_V52_VRAM_GAMMA_ENTRY
CBS_V52_VRAM_GET_BIT:
ADD A,A
RET NZ
LD A,(HL)
INC HL
RLA
RET
CBS_V52_VRAM_COMMAND_DATA:
CBS_V52_VRAM_DX:
DW 0
CBS_V52_VRAM_DY:
DW 0
CBS_V52_VRAM_NX:
DW 0
CBS_V52_VRAM_NY:
DW 0
CBS_V52_VRAM_CLR:
DB 0
DB 00h
DB F0h
The downloadable package uses the same compiled engine and parameters as this page. It handles continuous data, Screen 5–8 images, native BMP/PNG import, SC5–SC8 BSAVE files, CB5 decrunching, RAM-tail output, linear VRAM and one-image MSX test ROMs.
python cbs_cruncher.py game.bin
python cbs_cruncher.py game.bin --ram-tailpython cbs_cruncher.py picture.png --mode 7 --rom
python cbs_cruncher.py tiles.bin --linear-vram-screen 5 --romRequires Python 3.10+ and Node.js 22+. Pillow is needed only for BMP/PNG input. The website-only multi-image demonstration ROM remains intentionally separate from the CLI.
The browser no longer presents every abandoned experiment. These are the implemented paths that have survived compression, browser round-trip and generated-ROM testing.
Compact pair v5.2, combined state-flow v5.3/v5.4 and the v4.3 baseline, with matching RAM decrunchers.
Safe files use in-place tail mode and relocate themselves with LDDR before forward expansion.
v5.3, v5.4 and line-aware v5.5 share paired payloads; change the destination without recompressing.
v5.3/v5.4 linear VRAM mode follows physical Screen line width without forcing rectangle geometry.
One mapped Screen 5–8 decoder mixes buffered and direct images across ASCII8, ASCII16, ASCII16-X, Konami and Konami SCC banks.
Single images and ordered multi-screen mapper ROMs verify decompression, palettes and VDP delivery.
Cartridge ROM mapper reading now serves both buffered and direct VRAM. What remains is unpacking into selected MSX Memory Mapper segments, plus an optional hybrid row/chunk backend if a real game benefits from it.
A Motorola 68000 cruncher and tiny Z80 decruncher became a verified browser tool, a family of RAM/VRAM writers and a real MSX ROM builder.
The Help page now starts with the compact v5.5 route, publishes exact shipped decruncher sizes, and compares reusable 1, 2, 4, 8 and 16 KB windows on a verified 24 KB Screen 5 image. The 8 KB window is recommended as the usual balance—not a universal winner—while v5.6 is presented as the stronger directional compressor with a larger decoder. The older tile-grid experiment remains available at the end as research rather than the default workflow.
The v5.5 chunked container divides an image into complete-row, EOS-terminated streams sized for the game's selected 1–16 KB buffer. One CBS_DECRUNCH call expands every chunk, uploads it with HMMC, advances Y and reuses the same RAM automatically. The shared v5.5 buffered decoder is 321–358 bytes (321 B fixed Screen 5/7, 358-byte buffered decoder with dynamic init); measured examples showed a substantial display-speed advantage over Direct while allowing each project to trade RAM against packed size.
The 68000 cruncher produces CBS v3. Its small Z80 decruncher originated on the ZX Spectrum and also works on MSX. A ROM-reading MSX version was later developed during the Romancia translation while the executable decoder still ran from RAM.
We restored v3 and v4.3, reproduced the Romancia result, replaced the unsafe four-byte overlap assumption with exact measurement, imported BMP/PNG pixels, and developed the v5.2 compact formats with a documented three-byte header and optimized Z80 RAM core.
ZX0's state flow, interlaced Elias coding, last offset on the stack, EX (SP),HL copying and impossible-offset EOS guided v5.3 and v5.4. CBS kept its own headers, offsets, adaptive-near choice and destination system.
CPU literals, HMMM matches and HMMV fills made direct Screen 5–8 output practical. Five cartridge mapper readers, palettes, RAM decoder relocation, F500h controller code and ordered multi-image ROMs followed.
v5.3/v5.4 gained one combined RAM decoder; v5.5 line offsets joined buffered and direct image routes; linear VRAM mode was added; measured RAM-tail relocation was added; the Python CLI gained matching outputs; and the website was divided into a focused compressor plus this complete integration guide.
The five cartridge-mapper builds gained a shared consecutive-bank reader and an optional mixed destination dispatcher. Direct-only lists now keep the smaller direct decoder; lists containing buffered streams automatically receive the combined RAM-stage/HMMC and direct-VRAM decoder. Machine ports, the initial Screen mode and an unchanged palette are initialized once before playback, while later setup runs only for a real mode or palette transition. The buffered uploader now waits for final HMMC completion before returning, and demo ROMs disable sprites to prevent artifacts while freeing VDP bandwidth.
v5.6 searches horizontal and vertical traversal orders in both directions, keeps the smallest permitted v5.5-based stream, and reconstructs exact plus horizontally, vertically and two-axis-mirrored rectangles in place with VDP commands. One Screen 5 decoder accepts all four orders. Its VDP ports are patched once from the BIOS, register writes are inlined, unnecessary address helpers and command waits were removed, interrupts remain enabled outside short VDP latch sequences, and compatible HMMM work is coalesced.
The optimized Direct decoder is 2,078 bytes and expands a complete 32 KB tile-image benchmark in 7.831 seconds, faster than the measured v5.4 and v5.5 Direct routes. Optional 1–16 KB rolling builds use the identical packed file and are 2,639 bytes; they currently help horizontal delivery most. v5.5 remains the compact line-aware alternative: its 321–358-byte buffered decoder (321 B fixed Screen 5/7, 358 B dynamic) automatically expands independent EOS-terminated chunks through one reusable caller-selected buffer and advances Y internally. The earlier version restored the same test in roughly 2.5–2.8 seconds depending on the chosen split; those timings predate the latest optimizations.
A dedicated local WebMSX player replaced ROM-sized Base64 launch URLs. The browser now transfers complete cartridges directly between local tabs, preserving Japanese MSX2 NTSC, palette and mapper selection without uploading the ROM. The Python CLI was regenerated from the same engine, given reproducible packaging and tested across every continuous codec, buffered/direct images, linear VRAM, RAM-tail, BSAVE handling and all five mapped-ROM readers.
The multi-image builder replaced per-picture playback code with one shared loop and a compact RAM table containing Screen mode, VRAM page, mapper bank, packed address, palette and clear-command references. The HMMV clear now sends an eleven-byte VDP command block with OTIR, identical mode/black-colour blocks are reused, and a new public CBS_WAIT_VDP decoder entry owns VDP-command waiting. A ten-image mixed ASCII16 player now occupies only 643 bytes at F500h, including its table, palettes and clear parameters.
The Help & format guide contains the byte layout, calling conventions, worked examples and remaining roadmap.