mirror of
https://github.com/transistorfet/moa.git
synced 2024-12-22 12:29:51 +00:00
Fixed interrupts and added tx enable for OS buffered output
This commit is contained in:
parent
ecbaf6a68b
commit
8bb43f61ee
Binary file not shown.
@ -124,6 +124,7 @@ impl MC68010 {
|
||||
return Ok(true);
|
||||
},
|
||||
"c" | "continue" => {
|
||||
self.debugger.use_tracing = false;
|
||||
self.debugger.use_debugger = false;
|
||||
return Ok(true);
|
||||
},
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
15
src/main.rs
15
src/main.rs
@ -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);
|
||||
},
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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);
|
||||
},
|
||||
_ => { },
|
||||
}
|
||||
|
5
todo.txt
5
todo.txt
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user