Rework the interrupt system a little bit and allow toggling debug output

This commit is contained in:
Ry 2022-09-20 00:24:38 -07:00
parent 42f26a48e7
commit 6fb6d07431
3 changed files with 80 additions and 48 deletions

View File

@ -3,9 +3,9 @@
// TODO: in the instruction match statement, all of the register ones have `let result` inside the if statement
// move this up to match all of the other ones (or move all of the other ones down, which would probably be better anyways)
use crate::Bus;
use std::sync::mpsc::Receiver;
const DEBUG: bool = false;
use crate::Bus;
#[derive(Copy, Clone)]
pub struct Flag {
@ -59,10 +59,18 @@ pub struct Cpu {
pub halted: bool,
pub bus: Bus,
pub next_interrupt: Option<u8>,
pub next_soft_interrupt: Option<u8>,
pub next_exception: Option<u8>,
pub next_exception_operand: Option<u32>,
pub debug: bool,
debug_toggle_receiver: Receiver<()>,
}
impl Cpu {
pub fn new(bus: Bus) -> Self {
pub fn new(bus: Bus, debug_toggle_receiver: Receiver<()>) -> Self {
Cpu {
instruction_pointer: 0xF0000000,
stack_pointer: 0x00000000,
@ -72,6 +80,12 @@ impl Cpu {
flag: Flag { swap_sp: false, interrupt: false, carry: false, zero: false },
halted: false,
bus,
next_interrupt: None,
next_soft_interrupt: None,
next_exception: None,
next_exception_operand: None,
debug: false,
debug_toggle_receiver,
}
}
fn check_condition(&self, condition: Condition) -> bool {
@ -225,39 +239,24 @@ impl Cpu {
None => None,
}
}
pub fn interrupt(&mut self, interrupt: Interrupt) {
if DEBUG { println!("interrupt(): enabled: {}", self.flag.interrupt); }
let is_exception = if let Interrupt::Exception(_) = interrupt { true } else { false };
if self.flag.interrupt || is_exception {
match interrupt {
Interrupt::Request(vector) => {
self.handle_interrupt(vector as u16);
}
Interrupt::Exception(exception) => {
match exception {
Exception::DivideByZero => {
let vector: u16 = 0;
self.handle_exception(vector, None);
}
Exception::InvalidOpcode(opcode) => {
let vector: u16 = 1;
self.handle_exception(vector, Some(opcode));
}
Exception::PageFaultRead(virtual_address) => {
let vector: u16 = 2;
self.handle_exception(vector, Some(virtual_address));
}
Exception::PageFaultWrite(virtual_address) => {
let vector: u16 = 3;
self.handle_exception(vector, Some(virtual_address));
}
}
}
pub fn exception_to_vector(&mut self, exception: Exception) -> (Option<u8>, Option<u32>) {
match exception {
Exception::DivideByZero => {
(Some(0), None)
}
Exception::InvalidOpcode(opcode) => {
(Some(1), Some(opcode))
}
Exception::PageFaultRead(virtual_address) => {
(Some(2), Some(virtual_address))
}
Exception::PageFaultWrite(virtual_address) => {
(Some(3), Some(virtual_address))
}
}
}
fn handle_interrupt(&mut self, vector: u16) {
if DEBUG { println!("interrupt!!! vector: {:#04X}", vector); }
fn handle_interrupt(&mut self, vector: u8) {
if self.debug { println!("interrupt!!! vector: {:#04X}", vector); }
let address_of_pointer = vector as u32 * 4;
let old_mmu_state = *self.bus.memory.mmu_enabled();
@ -285,9 +284,9 @@ impl Cpu {
self.flag.interrupt = false; // prevent interrupts while already servicing an interrupt
self.instruction_pointer = address;
}
fn handle_exception(&mut self, vector: u16, operand: Option<u32>) {
if DEBUG { println!("exception!!! vector: {:#04X}, operand: {:?}", vector, operand); }
let address_of_pointer = (256 + vector) as u32 * 4;
fn handle_exception(&mut self, vector: u8, operand: Option<u32>) {
if self.debug { println!("exception!!! vector: {:#04X}, operand: {:?}", vector, operand); }
let address_of_pointer = (256 + vector as u32) * 4;
let old_mmu_state = *self.bus.memory.mmu_enabled();
*self.bus.memory.mmu_enabled() = false;
@ -320,6 +319,24 @@ impl Cpu {
}
// execute instruction from memory at the current instruction pointer
pub fn execute_memory_instruction(&mut self) {
if let Some(vector) = self.next_exception {
self.handle_exception(vector, self.next_exception_operand);
self.next_exception = None;
self.next_exception_operand = None;
}
if let Some(vector) = self.next_soft_interrupt {
if self.flag.interrupt {
self.handle_interrupt(vector);
self.next_soft_interrupt = None;
}
}
if let Some(vector) = self.next_interrupt {
if self.flag.interrupt {
self.handle_interrupt(vector);
self.next_interrupt = None;
}
}
let opcode_maybe = self.bus.memory.read_16(self.instruction_pointer);
if opcode_maybe == None {
return;
@ -327,11 +344,14 @@ impl Cpu {
let opcode = opcode_maybe.unwrap();
if let Some(instruction) = Instruction::from_half(opcode) {
if DEBUG { println!("{:#010X}: {:?}", self.instruction_pointer, instruction); }
if self.debug { println!("{:#010X}: {:?}", self.instruction_pointer, instruction); }
let next_instruction_pointer = self.execute_instruction(instruction);
if let Some(next) = next_instruction_pointer {
self.instruction_pointer = next;
}
if let Ok(_) = self.debug_toggle_receiver.try_recv() {
self.debug = !self.debug;
}
} else {
let size = ((opcode & 0b1100000000000000) >> 14) as u8;
let instruction = ((opcode & 0b0011111100000000) >> 8) as u8;
@ -2658,9 +2678,8 @@ impl Cpu {
let (source_value, instruction_pointer_offset) = self.read_source(source)?;
let should_run = self.check_condition(condition);
if should_run {
self.instruction_pointer += instruction_pointer_offset;
self.handle_interrupt(source_value as u16);
Some(self.instruction_pointer)
self.next_soft_interrupt = Some(source_value as u8);
Some(self.instruction_pointer + instruction_pointer_offset)
} else {
Some(self.instruction_pointer + instruction_pointer_offset)
}

View File

@ -1,20 +1,23 @@
// keyboard.rs
use crate::warn;
use ringbuf::{Consumer, Producer, RingBuffer};
use std::sync::mpsc::Sender;
use winit::event::{ElementState, VirtualKeyCode};
pub struct Keyboard {
consumer: Consumer<u8>,
producer: Producer<u8>,
debug_toggle_sender: Sender<()>,
}
impl Keyboard {
pub fn new() -> Self {
pub fn new(debug_toggle_sender: Sender<()>) -> Self {
let buffer = RingBuffer::<u8>::new(32);
let (producer, consumer) = buffer.split();
Keyboard { consumer, producer }
Keyboard { consumer, producer, debug_toggle_sender }
}
fn keycode_to_scancode(&self, keycode: VirtualKeyCode) -> u8 {
@ -105,6 +108,10 @@ impl Keyboard {
if state == ElementState::Released && scancode != 0x00 {
scancode |= 0x80; // "break" scancode
}
if scancode == 0x57 {
self.debug_toggle_sender.send(()).unwrap();
return;
}
self.producer.push(scancode).unwrap_or_else(|_| warn("keyboard buffer full!"));
}

View File

@ -76,8 +76,10 @@ fn main() {
let args: Vec<String> = env::args().collect();
let (debug_toggle_sender, debug_toggle_receiver) = mpsc::channel::<()>();
let mut display = Display::new();
let keyboard = Arc::new(Mutex::new(Keyboard::new()));
let keyboard = Arc::new(Mutex::new(Keyboard::new(debug_toggle_sender)));
let mouse = Arc::new(Mutex::new(Mouse::new()));
let audio_channel_0 = Arc::new(Mutex::new(AudioChannel::new(0)));
@ -122,7 +124,7 @@ fn main() {
let rom_top_address = rom_bottom_address + rom_size - 1;
println!("ROM: {:.2} KiB mapped at physical {:#010X}-{:#010X}", rom_size / 1024, rom_bottom_address, rom_top_address);
let mut cpu = Cpu::new(bus);
let mut cpu = Cpu::new(bus, debug_toggle_receiver);
let event_loop = EventLoop::new();
let mut input = WinitInputHelper::new();
@ -160,10 +162,12 @@ fn main() {
loop {
while !cpu.halted {
if let Ok(exception) = exception_receiver.try_recv() {
cpu.interrupt(Interrupt::Exception(exception));
(cpu.next_exception, cpu.next_exception_operand) = cpu.exception_to_vector(exception);
} else {
if let Ok(interrupt) = interrupt_receiver.try_recv() {
cpu.interrupt(interrupt);
if let Interrupt::Request(vector) = interrupt {
cpu.next_interrupt = Some(vector);
}
}
}
cpu.execute_memory_instruction();
@ -182,8 +186,10 @@ fn main() {
break;
}
if let Ok(interrupt) = interrupt_receiver.recv() {
cpu.halted = false;
cpu.interrupt(interrupt);
if let Interrupt::Request(vector) = interrupt {
cpu.next_interrupt = Some(vector);
cpu.halted = false;
}
} else {
// sender is closed, break
break;