retroshield_z80_workbench/
lib.rs

1//! RetroShield Z80 Workbench
2//!
3//! A framework for building Z80 ROMs for RetroShield projects.
4//!
5//! # Example
6//!
7//! ```rust
8//! use retroshield_z80_workbench::prelude::*;
9//!
10//! let mut rom = CodeGen::new();
11//!
12//! // Setup
13//! rom.label("start");
14//! rom.ld_sp(0x3FFF);
15//! rom.di();
16//!
17//! // Include standard I/O
18//! rom.emit_io_routines();
19//! rom.emit_terminal_routines();
20//! rom.emit_math_routines();
21//!
22//! // Your code here
23//! rom.label("main");
24//! rom.call("clear_screen");
25//! rom.ld_hl_label("hello_msg");
26//! rom.call("print_string");
27//! rom.jp("main");
28//!
29//! // Data
30//! rom.label("hello_msg");
31//! rom.emit_string("Hello, RetroShield!\r\n");
32//!
33//! // Finalize and write
34//! rom.resolve_fixups();
35//! rom.write_bin("output.bin").unwrap();
36//! ```
37//!
38//! # Module Structure
39//!
40//! - `codegen` - Core emit/label/fixup machinery
41//! - `instructions` - Z80 instruction helpers
42//! - `stdlib::io` - MC6850 serial I/O routines
43//! - `stdlib::terminal` - VT100/ANSI terminal sequences
44//! - `stdlib::math` - Number conversion and math routines
45
46mod codegen;
47mod instructions;
48pub mod stdlib;
49
50pub use codegen::{CodeGen, RomConfig};
51
52/// Prelude - import this for convenient access to common types
53pub mod prelude {
54    pub use crate::codegen::{CodeGen, RomConfig};
55}
56
57/// Convenience extension methods for CodeGen
58impl CodeGen {
59    /// Standard RetroShield startup sequence
60    /// Sets up stack pointer and disables interrupts
61    pub fn emit_startup(&mut self, stack_top: u16) {
62        self.label("_start");
63        self.di();
64        self.ld_sp(stack_top);
65    }
66
67    /// Load HL with address of a label (for string pointers, etc.)
68    pub fn ld_hl_label(&mut self, label: &str) {
69        self.emit(&[0x21]); // LD HL, nn
70        self.fixup(label);
71    }
72
73    /// Load DE with address of a label
74    pub fn ld_de_label(&mut self, label: &str) {
75        self.emit(&[0x11]); // LD DE, nn
76        self.fixup(label);
77    }
78
79    /// Load BC with address of a label
80    pub fn ld_bc_label(&mut self, label: &str) {
81        self.emit(&[0x01]); // LD BC, nn
82        self.fixup(label);
83    }
84
85    /// Emit a labeled string constant
86    pub fn string_const(&mut self, label: &str, s: &str) {
87        self.label(label);
88        self.emit_string(s);
89    }
90
91    /// Include all standard library routines
92    /// This is a convenience method that includes:
93    /// - I/O routines (getchar, putchar, newline, print_string)
94    /// - Terminal routines (clear_screen, cursor_pos, etc.)
95    /// - Math routines (print_byte_dec, div16, etc.)
96    pub fn include_stdlib(&mut self) {
97        self.emit_io_routines();
98        self.emit_terminal_routines();
99        self.emit_math_routines();
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_basic_program() {
109        let mut rom = CodeGen::new();
110
111        rom.emit_startup(0x3FFF);
112        rom.label("main");
113        rom.halt();
114
115        rom.resolve_fixups();
116        assert!(rom.size() > 0);
117    }
118
119    #[test]
120    fn test_with_stdlib() {
121        let mut rom = CodeGen::new();
122
123        rom.emit_startup(0x3FFF);
124
125        // Main code
126        rom.label("main");
127        rom.call("clear_screen");
128        rom.ld_hl_label("msg");
129        rom.call("print_string");
130        rom.jp("main");
131
132        // Data
133        rom.string_const("msg", "Hello!\r\n");
134
135        // Include stdlib (must come after main code to not disrupt flow)
136        rom.include_stdlib();
137
138        rom.resolve_fixups();
139        println!("ROM size: {} bytes", rom.size());
140    }
141}