diff --git a/.forgejo/workflows/action.yml b/.forgejo/workflows/action.yml new file mode 120000 index 0000000..f8bb030 --- /dev/null +++ b/.forgejo/workflows/action.yml @@ -0,0 +1 @@ +./.github/workflows/action.yml \ No newline at end of file diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml index c164e33..191224b 100644 --- a/.github/workflows/action.yml +++ b/.github/workflows/action.yml @@ -14,27 +14,103 @@ jobs: - name: Run cargo tests (meowgb-core) run: cargo test -p meowgb-core - + - name: Run test ROM (blargg cpu_instrs) if: always() run: cargo run -p meowgb-tests --release -- test-roms/blargg/roms/cpu_instrs.gb test -m 100000000 -s meowgb-tests/expected_output/cpu_instrs.bin - + - name: Run test ROM (blargg instr_timing) if: always() run: cargo run -p meowgb-tests --release -- test-roms/blargg/roms/instr_timing.gb test -m 100000000 -s meowgb-tests/expected_output/instr_timing.bin + + - name: Run test ROM (blargg mem_timing) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/blargg/roms/mem_timing.gb test -m 100000000 -s meowgb-tests/expected_output/mem_timing.bin - - name: Run test ROM (mealybug-tearoom-tests intr_1_2_timing) + - name: Run test ROM (mooneye-test-suite basic) if: always() - run: cargo run -p meowgb-tests --release -- test-roms/mealybug-tearoom-tests/roms/intr_1_2_timing-GS.gb test -m 100000000 -s meowgb-tests/expected_output/intr_1_2_timing-GS.bin + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/basic.gb test -m 100000000 -s meowgb-tests/expected_output/basic.bin - - name: Run test ROM (mealybug-tearoom-tests intr_2_0_timing) + - name: Run test ROM (mooneye-test-suite boot_hwio-dmgABCmgb) if: always() - run: cargo run -p meowgb-tests --release -- test-roms/mealybug-tearoom-tests/roms/intr_2_0_timing.gb test -m 100000000 -s meowgb-tests/expected_output/intr_2_0_timing.bin + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/boot_hwio-dmgABCmgb.gb test -m 100000000 -s meowgb-tests/expected_output/boot_hwio-dmgABCmgb.bin - - name: Run test ROM (mealybug-tearoom-tests stat_lyc_onoff) + - name: Run test ROM (mooneye-test-suite boot_regs-dmgABC) if: always() - run: cargo run -p meowgb-tests --release -- test-roms/mealybug-tearoom-tests/roms/stat_lyc_onoff.gb test -m 100000000 -s meowgb-tests/expected_output/stat_lyc_onoff.bin - - - name: Run test ROM (mealybug-tearoom-tests stat_irq_blocking) + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/boot_regs-dmgABC.gb test -m 100000000 -s meowgb-tests/expected_output/boot_regs-dmgABC.bin + + - name: Run test ROM (mooneye-test-suite daa) if: always() - run: cargo run -p meowgb-tests --release -- test-roms/mealybug-tearoom-tests/roms/stat_irq_blocking.gb test -m 100000000 -s meowgb-tests/expected_output/stat_irq_blocking.bin + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/daa.gb test -m 100000000 -s meowgb-tests/expected_output/daa.bin + + - name: Run test ROM (mooneye-test-suite ei_sequence) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/ei_sequence.gb test -m 100000000 -s meowgb-tests/expected_output/ei_sequence.bin + + - name: Run test ROM (mooneye-test-suite ei_timing) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/ei_timing.gb test -m 100000000 -s meowgb-tests/expected_output/ei_timing.bin + + - name: Run test ROM (mooneye-test-suite halt_ime0_ei) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/halt_ime0_ei.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime0_ei.bin + + - name: Run test ROM (mooneye-test-suite intr_1_2_timing-GS) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/intr_1_2_timing-GS.gb test -m 100000000 -s meowgb-tests/expected_output/intr_1_2_timing-GS.bin + + - name: Run test ROM (mooneye-test-suite intr_2_0_timing) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/intr_2_0_timing.gb test -m 100000000 -s meowgb-tests/expected_output/intr_2_0_timing.bin + + - name: Run test ROM (mooneye-test-suite mem_oam) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/mem_oam.gb test -m 100000000 -s meowgb-tests/expected_output/mem_oam.bin + + - name: Run test ROM (mooneye-test-suite oam_dma_restart) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/oam_dma_restart.gb test -m 100000000 -s meowgb-tests/expected_output/oam_dma_restart.bin + + - name: Run test ROM (mooneye-test-suite oam_dma_start) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/oam_dma_start.gb test -m 100000000 -s meowgb-tests/expected_output/oam_dma_start.bin + + - name: Run test ROM (mooneye-test-suite oam_dma_timing) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/oam_dma_timing.gb test -m 100000000 -s meowgb-tests/expected_output/oam_dma_timing.bin + + - name: Run test ROM (mooneye-test-suite rapid_di_ei) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/rapid_di_ei.gb test -m 100000000 -s meowgb-tests/expected_output/rapid_di_ei.bin + + - name: Run test ROM (mooneye-test-suite reg_f) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/reg_f.gb test -m 100000000 -s meowgb-tests/expected_output/reg_f.bin + + - name: Run test ROM (mooneye-test-suite reg_read) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/reg_read.gb test -m 100000000 -s meowgb-tests/expected_output/reg_read.bin + + - name: Run test ROM (mooneye-test-suite stat_irq_blocking) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/stat_irq_blocking.gb test -m 100000000 -s meowgb-tests/expected_output/stat_irq_blocking.bin + + - name: Run test ROM (mooneye-test-suite stat_lyc_onoff) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/stat_lyc_onoff.gb test -m 100000000 -s meowgb-tests/expected_output/stat_lyc_onoff.bin + + - name: Run test ROM (mooneye-test-suite tim00_div_trigger) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/tim00_div_trigger.gb test -m 100000000 -s meowgb-tests/expected_output/tim00_div_trigger.bin + + - name: Run test ROM (mooneye-test-suite tim01) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/tim01.gb test -m 100000000 -s meowgb-tests/expected_output/tim01.bin + + - name: Run test ROM (mooneye-test-suite tim11_div_trigger) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/tim11_div_trigger.gb test -m 100000000 -s meowgb-tests/expected_output/tim11_div_trigger.bin + + - name: Run test ROM (mooneye-test-suite unused_hwio-GS) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/unused_hwio-GS.gb test -m 100000000 -s meowgb-tests/expected_output/unused_hwio-GS.bin diff --git a/.gitignore b/.gitignore index 98d60c8..53bf464 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /bmp/ config.toml /.vscode/ -out \ No newline at end of file +out +generate-output.sh \ No newline at end of file diff --git a/generate-action.sh b/generate-action.sh new file mode 100755 index 0000000..7afd84f --- /dev/null +++ b/generate-action.sh @@ -0,0 +1,55 @@ +#!/bin/bash +OUTPUT_FILE=./.github/workflows/action.yml + +cat >$OUTPUT_FILE << EOF +on: + - push + +jobs: + main_test: + name: Test changes to main + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install toolchain + run: curl https://sh.rustup.rs -sSf | sh -s -- --profile minimal --default-toolchain stable -y && echo "\$HOME/.cargo/bin" >> \$GITHUB_PATH + + - name: Run cargo tests (meowgb-core) + run: cargo test -p meowgb-core +EOF + + +for f in ./test-roms/blargg/roms/* +do + f="${f##*/}"; f="${f%.*}"; + cat >>$OUTPUT_FILE << EOF + + - name: Run test ROM (blargg $f) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/blargg/roms/$f.gb test -m 100000000 -s meowgb-tests/expected_output/$f.bin +EOF +done + +# for f in ./test-roms/mealybug-tearoom-tests/roms/* +# do +# f="${f##*/}"; f="${f%.*}"; +# cat >>$OUTPUT_FILE << EOF + +# - name: Run test ROM (mealybug-tearoom-tests $f) +# if: always() +# run: cargo run -p meowgb-tests --release -- test-roms/mealybug-tearoom-tests/roms/$f.gb test -m 100000000 -s meowgb-tests/expected_output/$f.bin +# EOF +# done + +for f in ./test-roms/mooneye-test-suite/roms/* +do + f="${f##*/}"; f="${f%.*}"; + cat >>$OUTPUT_FILE << EOF + + - name: Run test ROM (mooneye-test-suite $f) + if: always() + run: cargo run -p meowgb-tests --release -- test-roms/mooneye-test-suite/roms/$f.gb test -m 100000000 -s meowgb-tests/expected_output/$f.bin +EOF +done diff --git a/meowgb-core/src/gameboy.rs b/meowgb-core/src/gameboy.rs index 21341fa..4177cbe 100644 --- a/meowgb-core/src/gameboy.rs +++ b/meowgb-core/src/gameboy.rs @@ -1,5 +1,6 @@ pub mod bootrom; pub mod cpu; +pub mod dma; pub mod interrupts; pub mod joypad; pub mod mapper; @@ -22,25 +23,9 @@ use self::{ cpu::Registers, mapper::{mbc1::MBC1, NoMBC}, serial::{Serial, SerialWriter}, - sound::Sound, + sound::Sound, dma::DmaState, }; -pub struct DmaState { - pub base: u8, - pub remaining_cycles: u8, -} - -impl DmaState { - pub fn new() -> Self { - Self { base: 0, remaining_cycles: 0 } - } - - pub fn init_request(&mut self, base: u8) { - self.base = base; - self.remaining_cycles = 0xA0; - } -} - pub struct RingBuffer { buffer: [T; SIZE], size: usize, @@ -155,7 +140,7 @@ impl Gameboy { true => Registers::default(), false => Registers::post_rom(), }, - sound: Sound::default(), + sound: Sound::new(), single_step: false, breakpoints: [false; u16::MAX as usize + 1], mem_read_breakpoints: [false; u16::MAX as usize + 1], @@ -402,7 +387,7 @@ impl Gameboy { cpu::tick_cpu(self); let redraw_requested = self.ppu.tick(&mut self.interrupts); - self.tick_dma(); + self.dma.tick_dma(&mut self.ppu, &self.memory, self.cartridge.as_deref()); if self.serial.tick() { self.interrupts.write_if_serial(true); } @@ -416,39 +401,11 @@ impl Gameboy { } } - fn tick_dma(&mut self) { - self.ppu.dma_occuring = self.dma.remaining_cycles > 0; - if self.dma.remaining_cycles > 0 { - let offset = 0xA0 - self.dma.remaining_cycles; - - let value = if self.dma.base <= 0x7F { - match self.cartridge.as_ref() { - Some(cart) => cart.read_rom_u8((self.dma.base as u16) << 8 | offset as u16), - None => 0xFF, - } - } else if self.dma.base <= 0x9F { - let address = (((self.dma.base as usize) << 8) | offset as usize) - 0x8000; - self.ppu.vram[address] - } else if self.dma.base <= 0xDF { - let address = ((self.dma.base as usize) << 8 | offset as usize) - 0xC000; - self.memory.wram[address] - } else if self.dma.base <= 0xFD { - let address = ((self.dma.base as usize) << 8 | offset as usize) - 0xE000; - self.memory.wram[address] - } else { - 0xFF - }; - - self.ppu.dma_write_oam(offset, value); - self.dma.remaining_cycles -= 1; - } - } - fn cpu_read_io(&self, address: u16) -> u8 { match address { 0xFF00 => self.joypad.cpu_read(), 0xFF01 => self.serial.sb, - 0xFF02 => self.serial.sc, + 0xFF02 => self.serial.get_sc(), 0xFF03 => 0xFF, // Unused 0xFF04 => self.timer.div, 0xFF05 => self.timer.tima, @@ -495,7 +452,7 @@ impl Gameboy { 0xFF4B => self.ppu.registers.wx, 0xFF4C..=0xFF4E => 0xFF, // Unused 0xFF4F => 0xFF, // CGB VRAM Bank Select - 0xFF50 => self.memory.bootrom_disabled as u8, + 0xFF50 => self.memory.get_bootrom_disabled(), 0xFF51..=0xFF55 => 0xFF, // CGB VRAM DMA 0xFF56..=0xFF67 => 0xFF, // Unused 0xFF68..=0xFF69 => 0xFF, // BJ/OBJ Palettes @@ -510,7 +467,7 @@ impl Gameboy { match address { 0xFF00 => self.joypad.cpu_write(value), 0xFF01 => self.serial.sb = value, - 0xFF02 => self.serial.sc = value, + 0xFF02 => self.serial.set_sc(value), 0xFF03 => {} // Unused 0xFF04 => self.timer.div = 0, 0xFF05 => self.timer.tima = value, @@ -518,7 +475,7 @@ impl Gameboy { 0xFF07 => self.timer.write_tac(value), 0xFF08..=0xFF0E => {} // Unused 0xFF0F => self.interrupts.interrupt_flag = value | !0b1_1111, - 0xFF10 => self.sound.nr10 = value, + 0xFF10 => {} //self.sound.nr10 = value, - Unwritable on DMG 0xFF11 => self.sound.nr11 = value, 0xFF12 => self.sound.nr12 = value, 0xFF13 => self.sound.nr13 = value, @@ -528,19 +485,19 @@ impl Gameboy { 0xFF17 => self.sound.nr22 = value, 0xFF18 => self.sound.nr23 = value, 0xFF19 => self.sound.nr24 = value, - 0xFF1A => self.sound.nr30 = value, + 0xFF1A => {}, //self.sound.nr30 = value, - Unwritable on DMG 0xFF1B => self.sound.nr31 = value, - 0xFF1C => self.sound.nr32 = value, + 0xFF1C => {}, //self.sound.nr32 = value, - Unwritable on DMG 0xFF1D => self.sound.nr33 = value, 0xFF1E => self.sound.nr34 = value, 0xFF1F => {} - 0xFF20 => self.sound.nr41 = value, + 0xFF20 => {}, //self.sound.nr41 = value, - Unwritable on DMG 0xFF21 => self.sound.nr42 = value, 0xFF22 => self.sound.nr43 = value, - 0xFF23 => self.sound.nr44 = value, + 0xFF23 => {}, //self.sound.nr44 = value, - Unwritable on DMG 0xFF24 => self.sound.nr50 = value, 0xFF25 => self.sound.nr51 = value, - 0xFF26 => self.sound.nr52 = value, + 0xFF26 => {}, //self.sound.nr52 = value, - Unwritable on DMG 0xFF27..=0xFF2F => {} 0xFF30..=0xFF3F => self.sound.wave_pattern_ram[address as usize - 0xFF30] = value, 0xFF40 => { @@ -558,11 +515,7 @@ impl Gameboy { 0xFF43 => self.ppu.registers.scx = value, 0xFF44 => {} // LY is read only 0xFF45 => self.ppu.set_lyc(&mut self.interrupts, value), - 0xFF46 => { - if self.dma.remaining_cycles == 0 { - self.dma.init_request(value); - } - } + 0xFF46 => self.dma.init_request(value), 0xFF47 => self.ppu.bgp.write_bgp(value), 0xFF48 => self.ppu.obp[0].write_obp(value), 0xFF49 => self.ppu.obp[1].write_obp(value), @@ -642,12 +595,12 @@ impl Gameboy { 0xFEA0..=0xFEFF => {} 0xFF00..=0xFF7F => self.cpu_write_io(address, value), 0xFF80..=0xFFFE => self.memory.hram[address as usize - 0xFF80] = value, - 0xFFFF => self.interrupts.interrupt_enable = value & 0b1_1111, + 0xFFFF => self.interrupts.cpu_set_interrupt_enable(value), } } fn internal_cpu_read_u8(&self, address: u16) -> u8 { - if self.dma.remaining_cycles == 0 { + if !self.ppu.dma_occuring { match address { 0..=0xFF if !self.memory.bootrom_disabled => self.memory.bootrom[address as usize], 0..=0x7FFF => match self.cartridge.as_ref() { @@ -669,7 +622,7 @@ impl Gameboy { } } else { match address { - 0..=0xFEFF => 0, + 0..=0xFEFF => 0xFF, 0xFF00..=0xFF7F => self.cpu_read_io(address), 0xFF80..=0xFFFE => self.memory.hram[address as usize - 0xFF80], 0xFFFF => self.interrupts.interrupt_enable, @@ -699,7 +652,7 @@ impl Gameboy { log::info!("Triggered write bp @ {:#X} (value: {:#02X})", address, value); } - if self.dma.remaining_cycles == 0 { + if !self.ppu.dma_occuring { match address { 0..=0xFF if !self.memory.bootrom_disabled => {} 0..=0x7FFF => { @@ -719,14 +672,14 @@ impl Gameboy { 0xFEA0..=0xFEFF => {} 0xFF00..=0xFF7F => self.cpu_write_io(address, value), 0xFF80..=0xFFFE => self.memory.hram[address as usize - 0xFF80] = value, - 0xFFFF => self.interrupts.interrupt_enable = value & 0b1_1111, + 0xFFFF => self.interrupts.cpu_set_interrupt_enable(value), } } else { match address { 0..=0xFEFF => {} 0xFF00..=0xFF7F => self.cpu_write_io(address, value), 0xFF80..=0xFFFE => self.memory.hram[address as usize - 0xFF80] = value, - 0xFFFF => self.interrupts.interrupt_enable = value & 0b1_1111, + 0xFFFF => self.interrupts.cpu_set_interrupt_enable(value), } } } diff --git a/meowgb-core/src/gameboy/dma.rs b/meowgb-core/src/gameboy/dma.rs new file mode 100644 index 0000000..c419e98 --- /dev/null +++ b/meowgb-core/src/gameboy/dma.rs @@ -0,0 +1,61 @@ +use super::{ppu::Ppu, memory::Memory, mapper::Mapper}; + +#[derive(Debug)] +pub struct DmaState { + original_base: u8, + pub base: u8, + pub remaining_cycles: u8, + restarting: Option<(u8, bool)>, +} + +impl DmaState { + pub fn new() -> Self { + Self { original_base: 0, base: 0, remaining_cycles: 0, restarting: None } + } + + pub fn init_request(&mut self, base: u8) { + self.base = base; + self.restarting = Some((base, false)); + } + + pub fn tick_dma(&mut self, ppu: &mut Ppu, memory: &Memory, cartridge: Option<&(dyn Mapper + Send + Sync)>) { + match self.restarting { + Some((base, false)) => self.restarting = Some((base, true)), + Some((base, true)) => { + self.original_base = base; + self.remaining_cycles = 0xA0; + self.restarting = None; + } + None => {}, + } + + ppu.dma_occuring = self.remaining_cycles > 0; + + if self.remaining_cycles > 0 { + let offset = 0xA0 - self.remaining_cycles; + + let value = if self.original_base <= 0x7F { + match cartridge { + Some(cart) => cart.read_rom_u8((self.base as u16) << 8 | offset as u16), + None => 0xFF, + } + } else if self.original_base <= 0x9F { + let address = (((self.original_base as usize) << 8) | offset as usize) - 0x8000; + ppu.vram[address] + } else if self.original_base <= 0xDF { + let address = ((self.original_base as usize) << 8 | offset as usize) - 0xC000; + memory.wram[address] + } else if self.original_base <= 0xFD { + let address = ((self.original_base as usize) << 8 | offset as usize) - 0xE000; + memory.wram[address] + } else { + 0xFF + }; + + ppu.dma_write_oam(offset, value); + self.remaining_cycles -= 1; + } + + } + +} \ No newline at end of file diff --git a/meowgb-core/src/gameboy/interrupts.rs b/meowgb-core/src/gameboy/interrupts.rs index 7e934c0..6d61bbe 100644 --- a/meowgb-core/src/gameboy/interrupts.rs +++ b/meowgb-core/src/gameboy/interrupts.rs @@ -27,8 +27,8 @@ impl Interrupts { pub fn new() -> Self { Self { ime: false, - interrupt_enable: 0, - interrupt_flag: 0b11100000, + interrupt_enable: 0b0000_0000, + interrupt_flag: 0b1110_0001, ei_queued: false, cycle_passed: false, } @@ -51,4 +51,8 @@ impl Interrupts { self.ime = false; } } + + pub fn cpu_set_interrupt_enable(&mut self, value: u8) { + self.interrupt_enable = value; + } } diff --git a/meowgb-core/src/gameboy/joypad.rs b/meowgb-core/src/gameboy/joypad.rs index bf0a8e5..4b5e613 100644 --- a/meowgb-core/src/gameboy/joypad.rs +++ b/meowgb-core/src/gameboy/joypad.rs @@ -43,7 +43,7 @@ pub struct Joypad { impl Joypad { pub fn new() -> Self { Self { - mode: JoypadMode::Direction, + mode: JoypadMode::Both, down: false, up: false, left: false, @@ -57,7 +57,7 @@ impl Joypad { } pub fn cpu_read(&self) -> u8 { - match self.mode { + (0b11 << 6) | match self.mode { JoypadMode::Action => { (1 << 4) | ((!self.start as u8) << 3) @@ -72,7 +72,7 @@ impl Joypad { | ((!self.left as u8) << 1) | (!self.right as u8) } - JoypadMode::Both => 0x3F, + JoypadMode::Both => 0b1111, } } diff --git a/meowgb-core/src/gameboy/memory.rs b/meowgb-core/src/gameboy/memory.rs index b0901ee..c830095 100644 --- a/meowgb-core/src/gameboy/memory.rs +++ b/meowgb-core/src/gameboy/memory.rs @@ -15,4 +15,11 @@ impl Memory { bootrom_disabled: bootrom.is_none(), } } + + pub fn get_bootrom_disabled(&self) -> u8 { + match self.bootrom_disabled { + true => 0xFF, + false => 0x00, + } + } } diff --git a/meowgb-core/src/gameboy/ppu.rs b/meowgb-core/src/gameboy/ppu.rs index e6533d3..4ad8dc0 100644 --- a/meowgb-core/src/gameboy/ppu.rs +++ b/meowgb-core/src/gameboy/ppu.rs @@ -3,6 +3,10 @@ use super::interrupts::Interrupts; pub const FB_HEIGHT: u32 = 144; pub const FB_WIDTH: u32 = 160; pub const PIXEL_SIZE: usize = 4; // RGBA +/// Helper for debugging PPU timings that allows read/writes to PPU memory no +/// matter what mode it is in. This also allows the PPU to bypass a DMA +/// currently occuring which is blocking access to the memory bus. +const OVERRIDE_PPU_MEMORY_ACCESS: bool = false; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Palette { @@ -36,7 +40,7 @@ enum LineDrawingState { impl Palette { pub fn new_bgp() -> Self { - Self { id0: Color::White, id1: Color::LGray, id2: Color::DGray, id3: Color::Black } + Self { id0: Color::White, id1: Color::Black, id2: Color::Black, id3: Color::Black } } pub fn new_obp() -> Self { @@ -325,7 +329,7 @@ impl Ppu { pub fn new(bootrom_ran: bool) -> Self { Self { registers: PpuRegisters { - lcdc: (!bootrom_ran).then_some(0b1000_0000).unwrap_or_default(), + lcdc: (!bootrom_ran).then_some(0b1001_0001).unwrap_or_default(), stat_flags: StatFlags::default(), mode: PPUMode::HBlank, scy: 0, @@ -445,7 +449,7 @@ impl Ppu { } fn internal_read_oam(&mut self, offset: usize) -> u8 { - match self.dma_occuring { + match self.dma_occuring && !OVERRIDE_PPU_MEMORY_ACCESS { true => 0xFF, false => self.oam[offset as usize], } @@ -458,7 +462,7 @@ impl Ppu { pub fn cpu_read_oam(&self, address: u16) -> u8 { let decoded_address = address - 0xFE00; - if self.enabled() && !self.first_frame { + if self.enabled() && !self.first_frame && !OVERRIDE_PPU_MEMORY_ACCESS { match self.mode() { PPUMode::HBlank | PPUMode::VBlank => self.oam[decoded_address as usize], PPUMode::SearchingOAM | PPUMode::TransferringData => 0xFF, @@ -470,7 +474,7 @@ impl Ppu { pub fn cpu_write_oam(&mut self, address: u16, value: u8) { let decoded_address = address - 0xFE00; - if self.enabled() && !self.first_frame { + if self.enabled() && !self.first_frame && !OVERRIDE_PPU_MEMORY_ACCESS { match self.mode() { PPUMode::HBlank | PPUMode::VBlank => self.oam[decoded_address as usize] = value, PPUMode::SearchingOAM | PPUMode::TransferringData => {} @@ -482,7 +486,7 @@ impl Ppu { pub fn cpu_read_vram(&self, address: u16) -> u8 { let decoded_address = address - 0x8000; - if self.enabled() && !self.first_frame { + if self.enabled() && !self.first_frame && !OVERRIDE_PPU_MEMORY_ACCESS { match self.mode() { PPUMode::HBlank | PPUMode::VBlank | PPUMode::SearchingOAM => { self.vram[decoded_address as usize] @@ -496,7 +500,7 @@ impl Ppu { pub fn cpu_write_vram(&mut self, address: u16, value: u8) { let decoded_address = address - 0x8000; - if self.enabled() && !self.first_frame { + if self.enabled() && !self.first_frame && !OVERRIDE_PPU_MEMORY_ACCESS { match self.mode() { PPUMode::HBlank | PPUMode::VBlank | PPUMode::SearchingOAM => { self.vram[decoded_address as usize] = value @@ -629,9 +633,7 @@ impl Ppu { false } PPUMode::HBlank => { - if self.first_line && self.current_dot == 16 && self.dot_target == 0 { - // Special case for resets - // self.handle_stat_irq(interrupts, false); + if self.first_line && self.current_dot == 64 && self.dot_target == 0 { self.set_mode(interrupts, PPUMode::TransferringData); } else if self.dot_target != 0 && self.current_dot == self.dot_target { self.set_scanline(interrupts, self.registers.ly + 1); @@ -639,7 +641,7 @@ impl Ppu { assert_eq!( self.total_dots, match self.first_frame && self.first_line { - true => 456 - (80 - 16), + true => 456 - (80 - 64), false => 456, } ); diff --git a/meowgb-core/src/gameboy/serial.rs b/meowgb-core/src/gameboy/serial.rs index a1acf03..a4eca9e 100644 --- a/meowgb-core/src/gameboy/serial.rs +++ b/meowgb-core/src/gameboy/serial.rs @@ -15,7 +15,7 @@ impl SerialWriter for T { pub struct Serial { pub sb: u8, - pub sc: u8, + sc: u8, internal_tick: u16, writer: S, @@ -39,6 +39,14 @@ impl Serial { self.sc & 0b1 == 1 } + pub fn set_sc(&mut self, value: u8) { + self.sc = value | (0b0111_1110); + } + + pub fn get_sc(&self) -> u8 { + self.sc | (0b0111_1110) + } + #[allow(unused)] pub fn set_side(&mut self, conductor: bool) { self.sc &= !0b1; diff --git a/meowgb-core/src/gameboy/sound.rs b/meowgb-core/src/gameboy/sound.rs index 74d40f2..191b71d 100644 --- a/meowgb-core/src/gameboy/sound.rs +++ b/meowgb-core/src/gameboy/sound.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Sound { pub nr10: u8, pub nr11: u8, @@ -28,3 +28,32 @@ pub struct Sound { pub wave_pattern_ram: [u8; 0x10], } + +impl Sound { + pub fn new() -> Self { + Self { + nr10: 0b1000_0000, + nr11: 0b1011_1111, + nr12: 0b1111_0011, + nr13: 0b1111_1111, + nr14: 0b1011_1111, + nr21: 0b0011_1111, + nr22: 0b0000_0000, + nr23: 0b1111_1111, + nr24: 0b1011_1111, + nr30: 0b0111_1111, + nr31: 0b1111_1111, + nr32: 0b1001_1111, + nr33: 0b1111_1111, + nr34: 0b1011_1111, + nr41: 0b1111_1111, + nr42: 0b0000_0000, + nr43: 0b0000_0000, + nr44: 0b1011_1111, + nr50: 0b0111_0111, + nr51: 0b1111_0011, + nr52: 0b1111_0001, + wave_pattern_ram: [0u8;16], + } + } +} diff --git a/meowgb-core/src/gameboy/timer.rs b/meowgb-core/src/gameboy/timer.rs index 99c39b6..00d19bf 100644 --- a/meowgb-core/src/gameboy/timer.rs +++ b/meowgb-core/src/gameboy/timer.rs @@ -17,7 +17,7 @@ impl Timer { clock: TimerClock::C1024, tima: 0, tma: 0, - div: 0, + div: 0xAD, div_counter: 0, tima_counter: 0, overflow: false, @@ -50,7 +50,7 @@ impl Timer { } pub fn read_tac(&self) -> u8 { - ((self.enable as u8) << 2) | self.clock.tac_clock() + ((self.enable as u8) << 2) | self.clock.tac_clock() | 0b1111_1000 } pub fn write_tac(&mut self, value: u8) { diff --git a/meowgb-tests/expected_output/basic.bin b/meowgb-tests/expected_output/basic.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/basic.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/basic.gb b/meowgb-tests/expected_output/basic.gb new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/basic.gb @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/boot_hwio-dmgABCmgb.bin b/meowgb-tests/expected_output/boot_hwio-dmgABCmgb.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/boot_hwio-dmgABCmgb.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/boot_regs-dmgABC.bin b/meowgb-tests/expected_output/boot_regs-dmgABC.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/boot_regs-dmgABC.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/daa.bin b/meowgb-tests/expected_output/daa.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/daa.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/ei_sequence.bin b/meowgb-tests/expected_output/ei_sequence.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/ei_sequence.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/ei_timing.bin b/meowgb-tests/expected_output/ei_timing.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/ei_timing.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/halt_ime0_ei.bin b/meowgb-tests/expected_output/halt_ime0_ei.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/halt_ime0_ei.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/mem_oam.bin b/meowgb-tests/expected_output/mem_oam.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/mem_oam.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/oam_dma_restart.bin b/meowgb-tests/expected_output/oam_dma_restart.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/oam_dma_restart.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/oam_dma_start.bin b/meowgb-tests/expected_output/oam_dma_start.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/oam_dma_start.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/oam_dma_timing.bin b/meowgb-tests/expected_output/oam_dma_timing.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/oam_dma_timing.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/rapid_di_ei.bin b/meowgb-tests/expected_output/rapid_di_ei.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/rapid_di_ei.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/reg_f.bin b/meowgb-tests/expected_output/reg_f.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/reg_f.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/reg_read.bin b/meowgb-tests/expected_output/reg_read.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/reg_read.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/tim00_div_trigger.bin b/meowgb-tests/expected_output/tim00_div_trigger.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/tim00_div_trigger.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/tim01.bin b/meowgb-tests/expected_output/tim01.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/tim01.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/tim11_div_trigger.bin b/meowgb-tests/expected_output/tim11_div_trigger.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/tim11_div_trigger.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/meowgb-tests/expected_output/unused_hwio-GS.bin b/meowgb-tests/expected_output/unused_hwio-GS.bin new file mode 100644 index 0000000..2a02163 --- /dev/null +++ b/meowgb-tests/expected_output/unused_hwio-GS.bin @@ -0,0 +1 @@ + " \ No newline at end of file diff --git a/test-roms/mooneye-test-suite/LICENSE b/test-roms/mooneye-test-suite/LICENSE new file mode 100644 index 0000000..87f7a8e --- /dev/null +++ b/test-roms/mooneye-test-suite/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2022 Joonas Javanainen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/test-roms/mooneye-test-suite/README.md b/test-roms/mooneye-test-suite/README.md new file mode 100644 index 0000000..21a9de6 --- /dev/null +++ b/test-roms/mooneye-test-suite/README.md @@ -0,0 +1,5 @@ +# Mooneye Test Suite + +These test ROMs were written by "Gekkio", and are licensed by them under the MIT license, a copy of their license is in [LICENSE](./LICENSE). + +The original source code can be found at [https://github.com/Gekkio/mooneye-test-suite](https://github.com/Gekkio/mooneye-test-suite). diff --git a/test-roms/mooneye-test-suite/roms/basic.gb b/test-roms/mooneye-test-suite/roms/basic.gb new file mode 100644 index 0000000..c6d08bb Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/basic.gb differ diff --git a/test-roms/mooneye-test-suite/roms/boot_hwio-dmgABCmgb.gb b/test-roms/mooneye-test-suite/roms/boot_hwio-dmgABCmgb.gb new file mode 100644 index 0000000..6657ed8 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/boot_hwio-dmgABCmgb.gb differ diff --git a/test-roms/mooneye-test-suite/roms/boot_regs-dmgABC.gb b/test-roms/mooneye-test-suite/roms/boot_regs-dmgABC.gb new file mode 100644 index 0000000..8ea19f0 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/boot_regs-dmgABC.gb differ diff --git a/test-roms/mooneye-test-suite/roms/daa.gb b/test-roms/mooneye-test-suite/roms/daa.gb new file mode 100644 index 0000000..72e2727 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/daa.gb differ diff --git a/test-roms/mooneye-test-suite/roms/ei_sequence.gb b/test-roms/mooneye-test-suite/roms/ei_sequence.gb new file mode 100644 index 0000000..1e67b38 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/ei_sequence.gb differ diff --git a/test-roms/mooneye-test-suite/roms/ei_timing.gb b/test-roms/mooneye-test-suite/roms/ei_timing.gb new file mode 100644 index 0000000..46e7de5 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/ei_timing.gb differ diff --git a/test-roms/mooneye-test-suite/roms/halt_ime0_ei.gb b/test-roms/mooneye-test-suite/roms/halt_ime0_ei.gb new file mode 100644 index 0000000..ea8bdcc Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/halt_ime0_ei.gb differ diff --git a/test-roms/mealybug-tearoom-tests/roms/intr_1_2_timing-GS.gb b/test-roms/mooneye-test-suite/roms/intr_1_2_timing-GS.gb similarity index 100% rename from test-roms/mealybug-tearoom-tests/roms/intr_1_2_timing-GS.gb rename to test-roms/mooneye-test-suite/roms/intr_1_2_timing-GS.gb diff --git a/test-roms/mealybug-tearoom-tests/roms/intr_2_0_timing.gb b/test-roms/mooneye-test-suite/roms/intr_2_0_timing.gb similarity index 100% rename from test-roms/mealybug-tearoom-tests/roms/intr_2_0_timing.gb rename to test-roms/mooneye-test-suite/roms/intr_2_0_timing.gb diff --git a/test-roms/mooneye-test-suite/roms/mem_oam.gb b/test-roms/mooneye-test-suite/roms/mem_oam.gb new file mode 100644 index 0000000..8665fdc Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/mem_oam.gb differ diff --git a/test-roms/mooneye-test-suite/roms/oam_dma_restart.gb b/test-roms/mooneye-test-suite/roms/oam_dma_restart.gb new file mode 100644 index 0000000..c01ee3b Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/oam_dma_restart.gb differ diff --git a/test-roms/mooneye-test-suite/roms/oam_dma_start.gb b/test-roms/mooneye-test-suite/roms/oam_dma_start.gb new file mode 100644 index 0000000..df71501 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/oam_dma_start.gb differ diff --git a/test-roms/mooneye-test-suite/roms/oam_dma_timing.gb b/test-roms/mooneye-test-suite/roms/oam_dma_timing.gb new file mode 100644 index 0000000..4ffa037 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/oam_dma_timing.gb differ diff --git a/test-roms/mooneye-test-suite/roms/rapid_di_ei.gb b/test-roms/mooneye-test-suite/roms/rapid_di_ei.gb new file mode 100644 index 0000000..8d5677c Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/rapid_di_ei.gb differ diff --git a/test-roms/mooneye-test-suite/roms/reg_f.gb b/test-roms/mooneye-test-suite/roms/reg_f.gb new file mode 100644 index 0000000..21b9b7c Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/reg_f.gb differ diff --git a/test-roms/mooneye-test-suite/roms/reg_read.gb b/test-roms/mooneye-test-suite/roms/reg_read.gb new file mode 100644 index 0000000..fc756e9 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/reg_read.gb differ diff --git a/test-roms/mealybug-tearoom-tests/roms/stat_irq_blocking.gb b/test-roms/mooneye-test-suite/roms/stat_irq_blocking.gb similarity index 100% rename from test-roms/mealybug-tearoom-tests/roms/stat_irq_blocking.gb rename to test-roms/mooneye-test-suite/roms/stat_irq_blocking.gb diff --git a/test-roms/mealybug-tearoom-tests/roms/stat_lyc_onoff.gb b/test-roms/mooneye-test-suite/roms/stat_lyc_onoff.gb similarity index 100% rename from test-roms/mealybug-tearoom-tests/roms/stat_lyc_onoff.gb rename to test-roms/mooneye-test-suite/roms/stat_lyc_onoff.gb diff --git a/test-roms/mooneye-test-suite/roms/tim00_div_trigger.gb b/test-roms/mooneye-test-suite/roms/tim00_div_trigger.gb new file mode 100644 index 0000000..697ba03 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/tim00_div_trigger.gb differ diff --git a/test-roms/mooneye-test-suite/roms/tim01.gb b/test-roms/mooneye-test-suite/roms/tim01.gb new file mode 100644 index 0000000..fbc841e Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/tim01.gb differ diff --git a/test-roms/mooneye-test-suite/roms/tim11_div_trigger.gb b/test-roms/mooneye-test-suite/roms/tim11_div_trigger.gb new file mode 100644 index 0000000..bf30f17 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/tim11_div_trigger.gb differ diff --git a/test-roms/mooneye-test-suite/roms/unused_hwio-GS.gb b/test-roms/mooneye-test-suite/roms/unused_hwio-GS.gb new file mode 100644 index 0000000..8f898a1 Binary files /dev/null and b/test-roms/mooneye-test-suite/roms/unused_hwio-GS.gb differ