Crash-tan
CrashPlayer 1.33.0 Released!
Crash BASIC 1.33.0 Release Notes
written by Crash-tan
THE BIT-TWIDDLER'S RELEASE
Crash BASIC can now reach all the way down to the individual bits of an
integer. Nine new functions — BITAND, BITOR, BITXOR, BITNOT,SHL, SHR, BITTEST, BITSET, BITCLEAR — give you packed flags,
bitmasks, color channels, and bitboards without ever leaving BASIC.
If you've never needed bitwise math, nothing changes for you. If you
have been waiting for it: it's here, it's loud about mistakes, and it
behaves the same on every platform.
state = 0
state = BITSET(state, 2) ' raise bit 2
IF BITTEST(state, 2) THEN PRINT "bit 2 is up"
PRINT BITAND(12, 10) ' 8 (1100 & 1010 = 1000)
PRINT SHL(1, 4) ' 16 (1 shifted left 4)Why functions, not operators?
A fair question if you're coming from QBasic, where AND / OR / NOT
secretly do double duty as bitwise operators (it only works there
because QBasic's TRUE is -1, so all-bits-set and "true" happen to
coincide).
Crash BASIC doesn't play that game. Here TRUE is 1, AND / OR
/ NOT are strictly boolean — they short-circuit and return 0/1
by truthiness — and they are staying that way. Overloading them to
sometimes mean bitwise would make a AND b ambiguous depending on
context, which is exactly the kind of footgun this language tries not to
hand you.
So bitwise math gets its own clearly-named functions. No guessing.
PRINT (12 AND 10) ' 1 — both operands are truthy → boolean true
PRINT BITAND(12, 10) ' 8 — the actual bitwise ANDThe toolkit
Everything operates on Crash BASIC's 64-bit signed integers. Float
arguments are truncated to integer first, so SHL(2.9, 1) is SHL(2, 1).
The combiners — BITAND, BITOR, BITXOR, BITNOT
BITAND(a, b) ' bits set in BOTH — masking
BITOR(a, b) ' bits set in EITHER — combining flags
BITXOR(a, b) ' bits set in EXACTLY one — toggling
BITNOT(a) ' flip every bitBITNOT flips all 64 bits, so on a signed integer BITNOT(0) is -1
(every bit set). That's not a bug — that's what "all bits on" is in
two's complement. Mask it down to the width you care about:
low_byte = BITAND(BITNOT(0), 255) ' 255 — bottom 8 bits onlyThe shifters — SHL, SHR
SHL(a, n) ' shift left by n bits (a * 2^n)
SHR(a, n) ' shift right by n bits — LOGICAL, zero-filledTwo things worth knowing:
SHRis a logical (zero-fill) shift, not arithmetic. It treats
your integer as an unsigned 64-bit pattern, so the sign bit does not
smear downward. This is the right behavior for masks and bitboards —
bit 63 shifts cleanly down to bit 0 instead of dragging a wall of 1s
behind it.- Shift amounts must be
>= 0. A negative shift is a bug, so it
halts loud instead of guessing a direction. Shifting by>= 64
pushes every bit out the end and yields0.
The single-bit trio — BITTEST, BITSET, BITCLEAR
For when you don't want to hand-roll 1 << n masks every time. Bit 0
is the least-significant bit.
BITTEST(a, bit) ' 1 if that bit is set, else 0
BITSET(a, bit) ' return a with that bit turned ON
BITCLEAR(a, bit) ' return a with that bit turned OFFBITSET / BITCLEAR return a new value — they don't mutate in place,
so assign the result back:
flags = BITSET(flags, 3)A negative bit index is a bug and halts. A bit index >= 64 has no
representable bit, so it's handled gracefully rather than crashing:BITTEST reads 0, and BITSET / BITCLEAR return the value unchanged.
What you'll actually use it for
Packed flags — stuff a dozen booleans into one integer:
HAS_KEY = 0 ' bit positions
HAS_SWORD = 1
IS_POISONED = 2
inv = 0
inv = BITSET(inv, HAS_KEY)
inv = BITSET(inv, HAS_SWORD)
IF BITTEST(inv, HAS_KEY) THEN PRINT "the door clicks open"
inv = BITCLEAR(inv, HAS_KEY) ' key consumedColor channels — pack and unpack RGB in a single number:
rgb = BITOR(BITOR(SHL(red, 16), SHL(green, 8)), blue)
red = BITAND(SHR(rgb, 16), 255)
green = BITAND(SHR(rgb, 8), 255)
blue = BITAND(rgb, 255)Bitboards — represent an 8x8 board as a single 64-bit integer, one
bit per square, and test/flip squares with BITTEST / BITSET. This is
the fast foundation for board-game AI, and it's exactly why these landed
now.
Also fixed
PLAYMUSICin the browser could play silently. A song whose name
wasn't all-uppercase (e.g."Moon") would load fine but make no sound
on the web build, while playing correctly on desktop. Song names now
match case-insensitively in the browser, the same way every other audio
handle already does. Desktop was never affected.
Compatibility notes
- Purely additive. Nine new built-in functions; nothing existing
changed.AND/OR/NOTare still boolean, exactly as before. - No protocol / multiplayer changes. Wire-compatible with 1.32.x.
- Same answers everywhere. The bitwise functions are pure integer
math — identical results on Mac, Windows, Linux, iOS, Android, and the
browser.
Go forth and twiddle. One bit at a time.
— Crash-tan