123 lines
4.0 KiB
Rust
123 lines
4.0 KiB
Rust
use log::{debug, error};
|
|
use crate::chip8::delay_timer::DelayTimer;
|
|
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
|
|
use crate::chip8::registers::Chip8Registers;
|
|
use crate::chip8::sound_timer::SoundTimer;
|
|
use crate::chip8::util::InstructionUtil;
|
|
use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT};
|
|
|
|
use super::{
|
|
cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions, system_memory::Chip8SystemMemory, video::Chip8Video,
|
|
};
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct Chip8Computer {
|
|
pub sp: u8,
|
|
pub memory: Chip8SystemMemory,
|
|
pub registers: Chip8Registers,
|
|
pub sound_timer: SoundTimer,
|
|
pub delay_timer: DelayTimer,
|
|
pub video_memory: Chip8Video,
|
|
pub state: Chip8CpuStates,
|
|
}
|
|
|
|
impl Default for Chip8Computer {
|
|
fn default() -> Self {
|
|
Self {
|
|
sp: 0x00,
|
|
memory: Chip8SystemMemory::default(),
|
|
video_memory: Chip8Video::default(),
|
|
registers: Chip8Registers::default(),
|
|
sound_timer: SoundTimer::new(),
|
|
delay_timer: DelayTimer::new(),
|
|
state: Chip8CpuStates::WaitingForInstruction,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Chip8Computer {
|
|
pub fn new_with_program(new_program: Box<Vec<u16>>) -> Self {
|
|
let mut working = Chip8Computer::new();
|
|
for i in 0..new_program.len() {
|
|
let high_byte = (new_program[i as usize] >> 8) as u8;
|
|
let low_byte = (new_program[i] & 0xff) as u8;
|
|
let base_offset = i * 2;
|
|
working.memory.poke(base_offset as u16, high_byte);
|
|
working.memory.poke((base_offset + 1) as u16, low_byte);
|
|
}
|
|
working
|
|
}
|
|
pub fn new() -> Self {
|
|
Chip8Computer::default()
|
|
}
|
|
|
|
pub fn load_bytes_to_memory(&mut self, offset: u16, to_load: Box<Vec<u8>>) {
|
|
let total_len = to_load.len() as u16;
|
|
for current_index in 0..total_len {
|
|
let new_value = to_load[current_index as usize];
|
|
let new_location = current_index + offset;
|
|
self.memory.poke(new_location, new_value);
|
|
}
|
|
}
|
|
|
|
pub fn step_system(&mut self) -> &mut Chip8Computer {
|
|
// read the next instruction
|
|
|
|
let mut working_instruction: u16 = 0b0000000000000000;
|
|
let start_pc = self.registers.peek_pc();
|
|
let high_byte = (self.memory.clone().peek(start_pc) as u16).rotate_left(8);
|
|
let low_byte = self.memory.clone().peek(start_pc + 1) as u16;
|
|
|
|
working_instruction = InstructionUtil::join_bytes(high_byte as u8, low_byte as u8);
|
|
|
|
let decoded_instruction =
|
|
Chip8CpuInstructions::decode(working_instruction);
|
|
|
|
// println!("DECODED INSTRUCTION = {:?}", decoded_instruction);
|
|
// start by moving to the next instruction
|
|
// todo: THIS IS BAD AND IS A SIDE EFFECT
|
|
decoded_instruction.execute(self);
|
|
|
|
self.sound_timer.tick();
|
|
self.delay_timer.tick();
|
|
self
|
|
}
|
|
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use rand::random;
|
|
use crate::constants::CHIP8_VIDEO_MEMORY;
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn smoke() {
|
|
assert!(true)
|
|
}
|
|
|
|
#[test]
|
|
fn decoder_test_invalid_instructions() {
|
|
// 'bad' instructions that should be dropped...
|
|
// assert!(matches!(Chip8Computer::decode_instruction(0x5ab1), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
|
|
// assert!(matches!(Chip8Computer::decode_instruction(0x8ab8), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
|
|
// assert!(matches!(Chip8Computer::decode_instruction(0xeaba), Chip8CpuInstructions::XXXXERRORINSTRUCTION));
|
|
}
|
|
|
|
#[test]
|
|
fn new_with_program() {
|
|
// set a known program that sets some registers...
|
|
let new_program = Box::new(vec![
|
|
Chip8CpuInstructions::LdVxI(0x10).encode(),
|
|
Chip8CpuInstructions::LdVxVy(0x1, 0x1).encode(),
|
|
Chip8CpuInstructions::LdStVx(0xff).encode()
|
|
]);
|
|
|
|
// ...then load it...
|
|
let test_computer = Chip8Computer::new_with_program(new_program);
|
|
|
|
// ...and check the registers
|
|
}
|
|
}
|
|
|