Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

MinZ Fun Showcase

27 programs, 3 languages, 1 Z80 backend. All compile. All verified.

This directory contains showcase programs that demonstrate what MinZ can do on vintage Z80 hardware. Each program compiles to tight, hand-quality Z80 assembly through the MIR2 pipeline with PBQP register allocation and Z3-PFCCO calling convention optimization.

Compilation: mz fun/<file> -o build/<name>.a80


The Highlights

ADT Option: Safe Nullable Values

adt_option.nanz -- 5 asserts, all pass

enum Option { None, Some(u8) }

fun unwrap_or(opt: Option, def: u8) -> u8 {
    return match opt {
        Some(val) => val,
        None      => def,
    }
}

fun safe_div(a: u8, b: u8) -> Option {
    if b == 0 { return None }
    return Some(a / b)
}

assert unwrap_or(safe_div(10, 3), 255) == 3

The compiler constant-folds through the entire ADT. safe_div(10,3) evaluates at compile time through Some(3) into unwrap_or(Some(3), 255) and out to 3. The Z80 output:

test_safe_div_ok:    LD A, 3  / RET     ; the entire safe_div + match + unwrap chain
test_unwrap_some:    LD A, 42 / RET     ; unwrap_or(Some(42), 0)
test_is_none:        LD A, 0  / RET     ; is_some(None)
test_unwrap_none:    LD A, 77 / RET     ; unwrap_or(None, 77)

124 optimization passes. 4 pruned lambdas. Zero-cost ADTs on an 8-bit CPU from 1976.


Iterator Fusion: map + filter + forEach in One Loop

iterator_fusion.nanz

fun filter_map_explicit(buf: u16, n: u8, threshold: u8) -> void {
    for x: u8 in buf[0..n] {
        let doubled: u8 = x * 2
        if doubled > threshold {
            process(doubled)
        }
    }
}

Three operations -- load, double, filter -- fused into one tight loop with conditional CALL:

filter_map_explicit:
    LD B, C                    ; loop counter = n
    LD C, D                    ; threshold
.fe_head:
    LD A, B / AND A / RET Z    ; done?
.fe_body:
    LD D, (HL)                 ; load element
    LD A, D / ADD A, A         ; x * 2 (map)
    LD D, A
    LD A, C / CP D             ; threshold vs doubled (filter)
    CALL C, process            ; conditional CALL -- fires only when filter passes
.fe_cont:
    INC HL                     ; advance pointer
    ...DEC B / JRS .fe_head    ; next element

CALL C, process is a single Z80 instruction that replaces the usual JR NC, .skip / CALL process / .skip: pattern. MinZ detects the BrIf-over-single-call pattern at MIR2 level and emits a conditional CALL, saving 2 bytes and eliminating the branch.


Pipe Operator & Function Composition

pipes.frl (Frill -- ML-style functional language)

let double (x : u8) : u8 = x + x
let inc    (x : u8) : u8 = x + 1

let pipe_dbl_inc (x : u8) : u8 = x |> double |> inc

The pipe chain x |> double |> inc compiles to:

pipe_dbl_inc:
    ADD A, A     ; double
    INC A        ; inc
    RET

Both functions inlined. Zero-cost functional composition on Z80. Three instructions, zero overhead.


OOP: Zero-Cost Interfaces

oop_shapes.nanz -- traits + impl blocks

struct Rect { kind: u8, w: u8, h: u8 }
trait Shape { area, perimeter }

impl Shape for Rect {
    fun area(self) -> u8 { return self.w * self.h }
    fun perimeter(self) -> u8 { return (self.w + self.h) * 2 }
}

No vtables, no dynamic dispatch. rect.area() is a direct CALL Rect_area:

Rect_area:
    EX DE, HL              ; save self ptr
    LD BC, 2 / ADD HL, BC  ; &self.w
    LD B, (HL)             ; w
    LD H, D / LD L, E
    LD BC, 3 / ADD HL, BC  ; &self.h
    LD E, (HL)             ; h
    LD A, B / LD B, E
    JP __mul8              ; tail call: w * h, return in A

Rect_perimeter:
    ...
    LD A, (HL) / RLA       ; w * 2 (rotate left = shift left 1)
    ...
    LD A, (HL) / RLA       ; h * 2
    ADD A, C               ; w*2 + h*2
    RET

JP __mul8 instead of CALL __mul8 / RET -- tail call optimization saves 17 T-states. RLA for *2 -- the compiler chooses the single-byte rotate.


Tail Recursion: Fibonacci in O(1) Stack

tail_recursion.nanz -- asserts verified

fun fib_tail(n: u8, a: u8, b: u8) -> u8 {
    if n == 0 { return a }
    return fib_tail(n - 1, b, a + b)
}

assert fib_tail(10, 0, 1) == 55

Grace tail-recursion elimination converts the recursive call to a loop:

fib_tail:
    AND A / RET Z          ; n == 0? return a (in B)
    DEC A                  ; n - 1
    LD A, B / ADD A, C     ; a + b
    LD B, A                ; new accumulator
    ...JRS fib_tail        ; jump, not CALL -- O(1) stack

No CALL, no stack growth. Runs to n=255 on 256 bytes of stack.


Widemath: Operator Overloading for Arithmetic

widemath.nanz -- 26 functions, 31 asserts, all pass

fun *(a: u8, b: u8) -> u16 { ... }   // widening multiply

assert sat_add(200, 100) == 255       // saturates at u8 max
assert abs_diff(200, 50) == 150       // |a - b|
assert brightness_blend(100, 200, 128) == 150  // linear interpolation

Scalar operator overloading: a * b transparently dispatches to widening multiply when types match. Saturating arithmetic, absolute difference, pixel blending -- all verified against MIR2 VM.


Raymarcher: 3D on Z80

raymarcher.nanz -- 2816 lines of Z80 assembly output

struct Vec3 { x: i16, y: i16, z: i16 }

fun scene(p: Vec3) -> i16 {
    let sphere: i16 = sdf_sphere(p, 150)
    let box: i16 = sdf_box(p, Vec3{x: 100, y: 100, z: 100})
    return sdf_subtract(sphere, box)    // CSG: sphere minus box
}

Fixed-point 8.8 arithmetic, SDF primitives, CSG operations, normal estimation via central differences. Renders a sphere-minus-box scene on ZX Spectrum. The most complex MinZ program.


Bit Intent: Scalar Bit Selection

bit_intent.nanz

fun set_ptr_bit() -> u8 {
    var p: ptr = &flags_g
    p^.4 = 1              // set bit 4 via pointer
    return p^.4            // read bit 4 back
}

x.N syntax for bit access. Compiles to Z80 native SET 4, (HL) / BIT 4, (HL).


Tuple Returns: Multiple Values

tuple_return.nanz / triple_return_skip.nanz

fun minmax(a: u16, b: u16) -> (u16, u16) {
    if a <= b { return (a, b) }
    return (b, a)
}

let (lo, hi) = minmax(x, y)
let (first, _, last) = stats3(x, y, z)   // blank identifier skips middle value

Multiple return values via register pairs. Blank _ identifier skips unused values at zero cost.


LFSR Cascade: Generative Art

che_cascade.nanz / che_intro.nanz / che_nanz.nanz

fun xor_pixel(x: u8, y: u8) -> void {
    let addr: u16 = 0x4000 + y7*2048 + y2_0*256 + y5_3*32 + xbyte
    let p: ^u8 = addr
    p^ = p^ xor mask        // flip pixel via pointer XOR
}

Che Guevara portrait rendered by 64 LFSR-16 layers XOR'ing random pixels. Each layer uses a different seed from a precomputed table. Self-modifying code patches LFSR state into instruction immediates (TSMC tunnels).


SHA-256 on Z80

sha256.nanz -- 15 functions, 271 lines of asm

32-bit arithmetic via u16 pairs (DEHL convention). GPU-proven optimal shift sequences. Implements SHA-256 message schedule and compression on 3.5 MHz hardware.


Frill Graphics: Pixel Art Patterns

frill_graphics.frl -- ML-style functional

let sierpinski (x : u8) (y : u8) = if (x & y) == 0 then 1 else 0
let xor_texture (x : u8) (y : u8) = x ^ y
let checker (x : u8) (y : u8) = ((x / 8) + (y / 8)) & 1

Pattern generators for ZX Spectrum: Sierpinski triangles, XOR textures, checkerboards, diamonds, rings. Each is a pure function from (x, y) to pixel value.


fun/fun/ -- Animation & Replay

File Description
anim_player.nanz LFSR-16 AND-cascade renderer playing frame-based animation from binary data
che_intro.nanz Enhanced Che intro with runtime layer table at 0xC000
replay.nanz Seed-table-driven LFSR cascade renderer with 768-byte block buffer

Full Compilation Status

All 27 programs compile successfully. Programs with assert statements are verified via MIR2 VM.

File Lang Funcs ASM Lines Asserts Status
adt_option.nanz Nanz 8 131 5/5
iterator_fusion.nanz Nanz 3 81 --
oop_shapes.nanz Nanz 8 213 --
tail_recursion.nanz Nanz 8 165
raymarcher.nanz Nanz ~40 2816 --
sha256.nanz Nanz 15 271 --
widemath.nanz Nanz 26 566 31/31
vectors.nanz Nanz 16 836 --
che_cascade.nanz Nanz 12 554 --
che_intro.nanz Nanz 4 372 --
che_nanz.nanz Nanz 5 387 --
lfsr_decoder.nanz Nanz 12 334 --
state_machine.nanz Nanz 3 115 --
bit_intent.nanz Nanz 5 92 --
pointer_threading.nanz Nanz 2 87 --
tuple_return.nanz Nanz 4 89
triple_return_skip.nanz Nanz 4 42 --
addr_field_basic.nanz Nanz 2 38 --
addr_field_method.nanz Nanz 3 44 --
addr_field_aos_walk.nanz Nanz 2 56 --
addr_index_basic.nanz Nanz 2 34 --
pipes.frl Frill 10 108 --
frill_showcase.frl Frill 39 634 --
frill_graphics.frl Frill ~20 431 --
fun/anim_player.nanz Nanz 9 ~200 --
fun/che_intro.nanz Nanz 8 ~300 --
fun/replay.nanz Nanz 6 ~200 --

MinZ: Modern programming abstractions with zero-cost performance on vintage Z80 hardware.