Rework the interrupt system a little bit and allow toggling debug output
This commit is contained in:
parent
42f26a48e7
commit
6fb6d07431
99
src/cpu.rs
99
src/cpu.rs
|
@ -3,9 +3,9 @@
|
||||||
// TODO: in the instruction match statement, all of the register ones have `let result` inside the if statement
|
// 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)
|
// 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)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Flag {
|
pub struct Flag {
|
||||||
|
@ -59,10 +59,18 @@ pub struct Cpu {
|
||||||
pub halted: bool,
|
pub halted: bool,
|
||||||
|
|
||||||
pub bus: Bus,
|
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 {
|
impl Cpu {
|
||||||
pub fn new(bus: Bus) -> Self {
|
pub fn new(bus: Bus, debug_toggle_receiver: Receiver<()>) -> Self {
|
||||||
Cpu {
|
Cpu {
|
||||||
instruction_pointer: 0xF0000000,
|
instruction_pointer: 0xF0000000,
|
||||||
stack_pointer: 0x00000000,
|
stack_pointer: 0x00000000,
|
||||||
|
@ -72,6 +80,12 @@ impl Cpu {
|
||||||
flag: Flag { swap_sp: false, interrupt: false, carry: false, zero: false },
|
flag: Flag { swap_sp: false, interrupt: false, carry: false, zero: false },
|
||||||
halted: false,
|
halted: false,
|
||||||
bus,
|
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 {
|
fn check_condition(&self, condition: Condition) -> bool {
|
||||||
|
@ -225,39 +239,24 @@ impl Cpu {
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn interrupt(&mut self, interrupt: Interrupt) {
|
pub fn exception_to_vector(&mut self, exception: Exception) -> (Option<u8>, Option<u32>) {
|
||||||
if DEBUG { println!("interrupt(): enabled: {}", self.flag.interrupt); }
|
match exception {
|
||||||
let is_exception = if let Interrupt::Exception(_) = interrupt { true } else { false };
|
Exception::DivideByZero => {
|
||||||
if self.flag.interrupt || is_exception {
|
(Some(0), None)
|
||||||
match interrupt {
|
}
|
||||||
Interrupt::Request(vector) => {
|
Exception::InvalidOpcode(opcode) => {
|
||||||
self.handle_interrupt(vector as u16);
|
(Some(1), Some(opcode))
|
||||||
}
|
}
|
||||||
Interrupt::Exception(exception) => {
|
Exception::PageFaultRead(virtual_address) => {
|
||||||
match exception {
|
(Some(2), Some(virtual_address))
|
||||||
Exception::DivideByZero => {
|
}
|
||||||
let vector: u16 = 0;
|
Exception::PageFaultWrite(virtual_address) => {
|
||||||
self.handle_exception(vector, None);
|
(Some(3), Some(virtual_address))
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn handle_interrupt(&mut self, vector: u16) {
|
fn handle_interrupt(&mut self, vector: u8) {
|
||||||
if DEBUG { println!("interrupt!!! vector: {:#04X}", vector); }
|
if self.debug { println!("interrupt!!! vector: {:#04X}", vector); }
|
||||||
let address_of_pointer = vector as u32 * 4;
|
let address_of_pointer = vector as u32 * 4;
|
||||||
|
|
||||||
let old_mmu_state = *self.bus.memory.mmu_enabled();
|
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.flag.interrupt = false; // prevent interrupts while already servicing an interrupt
|
||||||
self.instruction_pointer = address;
|
self.instruction_pointer = address;
|
||||||
}
|
}
|
||||||
fn handle_exception(&mut self, vector: u16, operand: Option<u32>) {
|
fn handle_exception(&mut self, vector: u8, operand: Option<u32>) {
|
||||||
if DEBUG { println!("exception!!! vector: {:#04X}, operand: {:?}", vector, operand); }
|
if self.debug { println!("exception!!! vector: {:#04X}, operand: {:?}", vector, operand); }
|
||||||
let address_of_pointer = (256 + vector) as u32 * 4;
|
let address_of_pointer = (256 + vector as u32) * 4;
|
||||||
|
|
||||||
let old_mmu_state = *self.bus.memory.mmu_enabled();
|
let old_mmu_state = *self.bus.memory.mmu_enabled();
|
||||||
*self.bus.memory.mmu_enabled() = false;
|
*self.bus.memory.mmu_enabled() = false;
|
||||||
|
@ -320,6 +319,24 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
// execute instruction from memory at the current instruction pointer
|
// execute instruction from memory at the current instruction pointer
|
||||||
pub fn execute_memory_instruction(&mut self) {
|
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);
|
let opcode_maybe = self.bus.memory.read_16(self.instruction_pointer);
|
||||||
if opcode_maybe == None {
|
if opcode_maybe == None {
|
||||||
return;
|
return;
|
||||||
|
@ -327,11 +344,14 @@ impl Cpu {
|
||||||
let opcode = opcode_maybe.unwrap();
|
let opcode = opcode_maybe.unwrap();
|
||||||
|
|
||||||
if let Some(instruction) = Instruction::from_half(opcode) {
|
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);
|
let next_instruction_pointer = self.execute_instruction(instruction);
|
||||||
if let Some(next) = next_instruction_pointer {
|
if let Some(next) = next_instruction_pointer {
|
||||||
self.instruction_pointer = next;
|
self.instruction_pointer = next;
|
||||||
}
|
}
|
||||||
|
if let Ok(_) = self.debug_toggle_receiver.try_recv() {
|
||||||
|
self.debug = !self.debug;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let size = ((opcode & 0b1100000000000000) >> 14) as u8;
|
let size = ((opcode & 0b1100000000000000) >> 14) as u8;
|
||||||
let instruction = ((opcode & 0b0011111100000000) >> 8) 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 (source_value, instruction_pointer_offset) = self.read_source(source)?;
|
||||||
let should_run = self.check_condition(condition);
|
let should_run = self.check_condition(condition);
|
||||||
if should_run {
|
if should_run {
|
||||||
self.instruction_pointer += instruction_pointer_offset;
|
self.next_soft_interrupt = Some(source_value as u8);
|
||||||
self.handle_interrupt(source_value as u16);
|
Some(self.instruction_pointer + instruction_pointer_offset)
|
||||||
Some(self.instruction_pointer)
|
|
||||||
} else {
|
} else {
|
||||||
Some(self.instruction_pointer + instruction_pointer_offset)
|
Some(self.instruction_pointer + instruction_pointer_offset)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
// keyboard.rs
|
// keyboard.rs
|
||||||
|
|
||||||
|
|
||||||
use crate::warn;
|
use crate::warn;
|
||||||
|
|
||||||
use ringbuf::{Consumer, Producer, RingBuffer};
|
use ringbuf::{Consumer, Producer, RingBuffer};
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
use winit::event::{ElementState, VirtualKeyCode};
|
use winit::event::{ElementState, VirtualKeyCode};
|
||||||
|
|
||||||
pub struct Keyboard {
|
pub struct Keyboard {
|
||||||
consumer: Consumer<u8>,
|
consumer: Consumer<u8>,
|
||||||
producer: Producer<u8>,
|
producer: Producer<u8>,
|
||||||
|
debug_toggle_sender: Sender<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keyboard {
|
impl Keyboard {
|
||||||
pub fn new() -> Self {
|
pub fn new(debug_toggle_sender: Sender<()>) -> Self {
|
||||||
let buffer = RingBuffer::<u8>::new(32);
|
let buffer = RingBuffer::<u8>::new(32);
|
||||||
let (producer, consumer) = buffer.split();
|
let (producer, consumer) = buffer.split();
|
||||||
Keyboard { consumer, producer }
|
Keyboard { consumer, producer, debug_toggle_sender }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keycode_to_scancode(&self, keycode: VirtualKeyCode) -> u8 {
|
fn keycode_to_scancode(&self, keycode: VirtualKeyCode) -> u8 {
|
||||||
|
@ -105,6 +108,10 @@ impl Keyboard {
|
||||||
if state == ElementState::Released && scancode != 0x00 {
|
if state == ElementState::Released && scancode != 0x00 {
|
||||||
scancode |= 0x80; // "break" scancode
|
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!"));
|
self.producer.push(scancode).unwrap_or_else(|_| warn("keyboard buffer full!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -76,8 +76,10 @@ fn main() {
|
||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
|
let (debug_toggle_sender, debug_toggle_receiver) = mpsc::channel::<()>();
|
||||||
|
|
||||||
let mut display = Display::new();
|
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 mouse = Arc::new(Mutex::new(Mouse::new()));
|
||||||
|
|
||||||
let audio_channel_0 = Arc::new(Mutex::new(AudioChannel::new(0)));
|
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;
|
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);
|
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 event_loop = EventLoop::new();
|
||||||
let mut input = WinitInputHelper::new();
|
let mut input = WinitInputHelper::new();
|
||||||
|
@ -160,10 +162,12 @@ fn main() {
|
||||||
loop {
|
loop {
|
||||||
while !cpu.halted {
|
while !cpu.halted {
|
||||||
if let Ok(exception) = exception_receiver.try_recv() {
|
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 {
|
} else {
|
||||||
if let Ok(interrupt) = interrupt_receiver.try_recv() {
|
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();
|
cpu.execute_memory_instruction();
|
||||||
|
@ -182,8 +186,10 @@ fn main() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if let Ok(interrupt) = interrupt_receiver.recv() {
|
if let Ok(interrupt) = interrupt_receiver.recv() {
|
||||||
cpu.halted = false;
|
if let Interrupt::Request(vector) = interrupt {
|
||||||
cpu.interrupt(interrupt);
|
cpu.next_interrupt = Some(vector);
|
||||||
|
cpu.halted = false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// sender is closed, break
|
// sender is closed, break
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user