Skip to content

Commit

Permalink
restart the project...
Browse files Browse the repository at this point in the history
  • Loading branch information
gthvn1 committed Apr 26, 2024
1 parent 64f147a commit 44909f7
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 76 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
env_logger = "0.11.3"
log = "0.4.21"
minifb = "0.25.0"
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 2024-04-26
- Use env logger
- Remove the use of framebuffer for trying to solve the issue with IBM logo

## 2024-28-01
- Implement DXYN, 1NNN, 7XNN
- Implement ANNN, 6XNN
Expand Down
3 changes: 2 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

## What?
- A *chip8* emulator in rust
- To run it: `cargo run --bin emulator chip8-roms/IBM_logo.ch8`
- To debug prepend `RUST_LOG=debug`
- [Changelog](https://github.com/gthvn1/chip8-emulator/blob/master/Changelog.md)
- To run it: `cargo run -- chip8-roms/IBM_logo.ch8 > /tmp/IBM_logo.mem`

## Links
- [chip8 emulator](https://en.wikipedia.org/wiki/CHIP-8)
Expand Down
24 changes: 24 additions & 0 deletions src/bin/emulator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use chip8_emulator::chip8::Chip8;
use log;
use std::env;
use std::process::exit;

fn main() {
env_logger::init();

// First argument is the name of the binary
let a: Vec<String> = env::args().collect();

if a.len() < 2 {
log::error!("You need to pass filename for the ROM");
exit(1);
}

let filename = &a[1];
log::info!("Emulating {filename}");

let mut chip = Chip8::default();
chip.load(filename).unwrap();
//chip.dump_memory();
chip.run();
}
98 changes: 64 additions & 34 deletions src/chip8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
//! - A beeping sound is played when sound timer is nonzero.
mod opcode;

use log;
use opcode::Opcode;
use std::{fs::File, io::Read};

Expand All @@ -57,49 +58,63 @@ const VREGS_SIZE: usize = 16;
/// Opcode is 2 bytes
const OPCODE_SIZE: usize = 2;

#[derive(Debug)]
pub enum Chip8Error {
NotImplemented,
MemoryFull,
UnknownOpcode,
}

pub struct Chip8 {
/// 4K memory
mem: [u8; MEMSIZE],
/// program counter
pc: u16,
pc: usize,
/// Data registers from V0 to VF
vregs: [u8; VREGS_SIZE],
/// 16-bit register for memory address
i: u16,
}

impl Default for Chip8 {
fn default() -> Self {
Self::new()
}
}

impl Chip8 {
/// Loads in memory the `rom` passed as a parameter.
/// The `rom` must be a file that contains a valid ROM.
/// There is no check done when loading it.
pub fn new(rom: &str) -> Self {
let mut chip = Chip8 {
pub fn new() -> Self {
Chip8 {
mem: [0; MEMSIZE],
pc: 0x200, // Entry point of our code
vregs: [0; VREGS_SIZE],
i: 0,
};
}
}

/// Loads in memory the `rom` passed as a parameter.
/// The `rom` must be a file that contains a valid ROM.
/// There is no check done when loading it.
pub fn load(&mut self, from: &str) -> Result<(), Chip8Error> {
// We can read byte per byte
let mut byte: [u8; 1] = [0];
let mut pc = chip.pc as usize;

let mut f = File::open(rom).unwrap();
// We don't want to change the PC so don't use self.pc to load
// the program
let mut pc = self.pc;
let mut f = File::open(from).unwrap();

while let Ok(()) = f.read_exact(&mut byte) {
if pc >= 0x0EA0 {
println!("Memory is full");
break;
eprintln!("Memory is full");
return Err(Chip8Error::MemoryFull);
}
chip.mem[pc] = byte[0];
self.mem[pc] = byte[0];
pc += 1;
}

// Load the fonts
chip.mem[FONTS_OFFSET..(FONTS_OFFSET + FONTS_SIZE)].copy_from_slice(&[
// Load the fonts at FONTS_OFFSET
self.mem[FONTS_OFFSET..(FONTS_OFFSET + FONTS_SIZE)].copy_from_slice(&[
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
0x20, 0x60, 0x20, 0x20, 0x70, // 1
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
Expand All @@ -118,12 +133,12 @@ impl Chip8 {
0xF0, 0x80, 0xF0, 0x80, 0x80, // F
]);

// Write FF in display so we will be able to check that clean Display
// is working
chip.mem[DISPLAY_OFFSET..(DISPLAY_OFFSET + DISPLAY_SIZE)]
// Write 0xFF in display so we will be able to check that clean Display
// is working.
self.mem[DISPLAY_OFFSET..(DISPLAY_OFFSET + DISPLAY_SIZE)]
.copy_from_slice(&[0xFF; DISPLAY_SIZE]);

chip
Ok(())
}

/// Return the memory where fonts are loaded
Expand All @@ -148,15 +163,13 @@ impl Chip8 {
let _ = self.vregs; // TODO: use it for real
let _ = self.i; // TODO: use it for real

// Save the old PC before updating it
let pc = self.pc as usize;
self.pc += OPCODE_SIZE as u16;

let opcode = Opcode::new(u16::from_be_bytes(
self.mem[pc..pc + OPCODE_SIZE].try_into().unwrap(),
self.mem[self.pc..self.pc + OPCODE_SIZE].try_into().unwrap(),
));

println!("[debug] emulate {opcode}");
log::debug!("pc = {:#06x}, opcode = {}", self.pc, opcode);

self.pc += OPCODE_SIZE;

match opcode.upper4() {
0x0 => {
Expand All @@ -170,8 +183,7 @@ impl Chip8 {
return Err(Chip8Error::NotImplemented);
}
}
0x1 => self.pc = opcode.nn() as u16,

0x1 => self.pc = opcode.nnn() as usize,
0x2 => return Err(Chip8Error::NotImplemented),
0x3 => return Err(Chip8Error::NotImplemented),
0x4 => return Err(Chip8Error::NotImplemented),
Expand All @@ -191,20 +203,26 @@ impl Chip8 {
0xC => return Err(Chip8Error::NotImplemented),
0xD => {
// Draw a sprite 8xN at coordinate (VX, VY)
let x = self.vregs[opcode.x() as usize] as usize;
let y = self.vregs[opcode.y() as usize] as usize;
// VX and VY are in pixels
let vx = self.vregs[opcode.x() as usize] as usize;
let vy = self.vregs[opcode.y() as usize] as usize;
let n = opcode.n() as usize;

println!("Draw a 8x{n} sprite at ({vx}, {vy})");

let sprite = &self.mem[self.i as usize..(self.i as usize + n)];
println!("Sprite is {sprite:?}");

let mut fb = self.framebuffer().to_vec();

assert!(x + 8 < 64); // We have 8 bytes for a line (64 pixels)
assert!(y + n < 32); // We have at most 32 lines
assert!(vx + 8 < 64); // We have 8 bytes for a line (64 pixels)
assert!(vy + n < 32); // We have at most 32 lines

// We have 8 pixels per line
for (idx, pixels) in sprite.iter().enumerate() {
let x = x / 8; // Transform pixel to bytes
let offset = x + x * (y + idx);
println!("(x:{x}, y:{y}), idx:{idx}, {pixels:?} -> @ {offset}");
let x = vx / 8; // Transform pixel to bytes
let offset = x + x * (vy + idx);
//println!("(x:{x}, y:{vy}), idx:{idx}, {pixels:?} -> @ {offset}");
fb[offset] = *pixels;
}
if self.framebuffer().to_vec() == fb {
Expand All @@ -216,12 +234,24 @@ impl Chip8 {
}
0xE => return Err(Chip8Error::NotImplemented),
0xF => return Err(Chip8Error::NotImplemented),
_ => unreachable!(),
_ => {
eprintln!("unknown opcode: {opcode}");
return Err(Chip8Error::UnknownOpcode);
}
};

Ok(())
}

pub fn run(&mut self) {
loop {
if self.emulate_one_insn().is_err() {
eprint!("failed to emulate instruction\n");
break;
}
}
}

/// Dumps the content of all memory on stdin.
pub fn dump_memory(&self) {
for (i, byte) in self.mem.iter().enumerate() {
Expand Down
1 change: 1 addition & 0 deletions src/chip8/opcode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::fmt;

/// Opcode is always 2 bytes in CHIP8
pub struct Opcode {
value: u16,
}
Expand Down
41 changes: 0 additions & 41 deletions src/main.rs

This file was deleted.

26 changes: 26 additions & 0 deletions watch_src
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash

clear
echo "Monitoring src/ for changes..."
echo -n "Hint: By default it runs tests."
echo "Pass 'b' to build or 'r' to run the code"

case $1 in
b) CMD="cargo build" ;;
r) CMD="cargo run --bin emulator chip8-roms/IBM_logo.ch8" ;;
*) CMD="cargo test" ;;
esac

inotifywait -q -m -r -e modify src | while read -r _DIRECTORY EVENT _FILE; do
# echo $DIRECTORY $EVENT $FILE
case $EVENT in
MODIFY*)
clear
echo "= ${CMD} ==============================="
echo
RUST_LOG=debug bash -c "${CMD}"
echo
echo "= $(date) ==================="
;;
esac
done

0 comments on commit 44909f7

Please sign in to comment.