summaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
authorSamuel Perrouault <samuel.perrouault@gmail.com>2025-03-26 21:43:39 +0100
committerSamuel Perrouault <samuel.perrouault@gmail.com>2025-03-26 21:43:39 +0100
commitdca34690935f600413cefdb9f09880b767b201ac (patch)
tree2726622098df6224d937cbb935366eea598e404e /src/lib.rs
parentaaa7b07dd0add847a59fa9f0fd86b068cd7ccb91 (diff)
remove chip8 redundant module
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs277
1 files changed, 277 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..5ff782d
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,277 @@
+use std::io::Read;
+use std::io::Write;
+
+const WIDTH: u16 = 64;
+const HEIGHT: u16 = 32;
+const PIXELS: usize = (WIDTH * HEIGHT) as usize;
+
+fn unknown_opcode(opcode: u16) {
+ eprintln!("Unknwown opcode {:#06x}", opcode);
+ std::process::exit(1);
+}
+
+struct RandomGenerator {
+ random: u8,
+}
+
+impl RandomGenerator {
+ fn new() -> Self {
+ Self { random: 92 }
+ }
+}
+
+impl Iterator for RandomGenerator {
+ type Item = u8;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.random ^= self.random << 6;
+ self.random ^= self.random >> 4;
+ self.random ^= self.random << 1;
+ Some(self.random)
+ }
+}
+
+pub struct Chip8 {
+ v: [u8; 16],
+ i: u16,
+ pc: u16,
+ stack: [u16; 16],
+ sp: u16,
+ memory: [u8; 4096],
+ screen: [u8; PIXELS],
+ delay_timer: u8,
+ sound_timer: u8,
+ key: [u8; 16],
+ draw: bool,
+ random: RandomGenerator,
+}
+
+impl Chip8 {
+ pub fn new() -> Self {
+ Self {
+ v: [0; 16],
+ i: 0,
+ pc: 0x200,
+ stack: [0; 16],
+ sp: 0,
+ memory: [0; 4096],
+ screen: [0; PIXELS],
+ delay_timer: 0,
+ sound_timer: 0,
+ key: [0; 16],
+ draw: false,
+ random: RandomGenerator::new(),
+ }
+ }
+
+ pub fn load_rom<R: Read>(&mut self, mut reader: R) -> std::io::Result<()> {
+ reader.read(&mut self.memory[0x200..])?;
+ Ok(())
+ }
+
+ pub fn cycle(&mut self) {
+ let pc = self.pc as usize;
+ let opcode = ((self.memory[pc] as u16) << 8) | self.memory[pc + 1] as u16;
+ let x = ((opcode & 0x0F00) >> 8) as usize;
+ let y = ((opcode & 0x00F0) >> 4) as usize;
+ let n = opcode & 0x000F;
+ let nn = (opcode & 0x00FF) as u8;
+ let nnn = opcode & 0x0FFF;
+ self.pc += 2;
+ match opcode & 0xF000 {
+ 0x0000 => match opcode & 0x000F {
+ 0x0000 => {
+ eprintln!("CLS");
+ self.screen = [0; PIXELS];
+ self.draw = true;
+ }
+ 0x000E => {
+ eprintln!("RET");
+ self.sp -= 1;
+ self.pc = self.stack[self.sp as usize];
+ }
+ _ => unknown_opcode(opcode),
+ },
+ 0x1000 => {
+ eprintln!("JP {}", nnn);
+ self.pc = nnn;
+ }
+ 0x2000 => {
+ eprintln!("CALL {}", nnn);
+ self.stack[self.sp as usize] = self.pc;
+ self.sp += 1;
+ self.pc = nnn;
+ }
+ 0x3000 => {
+ eprintln!("SE V{}, {}", x, nn);
+ if self.v[x] == nn {
+ self.pc += 2;
+ }
+ }
+ 0x4000 => {
+ eprintln!("SNE V{}, {}", x, nn);
+ if self.v[x] != nn {
+ self.pc += 2;
+ }
+ }
+ 0x5000 => {
+ eprintln!("SE V{}, V{}", x, y);
+ if self.v[x] == self.v[y] {
+ self.pc += 2;
+ }
+ }
+ 0x6000 => {
+ eprintln!("LD V{}, {}", x, nn);
+ self.v[x] = nn;
+ }
+ 0x7000 => {
+ eprintln!("ADD V{}, {}", x, nn);
+ self.v[x] = self.v[x].wrapping_add(nn);
+ }
+ 0x8000 => match opcode & 0x000F {
+ 0x0000 => {
+ eprintln!("LD V{}, V{}", x, y);
+ self.v[x] = self.v[y];
+ }
+ 0x0001 => {
+ eprintln!("OR V{}, V{}", x, y);
+ self.v[x] |= self.v[y];
+ }
+ 0x0002 => {
+ eprintln!("AND V{}, V{}", x, y);
+ self.v[x] &= self.v[y];
+ }
+ 0x0003 => {
+ eprintln!("XOR V{}, V{}", x, y);
+ self.v[x] ^= self.v[y];
+ }
+ 0x0004 => {
+ eprintln!("ADD V{}, V{}", x, y);
+ let vx = self.v[x] as u16;
+ let vy = self.v[y] as u16;
+ let v = vx + vy;
+ self.v[0xF] = if v > 255 { 1 } else { 0 };
+ self.v[x] = v as u8;
+ }
+ 0x0005 => {
+ eprintln!("SUB V{}, V{}", x, y);
+ self.v[0xF] = if self.v[x] > self.v[y] { 1 } else { 0 };
+ self.v[x] = self.v[x].wrapping_sub(self.v[y]);
+ }
+ 0x0006 => {
+ eprintln!("SHR V{}", x);
+ self.v[0xF] = if self.v[x] & 1 == 1 { 1 } else { 0 };
+ self.v[x] /= 2;
+ }
+ 0x0007 => {
+ eprintln!("SUBN V{}, V{}", x, y);
+ self.v[0xF] = if self.v[y] > self.v[x] { 1 } else { 0 };
+ self.v[x] = self.v[y].wrapping_sub(self.v[x]);
+ }
+ 0x000E => {
+ eprintln!("SHL V{}", x);
+ self.v[0xF] = if (self.v[x] >> 7) & 1 == 1 { 1 } else { 0 };
+ self.v[x] = self.v[x].wrapping_mul(2);
+ }
+ _ => unknown_opcode(opcode),
+ },
+ 0x9000 => {
+ eprintln!("SNE V{}, V{}", x, y);
+ if self.v[x] != self.v[y] {
+ self.pc += 2;
+ }
+ }
+ 0xA000 => {
+ eprintln!("LD I, {}", nnn);
+ self.i = nnn;
+ }
+ 0xC000 => {
+ eprintln!("RND V{}, {}", x, nn);
+ self.v[x] = self.random.next().unwrap_or(0) & nn;
+ }
+ 0xD000 => {
+ eprintln!("DRW V{}, V{}, {}", x, y, n);
+ let vx = self.v[x] as u16;
+ let vy = self.v[y] as u16;
+ self.v[0xF] = 0;
+ for yy in 0..n {
+ let pixel = self.memory[(self.i + yy) as usize];
+ for xx in 0..8 {
+ if (pixel & (0x80 >> xx)) != 0 {
+ let si = vx + xx + ((vy + yy) * WIDTH);
+ if self.screen[si as usize] == 1 {
+ self.v[0xF] = 1;
+ }
+ self.screen[si as usize] ^= 1;
+ }
+ }
+ }
+ self.draw = true;
+ }
+ 0xF000 => match opcode & 0x00FF {
+ 0x001E => {
+ eprintln!("ADD, I, V{}", x);
+ self.i += self.v[x] as u16;
+ }
+ 0x0033 => {
+ eprintln!("LD, B, V{}", x);
+ let i = self.i as usize;
+ self.memory[i] = self.v[x] / 100;
+ self.memory[i + 1] = (self.v[x] / 10) % 10;
+ self.memory[i + 2] = (self.v[x] % 100) % 10;
+ }
+ 0x0055 => {
+ eprintln!("LD [I], V{}", x);
+ let i = self.i as usize;
+ self.memory[i..=i + x].copy_from_slice(&self.v[..=x]);
+ }
+ 0x0065 => {
+ eprintln!("LD V{}, [I]", x);
+ let i = self.i as usize;
+ self.v[..=x].copy_from_slice(&self.memory[i..=i + x]);
+ }
+ _ => unknown_opcode(opcode),
+ },
+ _ => unknown_opcode(opcode),
+ };
+
+ if self.delay_timer > 0 {
+ self.delay_timer -= 1;
+ }
+
+ if self.sound_timer > 0 {
+ if self.sound_timer == 1 {
+ eprintln!("BEEP");
+ }
+ self.sound_timer -= 1;
+ }
+ }
+
+ pub fn display(&mut self) -> std::io::Result<()> {
+ if self.draw {
+ self.draw = false;
+ self.draw()
+ } else {
+ Ok(())
+ }
+ }
+
+ fn draw(&self) -> std::io::Result<()> {
+ let stdout = std::io::stdout();
+ let mut handle = stdout.lock();
+ handle.write_all(b"\x1B[2J\n")?;
+ for y in 0..HEIGHT {
+ for x in 0..WIDTH {
+ let si = x + y * WIDTH;
+ let buf = if self.screen[si as usize] == 0 {
+ "░".as_bytes()
+ } else {
+ "█".as_bytes()
+ };
+ handle.write_all(buf)?;
+ }
+ handle.write_all(b"\n")?;
+ }
+ handle.flush()
+ }
+}