Something went wrong. Try again.
A programming language for the TRS-80 Color Computer
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394# 02: Sprite blitter into a PMODE 4 framebuffer.## Exercises: record types, nested loops, index-carrying iteration# (`enumerate`), the bounds-elision design principle, bitwise ops,# explicit integer widening.
# PMODE 4: 256x192, 1bpp, 32 bytes per row, 6144 bytes at $0E00.var framebuffer: [Uint8; 6144] at 0x0E00
const ROW_STRIDE: Uint16 = 32
# VDG/SAM setup for PMODE 4 with the display at $0E00. Each SAM bit is# a pair of addresses: writing anything to the odd one sets the bit,# the even one clears it.var vdg_mode: Uint8 at 0xFF22
var sam_v0_clear: Uint8 at 0xFFC0var sam_v1_set: Uint8 at 0xFFC3var sam_v2_set: Uint8 at 0xFFC5var sam_f0_set: Uint8 at 0xFFC7var sam_f1_set: Uint8 at 0xFFC9var sam_f2_set: Uint8 at 0xFFCBvar sam_f3_clear: Uint8 at 0xFFCCvar sam_f4_clear: Uint8 at 0xFFCEvar sam_f5_clear: Uint8 at 0xFFD0var sam_f6_clear: Uint8 at 0xFFD2
fn set_pmode4() { # VDG: graphics mode, GM = 111 (256x192 1bpp), green/black. vdg_mode = 0xF0 # SAM video mode V = 110. sam_v0_clear = 0 sam_v1_set = 0 sam_v2_set = 0 # SAM display offset = $0E00 / $200 = 7: F0..F2 set, F3..F6 clear. sam_f0_set = 0 sam_f1_set = 0 sam_f2_set = 0 sam_f3_clear = 0 sam_f4_clear = 0 sam_f5_clear = 0 sam_f6_clear = 0}
# Records use `type` with a struct body. All fields have fixed# footprints, so Sprite's size is known at compile time.type Sprite = { x_byte: Uint8, # horizontal position in bytes (0..31) y: Uint8, # vertical position in scanlines rows: [Uint8; 16], # 8 pixels wide, 16 tall, one byte per row}
fn blit(sprite: Sprite) { # `enumerate` yields (index, value) pairs. The index is proven # in-bounds for `rows` by construction, and the compiler can see # `ix < 16`, which feeds the framebuffer bounds proof below. for (ix, row_bits) in sprite.rows.enumerate() { # No implicit widening: Uint8 -> Uint16 must be spelled out. # On a 6809 this is a real codegen decision, so it's explicit. let y = (sprite.y as Uint16) + ix let offset = y * ROW_STRIDE + (sprite.x_byte as Uint16)
# `offset` can still exceed 6143 if y >= 192, so this check # survives -- but it's hoisted: one check per loop iteration, # not one per byte touched. framebuffer[offset] = framebuffer[offset] | row_bits }}
fn clear() { # Mutable iteration over an array: `var` binding in the loop # head gives a by-reference element you can assign through. for var byte in framebuffer { byte = 0x00 }}
fn main() { clear() set_pmode4()
let player = Sprite { x_byte: 12, y: 90, rows: [ 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, ], }
blit(player) loop {}}