mirror of https://github.com/Ivsucram/ivsemu.git
chip-8 architecture
This commit is contained in:
parent
48b56d4836
commit
9202042093
|
@ -1 +1,4 @@
|
|||
/target
|
||||
Cargo.lock
|
||||
|
||||
/src/SDL2.dll
|
|
@ -6,3 +6,8 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# crossterm = "0.19"
|
||||
# tui = { version = "0.15", default-features = false, features = ['crossterm'] }
|
||||
# druid = "0.7.0"
|
||||
sdl2 = "0.34.5"
|
||||
rand = "0.8.4"
|
|
@ -0,0 +1,232 @@
|
|||
// CHIP-8 means Compact Hexadecimal Interpretive Programming - 8-bit
|
||||
use ::sdl2;
|
||||
|
||||
use crate::chip_8::display;
|
||||
use rand::Rng;
|
||||
|
||||
const DISPLAY_WIDTH: usize = 64;
|
||||
const DISPLAY_HEIGHT: usize = 32;
|
||||
const DISPLAY_SCALE: u32 = 10;
|
||||
|
||||
pub struct ProgramCounter(usize);
|
||||
pub struct Registers([u8; 16]);
|
||||
pub struct Ram([u8; 4 * 1024]);
|
||||
pub struct Timer(u8);
|
||||
pub struct Timing(u16);
|
||||
pub struct DisplayBuffer([[bool; DISPLAY_HEIGHT]; DISPLAY_WIDTH]);
|
||||
pub struct Keypad {
|
||||
keys: [bool; 0xF],
|
||||
layout: String
|
||||
}
|
||||
|
||||
impl ProgramCounter {
|
||||
fn init() -> ProgramCounter {
|
||||
ProgramCounter {
|
||||
0: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn increase(&mut self) {
|
||||
self.0 += 2;
|
||||
}
|
||||
|
||||
fn set(&mut self, value: usize) {
|
||||
self.0 = value;
|
||||
}
|
||||
|
||||
fn get(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Registers {
|
||||
fn init() -> Registers {
|
||||
Registers {
|
||||
0: [0x0; 16]
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, register: usize) -> u8 {
|
||||
if register <= 0xF {
|
||||
self.0[register]
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&mut self, register: usize, value: u8) {
|
||||
if register <= 0xF {
|
||||
self.0[register] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ram {
|
||||
fn init() -> Ram {
|
||||
Ram {
|
||||
0: [0x00; 4 * 1024]
|
||||
}
|
||||
}
|
||||
|
||||
fn init_fonts(&mut self) {
|
||||
const FONTS: [u8; 80] = [
|
||||
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
|
||||
0x20, 0x60, 0x20, 0x20, 0x70, // 1
|
||||
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
|
||||
0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
|
||||
0x90, 0x90, 0xF0, 0x10, 0x10, // 4
|
||||
0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
|
||||
0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
|
||||
0xF0, 0x10, 0x20, 0x40, 0x40, // 7
|
||||
0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
|
||||
0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
|
||||
0xF0, 0x90, 0xF0, 0x90, 0x90, // A
|
||||
0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
|
||||
0xF0, 0x80, 0x80, 0x80, 0xF0, // C
|
||||
0xE0, 0x90, 0x90, 0x90, 0xE0, // D
|
||||
0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
|
||||
0xF0, 0x80, 0xF0, 0x80, 0x80]; // F
|
||||
for i in 0x50..0x80 {
|
||||
self.0[i] = FONTS[i];
|
||||
}
|
||||
}
|
||||
|
||||
fn load_rom(&mut self, rom: &[u8] ) {
|
||||
assert!(rom.len() <= self.0.len() - 0x200);
|
||||
for i in 0..rom.len() {
|
||||
self.0[0x200 + i] = rom[i];
|
||||
}
|
||||
}
|
||||
|
||||
fn read8(&self, addr: usize) -> u8 {
|
||||
assert!(addr <= self.0.len());
|
||||
self.0[addr]
|
||||
}
|
||||
|
||||
fn read16(&self, addr: usize) -> u16 {
|
||||
assert!(addr < self.0.len());
|
||||
// byteorder::LittleEndiar::read_u16(&self.ram[addr])
|
||||
|
||||
// u16::from_le_bytes(self.ram[addr..addr+2])
|
||||
// self.ram[addr..addr+2]
|
||||
(self.0[addr] as u16) << 8 | self.0[addr+1] as u16
|
||||
}
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
fn init() -> Timer {
|
||||
Timer {
|
||||
0: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) -> bool {
|
||||
self.0 -= 1;
|
||||
self.0 == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Keypad {
|
||||
fn init() -> Keypad {
|
||||
Keypad {
|
||||
keys: [false; 0xF],
|
||||
layout: String::from("123C456D789EA0BF")
|
||||
}
|
||||
}
|
||||
|
||||
fn set_layout(&mut self, layout: &str) {
|
||||
assert_eq!(layout.len(), self.layout.len());
|
||||
self.layout = layout.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
impl Timing {
|
||||
fn init() -> Timing {
|
||||
Timing {
|
||||
0: 1000 // 1 MHz
|
||||
}
|
||||
}
|
||||
|
||||
fn increase(&mut self) {
|
||||
self.0 += 10;
|
||||
}
|
||||
|
||||
fn decrease(&mut self) {
|
||||
self.0 -= 10;
|
||||
}
|
||||
|
||||
fn format(&self) -> String {
|
||||
let mut res = String::new();
|
||||
if self.0 >= 1000 {
|
||||
res = format!("{:.2}{}", self.0/1000, "MHz");
|
||||
} else {
|
||||
res = format!("{}{}", self.0, "Hz");
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayBuffer {
|
||||
fn init() -> DisplayBuffer {
|
||||
DisplayBuffer {
|
||||
0: [[false; DISPLAY_HEIGHT]; DISPLAY_WIDTH]
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.0 = [[false; DISPLAY_HEIGHT]; DISPLAY_WIDTH];
|
||||
}
|
||||
|
||||
fn random(&mut self) {
|
||||
for (x, col) in self.0.iter_mut().enumerate() {
|
||||
for (y, pixel) in col.iter_mut().enumerate() {
|
||||
let mut rng = rand::thread_rng();
|
||||
*pixel = rng.gen_range(0..2) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run() {
|
||||
let mut pc = ProgramCounter::init();
|
||||
let mut index_register: u16 = 0;
|
||||
let mut stack: Vec<u16>;
|
||||
let mut delay_timer = Timer::init();
|
||||
let mut sound_timer = Timer::init();
|
||||
let mut registers = Registers::init();
|
||||
let mut ram = Ram::init();
|
||||
let mut keypad = Keypad::init();
|
||||
let mut timing = Timing::init();
|
||||
let mut display_buffer = DisplayBuffer::init();
|
||||
|
||||
let sdl_context = sdl2::init().unwrap();
|
||||
|
||||
let mut display = crate::chip_8::display::Display::init(&sdl_context, DISPLAY_SCALE);
|
||||
|
||||
let mut event_listener = sdl_context.event_pump().unwrap();
|
||||
let mut is_running = true;
|
||||
'runner: loop {
|
||||
for event in event_listener.poll_iter() {
|
||||
match event {
|
||||
sdl2::event::Event::Quit {..} | sdl2::event::Event::KeyDown {
|
||||
keycode: Some(sdl2::keyboard::Keycode::Escape), .. } => {break 'runner},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
display_buffer.random();
|
||||
display.draw(&display_buffer.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetch(ram: Ram, pc: &mut ProgramCounter) -> u16{
|
||||
let opcode = ram.read16(pc.get());
|
||||
pc.increase();
|
||||
opcode
|
||||
}
|
||||
|
||||
pub fn decode() {}
|
||||
|
||||
pub fn execute() {}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
use sdl2::pixels::Color;
|
||||
use sdl2::rect::Rect;
|
||||
use sdl2::render::Canvas;
|
||||
use sdl2::Sdl;
|
||||
use sdl2::video::Window;
|
||||
|
||||
const WIDTH: usize = 64;
|
||||
const HEIGHT: usize = 32;
|
||||
|
||||
pub struct Display {
|
||||
canvas: Canvas<Window>,
|
||||
scale: u32,
|
||||
off_color: Color,
|
||||
on_color: Color
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn init(sdl_context: &Sdl, scale: u32) -> Display {
|
||||
let video_subsystem = sdl_context.video().unwrap();
|
||||
|
||||
let window = video_subsystem.window("Chip-8", WIDTH as u32 * scale,
|
||||
HEIGHT as u32 * scale)
|
||||
.position_centered()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let canvas = window.into_canvas().build().unwrap();
|
||||
|
||||
Display {
|
||||
canvas: canvas,
|
||||
scale: scale,
|
||||
off_color: Color::RGB(0, 0, 0),
|
||||
on_color: Color::RGB(255, 255, 255)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(self: &mut Display, buffer: &[[bool; HEIGHT]; WIDTH]) {
|
||||
self.canvas.set_draw_color(self.off_color);
|
||||
self.canvas.clear();
|
||||
|
||||
self.canvas.set_draw_color(self.on_color);
|
||||
for (x, col) in buffer.iter().enumerate() {
|
||||
for (y, pixel) in col.iter().enumerate() {
|
||||
if *pixel {
|
||||
let x = (x as u32 * self.scale) as i32;
|
||||
let y = (y as u32 * self.scale) as i32;
|
||||
let width = self.scale;
|
||||
let height = self.scale;
|
||||
self.canvas.fill_rect(Rect::new(x, y, width, height))
|
||||
.expect("Failed to draw pixel");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.canvas.present();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
pub mod chip_8_base_interpreter;
|
||||
pub mod cosmac_vip_interpreter;
|
||||
pub mod super_chip_interpreter;
|
||||
pub mod display;
|
Binary file not shown.
|
@ -1,3 +1,6 @@
|
|||
mod chip_8;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
chip_8::chip_8_base_interpreter::run();
|
||||
// println!("Hello Chip-8!");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue