Oliver VieBrooks

6pack / 6pack manual

Contents

6pack

Compressor and linker for the Commodore 64. Takes .prg files and produces smaller .prg files using a pluggable set of compression algorithms (ZX0, LZSA2, BB2, RLE).

Quick start

# Pack with default ZX0 — produces game.sfx.prg that RUNs directly
6pack sfx game.prg

# Pick a different algorithm
6pack sfx -a lzsa2 game.prg

# Compare compression across all algorithms
6pack bench game.prg

# Inspect a packed file
6pack info game.sfx.prg

# Unpack back to the original
6pack unpack game.sfx.prg

Modes

mode what it makes
sfx Self-extracting PRG — RUN it and it decompresses itself
chain Multi-stage SFX — decompresses multiple payloads in sequence
mem Compressed payload only — for linking with your own decompressor
level Indexed multi-file archive — for streaming level data at runtime
source Complete 6asm source file — decompressor + compressed data as .byte directives
asm Standalone decompressor source — configurable 6asm include file
info Inspect a PRG file — shows layout, algorithm, compression stats
bench Compare compression ratios across all algorithms (and external tools)
unpack Decompress a packed PRG back to the original

Algorithms

All four algorithms are fully supported across every mode (sfx, mem, level, source, asm, info, unpack, bench).

Algorithm Typical ratio 6502 core Best for
zx0 (default) best 210 bytes general-purpose
lzsa2 good 249 bytes fast decompression
bb2 good 215 bytes ByteBoozer2 compatibility
rle basic 74 bytes repetitive data, tiny core

Select an algorithm with -a <name> / --algorithm <name> on any mode that produces compressed output.


sfx — self-extracting PRG

Wraps the input in a BASIC stub and a 6502 decompressor. The result loads and runs like any other program: LOAD it, type RUN, and it decompresses itself to the original address and jumps to the entry point.

6pack sfx <input.prg[@addr]...> [options]

Multiple input files are merged into a single flat memory image before compression. Append @0xAAAA to a filename to override its load address.

Options

flag default meaning
-a, --algorithm zx0 Compression algorithm: zx0, lzsa2, bb2, rle.
-e, --effort 1-9 9 Compression effort. 9 = optimal (slow), 1 = fast.
-s, --sys addr load address Address to JMP to after decompression.
-o, --output file stem.sfx.prg Output path.
-b, --backward off Use backward (in-place) ZX0 decompressor.
-d, --dest addr $0801 SFX load address. When set, omits the BASIC stub — invoke via JMP/SYS.
-x, --effect name none Visual effect during decompression: none, border-cycle, bg-cycle, border-data, d418-data.
-i, --init spec none One-time setup before decompression. Repeatable. Presets: border=N, bg=N, fill-screen=N, fill-color=N, jsr=$XXXX. Raw hex also accepted.
--blank-screen off Blank the VIC-II screen ($D011=0) during decompression. Hides visual garbage and avoids badline cycle stealing. Screen restores automatically after decompression via the KERNAL IRQ.
--bank value $37 Processor port ($01) value after decompression. Common: $37 (normal), $35 (no KERNAL/BASIC), $34 (all RAM).
--fill byte $00 Fill byte for gaps when merging non-contiguous PRGs.
--classic off Use classic (non-inverted) Elias gamma (ZX0 only).
-v, --verbose off Print compression statistics and memory layout.
--run emu off Launch emulator after packing. auto tries x64sc/x64/vice.
--watch off Recompress whenever an input file changes. Ctrl+C to stop.

SYS sniffing

When -s/--sys is not specified, 6pack automatically extracts the SYS target address from the input PRG's BASIC stub (e.g., 10 SYS 2061). Falls back to the load address if no BASIC SYS line is found.

Custom effects

Use --effect custom=file.asm to provide your own 6502 effect routine. The .asm file is assembled on the fly via 6asm (org $0200). Raw binary files also accepted: --effect custom=file.bin.

Add ,preserve-a to make the literal byte value available in A: --effect custom=file.asm,preserve-a

Examples

# Basic usage — produces a RUN-able SFX
6pack sfx game.prg

# Specify entry point
6pack sfx game.prg -s 0x0810

# Use LZSA2 (larger ratio, faster decompression)
6pack sfx game.prg -a lzsa2

# Use BB2
6pack sfx game.prg -a bb2

# Blank screen during depack, exit with all-RAM banking
6pack sfx game.prg --blank-screen --bank 0x34

# Load SFX at custom address (no BASIC stub, invoke via SYS 16384)
6pack sfx game.prg -d 0x4000

# With border colour cycling effect
6pack sfx game.prg -x border-cycle

# Merge two PRGs and pack
6pack sfx main.prg music.prg@0xA000 -s 0x0810

# Verbose output showing full layout
6pack sfx game.prg -v

# Pack and immediately test in VICE
6pack sfx game.prg --run auto

# Watch for changes and auto-rebuild
6pack sfx game.prg --watch

# Both: rebuild on change, launch in VICE each time
6pack sfx game.prg --watch --run auto

# Custom decompression effect from assembly source
6pack sfx game.prg -x custom=myeffect.asm,preserve-a

Auto-mode selection

When the default $0801 layout doesn't fit (large programs that overlap the top-of-RAM staging area), the ZX0 builder automatically falls back to forward in-place decompression with safety padding. No manual flags needed — 6pack sfx bigfile.prg just works.

SFX binary layout

$0801  BASIC stub (12 bytes): line 6 SYS 2061
$080D  sfx_init: resets stack, SEI, banks ALL-RAM, copies core to $0100,
       sets ZP pointers, JMP $0100
$08xx  Decompressor core (algorithm-dependent size, runs at $0100)
$09xx  [optional safety padding] + compressed payload

Zero-page usage during decompression

address role
$F8/$F9 src — pointer into compressed stream
$FA/$FB dst — pointer into decompressed output
$FC/$FD working variables (varies by algorithm)
$FE/$FF working variables (varies by algorithm)

chain — multi-stage SFX

Builds a single PRG that decompresses multiple payloads in sequence. Each stage's decompressor exits by jumping to the next stage's init stub. The final stage jumps to the sys address.

Useful for large productions: intro → main part, or loader → game.

6pack chain <stage0.prg[@addr]> <stage1.prg[@addr]> [stageN.prg...] [options]
flag default meaning
-a, --algorithm zx0 Compression algorithm for all stages.
-e, --effort 1-9 9 Compression effort.
-s, --sys addr last stage load addr Final JMP target after all stages decompress.
-o, --output file stem.chain.prg Output path.
--blank-screen off Blank VIC-II during decompression.
--bank value $37 Exit banking value.
-v, --verbose off Show per-stage layout.

Examples

# Two-stage chain: intro decompresses first, then main
6pack chain intro.prg main.prg -s 0x0810

# Three stages with verbose layout
6pack chain loader.prg intro.prg game.prg -v

# Use BB2 compression
6pack chain -a bb2 stage1.prg stage2.prg

Layout

$0801  BASIC stub (SYS 2061)
$080D  Stage 0: sfx_init + core + compressed payload
       (exit JMP → stage 1 address)
$xxxx  Stage 1: sfx_init + core + compressed payload
       (exit JMP → sys address)

Each stage's SYS target is auto-detected from its BASIC SYS line (if present), or defaults to its load address. The explicit --sys flag only affects the final stage.


mem — compressed payload only

Compresses the input and writes just the compressed bytes as a .prg. Use this when you supply your own decompressor in your project.

6pack mem <input.prg> [options]
flag default meaning
-a, --algorithm zx0 Compression algorithm.
-e, --effort 1-9 9 Compression effort.
--classic off Classic Elias gamma (ZX0 only).
-o, --output file stem.mem.prg Output path.
-p, --pack-address addr source load address Load address for the .prg header.
-v, --verbose off Print compression stats.

level — indexed multi-file archive

Compresses multiple PRGs into one blob with an index table at the front. Useful for games that stream level data at runtime.

6pack level <file1.prg> [file2.prg ...] [options]
flag default meaning
-a, --algorithm zx0 Compression algorithm.
-e, --effort 1-9 9 Compression effort applied to all files.
--classic off Classic Elias gamma (ZX0 only).
-o, --output file stem.lvl.prg Output path.
--archive-address addr $0801 Load address in the .prg header.
-v, --verbose off Print compression stats.

Archive format

Offset  Size   Field
0       2      N — number of entries (little-endian)
2       N×4    Index table: load_address (2) + compressed_size (2) per entry
2+N×4   ...    Compressed payloads concatenated in order

To decompress entry i at runtime: 1. Read N from offset 0. 2. Read load_address[i] and compressed_size[i] from 2 + i*4. 3. Skip sum(compressed_size[0..i-1]) bytes past the end of the index (2 + N*4) to find the payload. 4. Call your decompressor with src = payload start, dst = load_address[i].


source — complete 6asm source output

Outputs a self-contained .asm file with the decompressor and compressed data as .byte directives. Assemble directly with 6asm.

6pack source <input.prg> [options]
flag default meaning
-a, --algorithm zx0 Compression algorithm.
-e, --effort 1-9 9 Compression effort.
-s, --sys addr load address JMP target after decompression.
-o, --output file stdout Output .asm file.
--blank-screen off Add screen blank/restore to the setup code.
--bank value $37 Exit banking value.

Example

6pack source game.prg -a lzsa2 -s 0x0810 -o depack.asm
6asm -o depack.prg depack.asm

The output contains: - depack label: setup code that loads ZP pointers, calls the decompressor, restores banking, and jumps to sys - dzx0 / rle_decomp / bb2_decomp / lzsa2_decomp label: the decompressor subroutine (RTS-based, callable via JSR) - zx0_data / rle_data / bb2_data / lzsa2_data label: compressed data as .byte directives


asm — standalone decompressor source

Outputs just the decompressor as a 6asm source file.

6pack asm [options]
flag default meaning
-a, --algorithm zx0 Which decompressor to output: zx0, lzsa2, bb2, rle.
--zp addr $F8 Zero-page base address (8 bytes used).
-o, --output file stdout Output .asm file.

Example

# ZX0 decompressor (default)
6pack asm -o zx0_decomp.asm

# RLE decompressor with ZP at $02
6pack asm -a rle --zp 0x02 -o rle_decomp.asm

# LZSA2 decompressor
6pack asm -a lzsa2 -o lzsa2_decomp.asm

info — inspect a PRG file

Shows file metadata, detects SFX files, identifies the compression algorithm, and prints stream statistics (for ZX0).

6pack info <file.prg>

Example output

$ 6pack info game.sfx.prg
file       game.sfx.prg  (7597 bytes)
load addr  $0801
end addr   $25AB
payload    7595 bytes
type       SFX (BASIC stub: SYS 2061)
algorithm  ZX0
  payload at +297 (7298 bytes compressed):
  compressed   7298 bytes → 7328 bytes  (99.6%)
  commands     37 total  (17 literal runs, 18 new-match, 2 rep-match)
  literals         17 runs    ·     7242 raw bytes  (98.8% of output)
  new-match        18 matches · offset $0001–$1C5F  · length 2–12 avg 4
  rep-match         2 matches · length 2–3 avg 2
  max surplus  34 bytes

Detects: - SFX with BASIC stub at $0801 - SFX without BASIC stub (custom --dest address) - Algorithm: ZX0, LZSA2, BB2, or RLE (by 6502 core binary signature)


bench — compare compression ratios

Compresses with all built-in algorithms and shows a comparison table. Also probes for external tools (exomizer, pucrunch) if installed, for competitive comparison.

6pack bench <file.prg> [-e effort] [--speed]

Pass --speed to also measure 6502 decompression cycles for each algorithm (runs the actual 6502 decompressor in the built-in emulator).

Example output

$ 6pack bench mandel.prg
bench: mandel.prg  (10001 bytes, load $6000)

  algorithm    compressed    ratio  surplus  core
  ------------ ---------- -------- -------- -----
  zx0                3254    32.5%     6749   210
  lzsa2              3544    35.4%     6460   249
  bb2                3390    33.9%     6613   215
  rle                4475    44.7%     5527    74

unpack — decompress a packed PRG

Decompresses a packed PRG back to the original. Auto-detects the algorithm for SFX files; for raw mem output, specify the algorithm manually.

6pack unpack <packed.prg> [options]
flag default meaning
-a, --algorithm auto-detect Algorithm: zx0, lzsa2, bb2, rle. Required for raw mem files.
--classic on Use classic Elias gamma (off = inverted). Only affects ZX0 mem files.
-o, --output file stem.unpacked.prg Output path.
--load-address addr from SFX / same as input Override the output PRG load address.

Examples

# Unpack an SFX (algorithm auto-detected)
6pack unpack game.sfx.prg

# Unpack a raw mem file (must specify algorithm)
6pack unpack game.mem.prg -a zx0

# Unpack a BB2 file
6pack unpack game.bb2.prg -a bb2

Addresses

All address arguments accept these formats:

format example
Hex with $ $0801
Hex with 0x 0x0801
Decimal 2049

Algorithm details

ZX0 (default)

Einar Saukas's LZ+Elias-gamma format. Best compression ratio for general C64 data. Classic (non-inverted) gamma is used for the SFX 6502 decompressor.

LZSA2

Emmanuel Marty's LZSA2 format. LZ77 with variable-width offsets and nibble-encoded lengths. Competitive compression, faster decompression than ZX0.

BB2 (ByteBoozer2)

David Malmborg's (HCL) ByteBoozer2 format. LZ77 with interleaved-gamma lengths and two offset tables (short for 2-byte matches, long for 3+ byte matches).

RLE

Simple byte-oriented run-length encoding. Faster decompression, smaller core, but worse compression ratio than LZ algorithms.