Skip to content

UltimateBasic 64 1.6.0

Choose a tag to compare

@zstarczali zstarczali released this 03 Oct 14:32
· 48 commits to main since this release
cc44002

Ultimate Basic v1.6.0 – Release Notes

2026-10-03 – Release 1.6.0: optimised 6502 code generation

Performance

  • Conditions (if / while / until / select) compile to CMP + branch
    instead of materialising a 0/1 value and testing it again
    (if x == 10: 24 -> 9 bytes). x > n becomes x >= n+1; and / or of
    comparisons short-circuit when the right side is side-effect free;
    while loops are rotated (test at the bottom); select compares the
    variable in place.
  • Constants and byte variables are used directly as operands
    (ADC #n, CMP zp) instead of being staged in scratch zero page; poke to a
    constant or word address stores directly; identity operations vanish.
  • * by a constant: shifts / shift-add (x * 8 -> 3x ASL); otherwise a
    shift-add loop of at most 8 rounds (the old repeated-addition loop
    needed ~16 cycles per unit of the multiplier).
  • / and mod by a power of two: LSR / AND #(n-1); otherwise an 8-round
    shift-subtract loop (the old loop ran once per unit of the quotient).
  • x = x +/- 1|2 -> INC / DEC (word: 16-bit INC / DEC for +/-1).
  • Arrays with a variable index use base,Y addressing instead of a
    zero-page pointer.
  • for loops are rotated (no JMP per iteration); constant limits / steps
    are immediates and use no zero page.
  • A-register reuse: the leading LDA of a statement is skipped when it
    reloads the value the previous statement just stored.

Fixes

  • Division by zero used to hang: now x / 0 = 255 and x mod 0 = x.
  • for i = 0 to 255 looped forever; the step now detects the 8-bit
    wrap-around in both directions, so the loop runs 256 times and stops.

Tests

  • Full 6502 emulator for tests (tests/common/cpu6502.rs), semantic tests
    that run compiled programs (tests/codegen_semantics.rs), code-shape /
    cycle tests (tests/codegen_opt.rs) and an ignored differential fuzzer
    against a reference compiler (tests/differential_fuzz.rs).

2026-09-26 – chain: load and start another program

New language elements

  • Added chain "FILE" [, device]: loads another program to its own load
    address (normally $0801, right over the running program) and RUNs it, as
    if LOAD"FILE",8,1 and RUN had been typed. For boot / title programs.
    A 46-byte position-independent loader and the file name (1-16
    characters) are copied to the cassette buffer $033C. Before the load the
    I/O is reset (VIC IRQs off, SID volume 0, IOINIT, RESTOR, $01 = $37);
    after it the end-of-program pointer $2D/$2E is set, BASIC is
    re-initialised ($E453, $E3BF - a self-extracting cruncher such as
    Exomizer sfx may have used BASIC's zero page) and the program is RUN
    ($A659, $A7AE). Without a device the drive the program came from ($BA)
    is used, else drive 8. If the file cannot be loaded, the program
    continues with the statement after chain.

Fixes

  • load: the secondary address was swapped. load "FILE" used secondary
    0 with X/Y = 0 and loaded the file to $0000; load "FILE", addr used
    secondary 1 and ignored addr. Now load "FILE" loads to the file's own
    address (secondary 1) and load "FILE", addr to addr (secondary 0).
  • --d64: directory entries were written 30 bytes apart instead of 32, so
    every file after the first had a broken entry (e.g. --add data.prg
    showed up as a garbled name and could not be loaded). Also fixed: disk ID
    and DOS type were at the wrong BAM offsets; file data only used tracks
    1-17 (a larger disk crashed the build instead of reporting it); sectors
    are now allocated like the 1541 DOS does (tracks 17->1, then 19->35,
    interleave 10). A full disk is reported as "D64 error: disk full ...".
    New tests: tests/d64_tests.rs reads the image back (BAM, directory,
    sector chains) and compares every file.

2026-09-25 – Initialised arrays

New language elements

  • Added data <array>: v, v, ...: fills a declared array / array_word
    with constant values. Further lines continue where the previous one
    stopped; word arrays take 16-bit values (stored lo, hi). The values are
    stored after the code and copied into the array once at program start
    (right after the arrays are zeroed), so the table is writable and indexed
    like any other array (sinus[i]). These lines do not affect read.
    An undeclared name or more values than the array holds is a compile-time
    error. This is what the Visual Assembler Curve Editor's "Export to
    UltimateBasic" and "Export UltimateBasic demo" generate.