Compare commits

..

No commits in common. "25338bc0eee5a8a541c5629c8ba36227c1f1bbf8" and "dec36156df67a2d718c54c54bde3671738868bd3" have entirely different histories.

4 changed files with 142 additions and 268 deletions

View File

@ -14,7 +14,6 @@ pub struct Bus<M: Mapper> {
ppu: Ppu<M>, ppu: Ppu<M>,
apu: Ram<0x1F>,//TODO apu: Ram<0x1F>,//TODO
cartridge: Rc<RefCell<M>>, cartridge: Rc<RefCell<M>>,
tick_counter: u8,
} }
impl<M: Mapper> fmt::Debug for Bus<M> { impl<M: Mapper> fmt::Debug for Bus<M> {
@ -120,20 +119,11 @@ impl<M: Mapper> Bus<M> {
ppu: Ppu::new(mapper.clone(), screen, pattern_table), ppu: Ppu::new(mapper.clone(), screen, pattern_table),
apu: Ram::<0x1F>::new(), apu: Ram::<0x1F>::new(),
cartridge: mapper, cartridge: mapper,
tick_counter: 0,
} }
} }
pub fn tick(&mut self) { pub fn tick(&mut self) {
self.ppu.tick();
if self.tick_counter >= 3 {
self.tick_counter = 0;
self.ppu.tick();
} else {
self.tick_counter += 1;
}
} }
} }

View File

@ -151,7 +151,6 @@ pub struct Cpu<M: Mapper> {
s: u8, s: u8,
p: StatusReg, p: StatusReg,
bus: Bus<M>, bus: Bus<M>,
tick_counter: u8,
} }
impl<M: Mapper> fmt::Debug for Cpu<M> { impl<M: Mapper> fmt::Debug for Cpu<M> {
@ -190,23 +189,16 @@ impl<M: Mapper> Cpu<M> {
s: 0xFD, s: 0xFD,
p: StatusReg::from_bits(0x34).unwrap(), p: StatusReg::from_bits(0x34).unwrap(),
bus, bus,
tick_counter: 0,
} }
} }
pub fn tick(&mut self) { pub fn tick(&mut self) {
if self.tick_counter >= 11 { self.execute();
self.tick_counter = 0; self.bus.tick();
self.execute(); }
} else {
self.tick_counter += 1;
}
self.bus.tick(); fn execute(&mut self) {
}
fn execute(&mut self) {
let opcode = self.read_pc_addr(); let opcode = self.read_pc_addr();
// if self.pc-1 > 0x36D0 && self.pc-1 < 0x36D8 { // if self.pc-1 > 0x36D0 && self.pc-1 < 0x36D8 {

View File

@ -25,12 +25,6 @@ use canvas::{
}; };
use utils::DisplayBuffer; use utils::DisplayBuffer;
///Nanoseconds for 12 periods of the master clock (21.478 MHz). The frequency is divided by 12 since
///this is the CPU clock. All faster peripherals (i.e. the PPU, will be run whithout timing
///considerations whithing these 12 periods. This shouldn't affect the system's behavior while
///improving the clock's precision
const MASTER_CLOCK_PERIOD_NS: u128 = 559;
struct EmulatorState { struct EmulatorState {
pub screen: Rc<RefCell<DisplayBuffer>>, pub screen: Rc<RefCell<DisplayBuffer>>,
pub screen_sprite: TextureSprite, pub screen_sprite: TextureSprite,
@ -101,31 +95,15 @@ impl Application<EmulatorState> for NesEmulator {
fn tick(state: &mut EmulatorState, canvas: &mut Canvas) -> Result<(), &'static str> { fn tick(state: &mut EmulatorState, canvas: &mut Canvas) -> Result<(), &'static str> {
//save frame start for frame time computation let frame_time = state.start.elapsed().as_secs_f32();
let frame_start = state.start; state.start = Instant::now();
//tick the system for 1 frame
while state.screen.borrow_mut().is_ready == false {
let tick_period = state.start.elapsed().as_nanos();
//this should never fail as id frame_period doesn't fit in a u64, then the substraction
//saturated and the resulting 0 now fits
let delta = MASTER_CLOCK_PERIOD_NS.saturating_sub(tick_period) as u64;
std::thread::sleep(std::time::Duration::from_nanos(delta));
state.start = Instant::now();
for _ in 0..12 {
//TODO the cpu is ticked 12 times since MASTER_CLOCK_PERIOD_NS corresponds to 12
//clock periods. Further division is done in the function
state.cpu.tick();
}
}
state.screen.borrow_mut().is_ready = false;
//draw frame
let frame_time = frame_start.elapsed().as_secs_f32();
canvas.clear(); canvas.clear();
state.cpu.tick();
if state.screen.borrow_mut().is_ready {
state.screen.borrow_mut().is_ready = false;
}
canvas.draw(&mut state.screen_sprite); canvas.draw(&mut state.screen_sprite);
canvas.draw(&mut state.pattern_sprite); canvas.draw(&mut state.pattern_sprite);

View File

@ -2,259 +2,173 @@
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use std::{ use std::{
rc::Rc, rc::Rc,
cell::RefCell, cell::RefCell,
}; };
use bitflags::bitflags; use bitflags::bitflags;
use crate::{ use crate::{
peripherals::{Peripheral, Ram, Mapper}, peripherals::{Peripheral, Ram, Mapper},
utils::DisplayBuffer, utils::DisplayBuffer,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
bitflags! { bitflags! {
struct PpuCtrl: u8 { struct PpuCtrl: u8 {
const NL = 0x1 << 0; const Nl = 0x1 << 0;
const NH = 0x1 << 1; const Nh = 0x1 << 1;
const I = 0x1 << 2; const I = 0x1 << 2;
const S = 0x1 << 3; const S = 0x1 << 3;
const B = 0x1 << 4; const B = 0x1 << 4;
const H = 0x1 << 5; const H = 0x1 << 5;
const P = 0x1 << 6; const P = 0x1 << 6;
const V = 0x1 << 7; const V = 0x1 << 7;
} }
} }
bitflags! { bitflags! {
struct PpuMask: u8 { struct PpuMask: u8 {
const GR = 0x1 << 0; const Gr = 0x1 << 0;
const M_ = 0x1 << 1; const m = 0x1 << 1;
const M = 0x1 << 2; const M = 0x1 << 2;
const B_ = 0x1 << 3; const b = 0x1 << 3;
const S_ = 0x1 << 4; const s = 0x1 << 4;
const R = 0x1 << 5; const R = 0x1 << 5;
const G = 0x1 << 6; const G = 0x1 << 6;
const B = 0x1 << 7; const B = 0x1 << 7;
} }
} }
bitflags! { bitflags! {
struct PpuStatus: u8 { struct PpuStatus: u8 {
const O = 0x1 << 5; const O = 0x1 << 5;
const S = 0x1 << 6; const S = 0x1 << 6;
const V = 0x1 << 7; const V = 0x1 << 7;
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
pub struct Ppu<M: Mapper> { pub struct Ppu<M: Mapper> {
//peripherals vbus: Rc<RefCell<M>>,
vbus: Rc<RefCell<M>>, ppu_ctrl: PpuCtrl,
ppu_mask: PpuMask,
ppu_status: PpuStatus,
scroll: u8,
addr: u8,
data: u8,
oam_addr: u8,
oam_data: u8,
oam_dma: u8,
oam: Ram<0x100>,
blanking: bool,
line: u16,
dot: u16,
nmi: bool,
//public registers // outputs
ppu_ctrl: PpuCtrl, screen: Rc<RefCell<DisplayBuffer>>,
ppu_mask: PpuMask, pattern_table: Rc<RefCell<DisplayBuffer>>,
ppu_status: PpuStatus,
scroll: u8,
addr: u8,
data: u8,
//OAM
oam_addr: u8,
oam_data: u8,
oam_dma: u8,
oam: Ram<0x100>,
blanking: bool,
line: u16,
dot: u16,
nmi: bool,
odd: bool,
// outputs
screen: Rc<RefCell<DisplayBuffer>>,
pattern_table: Rc<RefCell<DisplayBuffer>>,
} }
impl<M: Mapper> Peripheral for Ppu<M> { impl<M: Mapper> Peripheral for Ppu<M> {
fn read_addr(&self, _addr: u16) -> u8 { fn read_addr(&self, _addr: u16) -> u8 {
unimplemented!(); unimplemented!();
} }
fn write_addr(&mut self, _addr: u16, _data: u8) { fn write_addr(&mut self, _addr: u16, _data: u8) {
unimplemented!(); unimplemented!();
} }
} }
impl<M: Mapper> Ppu<M> { impl<M: Mapper> Ppu<M> {
pub fn new(mapper: Rc<RefCell<M>>, pub fn new(mapper: Rc<RefCell<M>>,
screen: Rc<RefCell<DisplayBuffer>>, screen: Rc<RefCell<DisplayBuffer>>,
pattern_table: Rc<RefCell<DisplayBuffer>>) pattern_table: Rc<RefCell<DisplayBuffer>>)
-> Self { -> Self {
Self { Self {
vbus: mapper, vbus: mapper,
ppu_ctrl: PpuCtrl::from_bits(0).unwrap(), ppu_ctrl: PpuCtrl::from_bits(0).unwrap(),
ppu_mask: PpuMask::from_bits(0).unwrap(), ppu_mask: PpuMask::from_bits(0).unwrap(),
ppu_status: PpuStatus::from_bits(0).unwrap(), ppu_status: PpuStatus::from_bits(0).unwrap(),
scroll: 0, scroll: 0,
addr: 0, addr: 0,
data: 0, data: 0,
oam_addr: 0, oam_addr: 0,
oam_data: 0, oam_data: 0,
oam_dma: 0, oam_dma: 0,
oam: Ram::<0x100>::new(), oam: Ram::<0x100>::new(),
blanking: false, blanking: false,
line: 0, line: 0,
dot: 0, dot: 0,
nmi: false, nmi: false,
odd: false,
screen, screen,
pattern_table, pattern_table,
} }
} }
pub fn poll_nmi(&mut self) -> bool { pub fn poll_nmi(&mut self) -> bool {
let ret = self.nmi; let ret = self.nmi;
if self.nmi { if self.nmi {
self.nmi = false; self.nmi = false;
} }
ret ret
} }
pub fn tick(&mut self) { pub fn tick(&mut self) {
use crate::utils::Pixel; use crate::utils::Pixel;
// self.screen.borrow_mut() self.screen.borrow_mut()
// .set_pixel( .set_pixel(
// self.dot.into(), self.dot.into(),
// self.line.into(), self.line.into(),
// Pixel::rgba(self.data.into(), 0, 0, 255)); Pixel::rgba(self.data.into(), 0, 0, 255));
//
self.execute();
}
fn execute(&mut self) { self.execute();
}
match self.line { fn execute(&mut self) {
0..=239 => self.visible_line(),
241 => {
//start of vertical blank
self.post_render_line();
if self.dot == 1 {
}
}
261 => {
//end of vertical blank
self.pre_render_line();
}
240..=260 => (), //vertical blank
_ => panic!("{}: Invalid line number", self.line),
}
//increment current dot and line match self.line {
self.dot += 1; 0..=239 => self.render_frame(),
if self.dot > 340 { 241 => {
self.dot = 0; if self.dot == 1 {
self.line += 1; self.ppu_status.set(PpuStatus::V, true);
if self.line > 261 { }
self.line = 0; }
} 261 => {
} if self.dot == 1 {
} self.ppu_status.set(PpuStatus::V | PpuStatus::S | PpuStatus::O, false);
}
self.render_frame();
fn visible_line(&mut self) { //last line, got back to first line
match self.dot { self.line = 0;
0 => (), //nothing done on first cycle }
240..=260 => (), //vertical blank
_ => panic!("{}: Invalid line number", self.line),
}
1..=256 | 321..=336 => { self.dot = (self.dot + 1) % 256;
//background if self.dot == 0 {
match self.dot % 8 { self.line = (self.line + 1) % 240;
0 => (), //render inc hori v, BG msbit if self.line == 0 {
2 => (), //load NT self.screen.borrow_mut().is_ready = true;
4 => (), //load AT self.data = (self.data + 1) % 255;
6 => (), //BG lsbit }
_ => (), //idle clock cycle }
} }
//OAM
match self.dot {
1..=64 => (), //secondary OAM clear
65..=256 => (), //sprite evalutation for next scanline
_ => (), //idle clock cycle
}
},
257 => (), // hori(v) = hori(t)
//sprites fn render_frame(&mut self) {
258..=320 => {
//sprites
match self.dot % 8 {
0 => (), //sprite msbit
2 => (), //garbage NT
4 => (), //garbageAT
6 => (), //sprite lsbit
_ => (), //idle clock cycle
}
},
_ => (), //idle or unused clock cycles
}
}
fn pre_render_line(&mut self) {
match self.dot {
0 => (),
//background
1 => {
//clear vblank, sprite 0, overflow
self.ppu_status.set(PpuStatus::V | PpuStatus::S | PpuStatus::O, false);
},
2..=248 | 321..=336 => match self.dot % 8 {
0 => (), //render inc hori v, BG msbit
2 => (), //load NT
4 => (), //load AT
6 => (), //BG lsbit
_ => (), //idle clock cycle
}
257 => (), // hori(v) = hori(t)
258..=320 => {
//sprites
match self.dot % 8 {
0 => (), //sprite msbit
2 => (), //garbage NT
4 => (), //garbageAT
6 => (), //sprite lsbit
_ => (), //idle clock cycle
}
//miscelaneous
match self.dot {
280..=304 => (), //vert(v) = vert(t)
_ => (), //idle clock cycle
}
},
339 => if self.odd { self.dot = 340 }, //skip on clock cycle every 2 frames
_ => (), //idle or unused clock cycles
}
}
fn post_render_line(&mut self) {
match self.dot {
1 => {
self.ppu_status.set(PpuStatus::V, true);
self.screen.borrow_mut().is_ready = true; //screen can be displayed
},
_ => (), //idle or unused clock cycles
}
}
match self.dot {
0 => (),
33..=40 => (),
_ => (),
}
}
} }