Skip to content

Commit

Permalink
Merge pull request #86 from qoda-dev/feature/pre-release
Browse files Browse the repository at this point in the history
Feature/pre release
  • Loading branch information
nkrs-lab authored Apr 23, 2023
2 parents 5ae9f2c + ee55f0e commit accf3d0
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 81 deletions.
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "Qoboy"
version = "0.1.0"
name = "qoboy"
version = "1.0.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,17 @@ Source files can be found [here](https://github.com/mattcurrie/dmg-acid2). This

| test rom | Comment | Result |
| -------- | ------- | ------ |
| dmg_acid2 | none | :x: |
| dmg_acid2 | sprite priority follows GB color behaviour | :x: |

## Features

- [X] implement a gameboy emulator which passes all cpu_instr and instr_timing tests
- [X] add support to no_mbc / mbc1 / mbc3 cartridge types
- [X] implement a lightweight debugger
- [X] implement a vram viewer
- [ ] fix sprite priority to pass ACID2 test
- [ ] add possibility to save a game
- [ ] use winit and softbuffer instead of minifb (which is not as stable as expected)

## Ressources

Expand Down
1 change: 1 addition & 0 deletions src/debug.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::emulator::{Emulator, EmulatorState, ONE_FRAME_IN_NS, ONE_FRAME_IN_CYCLES};
use crate::soc::peripheral::IoAccess;
use std::time::Instant;

use std::io::{stdin, stdout, Write};
Expand Down
45 changes: 23 additions & 22 deletions src/soc/cpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use instruction::{
};
use register::Registers;

use crate::soc::peripheral::{Peripheral, VBLANK_VECTOR, LCDSTAT_VECTOR, TIMER_VECTOR};
use crate::soc::peripheral::{IoAccess, Interrupt, VBLANK_VECTOR, LCDSTAT_VECTOR, TIMER_VECTOR};
use crate::soc::peripheral::nvic::InterruptSources;

const RUN_0_CYCLE: u8 = 0;
Expand Down Expand Up @@ -485,7 +485,7 @@ macro_rules! reset {

macro_rules! interrupt_enable {
($enable: ident, $self:ident, $peripheral:expr) => {{
$peripheral.nvic.master_enable($enable);
$peripheral.master_enable($enable);
$self.pc.wrapping_add(1)
}};
}
Expand Down Expand Up @@ -737,7 +737,7 @@ impl Cpu {
}
}

fn decode(&mut self, instruction_byte: u8, peripheral: &mut Peripheral) -> Option<Instruction> {
fn decode<T: IoAccess>(&mut self, instruction_byte: u8, peripheral: &mut T) -> Option<Instruction> {
if Instruction::is_long_instruction(instruction_byte) {
let long_instruction_byte = peripheral.read(self.pc.wrapping_add(1));
Instruction::from_long_byte(long_instruction_byte)
Expand All @@ -746,9 +746,9 @@ impl Cpu {
}
}

pub fn run(&mut self, peripheral: &mut Peripheral) -> u8 {
pub fn run<T: IoAccess + Interrupt>(&mut self, peripheral: &mut T) -> u8 {
// catch interrupt as soon as possible
if peripheral.nvic.is_an_interrupt_to_run() {
if peripheral.is_an_interrupt_to_run() {
self.mode = CpuMode::INTERRUPT;
}

Expand All @@ -775,11 +775,11 @@ impl Cpu {

CpuMode::INTERRUPT => {
// get the interrupt source
if let Some(interrupt_source) = peripheral.nvic.get_interrupt() {
if let Some(interrupt_source) = peripheral.get_interrupt() {
// set the cpu in RUN mode to handle interrupt routine
self.mode = CpuMode::RUN;
// disable interrupts while handling interrupt routine
peripheral.nvic.master_enable(false);
peripheral.master_enable(false);
// jump to interrupt routine
self.jump_to_interrupt_routine(interrupt_source, peripheral);
// 2 NOP (2 cycles) + PUSH (2 cycles) + set PC (1 cycle)
Expand All @@ -793,7 +793,7 @@ impl Cpu {

CpuMode::HALT => {
// exit HALT mode if an interrupt is pending
if peripheral.nvic.is_an_interrupt_pending() {
if peripheral.is_an_interrupt_pending() {
self.mode = CpuMode::RUN
}

Expand All @@ -809,7 +809,7 @@ impl Cpu {
}
}

fn jump_to_interrupt_routine(&mut self, interrupt_source: InterruptSources, peripheral: &mut Peripheral) {
fn jump_to_interrupt_routine<T: IoAccess>(&mut self, interrupt_source: InterruptSources, peripheral: &mut T) {
self.push(self.pc, peripheral);
match interrupt_source {
InterruptSources::VBLANK => self.pc = VBLANK_VECTOR,
Expand All @@ -819,7 +819,7 @@ impl Cpu {
}
}

fn execute(&mut self, instruction: Instruction, peripheral: &mut Peripheral) -> (u16, u8) {
fn execute<T: IoAccess + Interrupt>(&mut self, instruction: Instruction, peripheral: &mut T) -> (u16, u8) {
match instruction {
// Arithmetic instructions
Instruction::ADD(target) => arithmetic_instruction!(target, self.add, peripheral),
Expand Down Expand Up @@ -1017,7 +1017,7 @@ impl Cpu {
new_value
}

fn load(&mut self, input_register: ArithmeticTarget, main_register: IncDecTarget, peripheral: &mut Peripheral) -> (u16, u8) {
fn load<T: IoAccess>(&mut self, input_register: ArithmeticTarget, main_register: IncDecTarget, peripheral: &mut T) -> (u16, u8) {
match main_register {
IncDecTarget::A => load_input_register!(input_register => a, self, peripheral),
IncDecTarget::B => load_input_register!(input_register => b, self, peripheral),
Expand All @@ -1030,7 +1030,7 @@ impl Cpu {
}
}

fn load_sp(&mut self, target: SPTarget, peripheral: &mut Peripheral) -> (u16, u8) {
fn load_sp<T: IoAccess>(&mut self, target: SPTarget, peripheral: &mut T) -> (u16, u8) {
match target {
SPTarget::FROM_SP => ({
let low_byte_address = peripheral.read(self.pc.wrapping_add(1)) as u16;
Expand Down Expand Up @@ -1071,7 +1071,7 @@ impl Cpu {
}
}

fn load_store_ram(&mut self, target: RamTarget, load: bool, peripheral: &mut Peripheral) -> (u16, u8) {
fn load_store_ram<T: IoAccess>(&mut self, target: RamTarget, load: bool, peripheral: &mut T) -> (u16, u8) {
match target {
RamTarget::OneByteAddress => ({
// get address from instruction
Expand Down Expand Up @@ -1128,7 +1128,7 @@ impl Cpu {
}
}

fn jump_relative(&mut self, flag: bool, peripheral: &mut Peripheral) -> (u16, u8) {
fn jump_relative<T: IoAccess>(&mut self, flag: bool, peripheral: &mut T) -> (u16, u8) {
// get the immediate from memory
let immediate_address = self.pc.wrapping_add(1);
let immediate = peripheral.read(immediate_address) as i8 as u16;
Expand All @@ -1142,7 +1142,7 @@ impl Cpu {
}
}

fn jump_immediate(&mut self, flag: bool, peripheral: &mut Peripheral) -> (u16, u8) {
fn jump_immediate<T: IoAccess>(&mut self, flag: bool, peripheral: &mut T) -> (u16, u8) {
// get the immediate from memory
let low_immediate = peripheral.read(self.pc.wrapping_add(1)) as u16;
let high_immediate = peripheral.read(self.pc.wrapping_add(2)) as u16;
Expand All @@ -1161,7 +1161,7 @@ impl Cpu {
self.registers.read_hl()
}

fn pop(&mut self, peripheral: &mut Peripheral) -> u16 {
fn pop<T: IoAccess>(&mut self, peripheral: &mut T) -> u16 {
// get stack pointer values
let low_stack_address = self.sp;
let high_stack_address = self.sp.wrapping_add(1);
Expand All @@ -1173,7 +1173,7 @@ impl Cpu {
low_byte | (high_byte << 8)
}

fn push(&mut self, push_data: u16, peripheral: &mut Peripheral) {
fn push<T: IoAccess>(&mut self, push_data: u16, peripheral: &mut T) {
// get bytes from data
let high_byte = ((push_data & 0xFF00) >> 8) as u8;
let low_byte = (push_data & 0x00FF) as u8;
Expand All @@ -1187,7 +1187,7 @@ impl Cpu {
self.sp = self.sp.wrapping_sub(2);
}

fn add_sp(&mut self, peripheral: &mut Peripheral) -> u16 {
fn add_sp<T: IoAccess>(&mut self, peripheral: &mut T) -> u16 {
let immediate = peripheral.read(self.pc.wrapping_add(1)) as i8 as u16;
let result = self.sp.wrapping_add(immediate);

Expand All @@ -1203,19 +1203,19 @@ impl Cpu {
self.pc.wrapping_add(2)
}

fn reset(&mut self, addr_to_reset: u8, peripheral: &mut Peripheral) -> u16 {
fn reset<T: IoAccess>(&mut self, addr_to_reset: u8, peripheral: &mut T) -> u16 {
// save PC value on the stack
self.push(self.pc.wrapping_add(1), peripheral);
// return next PC value
addr_to_reset as u16
}

fn reti(&mut self, peripheral: &mut Peripheral) -> u16 {
peripheral.nvic.master_enable(true);
fn reti<T: IoAccess + Interrupt>(&mut self, peripheral: &mut T) -> u16 {
peripheral.master_enable(true);
self.pop(peripheral)
}

fn call(&mut self, flag: bool, peripheral: &mut Peripheral) -> (u16, u8) {
fn call<T: IoAccess>(&mut self, flag: bool, peripheral: &mut T) -> (u16, u8) {
// do the call following the flag value
if flag {
// save the return address on the stack
Expand Down Expand Up @@ -1469,6 +1469,7 @@ mod cpu_tests {
IncDecTarget, JumpTarget, Load16Target, PopPushTarget, ResetTarget, SPTarget, U16Target,
};
use crate::cartridge::{Cartridge, CARTRIDGE_TYPE_OFFSET, CARTRIDGE_RAM_SIZE_OFFSET, CARTRIDGE_ROM_SIZE_OFFSET};
use crate::soc::peripheral::Peripheral;

#[test]
fn test_add_registers() {
Expand Down
Loading

0 comments on commit accf3d0

Please sign in to comment.