Fixed interrupts and added tx enable for OS buffered output

This commit is contained in:
transistor 2021-10-08 10:52:15 -07:00
parent ecbaf6a68b
commit 8bb43f61ee
9 changed files with 251 additions and 61 deletions

Binary file not shown.

View File

@ -124,6 +124,7 @@ impl MC68010 {
return Ok(true);
},
"c" | "continue" => {
self.debugger.use_tracing = false;
self.debugger.use_debugger = false;
return Ok(true);
},

View File

@ -17,7 +17,7 @@ use super::decode::{
sign_extend_to_long
};
use super::state::{MC68010, Status, Flags};
use super::state::{MC68010, Status, Flags, InterruptPriority};
impl Steppable for MC68010 {
fn step(&mut self, system: &System) -> Result<Clock, Error> {
@ -31,8 +31,19 @@ impl Steppable for MC68010 {
}
impl Interruptable for MC68010 {
fn handle_interrupt(&mut self, _system: &System, number: u8) -> Result<(), Error> {
self.state.status = Status::PendingExecption(number);
fn interrupt_state_change(&mut self, system: &System, state: bool, priority: u8, number: u8) -> Result<(), Error> {
let ipl = if state {
InterruptPriority::from_u8(priority)
} else {
InterruptPriority::NoInterrupt
};
if ipl != self.state.pending_ipl {
self.state.pending_ipl = ipl;
if ipl != InterruptPriority::NoInterrupt {
self.state.ipl_ack_num = number;
}
}
Ok(())
}
}
@ -62,22 +73,40 @@ impl MC68010 {
self.execute_current(system)?;
self.timer.cycle.end(timer);
if (self.timer.cycle.events % 500) == 0 {
println!("{}", self.timer);
}
//if (self.timer.cycle.events % 500) == 0 {
// println!("{}", self.timer);
//}
self.check_pending_interrupts(system)?;
Ok(())
},
Status::PendingExecption(num) => {
self.exception(system, num)?;
self.state.status = Status::Running;
Ok(())
}
}
}
pub fn check_pending_interrupts(&mut self, system: &System) -> Result<(), Error> {
let current_ipl = self.state.current_ipl as u8;
let pending_ipl = self.state.pending_ipl as u8;
if self.state.pending_ipl != InterruptPriority::NoInterrupt {
let priority_mask = ((self.state.sr & 0x700) >> 8) as u8;
if (pending_ipl >= priority_mask || pending_ipl == 7) && pending_ipl >= current_ipl {
self.state.current_ipl = self.state.pending_ipl;
self.exception(system, self.state.ipl_ack_num)?;
return Ok(());
}
}
if pending_ipl < current_ipl {
self.state.current_ipl = self.state.pending_ipl;
}
Ok(())
}
pub fn exception(&mut self, system: &System, number: u8) -> Result<(), Error> {
println!("raising exeception {:x}", number);
println!("raising exception {}", number);
let offset = (number as u16) << 2;
self.push_word(system, offset)?;
self.push_long(system, self.state.pc)?;
@ -133,7 +162,7 @@ impl MC68010 {
self.set_logic_flags(result, size);
},
Instruction::ANDtoCCR(value) => {
self.state.sr = self.state.sr | value as u16;
self.state.sr = self.state.sr | (value as u16);
},
Instruction::ANDtoSR(value) => {
self.state.sr = self.state.sr | value;
@ -244,7 +273,7 @@ impl MC68010 {
self.set_logic_flags(result, size);
},
Instruction::EORtoCCR(value) => {
self.state.sr = self.state.sr ^ value as u16;
self.state.sr = self.state.sr ^ (value as u16);
},
Instruction::EORtoSR(value) => {
self.state.sr = self.state.sr ^ value;
@ -431,7 +460,7 @@ impl MC68010 {
self.set_logic_flags(result, size);
},
Instruction::ORtoCCR(value) => {
self.state.sr = self.state.sr | value as u16;
self.state.sr = self.state.sr | (value as u16);
},
Instruction::ORtoSR(value) => {
self.state.sr = self.state.sr | value;

View File

@ -29,13 +29,43 @@ pub const ERR_ILLEGAL_INSTRUCTION: u32 = 4;
pub enum Status {
Init,
Running,
PendingExecption(u8),
Stopped,
Halted,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum InterruptPriority {
NoInterrupt = 0,
Level1 = 1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
Level5 = 5,
Level6 = 6,
Level7 = 7,
}
impl InterruptPriority {
pub fn from_u8(priority: u8) -> InterruptPriority {
match priority {
0 => InterruptPriority::NoInterrupt,
1 => InterruptPriority::Level1,
2 => InterruptPriority::Level2,
3 => InterruptPriority::Level3,
4 => InterruptPriority::Level4,
5 => InterruptPriority::Level5,
6 => InterruptPriority::Level6,
_ => InterruptPriority::Level7,
}
}
}
pub struct MC68010State {
pub status: Status,
pub current_ipl: InterruptPriority,
pub pending_ipl: InterruptPriority,
pub ipl_ack_num: u8,
pub pc: u32,
pub sr: u16,
@ -51,6 +81,9 @@ impl MC68010State {
pub fn new() -> MC68010State {
MC68010State {
status: Status::Init,
current_ipl: InterruptPriority::NoInterrupt,
pending_ipl: InterruptPriority::NoInterrupt,
ipl_ack_num: 0,
pc: 0,
sr: FLAGS_ON_RESET,

View File

@ -38,10 +38,9 @@ fn main() {
let mut cpu = MC68010::new();
//cpu.enable_tracing();
//cpu.add_breakpoint(0x0c94);
//cpu.add_breakpoint(0x103234);
//cpu.add_breakpoint(0x224);
//cpu.add_breakpoint(0x106ed2);
//cpu.add_breakpoint(0x10781a);
//cpu.add_breakpoint(0x10bc9c);
//cpu.add_breakpoint(0x106a94);
system.add_interruptable_device(wrap_interruptable(cpu)).unwrap();
loop {
@ -56,13 +55,13 @@ fn main() {
/*
// TODO I need to add a way to decode and dump the assembly for a section of code, in debugger
cpu.state.pc = 0x00100000;
cpu.state.pc = 0x0010c270;
cpu.enable_tracing();
cpu.state.pc = 0x0010781a;
while cpu.is_running() {
match cpu.decode_next(&mut space) {
match cpu.decode_next(&system) {
Ok(()) => { },
Err(err) => {
cpu.dump_state(&mut space);
cpu.dump_state(&system);
panic!("{:?}", err);
},
}

View File

@ -74,13 +74,19 @@ impl Addressable for AtaDevice {
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE -1 - self.selected_count)) as usize;
data[0] = self.contents[offset];
data[1] = self.contents[offset + 1];
println!(">> {:x}{:x}", data[0], data[1]);
if self.selected_count == 0 {
self.selected_sector = 0;
self.selected_count = 0;
}
},
ATA_REG_DATA_BYTE => {
self.selected_count -= 1;
let offset = ((self.selected_sector * ATA_SECTOR_SIZE) + (ATA_SECTOR_SIZE - 1 - self.selected_count)) as usize;
data[0] = self.contents[offset];
println!(">> {:x}", data[0]);
if self.selected_count == 0 {
self.selected_sector = 0;
self.selected_count = 0;
}
},
ATA_REG_STATUS => {
data[0] = ATA_ST_DATA_READY;

View File

@ -19,6 +19,14 @@ const REG_CSRA_WR: Address = 0x03;
const REG_CRA_WR: Address = 0x05;
const REG_TBA_WR: Address = 0x07;
const REG_RBA_RD: Address = 0x07;
const REG_MR1B_MR2B: Address = 0x700011;
const REG_SRB_RD: Address = 0x700013;
const REG_CSRB_WR: Address = 0x700013;
const REG_CRB_WR: Address = 0x700015;
const REG_TBB_WR: Address = 0x700017;
const REG_RBB_RD: Address = 0x700017;
const REG_ACR_WR: Address = 0x09;
const REG_CTUR_WR: Address = 0x0D;
@ -63,26 +71,47 @@ const DEV_NAME: &'static str = "mc68681";
pub struct MC68681 {
pub tty: Option<PtyMaster>,
pub status: u8,
pub input: u8,
pub acr: u8,
pub status_a: u8,
pub input_a: u8,
pub tx_a_enabled: bool,
pub rx_a_enabled: bool,
pub status_b: u8,
pub input_b: u8,
pub tx_b_enabled: bool,
pub rx_b_enabled: bool,
pub int_mask: u8,
pub int_status: u8,
pub int_vector: u8,
pub int_edge_trigger: u8,
pub timer: u16,
pub is_interrupt: bool,
pub timer_preload: u16,
pub timer_count: u16,
pub is_timing: bool,
}
impl MC68681 {
pub fn new() -> Self {
MC68681 {
tty: None,
status: 0x0C,
input: 0,
acr: 0,
status_a: 0,
input_a: 0,
tx_a_enabled: false,
rx_a_enabled: false,
status_b: 0,
input_b: 0,
tx_b_enabled: false,
rx_b_enabled: false,
int_mask: 0,
int_status: 0,
int_vector: 0,
int_edge_trigger: 0,
timer: 0,
is_interrupt: false,
timer_preload: 0,
timer_count: 0,
is_timing: true,
}
}
@ -107,14 +136,15 @@ impl MC68681 {
}
pub fn step_internal(&mut self, system: &System) -> Result<(), Error> {
if !self.rx_ready() && self.tty.is_some() {
if self.rx_a_enabled && !self.rx_ready() && self.tty.is_some() {
let mut buf = [0; 1];
let tty = self.tty.as_mut().unwrap();
match tty.read(&mut buf) {
Ok(count) => {
println!("READ {:?}", count);
self.input = buf[0];
self.status |= SR_RX_READY;
self.input_a = buf[0];
self.status_a |= SR_RX_READY;
self.int_status |= ISR_CH_A_RX_READY_FULL;
},
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => { },
Err(err) => {
@ -123,22 +153,39 @@ impl MC68681 {
}
}
// TODO this is a hack
if (system.clock % 10000) == 0 {
if self.is_timing {
self.timer_count = self.timer_count.wrapping_sub(1);
if self.timer_count == 0 {
self.int_status |= ISR_TIMER_CHANGE;
self.int_edge_trigger = self.int_status;
if (self.acr & 0x40) == 0 {
self.is_timing = false;
} else {
self.timer_count = self.timer_preload;
}
}
}
if (self.int_edge_trigger & self.int_mask) != 0 {
system.trigger_interrupt(self.int_vector)?;
self.int_edge_trigger = 0;
}
self.check_interrupt_state(system)?;
Ok(())
}
fn check_interrupt_state(&mut self, system: &System) -> Result<(), Error> {
if !self.is_interrupt && (self.int_status & self.int_mask) != 0 {
self.is_interrupt = true;
system.change_interrupt_state(self.is_interrupt, 4, self.int_vector)?;
}
if self.is_interrupt && (self.int_status & self.int_mask) == 0 {
self.is_interrupt = false;
system.change_interrupt_state(self.is_interrupt, 4, self.int_vector)?;
}
Ok(())
}
pub fn rx_ready(&self) -> bool {
(self.status & SR_RX_READY) != 0
(self.status_a & SR_RX_READY) != 0
}
}
@ -150,41 +197,110 @@ impl Addressable for MC68681 {
fn read(&mut self, addr: Address, count: usize) -> Result<Vec<u8>, Error> {
let mut data = vec![0; count];
// TODO this is temporary
//self.step();
if addr != REG_SRA_RD && addr != REG_SRB_RD {
println!("{}: reading from {:0x}", DEV_NAME, addr);
}
match addr {
REG_SRA_RD => {
data[0] = self.status
data[0] = self.status_a
},
REG_RBA_RD => {
data[0] = self.input;
self.status &= !SR_RX_READY;
data[0] = self.input_a;
self.status_a &= !SR_RX_READY;
self.int_status &= !ISR_CH_A_RX_READY_FULL;
},
REG_SRB_RD => {
data[0] = self.status_b
},
REG_RBB_RD => {
data[0] = self.input_b;
self.status_b &= !SR_RX_READY;
self.int_status &= !ISR_CH_B_RX_READY_FULL;
},
REG_ISR_RD => {
data[0] = self.int_status;
self.int_status = 0;
},
_ => { println!("{}: reading from {:0x}", DEV_NAME, addr); data[0] = self.input; },
REG_START_RD => {
self.timer_count = self.timer_preload;
self.is_timing = true;
},
REG_STOP_RD => {
self.int_status &= !ISR_TIMER_CHANGE;
if (self.acr & 0x40) == 0 {
// Counter Mode
self.is_timing = false;
self.timer_count = self.timer_preload;
} else {
// Timer Mode
// Do nothing except reset the ISR bit
}
},
_ => { },
}
Ok(data)
}
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
println!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr);
match addr {
//REG_MR1A_MR2A | REG_ACR_WR => {
// // TODO config
//},
REG_ACR_WR => {
self.acr = data[0];
}
REG_TBA_WR => {
println!("{}: {}", DEV_NAME, data[0] as char);
println!("{}a: write {}", DEV_NAME, data[0] as char);
self.tty.as_mut().map(|tty| tty.write_all(&[data[0]]));
},
REG_CRA_WR => {
let rx_cmd = (data[0] & 0x03);
if rx_cmd == 0b01 {
self.rx_a_enabled = true;
} else if rx_cmd == 0b10 {
self.rx_a_enabled = false;
}
let tx_cmd = ((data[0] & 0x0C) >> 2);
if tx_cmd == 0b01 {
self.tx_a_enabled = true;
self.status_a |= SR_TX_READY | SR_TX_EMPTY;
self.int_status |= ISR_CH_A_TX_READY;
} else if tx_cmd == 0b10 {
self.tx_a_enabled = false;
self.status_a &= !(SR_TX_READY | SR_TX_EMPTY);
self.int_status &= !ISR_CH_A_TX_READY;
}
},
REG_TBB_WR => {
println!("{}b: write {:x}", DEV_NAME, data[0]);
},
REG_CRB_WR => {
let rx_cmd = (data[0] & 0x03);
if rx_cmd == 0b01 {
self.rx_b_enabled = true;
} else if rx_cmd == 0b10 {
self.rx_b_enabled = false;
}
let tx_cmd = ((data[0] & 0x0C) >> 2);
if tx_cmd == 0b01 {
self.tx_b_enabled = true;
self.status_b |= SR_TX_READY | SR_TX_EMPTY;
self.int_status |= ISR_CH_B_TX_READY;
} else if tx_cmd == 0b10 {
self.tx_b_enabled = false;
self.status_b &= !(SR_TX_READY | SR_TX_EMPTY);
self.int_status &= !ISR_CH_B_TX_READY;
}
},
REG_CTUR_WR => {
self.timer = (self.timer & 0x00FF) | ((data[0] as u16) << 8);
self.timer_preload = (self.timer_preload & 0x00FF) | ((data[0] as u16) << 8);
},
REG_CTLR_WR => {
self.timer = (self.timer & 0xFF00) | (data[0] as u16);
self.timer_preload = (self.timer_preload & 0xFF00) | (data[0] as u16);
},
REG_IMR_WR => {
self.int_mask = data[0];
@ -192,7 +308,7 @@ impl Addressable for MC68681 {
REG_IVR_WR => {
self.int_vector = data[0];
},
_ => { println!("{}: writing {:0x} to {:0x}", DEV_NAME, data[0], addr); },
_ => { },
}
Ok(())
}

View File

@ -15,7 +15,7 @@ pub trait Steppable {
}
pub trait Interruptable {
fn handle_interrupt(&mut self, system: &System, number: u8) -> Result<(), Error>;
fn interrupt_state_change(&mut self, system: &System, state: bool, priority: u8, number: u8) -> Result<(), Error>;
}
pub trait AddressableDevice: Addressable + Steppable { }
@ -86,14 +86,15 @@ impl System {
Ok(())
}
pub fn trigger_interrupt(&self, number: u8) -> Result<(), Error> {
pub fn change_interrupt_state(&self, state: bool, priority: u8, number: u8) -> Result<(), Error> {
// TODO how does this find the specific device it's connected to?
// TODO for the time being, this will find the first device to handle it or fail
println!("system: interrupt state changed to {} ({})", state, priority);
for dev in &self.devices {
match dev {
Device::Interruptable(dev) => {
return dev.borrow_mut().handle_interrupt(&self, number);
return dev.borrow_mut().interrupt_state_change(&self, state, priority, number);
},
_ => { },
}

View File

@ -1,4 +1,9 @@
* add a Signal struct which records its val and prev val, and can be used for state change things like interrupts
the int controller coild have multiple for different prioritis, and could maybe be activated by multiple sources
* the remaining issue with the shell is that putchar_buffered requires the TX interrupt and TX enable/disable commands to work in order to trigger a write
* how will you add the cpu, which is not addressable, but is steppable, and also interruptable... what else?
* should you simulate bus arbitration?
* if the bus had an Rc<RefCell<Box<dyn AddressableDevice>>>, then it could be like AddressSpace used to be, with it's own Addressable, and then you could