aboutsummaryrefslogtreecommitdiff
path: root/src/bin.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin.rs')
-rw-r--r--src/bin.rs305
1 files changed, 305 insertions, 0 deletions
diff --git a/src/bin.rs b/src/bin.rs
new file mode 100644
index 0000000..a66b430
--- /dev/null
+++ b/src/bin.rs
@@ -0,0 +1,305 @@
+extern crate core;
+mod memory;
+mod mos6502;
+mod ppu;
+mod cartridge;
+mod mapper;
+mod controller;
+
+use std::fs::File;
+use std::io::Read;
+use core::cell::RefCell;
+use core::intrinsics::transmute;
+use cartridge::*;
+use controller::stdctl;
+use std::time::{Instant, Duration};
+use std::thread::sleep;
+
+extern crate sdl2;
+
+use sdl2::pixels::Color;
+use sdl2::rect::Rect;
+use sdl2::pixels::PixelFormatEnum;
+use sdl2::event::Event;
+use sdl2::keyboard::Keycode;
+
+const PIXEL_SIZE: u32 = 2;
+const RGB_COLORS: [u32; 64] = [
+ 0x666666, 0x002a88, 0x1412a7, 0x3b00a4, 0x5c007e, 0x6e0040, 0x6c0600, 0x561d00,
+ 0x333500, 0x0b4800, 0x005200, 0x004f08, 0x00404d, 0x000000, 0x000000, 0x000000,
+ 0xadadad, 0x155fd9, 0x4240ff, 0x7527fe, 0xa01acc, 0xb71e7b, 0xb53120, 0x994e00,
+ 0x6b6d00, 0x388700, 0x0c9300, 0x008f32, 0x007c8d, 0x000000, 0x000000, 0x000000,
+ 0xfffeff, 0x64b0ff, 0x9290ff, 0xc676ff, 0xf36aff, 0xfe6ecc, 0xfe8170, 0xea9e22,
+ 0xbcbe00, 0x88d800, 0x5ce430, 0x45e082, 0x48cdde, 0x4f4f4f, 0x000000, 0x000000,
+ 0xfffeff, 0xc0dfff, 0xd3d2ff, 0xe8c8ff, 0xfbc2ff, 0xfec4ea, 0xfeccc5, 0xf7d8a5,
+ 0xe4e594, 0xcfef96, 0xbdf4ab, 0xb3f3cc, 0xb5ebf2, 0xb8b8b8, 0x000000, 0x000000,
+];
+
+const PIX_WIDTH: usize = 256;
+const PIX_HEIGHT: usize = 240;
+const FB_PITCH: usize = PIX_WIDTH * 3 * (PIXEL_SIZE as usize);
+const FB_SIZE: usize = PIX_HEIGHT * FB_PITCH * (PIXEL_SIZE as usize);
+const WIN_WIDTH: u32 = PIX_WIDTH as u32 * PIXEL_SIZE;
+const WIN_HEIGHT: u32 = PIX_HEIGHT as u32 * PIXEL_SIZE;
+
+pub struct SimpleCart {
+ chr_rom: Vec<u8>,
+ prg_rom: Vec<u8>,
+ sram: Vec<u8>,
+ pub mirror_type: MirrorType
+}
+
+impl SimpleCart {
+ pub fn new(chr_rom: Vec<u8>,
+ prg_rom: Vec<u8>,
+ sram: Vec<u8>,
+ mirror_type: MirrorType) -> Self {
+ SimpleCart{chr_rom, prg_rom, sram, mirror_type}
+ }
+}
+
+impl Cartridge for SimpleCart {
+ fn get_size(&self, kind: BankType) -> usize {
+ match kind {
+ BankType::PrgRom => self.prg_rom.len(),
+ BankType::ChrRom => self.chr_rom.len(),
+ BankType::Sram => self.sram.len()
+ }
+ }
+ fn get_bank(&mut self, base: usize, size: usize, kind: BankType) -> *mut [u8] {
+ &mut (match kind {
+ BankType::PrgRom => &mut self.prg_rom,
+ BankType::ChrRom => &mut self.chr_rom,
+ BankType::Sram => &mut self.sram,
+ })[base..base + size]
+ }
+ fn get_mirror_type(&self) -> MirrorType {self.mirror_type}
+}
+
+struct SDLWindow<'a> {
+ canvas: sdl2::render::WindowCanvas,
+ events: sdl2::EventPump,
+ frame_buffer: [u8; FB_SIZE],
+ texture: sdl2::render::Texture,
+ timer: Instant,
+ duration_per_frame: Duration,
+ p1_button_state: u8,
+ p1_ctl: &'a stdctl::Joystick,
+ p1_keymap: [u8; 256],
+}
+
+macro_rules! gen_keymap {
+ ($tab: ident, [$($x: expr, $y: expr), *]) => {
+ {
+ $(
+ $tab[($x as usize) & 0xff] = $y;
+ )*
+ }
+ };
+}
+
+impl<'a> SDLWindow<'a> {
+ fn new(p1_ctl: &'a stdctl::Joystick) -> Self {
+ use Keycode::*;
+ let sdl_context = sdl2::init().unwrap();
+ let video_subsystem = sdl_context.video().unwrap();
+ let window = video_subsystem.window("RuNES", WIN_WIDTH, WIN_HEIGHT)
+ .position_centered()
+ .opengl()
+ .build()
+ .unwrap();
+ let mut canvas = window.into_canvas()
+ .accelerated()
+ .build().unwrap();
+ let texture_creator = canvas.texture_creator();
+ canvas.set_draw_color(Color::RGB(255, 255, 255));
+ canvas.clear();
+ canvas.present();
+ let mut res = SDLWindow {
+ canvas,
+ events: sdl_context.event_pump().unwrap(),
+ frame_buffer: [0; FB_SIZE],
+ texture: texture_creator.create_texture_streaming(
+ PixelFormatEnum::RGB24, WIN_WIDTH, WIN_HEIGHT).unwrap(),
+ timer: Instant::now(),
+ duration_per_frame: Duration::from_millis(1000 / 60),
+ p1_button_state: 0,
+ p1_ctl, p1_keymap: [stdctl::NULL; 256]
+ };
+ {
+ let keymap = &mut res.p1_keymap;
+ gen_keymap!(keymap, [I, stdctl::UP,
+ K, stdctl::DOWN,
+ J, stdctl::LEFT,
+ L, stdctl::RIGHT,
+ Z, stdctl::A,
+ X, stdctl::B,
+ Return, stdctl::START,
+ S, stdctl::SELECT,
+ Up, stdctl::UP,
+ Down, stdctl::DOWN,
+ Left, stdctl::LEFT,
+ Right, stdctl::RIGHT
+ ]);
+ }
+ res
+ }
+
+ #[inline]
+ fn poll(&mut self) -> bool {
+ use Keycode::*;
+ let p1_keymap = &self.p1_keymap;
+ for event in self.events.poll_iter() {
+ match event {
+ Event::Quit {..} | Event::KeyDown { keycode: Some(Escape), .. } => {
+ return true;
+ },
+ Event::KeyDown { keycode: Some(c), .. } => {
+ self.p1_button_state |= p1_keymap[(c as usize) & 0xff];
+ self.p1_ctl.set(self.p1_button_state)
+ },
+ Event::KeyUp { keycode: Some(c), .. } => {
+ self.p1_button_state &= !p1_keymap[(c as usize) & 0xff];
+ self.p1_ctl.set(self.p1_button_state)
+ },
+ _ => ()
+ }
+ }
+ false
+ }
+}
+
+#[inline]
+fn get_rgb(color: u8) -> (u8, u8, u8) {
+ let c = RGB_COLORS[color as usize];
+ ((c >> 16) as u8, ((c >> 8) & 0xff) as u8, (c & 0xff) as u8)
+}
+
+impl<'a> ppu::Screen for SDLWindow<'a> {
+ fn put(&mut self, x: u8, y: u8, color: u8) {
+ let (r, g, b) = get_rgb(color);
+ let mut base = ((y as u32 * PIXEL_SIZE) as usize * FB_PITCH) +
+ (x as u32 * 3 * PIXEL_SIZE) as usize;
+ for _ in 0..PIXEL_SIZE {
+ let slice = &mut self.frame_buffer[base..base + 3 * PIXEL_SIZE as usize];
+ let mut j = 0;
+ for _ in 0..PIXEL_SIZE {
+ slice[j] = r;
+ slice[j + 1] = g;
+ slice[j + 2] = b;
+ j += 3;
+ }
+ base += FB_PITCH;
+ }
+ }
+
+ fn render(&mut self) {
+ self.texture.update(None, &self.frame_buffer, FB_PITCH).unwrap();
+ self.canvas.clear();
+ self.canvas.copy(&self.texture, None, Some(Rect::new(0, 0, WIN_WIDTH, WIN_HEIGHT))).unwrap();
+ self.canvas.present();
+ if self.poll() {std::process::exit(0);}
+ let e = self.timer.elapsed();
+ if self.duration_per_frame > e {
+ let diff = self.duration_per_frame - e;
+ sleep(diff);
+ //println!("{} faster", diff.subsec_nanos() as f64 / 1e6);
+ } else {
+ //println!("{} slower", (e - duration_per_frame).subsec_nanos() as f64 / 1e6);
+ }
+ self.timer = Instant::now();
+ //canvas.set_draw_color(Color::RGB(128, 128, 128));
+ }
+}
+
+#[repr(C, packed)]
+struct INesHeader {
+ magic: [u8; 4],
+ prg_rom_nbanks: u8,
+ chr_rom_nbanks: u8,
+ flags6: u8,
+ flags7: u8,
+ prg_ram_nbanks: u8,
+ flags9: u8,
+ flags10: u8,
+ padding: [u8; 5]
+}
+
+fn main() {
+ let fname = std::env::args().nth(1).unwrap();
+ let mut file = File::open(fname).unwrap();
+ let mut rheader = [0; 16];
+ println!("read {}", file.read(&mut rheader[..]).unwrap());
+ let header = unsafe{transmute::<[u8; 16], INesHeader>(rheader)};
+ let mirror = match ((header.flags6 >> 2) & 2) | (header.flags6 & 1) {
+ 0 => MirrorType::Horizontal,
+ 1 => MirrorType::Vertical,
+ 2 => MirrorType::Single0,
+ 3 => MirrorType::Single1,
+ _ => MirrorType::Four,
+ };
+ let mapper_id = (header.flags7 & 0xf0) | (header.flags6 >> 4);
+ println!("maigc:{} prg:{} chr:{} mirror:{} mapper:{}",
+ std::str::from_utf8(&header.magic).unwrap(),
+ header.prg_rom_nbanks,
+ header.chr_rom_nbanks,
+ mirror as u8,
+ mapper_id);
+ if header.flags6 & 0x04 == 0x04 {
+ let mut trainer: [u8; 512] = unsafe{std::mem::uninitialized()};
+ file.read(&mut trainer[..]).unwrap();
+ println!("skipping trainer");
+ }
+
+ let prg_len = header.prg_rom_nbanks as usize * 0x4000;
+ let mut chr_len = header.chr_rom_nbanks as usize * 0x2000;
+ if chr_len == 0 {
+ chr_len = 0x2000;
+ }
+ let mut prg_rom = Vec::<u8>::with_capacity(prg_len);
+ let mut chr_rom = Vec::<u8>::with_capacity(chr_len);
+ unsafe {
+ prg_rom.set_len(prg_len);
+ chr_rom.set_len(chr_len);
+ }
+ let sram = vec![0; 0x4000];
+ println!("read prg {}", file.read(&mut prg_rom[..]).unwrap());
+ /*
+ for (i, v) in prg_rom.iter().enumerate() {
+ print!("{:02x} ", v);
+ if i & 15 == 15 {
+ println!(" {:04x}", i);
+ }
+ }
+ */
+ println!("read chr {}", file.read(&mut chr_rom[..]).unwrap());
+ /*
+ for (i, v) in chr_rom.iter().enumerate() {
+ print!("{:02x} ", v);
+ if i & 15 == 15 {
+ println!("");
+ }
+ }
+ */
+ let p1ctl = stdctl::Joystick::new();
+ let mut win = SDLWindow::new(&p1ctl);
+ let mut m = match mapper_id {
+ 0 | 2 => mapper::Mapper2::new(SimpleCart::new(chr_rom, prg_rom, sram, mirror)),
+ _ => panic!("unsupported mapper {}", mapper_id)
+ };
+ let mt = m.get_cart().get_mirror_type();
+ let mapper = RefCell::new(&mut m as &mut memory::VMem);
+ let mut ppu = ppu::PPU::new(memory::PPUMemory::new(&mapper, mt), &mut win);
+ let mut cpu = mos6502::CPU::new(memory::CPUMemory::new(&mut ppu, &mapper, Some(&p1ctl), None));
+ let ptr = &mut cpu as *mut mos6502::CPU;
+ cpu.mem.init(ptr);
+ loop {
+ cpu.step();
+ while cpu.cycle > 0 {
+ if ppu.tick() || ppu.tick() || ppu.tick() {
+ cpu.trigger_delayed_nmi()
+ }
+ cpu.cycle -= 1;
+ }
+ }
+}