retroshield_z80_workbench/stdlib/
math.rs

1//! Math and number conversion routines
2//!
3//! Provides routines for number printing, division, multiplication, etc.
4
5use crate::CodeGen;
6
7impl CodeGen {
8    /// Emit print_byte_dec routine - prints A as decimal
9    /// Always prints exactly what's needed, no leading zeros except for zero itself
10    ///
11    /// Labels created: `print_byte_dec`
12    /// Requires: `putchar`
13    pub fn emit_print_byte_dec(&mut self) {
14        self.label("print_byte_dec");
15        // Use stack to reverse digits
16        self.ld_c(0);            // Digit count
17
18        let extract_loop = self.unique_label("pbd_ext");
19        self.label(&extract_loop);
20        self.ld_b(0);            // Quotient
21        let div_loop = self.unique_label("pbd_div");
22        self.label(&div_loop);
23        self.cp(10);
24        let div_done = self.unique_label("pbd_ddone");
25        self.jp_c(&div_done);
26        self.sub_a(10);
27        self.inc_b();
28        self.jp(&div_loop);
29
30        self.label(&div_done);
31        // A = remainder (digit), B = quotient
32        self.add_a(b'0');
33        self.push_af();
34        self.inc_c();
35        self.ld_a_b();
36        self.or_a_a();
37        self.jp_nz(&extract_loop);
38
39        // Pop and print digits
40        let print_loop = self.unique_label("pbd_print");
41        self.label(&print_loop);
42        self.pop_af();
43        self.call("putchar");
44        self.dec_c();
45        self.jp_nz(&print_loop);
46        self.ret();
47    }
48
49    /// Emit div16 routine - 16-bit division HL / DE -> HL quotient, DE remainder
50    ///
51    /// Labels created: `div16`, `div16_loop`, `div16_done`
52    pub fn emit_div16(&mut self) {
53        self.label("div16");
54        self.ld_bc(0);           // BC = quotient
55
56        self.label("div16_loop");
57        self.or_a_a();           // Clear carry
58        self.sbc_hl_de();        // HL = HL - DE
59        self.jp_c("div16_done");
60        self.inc_bc();
61        self.jp("div16_loop");
62
63        self.label("div16_done");
64        self.add_hl_de();        // Restore remainder to HL
65        self.ex_de_hl();         // DE = remainder
66        // Move BC to HL
67        self.emit(&[0x60]);      // LD H, B
68        self.emit(&[0x69]);      // LD L, C
69        self.ret();
70    }
71
72    /// Emit mul8 routine - 8-bit multiply A * B -> HL
73    ///
74    /// Labels created: `mul8`, `mul8_loop`
75    pub fn emit_mul8(&mut self) {
76        self.label("mul8");
77        self.ld_hl(0);
78        self.or_a_a();
79        self.ret_z();            // A * 0 = 0
80
81        self.ld_c_a();
82        self.ld_a_b();
83        self.or_a_a();
84        self.ret_z();            // 0 * B = 0
85
86        // HL = 0, C = multiplicand, B = multiplier
87        self.label("mul8_loop");
88        self.emit(&[0x79]);      // LD A, C
89        self.emit(&[0x85]);      // ADD A, L
90        self.emit(&[0x6F]);      // LD L, A
91        self.emit(&[0x30, 0x01]); // JR NC, +1
92        self.emit(&[0x24]);      // INC H
93        self.djnz("mul8_loop");
94        self.ret();
95    }
96
97    /// Emit negate_hl routine - negate HL (two's complement)
98    ///
99    /// Labels created: `negate_hl`
100    pub fn emit_negate_hl(&mut self) {
101        self.label("negate_hl");
102        self.emit(&[0x7C]);      // LD A, H
103        self.cpl();
104        self.emit(&[0x67]);      // LD H, A
105        self.emit(&[0x7D]);      // LD A, L
106        self.cpl();
107        self.emit(&[0x6F]);      // LD L, A
108        self.inc_hl();
109        self.ret();
110    }
111
112    /// Emit all math routines
113    pub fn emit_math_routines(&mut self) {
114        self.emit_print_byte_dec();
115        self.emit_div16();
116        self.emit_negate_hl();
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_div16_emits() {
126        let mut cg = CodeGen::new();
127        cg.emit_div16();
128        assert!(cg.has_label("div16"));
129        assert!(cg.has_label("div16_loop"));
130    }
131}