Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ajokela/kz80_lua

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

kz80_lua - Tiny Lua for RetroShield Z80

A Lua compiler that produces standalone 32KB ROM images for the RetroShield Z80 platform. The compiler generates a hybrid ROM containing both a native Z80 virtual machine and the compiled Lua bytecode as an embedded payload.

Overview

kz80_lua compiles a subset of Lua into a self-contained ROM image that runs directly on Z80 hardware. No separate interpreter is needed at runtime - the generated ROM includes everything required to execute the Lua program.

Architecture

The compiler produces a ROM with the following structure:

0x0000-0x00FF  RST vectors and interrupt handlers
0x0100-0x2FFF  Z80 native code (VM interpreter + built-ins)
0x3000-0x35FF  Constant pool (numbers, string pointers)
0x3600-0x37FF  Function address table
0x3800-0x3FFF  Global variable storage
0x4000-0x5FFF  Heap (string constants + runtime tables)
0x6000-0x6FFF  Value stack (VM evaluation stack)
0x7000-0x7FFF  Local variable stack (call frames)

Value Representation

Values are 3 bytes: 2 bytes for the value + 1 byte for the type tag.

Type Tag Value Bytes
nil 0x00 unused
boolean 0x01 0x00 (false) or 0x01 (true)
number 0x02 16-bit signed integer
string 0x03 pointer to null-terminated string
table 0x04 pointer to table structure
function 0x05 index into function table

Building

cargo build --release

Usage

# Compile Lua source to ROM image
./target/release/kz80_lua program.lua -o program.bin

# Run in interpreter mode (for testing)
./target/release/kz80_lua -i program.lua

Running on Hardware

The generated .bin file is a 32KB ROM image suitable for:

  • RetroShield Z80 with Arduino Mega
  • Other Z80 systems with MC6850 ACIA at ports 0x80/0x81

Serial settings: 9600 baud, 8N1

Running in Emulator

Use the RetroShield emulator from the parent project:

../emulator/retroshield program.bin

Supported Lua Features

Data Types

  • nil - null value
  • boolean - true/false
  • number - 16-bit signed integers (-32768 to 32767)
  • string - immutable strings (compared by content)
  • table - associative arrays with integer or string keys
  • function - first-class functions with closures

Operators

Arithmetic:

  • + - * / % (modulo) ^ (exponentiation)
  • Unary - (negation)

Comparison:

  • == ~= < <= > >=

Logical:

  • and or not

String:

  • .. (concatenation)
  • # (length)

Control Structures

-- If statement
if condition then
    -- body
elseif other then
    -- body
else
    -- body
end

-- While loop
while condition do
    -- body
end

-- Numeric for loop
for i = start, stop do
    -- body
end

for i = start, stop, step do
    -- body
end

-- Repeat-until
repeat
    -- body
until condition

Functions

-- Function declaration
function name(arg1, arg2)
    -- body
    return value
end

-- Anonymous functions
local f = function(x) return x * 2 end

-- Multiple return values (limited support)
return a, b

Tables

-- Empty table
local t = {}

-- Array-style initialization
local arr = {10, 20, 30}

-- Record-style initialization
local point = {x = 10, y = 20}

-- Mixed initialization
local mixed = {100, 200, name = "test"}

-- Table access
t[1] = "first"
t["key"] = "value"
t.field = 123

-- String keys are compared by content
t["foo"] = 1
print(t["foo"])  -- works even with different string literals

Built-in Functions

Function Description
print(...) Print values to serial output
type(v) Return type name as string
tostring(v) Convert value to string
tonumber(s) Convert string to number

Variables

-- Global variables
x = 10

-- Local variables
local y = 20

-- Multiple assignment
local a, b = 1, 2

Examples

Hello World

print("Hello, Z80!")

Factorial

function factorial(n)
    if n <= 1 then
        return 1
    end
    return n * factorial(n - 1)
end

print(factorial(5))  -- prints 120

Fibonacci

function fib(n)
    if n <= 1 then
        return n
    end
    return fib(n - 1) + fib(n - 2)
end

for i = 0, 10 do
    print(fib(i))
end

Working with Tables

-- Create a table
local scores = {}
scores["alice"] = 100
scores["bob"] = 85

print(scores["alice"])  -- 100

-- Array-style
local primes = {2, 3, 5, 7, 11}
print(primes[1])  -- 2 (Lua arrays are 1-indexed)
print(primes[3])  -- 5

Iterative Loops

-- Sum 1 to 100
local sum = 0
for i = 1, 100 do
    sum = sum + i
end
print(sum)  -- 5050

-- Countdown
local n = 10
while n > 0 do
    print(n)
    n = n - 1
end
print("Liftoff!")

Limitations

Not Supported

  • Floating-point numbers (integers only, 16-bit)
  • Metatables and metamethods
  • Coroutines
  • File I/O
  • pairs() / ipairs() iterators
  • Standard library functions (math, string, table, etc.)
  • Multiple return value assignment beyond 2 values
  • Vararg functions (...)
  • Generic for loops (for k, v in pairs(t))

Constraints

  • Maximum 256 constants per function
  • Maximum 256 local variables per function
  • Maximum 256 global variables
  • Table capacity: 8 entries (fixed)
  • String comparison: O(n) character-by-character
  • No garbage collection (heap grows monotonically)
  • Stack depth limited by available RAM

Project Structure

kz80_lua/
├── Cargo.toml
├── README.md
├── src/
│   ├── main.rs        # CLI entry point
│   ├── lexer.rs       # Tokenizer
│   ├── token.rs       # Token definitions
│   ├── parser.rs      # Recursive descent parser
│   ├── ast.rs         # Abstract syntax tree
│   ├── compiler.rs    # AST to bytecode compiler
│   ├── bytecode.rs    # Bytecode definitions
│   ├── codegen.rs     # Z80 native code generator
│   └── interpreter.rs # Tree-walking interpreter (for -i mode)
└── examples/
    ├── simple.lua
    ├── loops.lua
    ├── factorial.lua
    ├── fibonacci.lua
    └── tables.lua

Bytecode Reference

The VM uses a stack-based bytecode with the following opcodes:

Opcode Hex Description
Nop 0x00 No operation
Pop 0x01 Pop top of stack
Dup 0x02 Duplicate top of stack
Rot3 0x03 Rotate: (a,b,c) -> (b,c,a)
LoadNil 0x10 Push nil
LoadTrue 0x11 Push true
LoadFalse 0x12 Push false
LoadConst 0x13 Push constant (1-byte index)
GetLocal 0x20 Push local variable
SetLocal 0x21 Pop to local variable
GetGlobal 0x22 Push global variable
SetGlobal 0x23 Pop to global variable
NewTable 0x30 Create new table
GetTable 0x31 table[key] -> value
SetTable 0x32 table[key] = value
GetField 0x33 table.field -> value
SetField 0x34 table.field = value
Add 0x40 a + b
Sub 0x41 a - b
Mul 0x42 a * b
Div 0x43 a / b
Mod 0x44 a % b
Pow 0x45 a ^ b (exponent)
Neg 0x46 -a
Eq 0x50 a == b
Lt 0x52 a < b
Le 0x53 a <= b
Not 0x60 not a
Concat 0x70 a .. b
Len 0x71 #a (length)
Jump 0x80 Unconditional jump
JumpIfFalse 0x81 Jump if top is falsy
Call 0x90 Call function
Return 0x91 Return from function
Print 0xA0 Print values
Type 0xA1 Return type as string
ToNumber 0xA2 Convert to number
ToString 0xA3 Convert to string
Halt 0xFF Stop execution

License

BSD-3-Clause

Author

Alex Jokela

Acknowledgments

  • Inspired by Lua 5.x semantics
  • Built for the RetroShield Z80 platform by 8bitforce
  • Z80 instruction set reference from Zilog documentation

About

A Tiny Lua compiler for RetroShield Z80 - generates standalone 32KB ROM images

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages