add almost all insturctions
This commit is contained in:
parent
afdc8c458d
commit
98c270218c
3 changed files with 204 additions and 13 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -4,4 +4,4 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "celc8"
|
name = "celc8"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "celc8"
|
name = "celc8"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
edition = "2021"
|
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
|
||||||
|
|
207
src/main.rs
207
src/main.rs
|
@ -1,3 +1,7 @@
|
||||||
|
use std::io;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
struct Cpu {
|
struct Cpu {
|
||||||
memory: Vec<u8>,
|
memory: Vec<u8>,
|
||||||
pc: u16,
|
pc: u16,
|
||||||
|
@ -14,6 +18,10 @@ impl Cpu {
|
||||||
&self.memory[index]
|
&self.memory[index]
|
||||||
}
|
}
|
||||||
fn ramwrite(&mut self, index: usize, value: u8) {
|
fn ramwrite(&mut self, index: usize, value: u8) {
|
||||||
|
if index < 512 {
|
||||||
|
println!("Segmentation fault in emulated CPU! Bailing out!");
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
self.memory[index] = value;
|
self.memory[index] = value;
|
||||||
}
|
}
|
||||||
fn regread(&self, index: usize) -> &u8 {
|
fn regread(&self, index: usize) -> &u8 {
|
||||||
|
@ -31,9 +39,31 @@ impl Cpu {
|
||||||
fn incrementpc(&mut self) {
|
fn incrementpc(&mut self) {
|
||||||
self.pc = self.pc + 2
|
self.pc = self.pc + 2
|
||||||
}
|
}
|
||||||
fn fetch(&mut self) -> u16 {
|
fn fetch(&self) -> u16 {
|
||||||
return ((self.memory[self.pc as usize] as u16) << 8 | self.memory[self.pc as usize + 1 as usize] as u16)
|
return ((self.memory[self.pc as usize] as u16) << 8 | self.memory[self.pc as usize + 1 as usize] as u16)
|
||||||
}
|
}
|
||||||
|
fn setpc(&mut self, prc: u16) {
|
||||||
|
self.pc = prc;
|
||||||
|
}
|
||||||
|
fn incrementsp(&mut self) {
|
||||||
|
self.sp = self.sp + 1
|
||||||
|
}
|
||||||
|
fn stackpush(&mut self) {
|
||||||
|
if self.stack.len() == 15 {
|
||||||
|
println!("Stack overflow in emulated CPU! Bailing out!");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
self.stack.push(self.pc);
|
||||||
|
}
|
||||||
|
fn stackpop(&mut self) -> u16 {
|
||||||
|
if self.stack.len() == 0 {
|
||||||
|
println!("Stack underflow in emulated CPU! Bailing out!");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
let stacker = *self.stack.last().unwrap() as u16;
|
||||||
|
self.stack.pop();
|
||||||
|
return stacker;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Instruction {
|
struct Instruction {
|
||||||
|
@ -216,30 +246,191 @@ fn execute(opcode: u16, instruction: Instruction, mut cpu: Cpu) -> Cpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if instruction.name == "LD_VX_BYTE" {
|
if instruction.name == "JP_ADDR" { cpu.setpc(args[0] as u16); }
|
||||||
|
else if instruction.name == "CALL_ADDR" {
|
||||||
|
cpu.incrementsp();
|
||||||
|
cpu.stackpush();
|
||||||
|
cpu.setpc(args[0] as u16);
|
||||||
|
}
|
||||||
|
else if instruction.name == "SE_VX_BYTE" {
|
||||||
|
let register = args[0] as usize;
|
||||||
|
let byte = args[1] as u8;
|
||||||
|
|
||||||
|
if cpu.regread(register) == &byte {
|
||||||
|
cpu.incrementpc();
|
||||||
|
cpu.incrementpc();
|
||||||
|
} else {
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if instruction.name == "SNE_VX_BYTE" {
|
||||||
|
let register = args[0] as usize;
|
||||||
|
let byte = args[1] as u8;
|
||||||
|
|
||||||
|
if cpu.regread(register) != &byte {
|
||||||
|
cpu.incrementpc();
|
||||||
|
cpu.incrementpc();
|
||||||
|
} else {
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if instruction.name == "SE_VX_VY" {
|
||||||
|
let register1 = args[0] as usize;
|
||||||
|
let register2 = args[1] as usize;
|
||||||
|
|
||||||
|
if cpu.regread(register1) == cpu.regread(register2) {
|
||||||
|
cpu.incrementpc();
|
||||||
|
cpu.incrementpc();
|
||||||
|
} else {
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if instruction.name == "LD_VX_BYTE" {
|
||||||
cpu.regwrite(args[0] as usize, args[1] as u8);
|
cpu.regwrite(args[0] as usize, args[1] as u8);
|
||||||
cpu.incrementpc();
|
cpu.incrementpc();
|
||||||
}
|
}
|
||||||
else { println!("Unimplemented instruction {}. Skipping.", instruction.name) }
|
else if instruction.name == "ADD_VX_BYTE" {
|
||||||
|
cpu.regwrite(args[0] as usize, (*cpu.regread(args[0] as usize) + args[1] as u8));
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else if instruction.name == "LD_VX_VY" {
|
||||||
|
cpu.regwrite(args[0] as usize, *cpu.regread(args[1] as usize));
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else if instruction.name == "OR_VX_VY" {
|
||||||
|
let result = *cpu.regread(args[0] as usize) | *cpu.regread(args[1] as usize);
|
||||||
|
cpu.regwrite(args[0] as usize, result as u8);
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else if instruction.name == "AND_VX_VY" {
|
||||||
|
let result = *cpu.regread(args[0] as usize) & *cpu.regread(args[1] as usize);
|
||||||
|
cpu.regwrite(args[0] as usize, result as u8);
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else if instruction.name == "XOR_VX_VY" {
|
||||||
|
let result = *cpu.regread(args[0] as usize) ^ *cpu.regread(args[1] as usize);
|
||||||
|
cpu.regwrite(args[0] as usize, result as u8);
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else if instruction.name == "ADD_VX_VY" {
|
||||||
|
let reg1 = *cpu.regread(args[0] as usize) as u16;
|
||||||
|
let reg2 = *cpu.regread(args[1] as usize) as u16;
|
||||||
|
|
||||||
|
let bigresult = reg1 + reg2;
|
||||||
|
|
||||||
|
if bigresult > 255 {
|
||||||
|
// According to Cowgod, we can only keep the
|
||||||
|
// lowest 8 bits and Rust does this fine when
|
||||||
|
// you do bigresult as u8 so we are all good!
|
||||||
|
cpu.regwrite(0xf as usize, 1 as u8);
|
||||||
|
cpu.regwrite(args[0] as usize, bigresult as u8);
|
||||||
|
} else {
|
||||||
|
cpu.regwrite(0xf as usize, 0 as u8);
|
||||||
|
cpu.regwrite(args[0] as usize, bigresult as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else if instruction.name == "SUB_VX_VY" {
|
||||||
|
let reg1 = *cpu.regread(args[0] as usize) as u16;
|
||||||
|
let reg2 = *cpu.regread(args[1] as usize) as u16;
|
||||||
|
|
||||||
|
// Rust doesn't like it when you do arithmetic overflow so
|
||||||
|
// I'm gonna fudge it :D
|
||||||
|
if reg1 < reg2 {
|
||||||
|
cpu.regwrite(args[0] as usize, 0);
|
||||||
|
} else {
|
||||||
|
if reg1 > reg2 {
|
||||||
|
cpu.regwrite(0xf as usize, 1 as u8);
|
||||||
|
} else {
|
||||||
|
cpu.regwrite(0xf as usize, 0 as u8);
|
||||||
|
}
|
||||||
|
cpu.regwrite(args[0] as usize, (reg1 - reg2) as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else if instruction.name == "SHR_VX_VY" {
|
||||||
|
let reg1 = *cpu.regread(args[0] as usize) as u16;
|
||||||
|
|
||||||
|
if reg1 % 2 != 0 {
|
||||||
|
cpu.regwrite(0xf as usize, 1 as u8);
|
||||||
|
cpu.regwrite(args[0] as usize, ((reg1 - 2) / 2) as u8);
|
||||||
|
} else {
|
||||||
|
cpu.regwrite(0xf as usize, 0 as u8);
|
||||||
|
cpu.regwrite(args[0] as usize, (reg1 / 2) as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else if instruction.name == "SUBN_VX_VY" {
|
||||||
|
let reg1 = *cpu.regread(args[0] as usize) as u16;
|
||||||
|
let reg2 = *cpu.regread(args[1] as usize) as u16;
|
||||||
|
|
||||||
|
if reg1 > reg2 {
|
||||||
|
cpu.regwrite(args[0] as usize, 0);
|
||||||
|
} else {
|
||||||
|
if reg1 < reg2 {
|
||||||
|
cpu.regwrite(0xf as usize, 1 as u8);
|
||||||
|
} else {
|
||||||
|
cpu.regwrite(0xf as usize, 0 as u8);
|
||||||
|
}
|
||||||
|
cpu.regwrite(args[0] as usize, (reg2 - reg1) as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else if instruction.name == "SHL_VX_VY" {
|
||||||
|
let reg1 = *cpu.regread(args[0] as usize) as u16;
|
||||||
|
|
||||||
|
if reg1 % 2 != 0 {
|
||||||
|
cpu.regwrite(0xf as usize, 1 as u8);
|
||||||
|
cpu.regwrite(args[0] as usize, (reg1 * 2) as u8);
|
||||||
|
} else {
|
||||||
|
cpu.regwrite(0xf as usize, 0 as u8);
|
||||||
|
cpu.regwrite(args[0] as usize, (reg1 * 2) as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println!("Unimplemented instruction {}. Skipping.", instruction.name);
|
||||||
|
cpu.incrementpc();
|
||||||
|
}
|
||||||
|
|
||||||
return cpu;
|
return cpu;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> io::Result<()> {
|
||||||
|
let mut f = File::open("program.ch8")?;
|
||||||
|
let mut buffer: Vec<u8> = vec![];
|
||||||
|
println!("Buffer initialised!");
|
||||||
|
|
||||||
|
f.read_to_end(&mut buffer)?;
|
||||||
|
|
||||||
let mut cpu = Cpu { memory: vec![0; 4096], pc: 0x200, registers: vec![0; 16], i: 0, stack: vec![0; 16], sp: -1, dt: 0, st: 0 };
|
let mut cpu = Cpu { memory: vec![0; 4096], pc: 0x200, registers: vec![0; 16], i: 0, stack: vec![0; 16], sp: -1, dt: 0, st: 0 };
|
||||||
println!("CPU initialised!");
|
println!("CPU initialised!");
|
||||||
|
|
||||||
|
let mut i = 0x200;
|
||||||
|
|
||||||
|
for byte in buffer {
|
||||||
|
cpu.ramwrite(i, byte);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("File loaded to memory!");
|
||||||
|
|
||||||
torture();
|
torture();
|
||||||
println!("If you see messages above that are about opcodes, there's a bug!");
|
println!("If you see messages above that are about opcodes, there's a bug!");
|
||||||
|
|
||||||
println!("Executing LD_VX_BYTE with opcode 0x6010 through memory.");
|
loop {
|
||||||
cpu.memory[0x200] = 0x60;
|
|
||||||
cpu.memory[0x201] = 0x10;
|
|
||||||
let mut opcode = cpu.fetch();
|
let mut opcode = cpu.fetch();
|
||||||
let mut instruction = disassemble(opcode);
|
let mut instruction = disassemble(opcode);
|
||||||
cpu = execute(opcode, instruction, cpu);
|
cpu = execute(opcode, instruction, cpu);
|
||||||
|
}
|
||||||
|
|
||||||
println!("Register 0 is {}!", cpu.registers[0]);
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn torture() {
|
fn torture() {
|
||||||
|
|
Reference in a new issue