use x86_64::instructions::port::Port;
use crate::post::post_debug_delay;
use crate::regs::*;
use crate::*;
#[derive(Debug, Clone, Copy)]
pub enum PicIRQMapping {
Master(Option<ICW3_MASTER>),
Slave(ICW3_SLAVE),
}
#[derive(Debug)]
pub struct Pic {
pub offset: u8,
op_mode: PicOperationMode,
automatic_interrupts: bool,
command: Port<u8>,
data: Port<u8>,
}
impl Pic {
pub const unsafe fn new(offset: u8, command_port: u16, data_port: u16) -> Self {
Self {
offset,
op_mode: PicOperationMode::FullyNested, automatic_interrupts: false,
command: Port::new(command_port),
data: Port::new(data_port),
}
}
pub unsafe fn init(&mut self, pic_map: PicIRQMapping, automatic_interrupts: bool) {
unsafe {
let mask = self.mask_read();
let icw1 =
ICW1::IC4 |
match pic_map {
PicIRQMapping::Master(opt) => if opt.is_none() {
ICW1::SNGL
} else { ICW1::empty() },
_ => ICW1::empty(),
};
let icw2 = self.offset & ICW2::INTERRUPT_VECTOR_ADDRESS_8086_8088.bits();
let icw3 = pic_map;
let icw4 =
ICW4::MPM |
if automatic_interrupts { ICW4::AEOI } else { ICW4::empty() };
self.command.write(icw1.bits());
post_debug_delay();
self.data.write(icw2);
post_debug_delay();
match icw3 {
PicIRQMapping::Master(opt) => opt.map(|some| {
self.command.write(some.bits());
post_debug_delay();
}).unwrap_or(()),
PicIRQMapping::Slave(some) => {
self.command.write(some.bits());
post_debug_delay();
},
}
self.data.write(icw4.bits());
post_debug_delay();
self.mask_write(mask);
}
}
pub fn handles_interrupt(&self, interrupt_id: u8) -> bool {
(self.offset..self.offset + 8).contains(&interrupt_id)
}
pub fn operation_mode_current(&self) -> PicOperationMode {
self.op_mode
}
pub fn operation_mode_change(&mut self, new_op_mode: PicOperationMode) {
use PicOperationMode::*;
unsafe {
let fully_nested_arm = |s: &mut Self| s.set_lowest_priority(7);
let automatic_rotation_arm = |s: &mut Self|
if s.automatic_interrupts {
s.command.write(OCW2::ROTATE_IN_AUTOMATIC_EOI_MODE_SET.bits());
};
let special_mask_arm = |s: &mut Self| s.command.write(OCW3::SET_SPECIAL_MASK.bits());
let polled_mode_arm = |s: &mut Self| {
s.mask_write(OCW1::all());
s.command.write(OCW3::POLL.bits());
};
match self.op_mode {
FullyNested => match new_op_mode {
FullyNested => return,
AutomaticRotation => automatic_rotation_arm(self),
SpecialMask => special_mask_arm(self),
PolledMode => polled_mode_arm(self),
},
AutomaticRotation => {
if self.automatic_interrupts {
self.command.write(OCW2::ROTATE_IN_AUTOMATIC_EOI_MODE_CLEAR.bits());
}
match new_op_mode {
FullyNested => fully_nested_arm(self),
AutomaticRotation => return,
SpecialMask => special_mask_arm(self),
PolledMode => polled_mode_arm(self),
}
},
SpecialMask => {
self.command.write(OCW3::RESET_SPECIAL_MASK.bits());
match new_op_mode {
FullyNested => fully_nested_arm(self),
AutomaticRotation => automatic_rotation_arm(self),
SpecialMask => return,
PolledMode => polled_mode_arm(self)
}
},
PolledMode => match new_op_mode {
FullyNested => fully_nested_arm(self),
AutomaticRotation => automatic_rotation_arm(self),
SpecialMask => special_mask_arm(self),
PolledMode => return,
},
}
}
post_debug_delay();
self.op_mode = new_op_mode;
}
pub unsafe fn is_spurious(&mut self, id: u8) -> bool {
assert!(id >= 32 && self.offset + 8 > id, "The provided interrupt vector is outside of scope of this PIC chip.");
let irq = ISR::from_bits_truncate(1 << id.saturating_sub(self.offset));
!self.read_isr().contains(irq)
}
pub fn read_isr(&mut self) -> ISR {
unsafe {
match self.op_mode {
PicOperationMode::PolledMode => return ISR::empty(),
PicOperationMode::SpecialMask => {
self.command.write(
OCW3::READ_REG_ISR.bits() | OCW3::SET_SPECIAL_MASK.bits()
);
},
_ => {
self.command.write(
OCW3::READ_REG_ISR.bits()
);
}
}
ISR::from_bits_truncate(self.command.read())
}
}
pub fn read_irr(&mut self) -> IRR {
unsafe {
match self.op_mode {
PicOperationMode::SpecialMask => {
self.command.write(
OCW3::READ_REG_IRR.bits() | OCW3::SET_SPECIAL_MASK.bits()
);
},
_ => {
self.command.write(
OCW3::READ_REG_IRR.bits()
);
}
}
if self.op_mode == PicOperationMode::PolledMode {
self.command.write(OCW3::POLL.bits())
}
IRR::from_bits_truncate(self.command.read())
}
}
pub fn mask_read(&mut self) -> OCW1 {
unsafe {
OCW1::from_bits_truncate(self.data.read())
}
}
pub fn poll(&mut self) -> Option<u8> {
match self.op_mode {
PicOperationMode::PolledMode => unsafe { Some(self.command.read()) },
_ => None
}
}
pub unsafe fn mask_write(&mut self, ocw1: OCW1) {
self.data.write(ocw1.bits());
}
pub fn end_of_interrupt(&mut self) {
if !self.automatic_interrupts {
match self.op_mode {
PicOperationMode::AutomaticRotation => unsafe {
self.command.write(OCW2::ROTATE_ON_NON_SPECIFIC_EOI_COMMAND.bits());
},
PicOperationMode::PolledMode => (), _ => unsafe { self.non_specified_eoi() },
}
}
}
pub unsafe fn end_of_interrupt_specific(&mut self, irq: u8) {
assert!(irq < 8, "Level is written in binary format (0 .. 7).");
if !self.automatic_interrupts {
match self.op_mode {
PicOperationMode::AutomaticRotation => unsafe {
self.command.write(OCW2::ROTATE_ON_SPECIFIC_EOI_COMMAND.bits() | irq << OCW2::LEVEL.bits());
},
PicOperationMode::PolledMode => (), _ => unsafe { self.specified_eoi(irq) },
}
}
}
pub unsafe fn set_lowest_priority(&mut self, level: u8) {
assert!(level < 8, "Level is written in binary format (0 .. 7).");
self.command.write(
OCW2::SET_PRIORITY_COMMAND.bits() | (level << OCW2::LEVEL.bits())
);
}
pub unsafe fn specified_eoi(&mut self, level: u8) {
self.command.write(
OCW2::SPECIFIC_EOI_COMMAND.bits() | (level << OCW2::LEVEL.bits())
);
}
pub unsafe fn non_specified_eoi(&mut self) {
self.command.write(
OCW2::NON_SPECIFIC_EOI_COMMAND.bits()
);
}
}