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}