iz80/
opcode_ld.rs

1use super::opcode::Opcode;
2use super::environment::Environment;
3use super::registers::{Flag, Reg16, Reg8};
4
5/*
6    Load: http://z80-heaven.wikidot.com/instructions-set:ld
7
8    Flags:
9        No flags are altered except in the cases of the I or R registers.
10        In those cases, C is preserved, H and N are reset, and alters Z
11        and S. P/V is set if interrupts are enabled, reset otherwise.
12
13    Variants:
14        r, r'       4 - Done
15        r, X        7 - Done
16        r, (hl)     7 - Done
17        r, (ix+X)   19
18        r, (iy+X)   19
19
20        a, (BC)     7 - Done
21        a, (DE)     7 - Done
22        a, (XX)     13 - Done
23        (BC), a     7 - Done
24        (DE), a     7 - Done
25        (XX), a     13 - Done
26
27        a, i        9 - Done
28        a, r        9 - Done
29        i, a        9 - Done
30        r, a        9 - Done
31
32        rr, XX      10 - Done
33        ix, XX      14
34        iy, XX      14
35
36        rr, (XX)    20 - Done
37        hl, (XX)    20 - Done
38        ix, (XX)    20
39        iy, (XX)    20
40        (XX), rr    20 - DONE
41        (XX), hl    20 - Done
42        (XX), ix    20
43        (XX), iy    20
44
45        sp, hl      6 - Done
46        sp, ix      10
47        sp, iy      10
48
49        TODO: ix and iy based opcodes-
50*/
51
52// 8 bit load
53pub fn build_ld_r_r(dst: Reg8, src: Reg8, _special: bool) -> Opcode {
54    if src != Reg8::_HL && dst != Reg8::_HL
55            && src != Reg8::H && dst != Reg8::H
56            && src != Reg8::L && dst != Reg8::L {
57        // Faster version
58        Opcode::new(
59            format!("LD {dst}, {src}"),
60            move |env: &mut Environment| {
61                let value = env.state.reg.get8(src);
62                env.state.reg.set8(dst, value);
63                if dst == Reg8::A && (src == Reg8::I || src == Reg8::R) {
64                    // LDA A,I and LDA A,R copy the IFF2 flag into the P flag
65                    env.state.reg.update_p_flag_with_iff2();
66                }
67            }
68        )
69    } else {
70        // Full version
71        Opcode::new(
72            format!("LD {dst}, {src}"),
73            move |env: &mut Environment| {
74                /*
75                If the next opcode makes use of (HL), it will be replaced by (IX+d), and any other
76                instances of H and L will be unaffected. Therefore, an instruction like LD IXH, (IX+d)
77                does not exist, but LD H, (IX+d) does. It's impossible for both src and dst to be (HL)
78                */
79                let value = if dst == Reg8::_HL {
80                    env.state.reg.get8(src)
81                } else {
82                    env.reg8_ext(src)
83                };
84                if src == Reg8::_HL {
85                    env.state.reg.set8(dst, value);
86                } else {
87                    env.set_reg(dst, value);
88                }
89            }
90        )
91    }
92}
93
94pub fn build_ld_r_n(r: Reg8) -> Opcode {
95    Opcode::new(
96        format!("LD {r}, n"),
97        move |env: &mut Environment| {
98            let value = env.advance_pc();
99            env.set_reg(r, value);
100        }
101    )
102}
103
104pub fn build_ld_a_prr(rr: Reg16) -> Opcode {
105    // rr can be only BC or DE
106    Opcode::new(
107        format!("LD A, ({rr:?})"),
108        move |env: &mut Environment| {
109            let address = env.state.reg.get16(rr);
110            let value = env.sys.peek(address);
111            env.state.reg.set_a(value);
112        }
113    )
114}
115
116pub fn build_ld_a_pnn() -> Opcode {
117    Opcode::new(
118        "LD A, (nn)".to_string(),
119        |env: &mut Environment| {
120            let address = env.advance_immediate16();
121            let value = env.sys.peek(address);
122            env.state.reg.set_a(value);
123        }
124    )
125}
126
127pub fn build_ld_prr_a(rr: Reg16) -> Opcode {
128    // rr can be only BC or DE
129    Opcode::new(
130        format!("LD ({rr:?}), A"),
131        move |env: &mut Environment| {
132            let value = env.state.reg.a();
133            let address = env.state.reg.get16(rr);
134            env.sys.poke(address, value);
135        }
136    )
137}
138
139pub fn build_ld_pnn_a() -> Opcode {
140    Opcode::new(
141        "LD (nn), A".to_string(),
142        |env: &mut Environment| {
143            let value = env.state.reg.a();
144            let address = env.advance_immediate16();
145            env.sys.poke(address, value);
146        }
147    )
148}
149
150
151// 16 bit load
152pub fn build_ld_rr_nn(rr: Reg16) -> Opcode {
153    Opcode::new(
154        format!("LD {rr:?}, nn"),
155        move |env: &mut Environment| {
156            let value = env.advance_immediate16();
157            env.set_reg16(rr, value);
158        }
159    )
160}
161
162pub fn build_ld_sp_hl() -> Opcode {
163    Opcode::new(
164        "LD SP, HL".to_string(),
165        |env: &mut Environment| {
166            let value = env.reg16_ext(Reg16::HL);
167            env.set_reg16(Reg16::SP, value);
168        }
169    )
170}
171
172pub fn build_ld_pnn_rr(rr: Reg16, _fast: bool) -> Opcode {
173    Opcode::new(
174        format!("LD (nn), {rr:?}"),
175        move |env: &mut Environment| {
176            let address = env.advance_immediate16();
177            let value = env.reg16_ext(rr);
178            env.sys.poke16(address, value);
179        }
180    )
181}
182
183pub fn build_ld_rr_pnn(rr: Reg16, _fast: bool) -> Opcode {
184    Opcode::new(
185        format!("LD {rr:?}, (nn)"),
186        move |env: &mut Environment| {
187            let address = env.advance_immediate16();
188            let value = env.sys.peek16(address);
189            env.set_reg16(rr, value);
190        }
191    )
192}
193
194pub fn build_ex_af() -> Opcode {
195    Opcode::new(
196        "EX AF, AF'".to_string(),
197        |env: &mut Environment| {
198            env.state.reg.swap(Reg16::AF);
199        }
200    )
201}
202
203pub fn build_exx() -> Opcode {
204    Opcode::new(
205        "EXX".to_string(),
206        |env: &mut Environment| {
207            env.state.reg.swap(Reg16::BC);
208            env.state.reg.swap(Reg16::DE);
209            env.state.reg.swap(Reg16::HL); // NO IX, IY variant
210        }
211    )
212}
213
214pub fn build_ex_de_hl() -> Opcode {
215    Opcode::new(
216        "EX DE, HL".to_string(),
217        |env: &mut Environment| {
218            let temp = env.state.reg.get16(Reg16::HL); // No IX/IY variant
219            env.state.reg.set16(Reg16::HL, env.state.reg.get16(Reg16::DE));
220            env.state.reg.set16(Reg16::DE, temp);
221        }     
222    )
223}
224
225pub fn build_ex_psp_hl() -> Opcode {
226    Opcode::new(
227        "EX (SP), HL".to_string(),
228        |env: &mut Environment| {
229            let address = env.state.reg.get16(Reg16::SP);
230
231            let temp = env.reg16_ext(Reg16::HL);
232            env.set_reg16(Reg16::HL, env.sys.peek16(address));
233            env.sys.poke16(address, temp);
234        }
235    )
236}
237
238pub fn build_ld_block((inc, repeat, postfix) : (bool, bool, &'static str)) -> Opcode {
239    Opcode::new(
240        format!("LD{postfix}"),
241        move |env: &mut Environment| {
242            let value = env.reg8_ext(Reg8::_HL);
243            let address = env.state.reg.get16(Reg16::DE);
244            env.sys.poke(address, value);
245
246            env.state.reg.inc_dec16(Reg16::DE, inc);
247            env.state.reg.inc_dec16(Reg16::HL, inc);
248            let bc = env.state.reg.inc_dec16(Reg16::BC, false /*decrement*/);
249
250            // TUZD-4.2
251            let n = value.wrapping_add(env.state.reg.a());
252            env.state.reg.update_undocumented_flags_block(n);
253            env.state.reg.clear_flag(Flag::N);
254            env.state.reg.clear_flag(Flag::H);
255            env.state.reg.put_flag(Flag::P, bc != 0);
256            // S, Z and C unchanged. What about N?
257
258            if repeat && bc != 0 {
259                // Back to redo the instruction
260                env.set_branch_taken();
261                let pc = env.state.reg.pc().wrapping_sub(2);
262                env.state.reg.set_pc(pc);
263            }
264        }        
265    )
266}