Skip to content

About

Best practices, tips, and techniques for squishing every last byte out of your retrocomputing code

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

4 Commits

Folders and files

Repository files navigation

Sizecoding in FastBasic  🗜️ 🕹️

How to fit a real game into ten lines of FastBasic on the Atari 8-bit, for the BASIC 10Liner contest: what the language lets you get away with, which tricks actually save characters, how to keep a ten-liner fast enough to never drop a frame, and a small toolchain that measures all of it so you don't have to guess.

Everything here was learned the hard way on real PUR-120 entries, then generalized. Every character count in these pages is checked against the real compiler by tests/tricks.py, every behavior claim is run in an emulator, and every timing comes from bench/. If a number here is wrong, a test should say so.

The pitch  📈

Ten lines. 120 characters each (or 256, if you're feeling EXTREME). No machine code. Go.

The short version  ⚡

If you read nothing else:

  1. Never abbreviate by hand. Write a readable, commented source and let a minimizer do the spelling. Use fbp with -O: on two full PUR-120 listings it beat FastBasic's own -ls by 31 and 21 characters, with a byte-identical XEX.
  2. Learn the precedence. &, ! and EXOR bind tighter than * and /, which bind tighter than + and -. I+U&B is I+(U&B). Most parentheses you'd type in another BASIC are dead weight here. (It cuts both ways: X+1&7 is X+1, not a wrap.)
  3. Comparisons are numbers. True is 1, false is 0, so F=A>B replaces a whole IF/ELSE/ENDIF (15 characters) and K=K+(B>9)*7 replaces an IF.
  4. One-letter names, given to the variables you use most. There are 27 of them: A to Z and _.
  5. Functions take a bare argument. PEEK 712, STICK 0, RAND 10.
  6. Strings are your data segment. Any byte but " and $9B fits in a literal, and &"..." is the address of a literal with no variable at all.
  7. The OS already did the work. Patch its display list instead of building one; the runtime already turned the sound off.
  8. Speed is a budget, not a feeling. A frame is 131 units of 2 scanlines (NTSC). Measure what statements cost, give heavy jobs their own frames, and count lost frames with a tool, not your eyes.
  9. Packing is its own problem. A statement never splits across lines, so the last 10 to 40 characters of each line are where listings go to die.
  10. Try ideas as variants, not edits, and keep the ones that measure better.

The pages  📚

1. The contest Categories, what counts as a character, what to submit
2. FastBasic for sizecoders The language facts every trick leans on: abbreviations, precedence, names, strings, blocks
3. Size tricks The catalogue: before, after, characters saved, and why it works
4. Atari tricks Display lists, players and missiles, DLIs, sound and input on a budget
5. Speed The frame budget, what each statement costs, scheduling, counting lost frames
6. Packing ten lines Line breaks, reordering, and why the end of a line matters most
7. Workflow Readable source, variants, measuring, testing: the loop that keeps it honest
8. Submission The ZIP, the proof image, the disk image
Abbreviations Every keyword's shortest spelling, generated from the grammar

The toolchain  🧰

Python 3 scripts in tools/, documented in tools/README.md:

Tool What it does
fbsize.py Measure, minimize, pack into 10 lines (with safe reordering), verify
variants.py Try size ideas as named, combinable edits and see what each saves
fbbench.py Time statements in the emulator, in units of 2 scanlines
fbframes.py Count lost frames in your game while a script plays it
proof.py Render a listing as an ATASCII image for the contest's proof
abbrevs.py Regenerate the abbreviation table from FastBasic's grammar
export FASTBASIC=/path/to/fastbasic   # the cross-compiler
export FBP=/path/to/fbp               # optional, but it's free characters
python3 tools/fbsize.py min game.bas -o GAME.min      # minimize + check the XEX is identical
python3 tools/fbsize.py pack GAME.min -o GAME.BAS     # break into 10 lines of 120
python3 tools/fbsize.py measure GAME.BAS              # what's left on every line
python3 tools/proof.py GAME.BAS -o proof.png          # the listing as the Atari sees it

The size tools need only Python and FastBasic. The emulator tools (fbbench, fbframes, the behavior half of the tests) need Linux with atari800, Xvfb, xdotool and ImageMagick.

Colophon  🙏

Shoutouts to @dmsc for FastBasic, which makes all of this possible (and fast), and to Gunnar and the NOMAM crew for running the BASIC 10Liner contest year after year. Timings were measured in atari800 with its built-in AltirraOS, thanks to Avery Lee's Altirra.

License  📜

The tools, tests and example programs are MIT. The writing (this README, docs/ and the benchmark results) is CC BY 4.0: share it, adapt it, just give credit.

Get in touch  📩

Found a trick that isn't here, or a number that's wrong? Open an issue or a PR, ideally with a trick(...) line for tests/tricks.py that proves it.

About

Best practices, tips, and techniques for squishing every last byte out of your retrocomputing code

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages