[gpu] Add screen

This commit is contained in:
2024-03-03 11:48:07 +05:30
parent 68e1866be9
commit 7262484ec3
8 changed files with 379 additions and 86 deletions

View File

@@ -1,65 +0,0 @@
use byteorder::{BigEndian, ByteOrder};
use crate::device::cpu::Instruction::{ClearScreen, JumpTo, SetRegister};
#[derive(Eq, PartialEq, Debug)]
enum Instruction {
ClearScreen,
/// Jump to location
JumpTo(u16),
SetRegister(usize, u8),
AddValueToRegister(usize, u8),
SetIndex(u16),
///
DRAW(usize, usize, u8),
}
impl Instruction {
pub fn parse_fetched_instruction(location: &[u8]) -> Instruction {
assert_eq!(location.len(), 2);
let instruction = BigEndian::read_u16(location);
if instruction == 0xe0 {
return ClearScreen;
} else if (instruction & 0x1000) == 0x1000 {
return JumpTo(instruction & 0xfff);
} else if (instruction & 0x6000) == 0x6000 {
return SetRegister(((instruction & 0x0f00)>>8) as usize, (instruction & 0xff) as u8);
} else {
todo!();
}
}
}
#[cfg(test)]
mod tests {
use crate::device::cpu::Instruction;
use crate::device::cpu::Instruction::{ClearScreen, JumpTo, SetRegister};
#[test]
fn test_clear_screen() {
let instruction_bytes = 0x00e0_u16.to_be_bytes();
let ins = Instruction::parse_fetched_instruction(&instruction_bytes);
assert_eq!(ins, ClearScreen);
}
#[test]
fn test_jump_to_instruction_1() {
let instruction_bytes = 0x1123_u16.to_be_bytes();
let ins = Instruction::parse_fetched_instruction(&instruction_bytes);
assert_eq!(ins, JumpTo(0x123));
}
#[test]
fn test_jump_to_instruction_2() {
let instruction_bytes = 0x1faf_u16.to_be_bytes();
let ins = Instruction::parse_fetched_instruction(&instruction_bytes);
assert_eq!(ins, JumpTo(0xfaf));
}
#[test]
fn test_set_instruction(){
let instruction_bytes = 0x6a00_u16.to_be_bytes();
let ins = Instruction::parse_fetched_instruction(&instruction_bytes);
assert_eq!(ins, SetRegister(10,0));
}
}

View File

@@ -1,8 +1,10 @@
use std::ops::SubAssign;
use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::thread;
use std::thread::{JoinHandle, sleep};
use std::time::Duration;
use crate::device::instruction::Instruction;
use crate::device::timer::Timer;
@@ -10,24 +12,30 @@ pub struct Device {
pub registers: RegisterFile,
pub memory: Box<[u8; Self::DEVICE_MEMORY_SIZE]>,
pub timer: Timer,
pub stack: Vec<u16>
pub stack: Vec<u16>,
pub frame_buffer: Arc<Mutex<Box<[u8;64*32]>>>
}
impl Device {
pub const DEVICE_MEMORY_SIZE: usize = 2 << 12;
pub fn new(timer: Timer) -> Device {
pub const DEVICE_MEMORY_SIZE: usize = 1 << 12;
pub const FRAME_BUFFER_WIDTH: usize = 64;
pub const FRAME_BUFFER_HEIGHT: usize = 32;
pub const FRAME_BUFFER_SIZE: usize = Self::FRAME_BUFFER_WIDTH*Self::FRAME_BUFFER_HEIGHT;
pub fn new(timer: Timer, fb: Arc<Mutex<Box<[u8;64*32]>>>) -> Device {
let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE].into_boxed_slice().try_into().unwrap();
log::trace!("Successfully initiated device memory");
Device {
registers: RegisterFile::new(),
memory,
stack:Vec::with_capacity(16),
frame_buffer: fb,
stack: Vec::with_capacity(16),
timer,
}
}
}
impl Device{
const DEFAULT_FONT:[u8;5*16] = [
impl Device {
const DEFAULT_FONT: [u8; 5 * 16] = [
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
0x20, 0x60, 0x20, 0x20, 0x70, // 1
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
@@ -45,30 +53,63 @@ impl Device{
0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
0xF0, 0x80, 0xF0, 0x80, 0x80 // F
];
const FONT_DEFAULT_MEM_LOCATION_START:usize = 0x50;
const FONT_DEFAULT_MEM_LOCATION_END:usize = 0x9F;
const ROM_START:usize = 0x200;
pub fn cycle(&mut self){
const FONT_DEFAULT_MEM_LOCATION_START: usize = 0x50;
const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F;
const ROM_START: usize = 0x200;
pub fn cycle(&mut self) {
let pc = self.registers.pc as usize;
let instr_slice = self.memory.get(pc..pc + 2).expect("Failed to get memory");
self.registers.pc += 2;
let instruction = Instruction::decode_instruction(instr_slice);
self.execute_instruction(instruction);
}
pub fn execute_instruction(&mut self, instruction: Instruction) {
// thread::sleep(Duration::from_millis(250));
log::trace!("Executing {:?}, {:?}",&instruction,&self.registers);
match instruction{
Instruction::PassThrough => {
log::info!("Executing passthrough");
}
Instruction::ClearScreen => {
log::info!("ClearScreen")
}
Instruction::JumpTo(new_pc) => {
self.registers.pc = new_pc;
}
Instruction::SetRegister(reg_location, value) => {
self.registers.v[reg_location] = value;
}
Instruction::AddValueToRegister(reg_location, value) => {
self.registers.v[reg_location] += value;
}
Instruction::SetIndex(value) => {
self.registers.i = value;
}
Instruction::Draw(x, y, n) => {
let frame_buffer = self.frame_buffer.lock();
log::warn!("Draw call unimplemented");
}
};
}
pub fn set_default_font(&mut self){
pub fn set_default_font(&mut self) {
log::info!("Loaded default font from memory");
self.memory[Self::FONT_DEFAULT_MEM_LOCATION_START..=Self::FONT_DEFAULT_MEM_LOCATION_END].copy_from_slice(&Self::DEFAULT_FONT);
}
/// load a rom from bytes
pub fn load_rom(&mut self,rom: &[u8]){
pub fn load_rom(&mut self, rom: &[u8]) {
log::info!("Loaded ROM from memory");
self.memory[Self::ROM_START..].copy_from_slice(rom);
}
}
impl Drop for Device{
impl Drop for Device {
fn drop(&mut self) {
self.timer.send_stop_signal()
}
}
#[derive(Debug)]
pub struct RegisterFile {
pub v: [u8; 0x10],
/// program counter - only u12 technically.
@@ -78,7 +119,7 @@ pub struct RegisterFile {
}
impl RegisterFile {
pub const DEFAULT_PC_VALUE:u16 = Device::ROM_START as u16;
pub const DEFAULT_PC_VALUE: u16 = Device::ROM_START as u16;
pub fn new() -> RegisterFile {
RegisterFile {
v: [0; 0x10],

131
src/device/instruction.rs Normal file
View File

@@ -0,0 +1,131 @@
use byteorder::{BigEndian, ByteOrder};
use crate::device::instruction::Instruction::{AddValueToRegister, ClearScreen, Draw, JumpTo, PassThrough, SetIndex, SetRegister};
#[derive(Eq, PartialEq, Debug)]
pub enum Instruction {
/// Blanket instruction that does nothing
PassThrough,
/// 00E0 - Clear the screen
ClearScreen,
/// 1NNN - Jump to location
JumpTo(u16),
/// 6XNN - Set register to value
SetRegister(usize, u8),
/// 7XNN - Add value to register
AddValueToRegister(usize, u8),
/// ANNN - Set index value
SetIndex(u16),
///
Draw(usize, usize, u8),
}
impl Instruction {
pub fn decode_instruction(location: &[u8]) -> Instruction {
assert_eq!(location.len(), 2);
let instruction = BigEndian::read_u16(location);
let outer_instruction_nibble = (instruction & 0xF000) >> 12;
match outer_instruction_nibble {
0x0 if instruction == 0xe0 => {
ClearScreen
}
0x0 => {
log::warn!("Ignoring unsupported instruction {}",instruction);
PassThrough
}
0x1 => {
JumpTo(instruction & 0xfff)
}
0x6 => {
SetRegister(((instruction & 0x0f00) >> 8) as usize, (instruction & 0xff) as u8)
}
0x7 => {
AddValueToRegister(((instruction & 0x0f00) >> 8) as usize, (instruction & 0xff) as u8)
}
0xA => {
SetIndex(instruction & 0xfff)
}
0xD => {
let x = (instruction & 0xf00) >> 8;
let y = (instruction & 0xf0) >> 4;
let n = instruction & 0xf;
Draw(x as usize, y as usize, n as u8)
}
0x8 => {
todo!("Arithmetic instructions pending")
}
_ => {
todo!("Unimplemented instruction")
}
}
}
}
#[cfg(test)]
mod tests {
use crate::device::instruction::Instruction;
use crate::device::instruction::Instruction::{AddValueToRegister, ClearScreen, Draw, JumpTo, SetIndex, SetRegister};
#[test]
fn test_clear_screen() {
let instruction_bytes = 0x00e0_u16.to_be_bytes();
let ins = Instruction::decode_instruction(&instruction_bytes);
assert_eq!(ins, ClearScreen);
}
#[test]
#[should_panic]
fn test_other_0x0nnn_instructions_panic() {
let instruction_bytes = 0x00f0_u16.to_be_bytes();
Instruction::decode_instruction(&instruction_bytes);
}
#[test]
fn test_jump_to_instruction_1() {
let instruction_bytes = 0x1123_u16.to_be_bytes();
let ins = Instruction::decode_instruction(&instruction_bytes);
assert_eq!(ins, JumpTo(0x123));
}
#[test]
fn test_jump_to_instruction_2() {
let instruction_bytes = 0x1faf_u16.to_be_bytes();
let ins = Instruction::decode_instruction(&instruction_bytes);
assert_eq!(ins, JumpTo(0xfaf));
}
#[test]
fn test_set_register_instruction() {
let instruction_bytes = 0x6a00_u16.to_be_bytes();
let ins = Instruction::decode_instruction(&instruction_bytes);
assert_eq!(ins, SetRegister(10, 0));
}
#[test]
fn test_set_register_instruction_2() {
let instruction_bytes = 0x6f23_u16.to_be_bytes();
let ins = Instruction::decode_instruction(&instruction_bytes);
assert_eq!(ins, SetRegister(15, 0x23));
}
#[test]
fn test_add_register_instruction_2() {
let instruction_bytes = 0x7f23_u16.to_be_bytes();
let ins = Instruction::decode_instruction(&instruction_bytes);
assert_eq!(ins, AddValueToRegister(15, 0x23));
}
#[test]
fn test_set_index() {
let instruction_bytes = 0xafaf_u16.to_be_bytes();
let ins = Instruction::decode_instruction(&instruction_bytes);
assert_eq!(ins, SetIndex(0xfaf));
}
#[test]
fn test_draw() {
let instruction_bytes = 0xdfab_u16.to_be_bytes();
let ins = Instruction::decode_instruction(&instruction_bytes);
assert_eq!(ins, Draw(0xf, 0xa, 0xb))
}
}

View File

@@ -1,6 +1,6 @@
pub mod timer;
mod device;
mod sound;
mod cpu;
pub mod instruction;
pub use device::*;