boytacean/
cpu.rs

1//! Implementation of the core CPU ([Sharp LR35902](https://en.wikipedia.org/wiki/Game_Boy)) logic for the Game Boy.
2//!
3//! Does not include the instruction set implementation, only the core
4//! CPU logic and the CPU struct definition.
5//!
6//! Most of the core CPU logic is implemented in the [`Cpu::clock`] method.
7
8use boytacean_common::{
9    data::{read_u16, read_u8, write_u16, write_u8},
10    error::Error,
11    util::SharedThread,
12};
13use std::{
14    fmt::{self, Display, Formatter},
15    io::Cursor,
16    sync::Mutex,
17};
18
19use crate::{
20    apu::Apu,
21    assert_pedantic_gb,
22    consts::LCDC_ADDR,
23    debugln,
24    dma::Dma,
25    gb::GameBoyConfig,
26    inst::{EXTENDED, INSTRUCTIONS},
27    mmu::Mmu,
28    pad::Pad,
29    ppu::Ppu,
30    serial::Serial,
31    state::{StateComponent, StateFormat},
32    timer::Timer,
33};
34
35pub const PREFIX: u8 = 0xcb;
36
37pub type Instruction = &'static (fn(&mut Cpu), u8, &'static str);
38
39pub struct Cpu {
40    pub pc: u16,
41    pub sp: u16,
42    pub a: u8,
43    pub b: u8,
44    pub c: u8,
45    pub d: u8,
46    pub e: u8,
47    pub h: u8,
48    pub l: u8,
49
50    ime: bool,
51    zero: bool,
52    sub: bool,
53    half_carry: bool,
54    carry: bool,
55    halted: bool,
56
57    /// Reference to the MMU (Memory Management Unit) to be used
58    /// for memory bus access operations.
59    pub mmu: Mmu,
60
61    /// Temporary counter used to control the number of cycles
62    /// taken by the current or last CPU operation.
63    pub cycles: u8,
64
65    /// Reference to the PC (Program Counter) of the previous executed
66    /// instruction, used to provide a reference to the instruction
67    /// so that it can be logged or used for debugging purposes.
68    pub ppc: u16,
69
70    /// The pointer to the parent configuration of the running
71    /// Game Boy emulator, that can be used to control the behaviour
72    /// of Game Boy emulation.
73    gbc: SharedThread<GameBoyConfig>,
74}
75
76impl Cpu {
77    pub fn new(mmu: Mmu, gbc: SharedThread<GameBoyConfig>) -> Self {
78        Self {
79            pc: 0x0,
80            sp: 0x0,
81            a: 0x0,
82            b: 0x0,
83            c: 0x0,
84            d: 0x0,
85            e: 0x0,
86            h: 0x0,
87            l: 0x0,
88            ime: false,
89            zero: false,
90            sub: false,
91            half_carry: false,
92            carry: false,
93            halted: false,
94            mmu,
95            cycles: 0,
96            ppc: 0x0,
97            gbc,
98        }
99    }
100
101    pub fn reset(&mut self) {
102        self.pc = 0x0;
103        self.sp = 0x0;
104        self.a = 0x0;
105        self.b = 0x0;
106        self.c = 0x0;
107        self.d = 0x0;
108        self.e = 0x0;
109        self.h = 0x0;
110        self.l = 0x0;
111        self.ime = false;
112        self.zero = false;
113        self.sub = false;
114        self.half_carry = false;
115        self.carry = false;
116        self.halted = false;
117        self.cycles = 0;
118    }
119
120    /// Sets the CPU registers and some of the memory space to the
121    /// expected state after a typical Game Boy boot ROM finishes.
122    ///
123    /// Using this strategy it's possible to skip the "normal" boot
124    /// loading process for the original DMG Game Boy.
125    pub fn boot(&mut self) {
126        self.pc = 0x0100;
127        self.sp = 0xfffe;
128        self.a = 0x01;
129        self.b = 0xff;
130        self.c = 0x13;
131        self.d = 0x00;
132        self.e = 0xc1;
133        self.h = 0x84;
134        self.l = 0x03;
135        self.zero = false;
136        self.sub = false;
137        self.half_carry = false;
138        self.carry = false;
139
140        // updates part of the MMU state, disabling the
141        // boot memory overlap and setting the LCD control
142        // register to enabled (required by some ROMs)
143        self.mmu.set_boot_active(false);
144        self.mmu.write(LCDC_ADDR, 0x91);
145    }
146
147    pub fn clock(&mut self) -> u8 {
148        // gathers the PC (program counter) reference that
149        // is going to be used in the fetching phase
150        let pc = self.pc;
151
152        // runs a series of assertions to guarantee CPU execution
153        // state, only if pedantic mode is set
154        assert_pedantic_gb!(
155            !(0x8000..=0x9fff).contains(&pc),
156            "Invalid PC area at 0x{:04x}",
157            pc
158        );
159        assert_pedantic_gb!(
160            !self.mmu.boot_active() || pc <= 0x08ff,
161            "Invalid boot address: {:04x}",
162            pc
163        );
164
165        // @TODO this is so bad, need to improve this by an order
166        // of magnitude, to be able to have better performance
167        // in case the CPU execution halted and there's an interrupt
168        // to be handled, releases the CPU from the halted state
169        // this verification is only done in case the IME (interrupt
170        // master enable) is disabled, otherwise the CPU halt disabled
171        // is going to be handled ahead
172        if self.halted
173            && !self.ime
174            && self.mmu.ie != 0x00
175            && (((self.mmu.ie & 0x01 == 0x01) && self.mmu.ppu().int_vblank())
176                || ((self.mmu.ie & 0x02 == 0x02) && self.mmu.ppu().int_stat())
177                || ((self.mmu.ie & 0x04 == 0x04) && self.mmu.timer().int_tima())
178                || ((self.mmu.ie & 0x08 == 0x08) && self.mmu.serial().int_serial())
179                || ((self.mmu.ie & 0x10 == 0x10) && self.mmu.pad().int_pad()))
180        {
181            self.halted = false;
182        }
183
184        // checks the IME (interrupt master enable) is enabled and then checks
185        // if there's any interrupt to be handled, in case there's one, tries
186        // to check which one should be handled and then handles it
187        // this code assumes that the're no more that one interrupt triggered
188        // per clock cycle, this is a limitation of the current implementation
189        if self.ime && self.mmu.ie != 0x00 {
190            if (self.mmu.ie & 0x01 == 0x01) && self.mmu.ppu().int_vblank() {
191                debugln!("Going to run V-Blank interrupt handler (0x40)");
192
193                self.disable_int();
194                self.push_word(pc);
195                self.pc = 0x40;
196
197                // notifies the MMU about the V-Blank interrupt,
198                // this may trigger some additional operations
199                self.mmu.vblank();
200
201                // acknowledges that the V-Blank interrupt has been
202                // properly handled
203                self.mmu.ppu().ack_vblank();
204
205                // in case the CPU is currently halted waiting
206                // for an interrupt, releases it
207                if self.halted {
208                    self.halted = false;
209                }
210
211                return 20;
212            } else if (self.mmu.ie & 0x02 == 0x02) && self.mmu.ppu().int_stat() {
213                debugln!("Going to run LCD STAT interrupt handler (0x48)");
214
215                self.disable_int();
216                self.push_word(pc);
217                self.pc = 0x48;
218
219                // acknowledges that the STAT interrupt has been
220                // properly handled
221                self.mmu.ppu().ack_stat();
222
223                // in case the CPU is currently halted waiting
224                // for an interrupt, releases it
225                if self.halted {
226                    self.halted = false;
227                }
228
229                return 20;
230            } else if (self.mmu.ie & 0x04 == 0x04) && self.mmu.timer().int_tima() {
231                debugln!("Going to run Timer interrupt handler (0x50)");
232
233                self.disable_int();
234                self.push_word(pc);
235                self.pc = 0x50;
236
237                // acknowledges that the timer interrupt has been
238                // properly handled
239                self.mmu.timer().ack_tima();
240
241                // in case the CPU is currently halted waiting
242                // for an interrupt, releases it
243                if self.halted {
244                    self.halted = false;
245                }
246
247                return 20;
248            } else if (self.mmu.ie & 0x08 == 0x08) && self.mmu.serial().int_serial() {
249                debugln!("Going to run Serial interrupt handler (0x58)");
250
251                self.disable_int();
252                self.push_word(pc);
253                self.pc = 0x58;
254
255                // acknowledges that the serial interrupt has been
256                // properly handled
257                self.mmu.serial().ack_serial();
258
259                // in case the CPU is currently halted waiting
260                // for an interrupt, releases it
261                if self.halted {
262                    self.halted = false;
263                }
264
265                return 20;
266            } else if (self.mmu.ie & 0x10 == 0x10) && self.mmu.pad().int_pad() {
267                debugln!("Going to run JoyPad interrupt handler (0x60)");
268
269                self.disable_int();
270                self.push_word(pc);
271                self.pc = 0x60;
272
273                // acknowledges that the pad interrupt has been
274                // properly handled
275                self.mmu.pad().ack_pad();
276
277                // in case the CPU is currently halted waiting
278                // for an interrupt, releases it
279                if self.halted {
280                    self.halted = false;
281                }
282
283                return 20;
284            }
285        }
286
287        // in case the CPU is currently in the halted state
288        // returns the control flow immediately with the associated
289        // number of cycles estimated for the halted execution
290        if self.halted {
291            return 4;
292        }
293
294        // fetches the current instruction and updates the PC
295        // (Program Counter) according to the final value returned
296        // by the fetch operation (we may need to fetch instruction
297        // more than one byte of length)
298        let (inst, pc) = self.fetch(self.pc);
299        self.ppc = self.pc;
300        self.pc = pc;
301
302        #[allow(unused_variables)]
303        let (inst_fn, inst_time, inst_str) = inst;
304
305        #[cfg(feature = "cpulog")]
306        if *inst_str == "! UNIMP !" || *inst_str == "HALT" {
307            if *inst_str == "HALT" {
308                debugln!("HALT with IE=0x{:02x} IME={}", self.mmu.ie, self.ime);
309            }
310            debugln!(
311                "{}\t(0x{:02x})\t${:04x} {}",
312                inst_str,
313                opcode,
314                pc,
315                is_prefix
316            );
317        }
318
319        #[cfg(feature = "cpulog")]
320        {
321            println!("{}", self.description(inst, self.ppc));
322        }
323
324        // calls the current instruction and increments the number of
325        // cycles executed by the instruction time of the instruction
326        // that has just been executed
327        self.cycles = 0;
328        inst_fn(self);
329        self.cycles = self.cycles.wrapping_add(*inst_time);
330
331        // returns the number of cycles that the operation
332        // that has been executed has taken
333        self.cycles
334    }
335
336    #[inline(always)]
337    fn fetch(&self, pc: u16) -> (Instruction, u16) {
338        let mut pc = pc;
339
340        // fetches the current instruction and increments
341        // the PC (program counter) accordingly
342        let mut opcode = self.mmu.read(pc);
343        pc = pc.wrapping_add(1);
344
345        // checks if the current instruction is a prefix
346        // instruction, in case it is, fetches the next
347        // instruction and increments the PC accordingly
348        let inst: Instruction;
349        let is_prefix = opcode == PREFIX;
350        if is_prefix {
351            opcode = self.mmu.read(pc);
352            pc = pc.wrapping_add(1);
353            inst = &EXTENDED[opcode as usize];
354        } else {
355            inst = &INSTRUCTIONS[opcode as usize];
356        }
357
358        // returns both the fetched instruction and the
359        // updated PC (Program Counter) value
360        (inst, pc)
361    }
362
363    #[inline(always)]
364    pub fn mmu(&mut self) -> &mut Mmu {
365        &mut self.mmu
366    }
367
368    #[inline(always)]
369    pub fn mmu_i(&self) -> &Mmu {
370        &self.mmu
371    }
372
373    #[inline(always)]
374    pub fn ppu(&mut self) -> &mut Ppu {
375        self.mmu().ppu()
376    }
377
378    #[inline(always)]
379    pub fn ppu_i(&self) -> &Ppu {
380        self.mmu_i().ppu_i()
381    }
382
383    #[inline(always)]
384    pub fn apu(&mut self) -> &mut Apu {
385        self.mmu().apu()
386    }
387
388    #[inline(always)]
389    pub fn apu_i(&self) -> &Apu {
390        self.mmu_i().apu_i()
391    }
392
393    #[inline(always)]
394    pub fn dma(&mut self) -> &mut Dma {
395        self.mmu().dma()
396    }
397
398    #[inline(always)]
399    pub fn dma_i(&self) -> &Dma {
400        self.mmu_i().dma_i()
401    }
402
403    #[inline(always)]
404    pub fn pad(&mut self) -> &mut Pad {
405        self.mmu().pad()
406    }
407
408    #[inline(always)]
409    pub fn pad_i(&self) -> &Pad {
410        self.mmu_i().pad_i()
411    }
412
413    #[inline(always)]
414    pub fn timer(&mut self) -> &mut Timer {
415        self.mmu().timer()
416    }
417
418    #[inline(always)]
419    pub fn timer_i(&self) -> &Timer {
420        self.mmu_i().timer_i()
421    }
422
423    #[inline(always)]
424    pub fn serial(&mut self) -> &mut Serial {
425        self.mmu().serial()
426    }
427
428    #[inline(always)]
429    pub fn serial_i(&self) -> &Serial {
430        self.mmu_i().serial_i()
431    }
432
433    #[inline(always)]
434    pub fn halted(&self) -> bool {
435        self.halted
436    }
437
438    #[inline(always)]
439    pub fn set_halted(&mut self, value: bool) {
440        self.halted = value
441    }
442
443    #[inline(always)]
444    pub fn cycles(&self) -> u8 {
445        self.cycles
446    }
447
448    #[inline(always)]
449    pub fn pc(&self) -> u16 {
450        self.pc
451    }
452
453    #[inline(always)]
454    pub fn set_pc(&mut self, value: u16) {
455        self.pc = value;
456    }
457
458    #[inline(always)]
459    pub fn sp(&self) -> u16 {
460        self.sp
461    }
462
463    #[inline(always)]
464    pub fn set_sp(&mut self, value: u16) {
465        self.sp = value;
466    }
467
468    #[inline(always)]
469    pub fn af(&self) -> u16 {
470        ((self.a as u16) << 8) | self.f() as u16
471    }
472
473    #[inline(always)]
474    pub fn bc(&self) -> u16 {
475        ((self.b as u16) << 8) | self.c as u16
476    }
477
478    #[inline(always)]
479    pub fn f(&self) -> u8 {
480        let mut f = 0x0u8;
481        if self.zero {
482            f |= 0x80;
483        }
484        if self.sub {
485            f |= 0x40;
486        }
487        if self.half_carry {
488            f |= 0x20;
489        }
490        if self.carry {
491            f |= 0x10;
492        }
493        f
494    }
495
496    #[inline(always)]
497    pub fn set_f(&mut self, value: u8) {
498        self.zero = value & 0x80 == 0x80;
499        self.sub = value & 0x40 == 0x40;
500        self.half_carry = value & 0x20 == 0x20;
501        self.carry = value & 0x10 == 0x10;
502    }
503
504    #[inline(always)]
505    pub fn set_af(&mut self, value: u16) {
506        self.a = (value >> 8) as u8;
507        self.set_f(value as u8);
508    }
509
510    #[inline(always)]
511    pub fn set_bc(&mut self, value: u16) {
512        self.b = (value >> 8) as u8;
513        self.c = value as u8;
514    }
515
516    #[inline(always)]
517    pub fn de(&self) -> u16 {
518        ((self.d as u16) << 8) | self.e as u16
519    }
520
521    #[inline(always)]
522    pub fn set_de(&mut self, value: u16) {
523        self.d = (value >> 8) as u8;
524        self.e = value as u8;
525    }
526
527    #[inline(always)]
528    pub fn hl(&self) -> u16 {
529        ((self.h as u16) << 8) | self.l as u16
530    }
531
532    #[inline(always)]
533    pub fn set_hl(&mut self, value: u16) {
534        self.h = (value >> 8) as u8;
535        self.l = value as u8;
536    }
537
538    #[inline(always)]
539    pub fn ime(&self) -> bool {
540        self.ime
541    }
542
543    #[inline(always)]
544    pub fn set_ime(&mut self, value: bool) {
545        self.ime = value;
546    }
547
548    #[inline(always)]
549    pub fn read_u8(&mut self) -> u8 {
550        let byte = self.mmu.read(self.pc);
551        self.pc = self.pc.wrapping_add(1);
552        byte
553    }
554
555    #[inline(always)]
556    pub fn read_u16(&mut self) -> u16 {
557        let byte1 = self.read_u8();
558        let byte2 = self.read_u8();
559
560        byte1 as u16 | ((byte2 as u16) << 8)
561    }
562
563    #[inline(always)]
564    pub fn push_byte(&mut self, byte: u8) {
565        self.sp = self.sp.wrapping_sub(1);
566        self.mmu.write(self.sp, byte);
567    }
568
569    #[inline(always)]
570    pub fn push_word(&mut self, word: u16) {
571        self.push_byte((word >> 8) as u8);
572        self.push_byte(word as u8);
573    }
574
575    #[inline(always)]
576    pub fn pop_byte(&mut self) -> u8 {
577        let byte = self.mmu.read(self.sp);
578        self.sp = self.sp.wrapping_add(1);
579        byte
580    }
581
582    #[inline(always)]
583    pub fn pop_word(&mut self) -> u16 {
584        self.pop_byte() as u16 | ((self.pop_byte() as u16) << 8)
585    }
586
587    #[inline(always)]
588    pub fn zero(&self) -> bool {
589        self.zero
590    }
591
592    #[inline(always)]
593    pub fn set_zero(&mut self, value: bool) {
594        self.zero = value
595    }
596
597    #[inline(always)]
598    pub fn sub(&self) -> bool {
599        self.sub
600    }
601
602    #[inline(always)]
603    pub fn set_sub(&mut self, value: bool) {
604        self.sub = value;
605    }
606
607    #[inline(always)]
608    pub fn half_carry(&self) -> bool {
609        self.half_carry
610    }
611
612    #[inline(always)]
613    pub fn set_half_carry(&mut self, value: bool) {
614        self.half_carry = value
615    }
616
617    #[inline(always)]
618    pub fn carry(&self) -> bool {
619        self.carry
620    }
621
622    #[inline(always)]
623    pub fn set_carry(&mut self, value: bool) {
624        self.carry = value;
625    }
626
627    #[inline(always)]
628    pub fn halt(&mut self) {
629        self.halted = true;
630    }
631
632    #[inline(always)]
633    pub fn stop(&mut self) {
634        let mmu = self.mmu();
635        if mmu.switching {
636            mmu.switch_speed()
637        }
638    }
639
640    #[inline(always)]
641    pub fn enable_int(&mut self) {
642        self.ime = true;
643    }
644
645    #[inline(always)]
646    pub fn disable_int(&mut self) {
647        self.ime = false;
648    }
649
650    pub fn set_gbc(&mut self, value: SharedThread<GameBoyConfig>) {
651        self.gbc = value;
652    }
653
654    pub fn description(&self, inst: Instruction, inst_pc: u16) -> String {
655        let (_, inst_time, inst_str) = inst;
656        let title_str: String = format!("[0x{inst_pc:04x}] {inst_str}");
657        let inst_time_str = format!("({inst_time} cycles)");
658        let registers_str = format!("[PC=0x{:04x} SP=0x{:04x}] [A=0x{:02x} B=0x{:02x} C=0x{:02x} D=0x{:02x} E=0x{:02x} H=0x{:02x} L=0x{:02x}]",
659        self.pc, self.sp, self.a, self.b, self.c, self.d, self.e, self.h, self.l);
660        format!("{title_str: <24} {inst_time_str: <11} {registers_str: <10}")
661    }
662
663    pub fn description_default(&self) -> String {
664        let (inst, _) = self.fetch(self.ppc);
665        self.description(inst, self.ppc)
666    }
667}
668
669impl StateComponent for Cpu {
670    fn state(&self, _format: Option<StateFormat>) -> Result<Vec<u8>, Error> {
671        let mut cursor = Cursor::new(vec![]);
672        write_u16(&mut cursor, self.pc)?;
673        write_u16(&mut cursor, self.sp)?;
674        write_u8(&mut cursor, self.a)?;
675        write_u8(&mut cursor, self.b)?;
676        write_u8(&mut cursor, self.c)?;
677        write_u8(&mut cursor, self.d)?;
678        write_u8(&mut cursor, self.e)?;
679        write_u8(&mut cursor, self.h)?;
680        write_u8(&mut cursor, self.l)?;
681        write_u8(&mut cursor, self.ime as u8)?;
682        write_u8(&mut cursor, self.zero as u8)?;
683        write_u8(&mut cursor, self.sub as u8)?;
684        write_u8(&mut cursor, self.half_carry as u8)?;
685        write_u8(&mut cursor, self.carry as u8)?;
686        write_u8(&mut cursor, self.halted as u8)?;
687        write_u8(&mut cursor, self.cycles)?;
688        write_u16(&mut cursor, self.ppc)?;
689        Ok(cursor.into_inner())
690    }
691
692    fn set_state(&mut self, data: &[u8], _format: Option<StateFormat>) -> Result<(), Error> {
693        let mut cursor = Cursor::new(data);
694        self.pc = read_u16(&mut cursor)?;
695        self.sp = read_u16(&mut cursor)?;
696        self.a = read_u8(&mut cursor)?;
697        self.b = read_u8(&mut cursor)?;
698        self.c = read_u8(&mut cursor)?;
699        self.d = read_u8(&mut cursor)?;
700        self.e = read_u8(&mut cursor)?;
701        self.h = read_u8(&mut cursor)?;
702        self.l = read_u8(&mut cursor)?;
703        self.ime = read_u8(&mut cursor)? != 0;
704        self.zero = read_u8(&mut cursor)? != 0;
705        self.sub = read_u8(&mut cursor)? != 0;
706        self.half_carry = read_u8(&mut cursor)? != 0;
707        self.carry = read_u8(&mut cursor)? != 0;
708        self.halted = read_u8(&mut cursor)? != 0;
709        self.cycles = read_u8(&mut cursor)?;
710        self.ppc = read_u16(&mut cursor)?;
711        Ok(())
712    }
713}
714
715impl Default for Cpu {
716    fn default() -> Self {
717        let gbc = SharedThread::new(Mutex::new(GameBoyConfig::default()));
718        Cpu::new(Mmu::default(), gbc)
719    }
720}
721
722impl Display for Cpu {
723    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
724        write!(f, "{}", self.description_default())
725    }
726}
727
728#[cfg(test)]
729mod tests {
730    use std::sync::Mutex;
731
732    use boytacean_common::util::SharedThread;
733
734    use crate::{gb::GameBoyConfig, mmu::Mmu, state::StateComponent};
735
736    use super::Cpu;
737
738    #[test]
739    fn test_cpu_clock() {
740        let mut cpu = Cpu::default();
741        cpu.boot();
742        cpu.mmu.allocate_default();
743
744        // test NOP instruction
745        cpu.pc = 0xc000;
746        cpu.mmu.write(0xc000, 0x00);
747        let cycles = cpu.clock();
748        assert_eq!(cycles, 4);
749        assert_eq!(cpu.pc, 0xc001);
750
751        // test LD A, d8 instruction
752        cpu.pc = 0xc000;
753        cpu.mmu.write(0xc000, 0x3e);
754        cpu.mmu.write(0xc001, 0x42);
755        let cycles = cpu.clock();
756        assert_eq!(cycles, 8);
757        assert_eq!(cpu.pc, 0xc002);
758        assert_eq!(cpu.a, 0x42);
759
760        // test LD (HL+), A instruction
761        cpu.pc = 0xc000;
762        cpu.mmu.write(0xc000, 0x22);
763        cpu.set_hl(0xc000);
764        let cycles = cpu.clock();
765        assert_eq!(cycles, 8);
766        assert_eq!(cpu.pc, 0xc001);
767        assert_eq!(cpu.hl(), 0xc001);
768        assert_eq!(cpu.mmu.read(cpu.hl()), 0x42);
769
770        // test INC A instruction
771        cpu.pc = 0xc000;
772        cpu.mmu.write(0xc000, 0x3c);
773        cpu.a = 0x42;
774        let cycles = cpu.clock();
775        assert_eq!(cycles, 4);
776        assert_eq!(cpu.pc, 0xc001);
777        assert_eq!(cpu.a, 0x43);
778
779        // test DEC A instruction
780        cpu.pc = 0xc000;
781        cpu.mmu.write(0xc000, 0x3d);
782        cpu.a = 0x42;
783        let cycles = cpu.clock();
784        assert_eq!(cycles, 4);
785        assert_eq!(cpu.pc, 0xc001);
786        assert_eq!(cpu.a, 0x41);
787
788        // test LD A, (HL) instruction
789        cpu.pc = 0xc000;
790        cpu.mmu.write(0xc000, 0x7e);
791        cpu.set_hl(0xc001);
792        cpu.mmu.write(0xc001, 0x42);
793        let cycles = cpu.clock();
794        assert_eq!(cycles, 8);
795        assert_eq!(cpu.pc, 0xc001);
796        assert_eq!(cpu.a, 0x42);
797        assert_eq!(cpu.hl(), 0xc001);
798
799        // test LD (HL), d8 instruction
800        cpu.pc = 0xc000;
801        cpu.mmu.write(0xc000, 0x36);
802        cpu.set_hl(0xc000);
803        cpu.mmu.write(0xc001, 0x42);
804        let cycles = cpu.clock();
805        assert_eq!(cycles, 12);
806        assert_eq!(cpu.pc, 0xc002);
807        assert_eq!(cpu.hl(), 0xc000);
808        assert_eq!(cpu.mmu.read(cpu.hl()), 0x42);
809
810        // test JR n instruction
811        cpu.pc = 0xc000;
812        cpu.mmu.write(0xc000, 0x18);
813        cpu.mmu.write(0xc001, 0x03);
814        let cycles = cpu.clock();
815        assert_eq!(cycles, 12);
816        assert_eq!(cpu.pc, 0xc005);
817
818        // test ADD A, d8 instruction
819        cpu.pc = 0xc000;
820        cpu.mmu.write(0xc000, 0xc6);
821        cpu.mmu.write(0xc001, 0x01);
822        cpu.a = 0x42;
823        let cycles = cpu.clock();
824        assert_eq!(cycles, 8);
825        assert_eq!(cpu.pc, 0xc002);
826        assert_eq!(cpu.a, 0x43);
827
828        // test SUB A, d8 instruction
829        cpu.pc = 0xc000;
830        cpu.mmu.write(0xc000, 0xd6);
831        cpu.mmu.write(0xc001, 0x01);
832        cpu.a = 0x42;
833        let cycles = cpu.clock();
834        assert_eq!(cycles, 8);
835        assert_eq!(cpu.pc, 0xc002);
836        assert_eq!(cpu.a, 0x41);
837
838        // test AND A, d8 instruction
839        cpu.pc = 0xc000;
840        cpu.mmu.write(0xc000, 0xe6);
841        cpu.mmu.write(0xc001, 0x0f);
842        cpu.a = 0x0a;
843        let cycles = cpu.clock();
844        assert_eq!(cycles, 8);
845        assert_eq!(cpu.pc, 0xc002);
846        assert_eq!(cpu.a, 0x0a & 0x0f);
847
848        // test OR A, d8 instruction
849        cpu.pc = 0xc000;
850        cpu.mmu.write(0xc000, 0xf6);
851        cpu.mmu.write(0xc001, 0x0f);
852        cpu.a = 0x0a;
853        let cycles = cpu.clock();
854        assert_eq!(cycles, 8);
855        assert_eq!(cpu.pc, 0xc002);
856        assert_eq!(cpu.a, 0x0a | 0x0f);
857
858        // test XOR A, d8 instruction
859        cpu.pc = 0xc000;
860        cpu.mmu.write(0xc000, 0xee);
861        cpu.mmu.write(0xc001, 0x0f);
862        cpu.a = 0x0a;
863        let cycles = cpu.clock();
864        assert_eq!(cycles, 8);
865        assert_eq!(cpu.pc, 0xc002);
866        assert_eq!(cpu.a, 0x0a ^ 0x0f);
867    }
868
869    #[test]
870    fn test_state_and_set_state() {
871        let cpu = Cpu {
872            pc: 0x1234,
873            sp: 0x5678,
874            a: 0x9a,
875            b: 0xbc,
876            c: 0xde,
877            d: 0xf0,
878            e: 0x12,
879            h: 0x34,
880            l: 0x56,
881            ime: true,
882            zero: true,
883            sub: false,
884            half_carry: true,
885            carry: false,
886            halted: true,
887            mmu: Mmu::default(),
888            cycles: 0x78,
889            ppc: 0x9abc,
890            gbc: SharedThread::new(Mutex::new(GameBoyConfig::default())),
891        };
892
893        let state = cpu.state(None).unwrap();
894        assert_eq!(state.len(), 20);
895
896        let mut new_cpu = Cpu::default();
897        new_cpu.set_state(&state, None).unwrap();
898
899        assert_eq!(new_cpu.pc, 0x1234);
900        assert_eq!(new_cpu.sp, 0x5678);
901        assert_eq!(new_cpu.a, 0x9a);
902        assert_eq!(new_cpu.b, 0xbc);
903        assert_eq!(new_cpu.c, 0xde);
904        assert_eq!(new_cpu.d, 0xf0);
905        assert_eq!(new_cpu.e, 0x12);
906        assert_eq!(new_cpu.h, 0x34);
907        assert_eq!(new_cpu.l, 0x56);
908        assert!(new_cpu.ime);
909        assert!(new_cpu.zero);
910        assert!(!new_cpu.sub);
911        assert!(new_cpu.half_carry);
912        assert!(!new_cpu.carry);
913        assert!(new_cpu.halted);
914        assert_eq!(new_cpu.cycles, 0x78);
915        assert_eq!(new_cpu.ppc, 0x9abc);
916    }
917}