Skip to main content

July 2026

A plug-in contract for chess engines

July 10 chess

More progress in the TalkChess discussion: an initial draft of a Hybrix interface chess_engine.hyb that allows different algorithms (e.g. Micro-Max) to be used interchangeably by a front end such as a chess game or perhaps one day a simple harness for community tournaments. The reference implementation subclass allows testing of the upcoming "Chombit Chess" ROM while the Micro-Max port is still in progress. In the future, if someone wants to implement a better computer chess algorithm using this interface, it can be easily loaded by any chess game.

The contract is split into four features, each optional to implement:

  • load_position(): ability to support loading/saving of board states
  • undo_move(): ability to undo one or more moves
  • collect_moves(): ability for a GUI to highlight possible next moves
  • find_move(): ability to search for a move for computer play

Chess Console

July 13 chess roms

New in the gallery: "Chess Console", a HASCII text chess game for testing chess_engine subclasses. Type a move like e2e4 and press ENTER, press ENTER alone to make the computer move, or ESC to interrupt its thinking. Each computer move reports its node count and elapsed milliseconds.

Click to see it run!

In other news, our artist ZaniPixels finished the sprites for the upcoming "Chombit Chess" graphical game:

Sprites for chess game

Framework updates

July 16 framework

Three recent improvements for the Hybrix framework:

  • The engine::wait_for_paint() API now supports unbuffered mode. In the original design, unbuffered mode was accomplished by skipping the call to this function, but now it works correctly for both modes. As part of this change, engine::init() now initializes with buffered mode.

  • A new field s_actor_spot.starting_frame_index allows an actor to specify its starting animation. In the screenshot above, the kinds of chess pieces (pawn, knight, bishop, etc.) are represented as animation frames with infinite delay, to avoid having to define separate clips for each one.

  • The FAIL event handler now uses a SLEEP instruction instead of an infinite loop, which avoids wasting battery when a program finishes or crashes.

Out parameters

July 18 language

The Hybrix language now supports out parameters that allow a function call to return multiple values. This simplified the chess program code, for example:

gameboard.hyb:

# ---------------------------------------------------------------------------
# A rank or file line counts as part of the square below or to the right of
# it. The extruded front edge belongs to no square.
func try_get_col_row(x: int, y: int, out col: int, out row: int): bool

wand_actor.hyb

var col: int, row: int
if gameboard::try_get_col_row(x + wand_actor::tip_x, y + wand_actor::tip_y,
| out col, out row) then
.square <- gameboard::get_square(col, row)
else
.square <- square::none
end if

To keep the calling convention simple, Hybrix out works by copying: the function has its own col and row variables, and when it returns, their values are assigned to the caller's col and row. This differs from C#, where an out parameter is a hidden reference to the caller's variable. Golang avoids parameters entirely, instead using return col, row, ok and then col, row, ok := try_get_col_row(x, y).

Constant expressions and folding

July 24 language

The compiler now implements constant folding, which means reducing arithmetic when the answer can be determined at compile time. For example, x <- 1 + 2 now emits move i:0, 3 in the disassembly pane.

By design, constant folding simulates what the CPU would do, including imprecisions and overflow. For example, x <- 1 / 3 reduces to x <- 0, because the DIVIDE instruction truncates. The expression math::shift_left(1, 40) reduces to 256 because the SHIFT instruction applies a mask to its shift count. Where the machine would set the overflow or carry flag, the compiler reports an error instead:

var x: int, b: byte
x <- 1 + 2 # compiles to x <- 3
x <- 1 / 3 # compiles to x <- 0
b <- 100 + 200 # ERROR: in the past, this would compute 44

The folding rules have a similar spirit to C#, whose checked() and unchecked() operators determine whether to report an overflow or silently accept the rolled-over value. These operators will be added to the Hybrix language eventually, which is why Chombit provides efficient FAIL tests for fail if overflow and fail if carry.

Constant folding eligibility also determines whether an expression can be supported in a const declaration. With this update, const now allows more complicated expressions including references to other constants:

module m
const c is 1 + 2
const d is c
end module

We found that these compiler improvements had relatively few breaking changes for existing code.

Mandelbrot sample program

July 29 language roms tutorials

We've added a Mandelbrot sample that illustrates how to use io_tilemap::tile_pitch to make a linear frame buffer.

It also showcases a new Hybrix language feature, the repeat-of initializer. You can now write repeat 100 of 0 to avoid duplication of array elements. For example, the Mandelbrot program uses it to avoid writing 176 zeros on each row of this tileset definition:

# The 16x16 border brick tile
data main::brick_pixels
[
# Appending 176 zeros to each line gives us tile_pitch=192
2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 2, repeat 176 of 0,
43, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 43, repeat 176 of 0,
43, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 43, repeat 176 of 0,
. . .
]
end data

Theme color indexes

July 30 machine

While improving the documentation for tile_pitch, we've introduced more rigorous terminology for three closely related concepts:

  • A system palette index (0 – 44) selects one of Palix's fixed hardware colors.
  • A theme color index is what a sprite or tile pixel byte contains. It indexes io_theme.color_map[], which produces a system palette index.
  • A theme index (0 – 31) selects which entry of io::themes[] performs that mapping.

Note that io::background_color and io::matte_color are the exceptions, specifying system palette indexes directly.

What happens when a pixel byte is larger than 63? The docs said it renders as black, whereas the emulator rendered it as clear. Thinking about this more deeply, real hardware would simply mask the value to 6 bits, since color_map[] has only 64 entries, which has a practical benefit: bits 6 and 7 of every pixel byte could be repurposed to store other data such as collision maps. This small Palix adjustment shouldn't break any correct programs today, but it provides an interesting possibility for advanced programs in the future.

Jamdac core, and what triggering means

July 31 jamdac

The jamdac.hyb comments previously marked certain features as "Jamdac Plus". It sounded like a paid add-on, so we've renamed these markers to [non-core], with the distinction explained in a new subsection.

What qualifies as "core"? A key principle is that it's self-contained: each channel's sound depends only on that channel's own events, and each note is fully determined at the moment it is triggered. Formalizing this uncovered an edge case where it wasn't true: if the channel effect used a delay line, triggering an instrument did not reset the delay line, so an echo tail from the previous note could bleed into the next one. It wasn't observable in practice, since the framework tracker frequently reloads instruments which also resets the delay line.

The docs now provide a precise specification for triggering, and the synthesizer now implements it. As part of this work, a small bug was fixed for RELEASE GLOBAL ENVELOPE, which had been overlooked because the framework tracker currently doesn't use this event.