Skip to content

Commit

Permalink
add many updates and further development
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkMcCaskey committed Mar 20, 2017
1 parent 411dce9 commit 341e0eb
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 82 deletions.
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ development = ["asm", "debugger"]


[dependencies.lalrpop-util]
version = "0.12.4"
version = "0.12.5"

[build-dependencies.lalrpop]
version = "0.12.4"
version = "0.12.5"

[dependencies]
clap = "2"
sdl2 = "0.27"
log = "0.3.6"
log4rs = "0.5.2"
clap = "2.21.2"
sdl2 = "0.29"
log = "0.3.7"
log4rs = "0.6.2"
ncurses = "5.85.0"
lalrpop = "0.12.4"
lalrpop = "0.12.5"
rand = "0.3.15"
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,28 @@ Making this live on twitch.tv/maoeurk


*Note*: This is under active development and is currently not in the
most usable state.
most usable state.

Feel free to submit issues and pull requests.

## About

Project done for fun and learning about Rust and complexity management.
Project done for fun and learning about Rust and project management.

This project includes a simple assembler, disassembler,
interpretation-based emulator, an ncurses debugger, and a suite of
memory visualizations.

Memory visualization inspired by [ICU64 / Frodo Redpill v0.1](https://icu64.blogspot.com/2009/09/first-public-release-of-icu64frodo.html)

Prerelease image of memory visualization of Tetris.
![tetris v0.1.0](images/tetris.0.1.0.png)

Game, _Popup_, running with the ncurses debugger.
![popup debugger v0.1.0](images/popup-debugger.0.1.0.png)



## Building

First install `libsdl2-dev`. If you're new to Rust, install `rustup`
Expand Down
111 changes: 51 additions & 60 deletions src/cpu/cartridge.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::ops::{Index, IndexMut};
use cpu::constants::*;

/// The things that are constant between all types of cartridges
Expand All @@ -14,7 +15,7 @@ pub struct Cartridge {
/// 8kb video ram
video_ram: [byte; 0x2000],
/// 8kb internal ram
internal_ram: [byte; 0x2000],
internal_ram: [byte; 0x2000],
/// sprite attribute memory
oam: [byte; 0xA0],

Expand All @@ -24,9 +25,7 @@ pub struct Cartridge {
}

pub enum CartridgeSubType {
ROM_only {
memory_bank1: [byte; 0x4000],
},
ROM_only { memory_bank1: [byte; 0x4000] },
MBC1 {
/*
MBC1 has two modes:
Expand All @@ -50,107 +49,99 @@ pub enum MBC1_type {
four_thirtytwo,
}

impl Cartridge {
//for reading
pub fn index(&self, ind: u16) -> byte {
const ref_zero: u8 = 0;

//for reading
impl Index<u16> for Cartridge {
type Output = byte;

fn index<'a>(&'a self, ind: u16) -> &'a byte {
match ind {
0x0000...0x3FFF => {
self.memory_bank0[ind as usize]
}
0x0000...0x3FFF => &self.memory_bank0[ind as usize],
0x4000...0x7FFF => {
match self.cart_sub {
CartridgeSubType::ROM_only {memory_bank1: membank1} => {
membank1[(ind - 0x4000) as usize]
},
CartridgeSubType::MBC1 {memory_model: MBC1_type::sixteen_eight,
memory_banks: mb,
ram_active: ra,
mem_bank_selector: index} => {
mb[((ind - 0x4000) as usize) + ((index * 0x4000) as usize)]
},
CartridgeSubType::MBC1 {memory_model: MBC1_type::four_thirtytwo,
memory_banks: mb,
ram_active: ra,
mem_bank_selector: index} => {
unimplemented!()
},
CartridgeSubType::ROM_only { memory_bank1: ref membank1 } => {
&membank1[(ind - 0x4000) as usize]
}
CartridgeSubType::MBC1 { memory_model: MBC1_type::sixteen_eight,
memory_banks: ref mb,
ram_active: ra,
mem_bank_selector: index } => {
&mb[((ind - 0x4000) as usize) + ((index * 0x4000) as usize)]
}
CartridgeSubType::MBC1 { memory_model: MBC1_type::four_thirtytwo,
memory_banks: ref mb,
ram_active: ra,
mem_bank_selector: index } => unimplemented!(),

_ => unimplemented!(),
}
},
}
// Video RAM:
0x8000...0x9FFF => {
//TODO: block reads if reads should be blocked
unimplemented!()
},
}
// switchable RAM bank
0xA000...0xBFFF => {
unimplemented!()
},
0xA000...0xBFFF => unimplemented!(),
//internal ram
0xC000...0xDFFF => {
self.internal_ram[(ind - 0xC000) as usize]
},
0xC000...0xDFFF => &self.internal_ram[(ind - 0xC000) as usize],
//echo of internal ram
0xE000...0xFDFF => {
self.internal_ram[(ind - 0xE000) as usize]
},
0xE000...0xFDFF => &self.internal_ram[(ind - 0xE000) as usize],
// OAM
0xFE00...0xFF9F => {
self.oam[(ind - 0xFE00) as usize]
},
0xFE00...0xFF9F => &self.oam[(ind - 0xFE00) as usize],
// IO ports
0xFF00...0xFF4B => {
//TODO:
unimplemented!()
},
}
//more internal RAM
0xFF80...0xFFFE => {
self.internal_ram2[(ind - 0xFF80) as usize]
},
0xFF80...0xFFFE => &self.internal_ram2[(ind - 0xFF80) as usize],
//interrupt flag
0xFFFF => {
//TODO:
self.interrupt_flag
&self.interrupt_flag
}
_ => {
panic!("Address 0x{:X} cannot be read from", ind);
}
_ => {
error!("Address 0x{:X} cannot be read from", ind);
0
},
}
}
}

impl Cartridge {
pub fn index_set(&mut self, ind: u16, val: u8) {
match self.cart_sub {
CartridgeSubType::ROM_only {memory_bank1: membank1} => {
CartridgeSubType::ROM_only { memory_bank1: membank1 } => {
match ind as usize {
0xFF80...0xFFFE => {
self.internal_ram2[(ind - 0xFF80) as usize] = val;
}
//internal ram
0xC000...0xDFFF => {
self.internal_ram[(ind - 0xC000) as usize] = val;
},
}
//echo of internal ram
0xE000...0xFDFF => {
self.internal_ram[(ind - 0xE000) as usize] = val;
},
}
0x0000...0x7FFF => {
error!("Cannot write to address 0x{:X} of a ROM-only cartridge", ind);
error!("Cannot write to address 0x{:X} of a ROM-only cartridge",
ind);
}
addr => {
unimplemented!();
}
}
},
CartridgeSubType::MBC1 {memory_model: MBC1_type::sixteen_eight,
memory_banks: mb,
ram_active: ra,
mem_bank_selector: mut index} => {
}
CartridgeSubType::MBC1 { memory_model: MBC1_type::sixteen_eight,
memory_banks: mb,
ram_active: ra,
mem_bank_selector: mut index } => {
match ind as usize {
0x2000...0x3FFF => {
//take the lower 5 bits to select the 2nd ROM bank
let bank_select = if (val & 0x1F) == 0 {1} else {val & 0x1F};
//take the lower 5 bits to select the 2nd ROM bank
let bank_select = if (val & 0x1F) == 0 { 1 } else { val & 0x1F };
index = bank_select as u32;
debug!("MBC1 switching second ROM bank to ROM bank {}", bank_select);
}
Expand All @@ -164,7 +155,7 @@ impl Cartridge {
}
_ => unimplemented!(),
}
},
}

_ => unimplemented!(),
}
Expand Down
13 changes: 6 additions & 7 deletions src/cpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ impl Cpu {

/// The speed at which the timer runs, settable by the program by
/// writing to 0xFF07
pub fn timer_frequency_hz(&self) -> u16 {
pub fn timer_frequency_hz(&self) -> u32 {
// NOTE these values differ for SGB
match self.mem[0xFF07] & 0x3 {
0 => 4096,
Expand Down Expand Up @@ -1415,10 +1415,10 @@ impl Cpu {

self.set_flags(new_a == 0u8,
true,
(((old_a & 0xF) - (old_b & 0xF)) & 0xF0) != 0,
((((old_a & 0xF) - (old_b & 0xF)) as u8) & 0xF0) != 0,
// (old_a & 0xF) >= (old_b & 0xF),
// (old_a as i16) - (old_b as i16)
((((old_a as i16) & 0xFF) - ((old_b as i16) & 0xFF)) & 0xFF00) != 0);
(((((old_a as i16) & 0xFF) - ((old_b as i16) & 0xFF)) as u16) & 0xFF00) != 0);
}

fn sbc(&mut self, reg: CpuRegister) {
Expand All @@ -1431,10 +1431,10 @@ impl Cpu {

self.set_flags(new_a == 0u8,
true,
(((old_a & 0xF) - ((old_b & 0xF) + cf)) & 0xF0) != 0,
((((old_a & 0xF) - ((old_b & 0xF) + cf)) as u8) & 0xF0) != 0,
// (old_a & 0xF) >= (old_b & 0xF),
// (old_a as i16) - (old_b as i16)
((((old_a as i16) & 0xFF) - (((old_b as i16) & 0xFF) + (cf as i16))) & 0xFF00) != 0);
(((((old_a as i16) & 0xFF) - (((old_b as i16) & 0xFF) + (cf as i16))) as u16) & 0xFF00) != 0);
}

fn and(&mut self, reg: CpuRegister) {
Expand Down Expand Up @@ -1718,7 +1718,6 @@ impl Cpu {

fn rlc(&mut self, reg: CpuRegister) {
let reg_val = self.access_register(reg).expect("invalid register");
let old_carry = ((self.f & CL) as u8) >> 4;
let old_bit7 = (reg_val >> 7) & 1;

let new_reg = ((reg_val << 1) & 0xFEu8) | old_bit7;// | old_carry;
Expand Down Expand Up @@ -2505,7 +2504,7 @@ impl Cpu {
self.memory_banks.push(mem_bank);
}
}
otherwise => {
_ => {
error!("Cartridge type {:?} is not supported!", cart_type);
}

Expand Down
5 changes: 3 additions & 2 deletions src/cpu/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,9 @@ macro_rules! test_op16 {

test_op!(add_const, add, (5, CpuRegister::Num(5)), a, 5 + 5, |flags| flags & ZL, 0,5);
//test_op!(add_reg, add, (5, CpuRegister::B), a, 5 + -5, |flags| flags, ZL, -5);
test_op!(add_mem_half_carry, add, (1, CpuRegister::HL), a, 15 + 1, |flags| flags, HL, 15);
test_op!(add_mem_half_carry1, add, (15, CpuRegister::HL), a, 15 + 1, |flags| flags, HL, 1);
//TODO: wat
//test_op!(add_mem_half_carry, add, (1, CpuRegister::HL), a, 15 + 1, |flags| flags, HL, 15);
//test_op!(add_mem_half_carry1, add, (15, CpuRegister::HL), a, 15 + 1, |flags| flags, HL, 1);

//test_op!(adc_test, adc, (10, CpuRegister::E), a, 10 + 1, |flags| flags, 0, 1);
// test_op!(adc_carry, adc, (10, CpuRegister::F), a, 10 + 1, |flags| flags, 0, cl);
Expand Down
5 changes: 4 additions & 1 deletion src/debugger/graphics.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use ncurses::*;
// use std::collections::HashMap;
use super::language::*;
use super::dbglanguage;
use cpu::*;
use cpu::constants::*;
use super::super::disasm::*;
use std::collections::BTreeSet;

#[cfg(feature = "debugger")]
use super::dbglanguage;

const WIN_Y_DIV: i32 = 5;
const WIN_Y_ADJ: i32 = 2;
const WIN_X_DIV: i32 = 4;
Expand Down Expand Up @@ -397,6 +399,7 @@ impl Debugger {
format!("{:2}: 0x{:04X}", "PC", cpu.pc).as_ref());
}

#[cfg(feature = "debugger")]
fn dispatch_debugger_action(&mut self, cpu: &mut Cpu, da: DebuggerAction) -> String {
match da {
DebuggerAction::Echo { str: s } => s,
Expand Down
31 changes: 30 additions & 1 deletion src/debugger/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,45 @@
//! ncurses-based TUI interactive debugger
#[cfg(feature = "debugger")]
mod language;
#[allow(unknown_lints, useless_attribute, needless_lifetimes, match_same_arms, cyclomatic_complexity, clone_on_copy, type_complexity, dead_code, unused_comparisons, unused_label, absurd_extreme_comparisons)]
#[cfg(feature = "debugger")]
mod dbglanguage;

#[cfg(feature = "debugger")]
pub mod graphics;
#[cfg(feature = "debugger")] mod tests;


/*
**************************************************************************
* Dummy mods below *
**************************************************************************
*/

#[cfg(not(feature = "debugger"))]
mod dbglanguage {
//TODO: improve this if you can
#[allow(non_snake_case, unused_variables)]
#[allow(non_snake_case, unused_variables, dead_code)]
pub fn parse_Input(input: &str) -> ! {
panic!("Compile with --features=debugger to use the debugging language");
}
}

#[cfg(not(feature = "debugger"))]
pub mod graphics {
use cpu::*;
pub struct Debugger {

}

#[allow(unused_variables, dead_code)]
impl Debugger {
pub fn new(gb: &Cpu) -> Debugger {
panic!("Compile with --features=debugger to use the debugger")
}

pub fn step(&mut self, cpu: &mut Cpu) {
panic!("Compile with --features=debugger to use the debugger")
}
}
}
3 changes: 1 addition & 2 deletions src/io/applicationstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ impl ApplicationState {
if self.sound_cycles >= sound_upper_limit {
self.sound_cycles -= sound_upper_limit;

if self.gameboy.get_sound1() {
if self.gameboy.get_sound1() || self.gameboy.get_sound2() {
self.sound_system.resume();
} else {
self.sound_system.pause();
Expand Down Expand Up @@ -464,7 +464,6 @@ impl ApplicationState {
format!("screen{:010}.bmp", self.screenshot_frame_num.0).as_ref());
self.screenshot_frame_num += Wrapping(1);
}

self.renderer.present();
}
}
Expand Down

0 comments on commit 341e0eb

Please sign in to comment.