Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a fast mode #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"rust-analyzer.linkedProjects": [
"Cargo.toml",
"./desktop/Cargo.toml",
"./3ds/Cargo.toml"
]
}
82 changes: 47 additions & 35 deletions 3ds/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,16 @@ const TOP_SCREEN_HEIGHT: usize = 240; // p
const BOTTOM_SCREEN_WIDTH: usize = 320; // px
const BOTTOM_SCREEN_HEIGHT: usize = 240; // px
const SAMPLE_RATE: f64 = 44_100.0; // Hz
const ROM_BYTES: &[u8] = include_bytes!("../assets/Kirby's Adventure.nes");
const ROM_BYTES: &[u8] = include_bytes!("../assets/Super Mario Bros.nes");
const LEFT_X_OFFSET_TOP: usize = (TOP_SCREEN_WIDTH - NES_SCREEN_WIDTH) / 2;
const LEFT_X_OFFSET_BOTTOM: usize = (BOTTOM_SCREEN_WIDTH - NES_SCREEN_WIDTH) / 2;
static LOGO: &[u8] = include_bytes!("../assets/logo.rgb");

fn is_key_active(hid: &Hid, key: KeyPad) -> bool {
hid.keys_down().contains(key) || hid.keys_held().contains(key)
hid.keys_held().contains(key)
}

fn main() {
let mut nes_frame_buffer: [u8; FRAME_BUFFER_BYTE_SIZE] = [0; FRAME_BUFFER_BYTE_SIZE];
let mut nes_frame_buffer = [0u8; FRAME_BUFFER_BYTE_SIZE];
ctru::use_panic_handler();

let gfx = Gfx::new().expect("Couldn't obtain GFX controller");
Expand All @@ -35,25 +34,18 @@ fn main() {
// println!("\x1b[0;3HPress L + R to exit.");

let mut top_screen = gfx.top_screen.borrow_mut();
let mut bottom_screen = gfx.bottom_screen.borrow_mut();
bottom_screen.set_double_buffering(false);
bottom_screen.swap_buffers();

// We don't need double buffering in this example.
// In this way we can draw our image only once on screen.
top_screen.set_double_buffering(false);
top_screen.swap_buffers();

let rom = ROM::new(ROM_BYTES.to_vec()).expect("Couldn't load ROM");
let mut nes = Nes::new(rom, SAMPLE_RATE);
let mut nes_frame_buffer = [0u8; FRAME_BUFFER_BYTE_SIZE];
let mut top_frame_buffer = [0u8; TOP_SCREEN_WIDTH * TOP_SCREEN_HEIGHT * 3];
let mut bottom_frame_buffer = [0u8; BOTTOM_SCREEN_WIDTH * BOTTOM_SCREEN_HEIGHT * 3];

let bottom_offset = LEFT_X_OFFSET_BOTTOM * BOTTOM_SCREEN_HEIGHT * 3;
bottom_frame_buffer[bottom_offset..(LOGO.len() + bottom_offset)].copy_from_slice(LOGO);
// let mut last_frame = std::time::Instant::now();

// Main loop
while apt.main_loop() {
// let frame_duration = last_frame.elapsed();
// last_frame = std::time::Instant::now();
// Scan all the inputs. This should be done once for each frame
hid.scan_input();

Expand All @@ -71,36 +63,47 @@ fn main() {
joypad1_state.insert(JoypadStatus::SELECT);
}

if is_key_active(&hid, KeyPad::A) {
if is_key_active(&hid, KeyPad::A) || is_key_active(&hid, KeyPad::Y) {
joypad1_state.insert(JoypadStatus::A);
}

if is_key_active(&hid, KeyPad::B) {
if is_key_active(&hid, KeyPad::B) || is_key_active(&hid, KeyPad::X) {
joypad1_state.insert(JoypadStatus::B);
}

if is_key_active(&hid, KeyPad::DPAD_UP) {
if is_key_active(&hid, KeyPad::DPAD_UP) || is_key_active(&hid, KeyPad::CPAD_UP) {
joypad1_state.insert(JoypadStatus::UP);
}

if is_key_active(&hid, KeyPad::DPAD_DOWN) {
if is_key_active(&hid, KeyPad::DPAD_DOWN) || is_key_active(&hid, KeyPad::CPAD_DOWN) {
joypad1_state.insert(JoypadStatus::DOWN);
}

if is_key_active(&hid, KeyPad::DPAD_LEFT) {
if is_key_active(&hid, KeyPad::DPAD_LEFT) || is_key_active(&hid, KeyPad::CPAD_LEFT) {
joypad1_state.insert(JoypadStatus::LEFT);
}

if is_key_active(&hid, KeyPad::DPAD_RIGHT) {
if is_key_active(&hid, KeyPad::DPAD_RIGHT) || is_key_active(&hid, KeyPad::CPAD_RIGHT) {
joypad1_state.insert(JoypadStatus::RIGHT);
}

nes.get_joypad1_mut().update(joypad1_state.bits());

nes.next_frame();
nes_frame_buffer.copy_from_slice(&nes.get_frame());

// rotate the frame buffer 90 degrees
// let t0 = std::time::Instant::now();
let offset = LEFT_X_OFFSET_TOP * NES_SCREEN_HEIGHT * 3;
// nes.next_frame_inaccurate(
// &mut top_frame_buffer[offset..offset + NES_SCREEN_WIDTH * NES_SCREEN_HEIGHT * 3],
// );

nes.next_frame_inaccurate(&mut nes_frame_buffer);

// let frame_time = t0.elapsed().as_millis() as usize;
// let d0 = t0.elapsed();
// let t1 = std::time::Instant::now();
// nes.get_frame(&mut nes_frame_buffer);
// let d1 = t1.elapsed();

// let t2 = std::time::Instant::now();
// // rotate the frame buffer 90 degrees
for y in 0..NES_SCREEN_HEIGHT {
for x in 0..NES_SCREEN_WIDTH {
let src_index = (y * NES_SCREEN_WIDTH + x) * 3;
Expand All @@ -113,24 +116,33 @@ fn main() {
}
}

// top_frame_buffer[..(NES_SCREEN_WIDTH * NES_SCREEN_HEIGHT * 3)]
// .copy_from_slice(&nes_frame_buffer[..]);

// let d2 = t2.elapsed();

// let t3 = std::time::Instant::now();
unsafe {
top_screen
.raw_framebuffer()
.ptr
.copy_from(top_frame_buffer.as_ptr(), top_frame_buffer.len());

bottom_screen
.raw_framebuffer()
.ptr
.copy_from(bottom_frame_buffer.as_ptr(), bottom_frame_buffer.len());
}

// Flush and swap framebuffers
top_screen.flush_buffers();
bottom_screen.flush_buffers();
// top_screen.swap_buffers();

// let d3 = t3.elapsed();

// println!(
// "nf {} gf {}, rt {} after {} frame {}",
// d0.as_millis(),
// d1.as_millis(),
// d2.as_millis(),
// d3.as_millis(),
// frame_duration.as_millis()
// );

//Wait for VBlank
gfx.wait_for_vblank();
// gfx.wait_for_vblank();
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Use the arrow keys to navigate the menus and press enter to validate
- VR / 3D mode with sprites in front and bg tiles in the background?
- Optimize! (JIT Compiler / frame by frame or scanline by scanline rendering instead of pixel by pixel)
- Wide mode (for scrolling games, visualize the prefilled tiles in advance)
- Switch savestates to proto-buf?

## Embedding

Expand Down
51 changes: 36 additions & 15 deletions desktop/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use sdl2::{

const SCALE_FACTOR: usize = 2;
const SAMPLE_RATE: f64 = 44_100.0;
const WIDTH: usize = SCREEN_WIDTH;
const HEIGHT: usize = SCREEN_HEIGHT;

fn build_controller_map() -> HashMap<Keycode, JoypadStatus> {
let mut controller_map = HashMap::new();
Expand All @@ -33,30 +35,39 @@ fn build_controller_map() -> HashMap<Keycode, JoypadStatus> {

struct APUCallback<'a> {
nes: &'a mut Nes,
frame: &'a mut [u8],
avoid_underruns: bool,
}

impl<'a> AudioCallback for APUCallback<'a> {
type Channel = f32;

fn callback(&mut self, out: &mut [f32]) {
self.nes.fill_audio_buffer(out, self.avoid_underruns);
self.nes
.fill_audio_buffer(out, self.frame, self.avoid_underruns);
}
}

fn handle_events(
event_pump: &mut EventPump,
controller: &mut Joypad,
controller_map: &HashMap<Keycode, JoypadStatus>,
paused: &mut bool,
) {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
Event::Quit { .. } => {
std::process::exit(0);
}
Event::KeyDown {
keycode: Some(Keycode::Escape),
..
}
| Event::KeyDown {
keycode: Some(Keycode::Tab),
..
} => {
std::process::exit(0);
*paused = !*paused;
}
Event::KeyDown { keycode, .. } => {
if let Some(&button) = keycode.and_then(|k| controller_map.get(&k)) {
Expand Down Expand Up @@ -96,8 +107,8 @@ fn main() {
let window = video_subsystem
.window(
"nessy",
(SCREEN_WIDTH * SCALE_FACTOR) as u32,
(SCREEN_HEIGHT * SCALE_FACTOR) as u32,
(WIDTH * SCALE_FACTOR) as u32,
(HEIGHT * SCALE_FACTOR) as u32,
)
.position_centered()
.build()
Expand All @@ -115,31 +126,41 @@ fn main() {

let creator = canvas.texture_creator();
let mut texture = creator
.create_texture_target(
PixelFormatEnum::RGB24,
SCREEN_WIDTH as u32,
SCREEN_HEIGHT as u32,
)
.create_texture_target(PixelFormatEnum::RGB24, WIDTH as u32, HEIGHT as u32)
.unwrap();

let mut event_pump = sdl_context.event_pump().unwrap();
let controller_map = build_controller_map();
let mut frame = [0; WIDTH * HEIGHT * 3];

let audio_device = audio_subsystem
.open_playback(None, &desired_audio_spec, |_| APUCallback {
nes: &mut nes,
frame: &mut frame,
avoid_underruns: false,
})
.unwrap();

audio_device.resume();

let mut paused = false;

loop {
handle_events(&mut event_pump, nes.get_joypad1_mut(), &controller_map);
nes.next_frame();
let frame = nes.get_frame();
texture.update(None, frame, SCREEN_WIDTH * 3).unwrap();
handle_events(
&mut event_pump,
nes.get_joypad1_mut(),
&controller_map,
&mut paused,
);

if !paused {
nes.next_frame_inaccurate(&mut frame);
}

// nes.get_frame(&mut frame);
texture.update(None, &frame, WIDTH * 3).unwrap();
canvas.copy(&texture, None, None).unwrap();

canvas.present();
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/bus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ impl Bus {
}
}

pub fn advance(&mut self, cpu_cycles: u32) {
pub fn advance(&mut self, cpu_cycles: u32, frame: &mut [u8]) {
let ppu_cycles = cpu_cycles * 3;

for _ in 0..ppu_cycles {
self.ppu.step();
self.ppu.step(frame);
}

for _ in 0..cpu_cycles {
Expand Down
4 changes: 3 additions & 1 deletion src/cpu/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ impl CPU {
self.irq();
}
}
Interrupt::Nmi => self.nmi(),
Interrupt::Nmi => {
self.nmi();
}
}

let op_code = self.next_byte();
Expand Down
Loading
Loading