feat: interpreter (not all instructions)

This commit is contained in:
rick 2022-02-09 02:52:30 +01:00
parent e565a98e6d
commit cf7807945c
Signed by: Rick
GPG key ID: 2B593F087240EE99
2 changed files with 439 additions and 69 deletions

View file

@ -6,5 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
rand = "0.8.4"
sdl2 = "0.35.1" sdl2 = "0.35.1"

View file

@ -1,16 +1,23 @@
extern crate sdl2; extern crate sdl2;
extern crate rand;
use sdl2::pixels::Color; use sdl2::pixels::Color;
#[allow(unused_imports)]
use sdl2::event::Event; use sdl2::event::Event;
use sdl2::EventPump;
#[allow(unused_imports)]
use sdl2::keyboard::Keycode; use sdl2::keyboard::Keycode;
use sdl2::video::Window; use sdl2::video::Window;
use sdl2::rect::Point;
use sdl2::render::Canvas;
use std::fs::File; use std::fs::File;
use std::path::Path; use std::path::Path;
use std::io; use std::io;
use std::io::Read; use std::io::Read;
const font: [u8; 80] = [
const FONT: [u8; 80] = [
0xF0, 0x90, 0x90, 0x90, 0xF0, 0xF0, 0x90, 0x90, 0x90, 0xF0,
0x20, 0x60, 0x20, 0x20, 0x70, 0x20, 0x60, 0x20, 0x20, 0x70,
0xF0, 0x10, 0xF0, 0x80, 0xF0, 0xF0, 0x10, 0xF0, 0x80, 0xF0,
@ -29,40 +36,40 @@ const font: [u8; 80] = [
0xF0, 0x80, 0xF0, 0x80, 0x80 0xF0, 0x80, 0xF0, 0x80, 0x80
]; ];
const HEIGHT: usize = 32;
const WIDTH: usize = 64;
#[allow(dead_code)]
struct Machine { struct Machine {
memory: [u8; 4096], memory: [u8; 4096],
key_inputs: [i32; 16], key_inputs: [i32; 16],
display_buffer: [[i32; 64]; 32], display_buffer: [[i32; WIDTH]; HEIGHT],
gpio: [i8; 16], gpio: [u8; 16],
index: i16, index: u16,
opcode: u16, //opcode: u16,
pc: usize, pc: usize,
stack: Vec<i32> stack: Vec<u16>
}
fn decodeOp(op1: u8, op2: u8) -> u16 {
let ret: u16 = u16::from(op1) << 2;
ret + u16::from(op2) >> 2
} }
#[allow(non_snake_case, unused_variables)]
impl Machine { impl Machine {
/// Initialise une machine en mettant tout à 0 (RAM, PC...) /// Initialise une machine en mettant tout à 0 (RAM, PC...)
pub fn new() -> Self { pub fn new() -> Self {
Machine { Self {
memory: [0; 4096], memory: [0; 4096],
key_inputs: [0; 16], key_inputs: [0; 16],
display_buffer: [[0; 64]; 32], display_buffer: [[0; WIDTH]; HEIGHT],
gpio: [0; 16], gpio: [0; 16],
index: 0, index: 0,
opcode: 0, //opcode: 0,
pc: 0, pc: 0,
stack: Vec::new(), stack: Vec::new(),
} }
} }
pub fn init(&mut self) { pub fn init(&mut self) {
for i in 0..font.len() { for i in 0..FONT.len() {
self.memory[i] = font[i]; self.memory[i] = FONT[i];
} }
self.pc = 0x200; self.pc = 0x200;
@ -81,93 +88,340 @@ impl Machine {
Ok(()) Ok(())
} }
pub fn cycle(&mut self, screen: &mut sdl2::render::Canvas<Window>) { pub fn cycle(&mut self, screen: &mut Canvas<Window>, events: &mut EventPump) {
self.opcode = ((self.memory[self.pc] as u16) << 8) let opcode = ((self.memory[self.pc] as u16) << 8)
+ (self.memory[self.pc + 1] as u16); + (self.memory[self.pc + 1] as u16);
self.pc += 2; let vx: usize = ((opcode & 0x0f00) >> 8) as usize;
//let o = self.opcode & 0xf000; let vy: usize = ((opcode & 0x00f0) >> 4) as usize;
let k: u8 = (opcode & 0x00ff) as u8;
match (self.opcode & 0xf000) >> 12 { self.pc += 2;
0 => match self.opcode {
0x00E0 => screen.clear(), match (opcode & 0xf000) >> 12 {
0x00EE => println!("RET"), 0 => match opcode {
0x00E0 => {
self.display_buffer = [[0; 64]; 32];
screen.set_draw_color(Color::RGB(0, 0, 0));
screen.clear();
screen.present();
},
0x00EE => self.pc = self.stack.pop().unwrap() as usize,
_ => println!("SYS"), _ => println!("SYS"),
} }
1 => println!("JP"), 1 => self.pc = (opcode & 0x0fff) as usize,
2 => println!("CALL"), 2 => {
3 => println!("SE"), self.stack.push(self.pc as u16);
4 => println!("SNE"), self.pc = (opcode & 0x0fff) as usize
5 => println!("SE 5"), },
6 => println!("LD"), 3 => if self.gpio[vx] == k as u8 {
7 => println!("ADD"), self.pc += 2;
},
4 => if self.gpio[vx] != k as u8 {
self.pc += 2;
},
5 => if self.gpio[vx] == self.gpio[vy] {
self.pc += 2;
}
6 => self.gpio[vx] = k,
7 => self.gpio[vx] = self.gpio[vx].saturating_add(k),
8 => { 8 => {
let x = (self.opcode & 0x0f00) >> 8; //let x = (opcode & 0x0f00) >> 8;
let y = (self.opcode & 0x00f0) >> 4; //let y = (opcode & 0x00f0) >> 4;
match self.opcode & 0x000f { match opcode & 0x000f {
0 => println!("LD 8"), 0 => self.gpio[vx] = self.gpio[vy],
1 => println!("OR"), 1 => self.gpio[vx] |= self.gpio[vy],
2 => println!("AND"), 2 => self.gpio[vx] &= self.gpio[vy],
3 => println!("XOR"), 3 => self.gpio[vx] ^= self.gpio[vy],
4 => println!("ADD"), 4 => {
5 => println!("SUB"), self.gpio[vx] = self.gpio[vx].saturating_add(self.gpio[vy]);
6 => println!("SHR"), let (_, overflow) = self.gpio[vx].overflowing_add(self.gpio[vy]);
7 => println!("SUBN"), if overflow {
0xE => println!("SHL"), self.gpio[0xf] = 1;
}
},
5 => {
if self.gpio[vx] > self.gpio[vy] {
self.gpio[0xf] = 1;
} else {
self.gpio[0xf] = 0;
}
self.gpio[vx] -= self.gpio[vy];
},
6 => {
if self.gpio[vx].trailing_ones() > 0 {
self.gpio[0xf] = 1;
} else {
self.gpio[0xf] = 0;
}
self.gpio[vx] >>= 1;
},
7 => {
if self.gpio[vx] < self.gpio[vy] {
self.gpio[0xf] = 1;
} else {
self.gpio[0xf] = 0;
}
self.gpio[vx] = self.gpio[vy] - self.gpio[vx];
},
0xE => {
if self.gpio[vx].leading_ones() > 0 {
self.gpio[0xf] = 1;
} else {
self.gpio[0xf] = 0;
}
self.gpio[vx] <<= 1;
},
_ => println!("error 8") _ => println!("error 8")
} }
} }
9 => println!("SNE 9"), 9 => if self.gpio[vx] != self.gpio[vy] {
0xA => println!("LD A"), self.pc += 2;
0xB => println!("JP B"), },
0xC => println!("RND "), 0xA => self.index = opcode & 0x0fff,
0xD => println!("DRW"), 0xB => self.pc = ((self.gpio[0]) as u16 + (opcode & 0x0fff)) as usize,
0xC => self.gpio[vx] = rand::random::<u8>() & k,
0xD => {
let x = self.gpio[vx] as u16;
let y = self.gpio[vy] as u16;
for i in 0..(opcode & 0x000f) {
//println!("{:?}", self.memory);
let mut sprite = self.memory[(self.index + i as u16) as usize];
for j in 0..8 {
let mut case = 0;
if sprite.leading_ones() > 0 { case = 1; }
//println!("I :: {}", self.index + i);
//println!("D :: {} - {}", sprite, case);
sprite = sprite << 1;
let ty: usize = (i + y) as usize % HEIGHT;
let tx: usize = (j + x) as usize % WIDTH;
let tmp = self.display_buffer[ty][tx];
self.display_buffer[ty][tx] ^= case;
if self.display_buffer[ty][tx] == 0 && tmp == 1 {
self.gpio[0xf] = 1;
}
}
}
},
0xE => { 0xE => {
let x = (self.opcode & 0x0f00) >> 8; let x = (opcode & 0x0f00) >> 8;
match self.opcode & 0x00ff { match opcode & 0x00ff {
0x9E => println!("SKP"), 0x9E => {
0xA1 => println!("SKNP"), if self.key_inputs[self.gpio[vx] as usize] == 1 {
self.pc += 2;
}
},
0xA1 => {
if self.key_inputs[self.gpio[vx] as usize] == 0 {
self.pc += 2;
}
},
_ => println!("error E") _ => println!("error E")
} }
} }
0xF => { 0xF => {
let x = (self.opcode & 0x0f00) >> 8; let x = (opcode & 0x0f00) >> 8;
match self.opcode & 0x00ff { match opcode & 0x00ff {
0x07 => println!("LD F 7"), 0x07 => println!("LD F 7"),
0x0A => println!("LD F A"), 0x0A => self.check_touch(vx, events),
0x15 => println!("LD F 15"), 0x15 => println!("LD F 15"),
0x18 => println!("LD F 18"), 0x18 => println!("LD F 18"),
0x1E => println!("ADD F 1E"), 0x1E => self.index += self.gpio[vx] as u16,
0x29 => println!("LD F 2"), // self.index = self.gpio[x] * 5) & 0x0fff 0x29 => self.index = (self.gpio[vx] as u16) * 5, // self.index = self.gpio[x] * 5) & 0x0fff
0x33 => println!("LD F 3"), 0x33 => println!("LD F 3"),
0x55 => println!("LD F 5"), 0x55 =>
0x65 => println!("LD F 6"), {
//let mut j: usize = self.index as usize;
for i in 0..(vx + 1) {
self.memory[(self.index as usize) + i] = self.gpio[i];
//j += 1;
}
},
0x65 => {
//let mut j: usize = self.index as usize;
for i in 0..(vx+1) {
self.gpio[i] = self.memory[(self.index as usize) + i];
//j += 1;
}
},
_ => println!("error F") _ => println!("error F")
} }
} }
_ => println!("error") _ => println!("error")
} }
} }
fn check_touch(&mut self, vx: usize, events: &mut EventPump) {
let touch = events.wait_event();
match touch {
Event::KeyDown { keycode: Some(Keycode::Num0), .. } => {
self.gpio[vx] = 0;
},
Event::KeyDown { keycode: Some(Keycode::Num1), .. } => {
self.gpio[vx] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num2), .. } => {
self.gpio[vx] = 2;
},
Event::KeyDown { keycode: Some(Keycode::Num3), .. } => {
self.gpio[vx] = 3;
},
Event::KeyDown { keycode: Some(Keycode::Num4), .. } => {
self.gpio[vx] = 4;
},
Event::KeyDown { keycode: Some(Keycode::Num5), .. } => {
self.gpio[vx] = 5;
},
Event::KeyDown { keycode: Some(Keycode::Num6), .. } => {
self.gpio[vx] = 6;
},
Event::KeyDown { keycode: Some(Keycode::Num7), .. } => {
self.gpio[vx] = 7;
},
Event::KeyDown { keycode: Some(Keycode::Num8), .. } => {
self.gpio[vx] = 8;
},
Event::KeyDown { keycode: Some(Keycode::Num9), .. } => {
self.gpio[vx] = 9;
},
Event::KeyDown { keycode: Some(Keycode::A), .. } => {
self.gpio[vx] = 0xa;
},
Event::KeyDown { keycode: Some(Keycode::B), .. } => {
self.gpio[vx] = 0xb;
},
Event::KeyDown { keycode: Some(Keycode::C), .. } => {
self.gpio[vx] = 0xc;
},
Event::KeyDown { keycode: Some(Keycode::D), .. } => {
self.gpio[vx] = 0xd;
},
Event::KeyDown { keycode: Some(Keycode::E), .. } => {
self.gpio[vx] = 0xe;
},
Event::KeyDown { keycode: Some(Keycode::F), .. } => {
self.gpio[vx] = 0xf;
},
_ => self.check_touch(vx, events),
}
}
} }
fn read_file(p: &Path) -> io::Result<()> {
let mut buffer = Vec::new();
let mut f = File::open(p)?;
let mut mem = [0; 4096];
f.read_to_end(&mut buffer)?;
for i in 0..buffer.len() {
mem[i] = buffer[i];
}
println!("{:?}", mem);
let mut i = 0;
while i < mem.len() {
let opcode = ((mem[i] as u16) << 8)
+ (mem[i + 1] as u16);
let vx: usize = ((opcode & 0x0f00) >> 8) as usize;
let vy: usize = ((opcode & 0x00f0) >> 4) as usize;
let k: u8 = (opcode & 0x00ff) as u8;
i += 2;
match (opcode & 0xf000) >> 12 {
0 => match opcode {
0x00E0 => println!("CLS"),
0x00EE => println!("RET"),
_ => println!("SYS"),
}
1 => println!("JP {}", opcode & 0x0fff),
2 => println!("CALL {}", opcode & 0x0fff),
3 => println!("3SE {} {}", vx, k),
4 => println!("4SNE {} {}", vx, k),
5 => println!("5SE {} {}", vx, vy),
6 => println!("6LD {} {}", vx, k),
7 => println!("7ADD {} {}", vx, k),
8 => {
//let x = (opcode & 0x0f00) >> 8;
//let y = (opcode & 0x00f0) >> 4;
match opcode & 0x000f {
0 => println!("8LD {} {}", vx, vy),
1 => println!("8OR {} {}", vx, vy),
2 => println!("8AND {} {}", vx, vy),
3 => println!("8XOR {} {}", vx, vy),
4 => println!("8ADD {} {}", vx, vy),
5 => println!("8SUB {} {}", vx, vy),
6 => println!("8SHR {} {}", vx, vy),
7 => println!("8SUBN {} {}", vx, vy),
0xE => println!("8SHL {} {}", vx, vy),
_ => println!("error 8")
}
}
9 => println!("9SNE {} {}", vx, vy),
0xA => println!("AADD I {}", opcode & 0x0fff),
0xB => println!("BJP 0 {}", opcode & 0x0fff),
0xC => println!("RND {} {}", vx, k),
0xD => println!("DRW {} {} {}", vx, vy, opcode & 0x000f),
0xE => {
let x = (opcode & 0x0f00) >> 8;
match opcode & 0x00ff {
0x9E => println!("SKP {}", vx),
0xA1 => println!("SKNP {}", vx),
_ => println!("error E")
}
}
0xF => {
let x = (opcode & 0x0f00) >> 8;
match opcode & 0x00ff {
0x07 => println!("FLD {} DT", vx),
0x0A => println!("FLD {} K", vx),
0x15 => println!("FLD DT {}", vx),
0x18 => println!("FLD ST {}", vx),
0x1E => println!("FADD I {}", vx),
0x29 => println!("FLD F {}", vx),
0x33 => println!("FLD B {}", vx),
0x55 => println!("FLD [I] {}", vx),
0x65 => println!("FLD {} [I]", vx),
_ => println!("error F")
}
}
_ => println!("error")
}
}
Ok(())
}
#[allow(unused_mut, unused_must_use, unused_labels, unused_variables)]
fn main() { fn main() {
//read_file(Path::new("15PUZZLE"));
//return;
let mut t : Machine = Machine::new(); let mut t : Machine = Machine::new();
println!("{:?}", t.memory); //println!("{:?}", t.memory);
t.init(); t.init();
t.loadRom(Path::new("15PUZZLE")); //t.loadRom(Path::new("15PUZZLE"));
println!("\n\n{:?}", t.memory); t.loadRom(Path::new("BLITZ"));
//t.cycle();
//println!("\n\n{:?}", t.memory);
let sdl_context = sdl2::init().unwrap(); let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap(); let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem.window("Chip8 Emulator", 900, 700) let window = video_subsystem.window("Chip8 Emulator", WIDTH as u32, HEIGHT as u32)
.position_centered() .position_centered()
.build() .build()
.unwrap(); .unwrap();
let mut canvas = window.into_canvas().build().unwrap(); let mut canvas = window.into_canvas().build().unwrap();
canvas.set_draw_color(Color::RGB(0, 255, 255)); canvas.set_draw_color(Color::RGB(0, 0, 0));
canvas.clear(); canvas.clear();
canvas.present(); canvas.present();
@ -180,18 +434,134 @@ fn main() {
canvas.set_draw_color(Color::RGB(i, 64, 255 - i)); canvas.set_draw_color(Color::RGB(i, 64, 255 - i));
canvas.clear(); canvas.clear();
*/ */
t.cycle(&mut canvas);
/* t.cycle(&mut canvas, &mut event_pump);
//canvas.set_draw_color(Color::RGB(0, 0, 0));
//canvas.clear();
//println!("{:?}", t.memory);
canvas.set_draw_color(Color::RGB(255, 255, 255));
//println!("{:?}", t.display_buffer);
//println!("\n===\n");
for i in 0..t.display_buffer.len() {
for j in 0..t.display_buffer[i].len() {
if t.display_buffer[i][j] == 1 {
canvas.draw_point(Point::new(j as i32, i as i32));
}
}
}
//canvas.draw_line(Point::new(10, 10), Point::new(20,20));
//canvas.draw_point(Point::new(10, 10));
for event in event_pump.poll_iter() { for event in event_pump.poll_iter() {
match event { match event {
Event::KeyDown { keycode: Some(Keycode::Escape), ..} => { Event::KeyDown { keycode: Some(Keycode::Escape), ..} => {
break 'running break 'running
}, },
Event::KeyDown { keycode: Some(Keycode::Num0), .. } => {
t.key_inputs[0] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num1), .. } => {
t.key_inputs[1] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num2), .. } => {
t.key_inputs[2] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num3), .. } => {
t.key_inputs[3] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num4), .. } => {
t.key_inputs[4] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num5), .. } => {
t.key_inputs[5] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num6), .. } => {
t.key_inputs[6] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num7), .. } => {
t.key_inputs[7] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num8), .. } => {
t.key_inputs[8] = 1;
},
Event::KeyDown { keycode: Some(Keycode::Num9), .. } => {
t.key_inputs[9] = 1;
},
Event::KeyDown { keycode: Some(Keycode::A), .. } => {
t.key_inputs[0xa] = 1;
},
Event::KeyDown { keycode: Some(Keycode::B), .. } => {
t.key_inputs[0xb] = 1;
},
Event::KeyDown { keycode: Some(Keycode::C), .. } => {
t.key_inputs[0xc] = 1;
},
Event::KeyDown { keycode: Some(Keycode::D), .. } => {
t.key_inputs[0xd] = 1;
},
Event::KeyDown { keycode: Some(Keycode::E), .. } => {
t.key_inputs[0xe] = 1;
},
Event::KeyDown { keycode: Some(Keycode::F), .. } => {
t.key_inputs[0xf] = 1;
},
Event::KeyUp { keycode: Some(Keycode::Num0), .. } => {
t.key_inputs[0] = 0;
},
Event::KeyUp { keycode: Some(Keycode::Num1), .. } => {
t.key_inputs[1] = 0;
},
Event::KeyUp { keycode: Some(Keycode::Num2), .. } => {
t.key_inputs[2] = 0;
},
Event::KeyUp { keycode: Some(Keycode::Num3), .. } => {
t.key_inputs[3] = 0;
},
Event::KeyUp { keycode: Some(Keycode::Num4), .. } => {
t.key_inputs[4] = 0;
},
Event::KeyUp { keycode: Some(Keycode::Num5), .. } => {
t.key_inputs[5] = 0;
},
Event::KeyUp { keycode: Some(Keycode::Num6), .. } => {
t.key_inputs[6] = 0;
},
Event::KeyUp { keycode: Some(Keycode::Num7), .. } => {
t.key_inputs[7] = 0;
},
Event::KeyUp { keycode: Some(Keycode::Num8), .. } => {
t.key_inputs[8] = 0;
},
Event::KeyUp { keycode: Some(Keycode::Num9), .. } => {
t.key_inputs[9] = 0;
},
Event::KeyUp { keycode: Some(Keycode::A), .. } => {
t.key_inputs[0xa] = 0;
},
Event::KeyUp { keycode: Some(Keycode::B), .. } => {
t.key_inputs[0xb] = 0;
},
Event::KeyUp { keycode: Some(Keycode::C), .. } => {
t.key_inputs[0xc] = 0;
},
Event::KeyUp { keycode: Some(Keycode::D), .. } => {
t.key_inputs[0xd] = 0;
},
Event::KeyUp { keycode: Some(Keycode::E), .. } => {
t.key_inputs[0xe] = 0;
},
Event::KeyUp { keycode: Some(Keycode::F), .. } => {
t.key_inputs[0xf] = 0;
},
_ => {} _ => {}
} }
} }
*/
canvas.present(); canvas.present();
} }