gabbro/cpu/instructions/
helpers.rs

1use crate::{
2    cpu::Cpu,
3    peripherals::{Cable, Joypad, Lcd, Speaker},
4};
5
6/// For invalid instructions.
7pub fn invalid() {
8    log::error!("Encountered an invalid instruction.");
9    panic!("Aborting");
10}
11
12/// Jump to address `addr`.
13pub fn jp<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, addr: u16)
14where
15    L: Lcd,
16    S: Speaker,
17    J: Joypad,
18    C: Cable,
19{
20    cpu.regs.set_pc(addr);
21}
22
23/// Jump to relative address `PC + val`.
24pub fn jr<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: i8)
25where
26    L: Lcd,
27    S: Speaker,
28    J: Joypad,
29    C: Cable,
30{
31    cpu.regs.set_pc(cpu.regs.pc().wrapping_add(val as u16));
32}
33
34/// Push `PC` to the stack, and jump to address `addr`. Takes two machine cycles.
35pub fn call<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, addr: u16)
36where
37    L: Lcd,
38    S: Speaker,
39    J: Joypad,
40    C: Cable,
41{
42    cpu.stack_push(cpu.regs.pc());
43    jp(cpu, addr);
44}
45
46/// Pop a value from the stack and jump to it. Takes two machine cycles.
47pub fn ret<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>)
48where
49    L: Lcd,
50    S: Speaker,
51    J: Joypad,
52    C: Cable,
53{
54    let addr = cpu.stack_pop();
55    jp(cpu, addr);
56}
57
58/// Calculates the binary-coded decimal of `A` right after an addition or subtraction.
59/// Flags: `Z-0C`.
60pub fn daa<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>)
61where
62    L: Lcd,
63    S: Speaker,
64    J: Joypad,
65    C: Cable,
66{
67    let mut res = cpu.regs.a() as u16;
68    if !cpu.regs.flags().n() {
69        if cpu.regs.flags().h() || (res & 0x0f) > 0x09 {
70            res = res.wrapping_add(0x06);
71        }
72        if cpu.regs.flags().c() || res > 0x9f {
73            res = res.wrapping_add(0x60);
74            cpu.regs.flags_mut().set_c(true);
75        }
76    } else {
77        if cpu.regs.flags().h() {
78            res = res.wrapping_sub(0x06);
79        }
80        if cpu.regs.flags().c() {
81            res = res.wrapping_sub(0x60);
82        }
83    }
84
85    cpu.regs.flags_mut().set_z(res as u8 == 0);
86    cpu.regs.flags_mut().set_h(false);
87
88    cpu.regs.set_a(res as u8);
89}
90
91/// Flips the bits of `A`.
92/// Flags: `-11-`.
93pub fn cpl<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>)
94where
95    L: Lcd,
96    S: Speaker,
97    J: Joypad,
98    C: Cable,
99{
100    cpu.regs.flags_mut().set_n(true);
101    cpu.regs.flags_mut().set_h(true);
102
103    cpu.regs.set_a(!cpu.regs.a());
104}
105
106/// Sets the carry flag
107/// Flags: `-001`.
108pub fn scf<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>)
109where
110    L: Lcd,
111    S: Speaker,
112    J: Joypad,
113    C: Cable,
114{
115    cpu.regs.flags_mut().set_n(false);
116    cpu.regs.flags_mut().set_h(false);
117    cpu.regs.flags_mut().set_c(true);
118}
119
120/// Flips the carry flag
121/// Flags: `-00C`.
122pub fn ccf<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>)
123where
124    L: Lcd,
125    S: Speaker,
126    J: Joypad,
127    C: Cable,
128{
129    let c = cpu.regs.flags().c();
130
131    cpu.regs.flags_mut().set_n(false);
132    cpu.regs.flags_mut().set_h(false);
133    cpu.regs.flags_mut().set_c(!c);
134}
135
136/// Returns `val + 1`.
137/// Flags: `Z0H-`.
138pub fn inc<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
139where
140    L: Lcd,
141    S: Speaker,
142    J: Joypad,
143    C: Cable,
144{
145    let res = val.wrapping_add(1);
146
147    cpu.regs.flags_mut().set_z(res == 0);
148    cpu.regs.flags_mut().set_n(false);
149    cpu.regs.flags_mut().set_h((val & 0x0f) == 0x0f);
150
151    res
152}
153
154/// Returns `val - 1`.
155/// Flags: `Z1H-`.
156pub fn dec<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
157where
158    L: Lcd,
159    S: Speaker,
160    J: Joypad,
161    C: Cable,
162{
163    let res = val.wrapping_sub(1);
164
165    cpu.regs.flags_mut().set_z(res == 0);
166    cpu.regs.flags_mut().set_n(true);
167    cpu.regs.flags_mut().set_h((val & 0x0f) == 0x00);
168
169    res
170}
171
172/// Adds `val` to `A`.
173/// Flags: `Z0HC`.
174pub fn add<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8)
175where
176    L: Lcd,
177    S: Speaker,
178    J: Joypad,
179    C: Cable,
180{
181    let a = cpu.regs.a();
182    let res = a as u16 + val as u16;
183
184    cpu.regs.flags_mut().set_z(res as u8 == 0);
185    cpu.regs.flags_mut().set_n(false);
186    cpu.regs.flags_mut().set_h((a & 0x0f) + (val & 0x0f) > 0x0f);
187    cpu.regs.flags_mut().set_c(res > 0xff);
188
189    cpu.regs.set_a(res as u8);
190}
191
192/// Adds `val` to `HL`.
193/// Flags: `-0HC`.
194pub fn add_hl<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u16)
195where
196    L: Lcd,
197    S: Speaker,
198    J: Joypad,
199    C: Cable,
200{
201    let hl = cpu.regs.hl();
202    let res = hl as u32 + val as u32;
203
204    cpu.regs.flags_mut().set_n(false);
205    cpu.regs
206        .flags_mut()
207        .set_h((hl & 0xfff) + (val & 0xfff) > 0xfff);
208    cpu.regs.flags_mut().set_c(res > 0xffff);
209
210    cpu.regs.set_hl(res as u16);
211}
212
213/// Returns `SP + val`.
214/// Flags: `00HC`.
215pub fn add_sp<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: i8) -> u16
216where
217    L: Lcd,
218    S: Speaker,
219    J: Joypad,
220    C: Cable,
221{
222    let uval = val as u16;
223    let sp = cpu.regs.sp();
224    let res = sp.wrapping_add(uval);
225
226    cpu.regs.flags_mut().set_z(false);
227    cpu.regs.flags_mut().set_n(false);
228    cpu.regs
229        .flags_mut()
230        .set_h((sp & 0x0f) + (uval & 0x0f) > 0x0f);
231    cpu.regs
232        .flags_mut()
233        .set_c((sp & 0xff) + (uval & 0xff) > 0xff);
234
235    res
236}
237
238/// Adds `val` and the carry flag to `A`.
239/// Flags: `Z0HC`.
240pub fn adc<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8)
241where
242    L: Lcd,
243    S: Speaker,
244    J: Joypad,
245    C: Cable,
246{
247    let a = cpu.regs.a();
248    let c = cpu.regs.flags().c() as u8;
249    let res = a as u16 + val as u16 + c as u16;
250
251    cpu.regs.flags_mut().set_z(res as u8 == 0);
252    cpu.regs.flags_mut().set_n(false);
253    cpu.regs
254        .flags_mut()
255        .set_h((a & 0x0f) + (val & 0x0f) + c > 0x0f);
256    cpu.regs.flags_mut().set_c(res > 0xff);
257
258    cpu.regs.set_a(res as u8);
259}
260
261/// Subtracts `val` from `A`.
262/// Flags: `Z1HC`.
263pub fn sub<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8)
264where
265    L: Lcd,
266    S: Speaker,
267    J: Joypad,
268    C: Cable,
269{
270    let a = cpu.regs.a();
271    let res = a.wrapping_sub(val);
272
273    cpu.regs.flags_mut().set_z(res == 0);
274    cpu.regs.flags_mut().set_n(true);
275    cpu.regs.flags_mut().set_h((a & 0x0f) < (val & 0x0f));
276    cpu.regs.flags_mut().set_c(a < val);
277
278    cpu.regs.set_a(res);
279}
280
281/// Subtracts `val` and the carry flag from `A`.
282/// Flags: `Z1HC`.
283pub fn sbc<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8)
284where
285    L: Lcd,
286    S: Speaker,
287    J: Joypad,
288    C: Cable,
289{
290    let a = cpu.regs.a();
291    let c = cpu.regs.flags().c() as u8;
292    let res = (a as u16).wrapping_sub(val as u16).wrapping_sub(c as u16);
293
294    cpu.regs.flags_mut().set_z(res as u8 == 0);
295    cpu.regs.flags_mut().set_n(true);
296    cpu.regs.flags_mut().set_h((a & 0x0f) < ((val & 0x0f) + c));
297    cpu.regs.flags_mut().set_c(res > 0xff);
298
299    cpu.regs.set_a(res as u8);
300}
301
302/// Sets `A` to the bitwise AND of `A` and `val`.
303/// Flags: `Z010`.
304pub fn and<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8)
305where
306    L: Lcd,
307    S: Speaker,
308    J: Joypad,
309    C: Cable,
310{
311    let a = cpu.regs.a();
312    let res = a & val;
313
314    cpu.regs.flags_mut().set_z(res == 0);
315    cpu.regs.flags_mut().set_n(false);
316    cpu.regs.flags_mut().set_h(true);
317    cpu.regs.flags_mut().set_c(false);
318
319    cpu.regs.set_a(res);
320}
321
322/// Sets `A` to the bitwise XOR of `A` and `val`.
323/// Flags: `Z000`.
324pub fn xor<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8)
325where
326    L: Lcd,
327    S: Speaker,
328    J: Joypad,
329    C: Cable,
330{
331    let a = cpu.regs.a();
332    let res = a ^ val;
333
334    cpu.regs.flags_mut().set_z(res == 0);
335    cpu.regs.flags_mut().set_n(false);
336    cpu.regs.flags_mut().set_h(false);
337    cpu.regs.flags_mut().set_c(false);
338
339    cpu.regs.set_a(res);
340}
341
342/// Sets `A` to the bitwise OR of `A` and `val`.
343/// Flags: `Z000`.
344pub fn or<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8)
345where
346    L: Lcd,
347    S: Speaker,
348    J: Joypad,
349    C: Cable,
350{
351    let a = cpu.regs.a();
352    let res = a | val;
353
354    cpu.regs.flags_mut().set_z(res == 0);
355    cpu.regs.flags_mut().set_n(false);
356    cpu.regs.flags_mut().set_h(false);
357    cpu.regs.flags_mut().set_c(false);
358
359    cpu.regs.set_a(res);
360}
361
362/// Compares `A` and `val`, and sets flags accordingly.
363/// Flags are set as if `val` is subtracted from `A`.
364/// Flags: `Z1HC`.
365pub fn cp<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8)
366where
367    L: Lcd,
368    S: Speaker,
369    J: Joypad,
370    C: Cable,
371{
372    let a = cpu.regs.a();
373
374    cpu.regs.flags_mut().set_z(a == val);
375    cpu.regs.flags_mut().set_n(true);
376    cpu.regs.flags_mut().set_h((a & 0x0f) < (val & 0x0f));
377    cpu.regs.flags_mut().set_c(a < val);
378}
379
380/// Rotate `val` to the left once.
381/// Flags: `Z00C`.
382pub fn rlc<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
383where
384    L: Lcd,
385    S: Speaker,
386    J: Joypad,
387    C: Cable,
388{
389    let bit = val >> 7;
390    let res = (val << 1) | bit;
391
392    cpu.regs.flags_mut().set_z(res == 0);
393    cpu.regs.flags_mut().set_n(false);
394    cpu.regs.flags_mut().set_h(false);
395    cpu.regs.flags_mut().set_c(bit != 0);
396
397    res
398}
399
400/// Rotate `val` to the right once.
401/// Flags: `Z00C`.
402pub fn rrc<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
403where
404    L: Lcd,
405    S: Speaker,
406    J: Joypad,
407    C: Cable,
408{
409    let bit = val & 0x01;
410    let res = (val >> 1) | (bit << 7);
411
412    cpu.regs.flags_mut().set_z(res == 0);
413    cpu.regs.flags_mut().set_n(false);
414    cpu.regs.flags_mut().set_h(false);
415    cpu.regs.flags_mut().set_c(bit != 0);
416
417    res
418}
419
420/// Rotate `val` to the left once, through the carry flag.
421/// Flags: `Z00C`.
422pub fn rl<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
423where
424    L: Lcd,
425    S: Speaker,
426    J: Joypad,
427    C: Cable,
428{
429    let c = cpu.regs.flags().c() as u8;
430    let bit = val >> 7;
431    let res = (val << 1) | c;
432
433    cpu.regs.flags_mut().set_z(res == 0);
434    cpu.regs.flags_mut().set_n(false);
435    cpu.regs.flags_mut().set_h(false);
436    cpu.regs.flags_mut().set_c(bit != 0);
437
438    res
439}
440
441/// Rotate `val` to the right once, through the carry flag.
442/// Flags: `Z00C`.
443pub fn rr<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
444where
445    L: Lcd,
446    S: Speaker,
447    J: Joypad,
448    C: Cable,
449{
450    let c = cpu.regs.flags().c() as u8;
451    let bit = val & 0x01;
452    let res = (val >> 1) | (c << 7);
453
454    cpu.regs.flags_mut().set_z(res == 0);
455    cpu.regs.flags_mut().set_n(false);
456    cpu.regs.flags_mut().set_h(false);
457    cpu.regs.flags_mut().set_c(bit != 0);
458
459    res
460}
461
462/// Shift `val` to the left once, into the carry flag.
463/// Least significant bit is set to 0.
464/// Flags: `Z00C`.
465pub fn sla<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
466where
467    L: Lcd,
468    S: Speaker,
469    J: Joypad,
470    C: Cable,
471{
472    let bit = val >> 7;
473    let res = val << 1;
474
475    cpu.regs.flags_mut().set_z(res == 0);
476    cpu.regs.flags_mut().set_n(false);
477    cpu.regs.flags_mut().set_h(false);
478    cpu.regs.flags_mut().set_c(bit != 0);
479
480    res
481}
482
483/// Shift `val` to the right once, into the carry flag.
484/// Most significant bit does not change.
485/// Flags: `Z00C`.
486pub fn sra<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
487where
488    L: Lcd,
489    S: Speaker,
490    J: Joypad,
491    C: Cable,
492{
493    let bit = val & 0x01;
494    let res = (val & 0x80) | (val >> 1);
495
496    cpu.regs.flags_mut().set_z(res == 0);
497    cpu.regs.flags_mut().set_n(false);
498    cpu.regs.flags_mut().set_h(false);
499    cpu.regs.flags_mut().set_c(bit != 0);
500
501    res
502}
503
504/// Swaps the upper and lower nibble of `val`.
505/// Flags: `Z000`.
506pub fn swap<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
507where
508    L: Lcd,
509    S: Speaker,
510    J: Joypad,
511    C: Cable,
512{
513    let res = (val << 4) | (val >> 4);
514
515    cpu.regs.flags_mut().set_z(res == 0);
516    cpu.regs.flags_mut().set_n(false);
517    cpu.regs.flags_mut().set_h(false);
518    cpu.regs.flags_mut().set_c(false);
519
520    res
521}
522
523/// Shift `val` to the right once, into the carry flag.
524/// Most significant bit is set to 0.
525/// Flags: `Z00C`.
526pub fn srl<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, val: u8) -> u8
527where
528    L: Lcd,
529    S: Speaker,
530    J: Joypad,
531    C: Cable,
532{
533    let bit = val & 0x01;
534    let res = val >> 1;
535
536    cpu.regs.flags_mut().set_z(res == 0);
537    cpu.regs.flags_mut().set_n(false);
538    cpu.regs.flags_mut().set_h(false);
539    cpu.regs.flags_mut().set_c(bit != 0);
540
541    res
542}
543
544/// Checks if the `bit`th bit of `val` is set,
545/// and sets the zero flag accordingly.
546/// Flags: `Z01-`.
547pub fn bit<L, S, J, C>(cpu: &mut Cpu<L, S, J, C>, bit: u8, val: u8)
548where
549    L: Lcd,
550    S: Speaker,
551    J: Joypad,
552    C: Cable,
553{
554    cpu.regs.flags_mut().set_z((val & (1 << bit)) == 0);
555    cpu.regs.flags_mut().set_n(false);
556    cpu.regs.flags_mut().set_h(true);
557}
558
559/// Resets the `bit`th bit of val.
560/// Flags: `----`.
561pub fn res(bit: u8, val: u8) -> u8 {
562    val & !(1 << bit)
563}
564
565/// Sets the `bit`th bit of val.
566/// Flags: `----`.
567pub fn set(bit: u8, val: u8) -> u8 {
568    val | (1 << bit)
569}