MinZ needs to handle iteration over:
- u8 arrays - Small arrays (≤255 elements) using DJNZ
- u16 arrays - Large arrays (>255 elements) using 16-bit counters
- Strings - Both u8 and u16 length-prefixed strings
- Mixed data types - Unified iterator interface
// MinZ strings are already optimized!
let msg: *u8 = "Hello!"; // Compiles to: DB 6, "Hello!"
; Our current print_string is already perfect for iteration!
print_string:
LD B, (HL) ; B = length from first byte
INC HL ; HL -> string data
LD A, B ; Check if length is zero
OR A
RET Z ; Return if empty string
print_loop:
LD A, (HL) ; Load character (7 T-states)
RST 16 ; Print character (11 T-states)
INC HL ; Next character (6 T-states)
DJNZ print_loop ; Decrement B and loop (13/8 T-states)
RETThis is PERFECT for string iteration! We just need to generalize it.
; For u8 length or known small arrays
small_array_iter:
LD HL, array_data ; Pointer to data
LD B, array_length ; Length in B (must be ≤255)
small_loop:
LD A, (HL) ; Load element
; ... process element ...
INC HL ; Next element (for u8)
DJNZ small_loop ; Super efficient!
; Cost: ~18 T-states per element; For u16 length arrays
large_array_iter:
LD HL, array_data ; Pointer to data
LD BC, array_length ; Length in BC (16-bit)
large_loop:
LD A, B ; Check if BC == 0
OR C
JR Z, large_done ; Exit if zero
LD A, (HL) ; Load element
; ... process element ...
INC HL ; Next element
DEC BC ; Decrement 16-bit counter
JR large_loop ; Continue
large_done:
; Cost: ~25 T-states per element (still good!); For strings (auto-detect length type)
string_iter:
LD A, (HL) ; Check first byte
CP 255 ; Is it 255 (extended length marker)?
JR Z, string_u16 ; If yes, use u16 length
string_u8: ; u8 length string
LD B, A ; Length in B
INC HL ; Skip length byte
; Use DJNZ pattern
JR string_common
string_u16: ; u16 length string
INC HL ; Skip marker
LD C, (HL) ; Low byte of length
INC HL
LD B, (HL) ; High byte of length
INC HL ; Skip length word
; Use 16-bit counter pattern
string_common:
; Process characters...enum IteratorType {
Small, // Uses DJNZ (≤255 elements)
Large, // Uses 16-bit counter (>255 elements)
String, // Auto-detects u8/u16 length
}
struct Iterator[T] {
ptr: *T, // Current element pointer (HL)
remaining: u16, // Elements remaining (B for small, BC for large)
iter_type: IteratorType,
element_size: u8, // Bytes per element (1=u8, 2=u16, etc.)
}
impl Iterator[T] for SmallArray[T] {
fun forEach(self, action: fn(T) -> void) -> void {
// Compiles to DJNZ pattern
asm {
"LD HL, ${self.ptr}"
"LD B, ${self.remaining}"
"loop:"
"LD A, (HL)"
// Call action(A)
"INC HL"
"DJNZ loop"
}
}
}
impl Iterator[T] for LargeArray[T] {
fun forEach(self, action: fn(T) -> void) -> void {
// Compiles to 16-bit counter pattern
asm {
"LD HL, ${self.ptr}"
"LD BC, ${self.remaining}"
"loop:"
"LD A, B"
"OR C"
"JR Z, done"
"LD A, (HL)"
// Call action(A)
"INC HL"
"DEC BC"
"JR loop"
"done:"
}
}
}
impl Iterator[char] for String {
fun forEach(self, action: fn(char) -> void) -> void {
// Auto-detect length type and use optimal pattern
if (self.length <= 255) {
// Use DJNZ pattern
self.as_small_array().forEach(action);
} else {
// Use 16-bit pattern
self.as_large_array().forEach(action);
}
}
}
[ Length: u8 ][ Data: char[] ]
// Small string (≤254 chars)
[ Length: u8 ][ Data: char[] ]
// Large string (≥255 chars)
[ Marker: 255 ][ Length: u16 ][ Data: char[] ]
fun get_string_length(str: *u8) -> u16 {
if (str[0] == 255) {
// u16 length: read next 2 bytes
return (str[2] << 8) | str[1];
} else {
// u8 length: just read first byte
return str[0] as u16;
}
}
fun get_string_data(str: *u8) -> *u8 {
if (str[0] == 255) {
return str + 3; // Skip marker + u16 length
} else {
return str + 1; // Skip u8 length
}
}
// In compiler
func (c *Compiler) optimizeIteration(arrayType Type, length int) IterPattern {
if length <= 255 && length > 0 {
return DJNZPattern // Use DJNZ for small arrays
} else if length > 255 {
return Counter16Pattern // Use 16-bit counter
} else {
return RuntimeDetection // Dynamic length detection
}
}func (c *Compiler) getAdvancementCode(elementSize int) string {
switch elementSize {
case 1: // u8, char
return "INC HL"
case 2: // u16, *T
return "INC HL\nINC HL"
case 4: // u32 (future)
return "LD DE, 4\nADD HL, DE"
default: // Variable size
return fmt.Sprintf("LD DE, %d\nADD HL, DE", elementSize)
}
}// Iterator chain fusion rules
type FusionRule struct {
CanUseSmallPattern bool // All arrays ≤255 elements
CanUse16BitPattern bool // Any arrays >255 elements
HasStringOps bool // Contains string operations
ElementSizeUniform bool // All same element size
}
func (r *FusionRule) SelectOptimalPattern() IterPattern {
if r.CanUseSmallPattern && r.ElementSizeUniform {
return DJNZFusedPattern // Optimal fusion
} else if r.CanUse16BitPattern {
return Counter16FusedPattern // Good fusion
} else {
return HybridFusedPattern // Mixed optimization
}
}// Game inventory (≤255 items)
let inventory: [Item; 64] = init_inventory();
inventory.filter!(|item| item.count > 0) // Remove empty slots
.map!(|item| item.update_durability()) // Update durability
.forEach(|item| draw_icon(item)); // Draw UI
// Compiles to single DJNZ loop!
// Particle system (>255 particles)
let particles: [Particle; 1000] = init_particles();
particles.filter!(|p| p.active) // Remove inactive
.map!(|p| p.update_physics(dt)) // Update physics
.filter!(|p| p.in_bounds(world)) // Bounds check
.forEach(|p| draw_particle(p)); // Render
// Compiles to optimized 16-bit counter loop!
// Text processing (length auto-detected)
let message: *u8 = get_user_input(); // Could be any length
message.chars() // Character iterator
.filter(|c| c.is_alphanumeric()) // Only letters/numbers
.map(|c| c.to_uppercase()) // Convert to uppercase
.forEach(|c| print_char(c)); // Display result
// Automatically uses DJNZ for short strings, 16-bit for long strings!
// Game entities with different sizes
let small_enemies: [Enemy; 32] = load_enemies(); // DJNZ
let large_bullets: [Bullet; 512] = load_bullets(); // 16-bit
let player_name: *u8 = get_player_name(); // Auto
// All use optimal iteration patterns automatically!
small_enemies.forEach(|e| e.update_ai());
large_bullets.forEach(|b| b.update_physics());
player_name.chars().forEach(|c| validate_char(c));
| Array Type | Length | Pattern | T-States/Element | Memory Usage |
|---|---|---|---|---|
| u8 array | ≤255 | DJNZ | 18-25 | Minimal |
| u16 array | >255 | 16-bit counter | 25-35 | Low |
| u8 string | ≤254 chars | DJNZ | 18-25 | Length + data |
| u16 string | ≥255 chars | 16-bit counter | 25-35 | 3 + data |
| Mixed | Variable | Hybrid | 20-30 avg | Adaptive |
// Optimal string layouts
Small string: [ 6 ][ H e l l o ! ] (7 bytes total)
Large string: [ 255 ][ 44 1 ][ ... 300 chars ... ] (304 bytes total)
// Array layouts
u8 array: [ data... ] (n bytes)
u16 array: [ data... ] (n*2 bytes)
- Implement u16 string length support
- Add string length auto-detection
- Create string character iterator
- Test with both small and large strings
- Add compile-time array size analysis
- Implement DJNZ vs 16-bit pattern selection
- Create fusion rules for mixed sizes
- Optimize element advancement
- Create generic Iterator[T] trait
- Implement size-aware implementations
- Add automatic pattern selection
- Test performance across all types
- Cross-array fusion optimization
- String-specific optimizations
- Memory layout improvements
- Benchmark against hand-written assembly
// Test all iteration patterns
fun test_small_arrays() {
let arr: [u8; 10] = [1,2,3,4,5,6,7,8,9,10];
arr.forEach(|x| assert(x > 0)); // Should use DJNZ
}
fun test_large_arrays() {
let arr: [u8; 500] = init_large_array();
arr.forEach(|x| process(x)); // Should use 16-bit counter
}
fun test_strings() {
let short: *u8 = "Hello!"; // Should use DJNZ
let long: *u8 = create_long_string(300); // Should use 16-bit
short.chars().forEach(|c| validate(c));
long.chars().forEach(|c| validate(c));
}
fun test_mixed_iteration() {
// Test arrays of different sizes in same program
test_small_arrays();
test_large_arrays();
test_strings();
}
# Verify optimal patterns are used
./minzc test_iteration.minz -o test.a80 --verbose-optimization
grep "DJNZ\|DEC BC" test.a80 # Should show appropriate patternsThis comprehensive system gives us:
- Automatic optimization - Compiler selects best pattern
- Universal interface - Same
.forEach()works on everything - Maximum performance - Each data type gets optimal code
- String power - Both u8 and u16 length strings supported
- Future-proof - Easy to add new data types
MinZ becomes the ONLY language with zero-cost iteration across all data types on 8-bit hardware! 🚀