AMIC: rework and improve interrupts.
This commit is contained in:
parent
ca83f7e8ef
commit
5b90a3e21d
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-22 divingkatae and maximum
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -73,6 +73,7 @@ AMIC::AMIC() : MMIODevice()
|
|||
// initialize on-board video
|
||||
this->disp_id = std::unique_ptr<DisplayID> (new DisplayID());
|
||||
this->def_vid = std::unique_ptr<PdmOnboardVideo> (new PdmOnboardVideo());
|
||||
this->def_vid->init_interrupts(this, SLOT_INT_VBL << 16);
|
||||
|
||||
// initialize floppy disk HW
|
||||
this->swim3 = dynamic_cast<Swim3::Swim3Ctrl*>(gMachineObj->get_comp_by_name("Swim3"));
|
||||
|
@ -152,9 +153,13 @@ uint32_t AMIC::read(uint32_t rgn_start, uint32_t offset, int size)
|
|||
switch(offset) {
|
||||
case AMICReg::Ariel_Config:
|
||||
return this->def_vid->get_vdac_config();
|
||||
case AMICReg::VIA2_Slot_IFR:
|
||||
return this->via2_slot_ifr;
|
||||
case AMICReg::VIA2_IFR:
|
||||
case AMICReg::VIA2_IFR_RBV:
|
||||
return this->via2_ifr;
|
||||
case AMICReg::VIA2_Slot_IER:
|
||||
return this->via2_slot_ier;
|
||||
case AMICReg::VIA2_IER:
|
||||
case AMICReg::VIA2_IER_RBV:
|
||||
return this->via2_ier;
|
||||
|
@ -257,16 +262,26 @@ void AMIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
|
|||
}
|
||||
|
||||
switch(offset) {
|
||||
case AMICReg::VIA2_Slot_IFR:
|
||||
if (value & SLOT_INT_VBL) {
|
||||
// clear pending VBL int
|
||||
this->ack_slot_int(SLOT_INT_VBL, 0);
|
||||
}
|
||||
break;
|
||||
case AMICReg::VIA2_IFR:
|
||||
// for each "1" in value clear the corresponding IRQ bit
|
||||
// TODO: is bit 7 read only?
|
||||
// TODO: update interrupts?
|
||||
this->via2_ifr &= ~(value & 0x7F);
|
||||
this->update_via2_irq();
|
||||
break;
|
||||
case AMICReg::VIA2_Slot_IER:
|
||||
LOG_F(INFO, "AMIC VIA2 Slot Interrupt Enable Register updated, val=%x", value);
|
||||
if (value & 0x80)
|
||||
this->via2_slot_ier |= value & 0x7F;
|
||||
else
|
||||
this->via2_slot_ier &= ~value;
|
||||
break;
|
||||
case AMICReg::VIA2_IER:
|
||||
case AMICReg::VIA2_IER_RBV:
|
||||
if (value & 0x80) {
|
||||
this->via2_ier |= value & 0x7F;
|
||||
} else {
|
||||
|
@ -304,16 +319,16 @@ void AMIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
|
|||
break;
|
||||
case AMICReg::Int_Ctrl:
|
||||
// reset CPU interrupt bit if requested
|
||||
if (value & AMIC_INT_CLR) {
|
||||
if (this->int_ctrl & 0x80) {
|
||||
this->int_ctrl &= ~AMIC_INT_CLR;
|
||||
if (value & CPU_INT_CLEAR) {
|
||||
if (this->int_ctrl & CPU_INT_FLAG) {
|
||||
this->int_ctrl &= ~CPU_INT_FLAG;
|
||||
ppc_release_int();
|
||||
LOG_F(5, "AMIC: CPU INT latch cleared");
|
||||
}
|
||||
}
|
||||
// keep interrupt mode bit
|
||||
// and discard read-only IQR state bits
|
||||
this->int_ctrl |= value & AMIC_INT_MODE;
|
||||
this->int_ctrl |= value & CPU_INT_MODE;
|
||||
break;
|
||||
case AMICReg::DMA_Base_Addr_0:
|
||||
case AMICReg::DMA_Base_Addr_1:
|
||||
|
@ -388,11 +403,13 @@ void AMIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
|
|||
uint32_t AMIC::register_dev_int(IntSrc src_id) {
|
||||
switch (src_id) {
|
||||
case IntSrc::VIA_CUDA:
|
||||
return 1;
|
||||
return CPU_INT_VIA1;
|
||||
case IntSrc::SCSI1:
|
||||
return 0x800;
|
||||
return VIA2_INT_SCSI_IRQ << 8;
|
||||
case IntSrc::SWIM3:
|
||||
return 0x2000;
|
||||
return VIA2_INT_SWIM3 << 8;
|
||||
case IntSrc::NMI:
|
||||
return CPU_INT_NMI;
|
||||
default:
|
||||
ABORT_F("AMIC: unknown interrupt source %d", src_id);
|
||||
}
|
||||
|
@ -405,39 +422,63 @@ uint32_t AMIC::register_dma_int(IntSrc src_id) {
|
|||
}
|
||||
|
||||
void AMIC::ack_int(uint32_t irq_id, uint8_t irq_line_state) {
|
||||
// dispatch AMIC interrupts from various sources
|
||||
// dispatch cascaded AMIC interrupts from various sources
|
||||
// irq_id format: 00DDCCBBAA where
|
||||
// - AA -> CPU interrupts
|
||||
// - BB -> pseudo VIA2 interrupts
|
||||
// - CC -> slot interrupts
|
||||
if (irq_id < 0x100) {
|
||||
this->ack_cpu_int(irq_id, irq_line_state);
|
||||
} else if (irq_id < 0x10000) {
|
||||
this->ack_via2_int(irq_id >> 8, irq_line_state);
|
||||
} else if (irq_id < 0x1000000) {
|
||||
this->ack_slot_int(irq_id >> 16, irq_line_state);
|
||||
} else {
|
||||
ABORT_F("AMIC: unknown interrupt source ID 0x%X", irq_id);
|
||||
}
|
||||
}
|
||||
|
||||
void AMIC::ack_slot_int(uint32_t irq_id, uint8_t irq_line_state) {
|
||||
// CAUTION: reverse logic (0 - true, 1 - false) in the IFR register!
|
||||
if (irq_line_state) {
|
||||
this->via2_slot_ifr &= ~irq_id;
|
||||
} else {
|
||||
this->via2_slot_ifr |= irq_id;
|
||||
}
|
||||
uint8_t new_irq = !!(~this->via2_slot_ifr & this->via2_slot_ier & 0x7F);
|
||||
if (new_irq != this->via2_slot_irq) {
|
||||
this->via2_slot_irq = new_irq;
|
||||
this->ack_via2_int(VIA2_INT_ALL_SLOT, new_irq);
|
||||
}
|
||||
}
|
||||
|
||||
void AMIC::update_via2_irq() {
|
||||
uint8_t new_irq = !!(this->via2_ifr & this->via2_ier & 0x7F);
|
||||
this->via2_ifr = (this->via2_ifr & 0x7F) | (new_irq << 7);
|
||||
if (new_irq != this->via2_irq) {
|
||||
this->via2_irq = new_irq;
|
||||
this->ack_cpu_int(CPU_INT_VIA2, new_irq);
|
||||
}
|
||||
}
|
||||
|
||||
void AMIC::ack_via2_int(uint32_t irq_id, uint8_t irq_line_state) {
|
||||
if (irq_line_state) {
|
||||
this->via2_ifr |= irq_id;
|
||||
} else {
|
||||
this->via2_ifr &= ~irq_id;
|
||||
}
|
||||
uint8_t new_irq = !!(this->via2_ifr & this->via2_ier & 0x7F);
|
||||
this->via2_ifr = (this->via2_ifr & 0x7F) | (new_irq << 7);
|
||||
if (new_irq != this->via2_irq) {
|
||||
this->via2_irq = new_irq;
|
||||
this->ack_cpu_int(2, new_irq);
|
||||
}
|
||||
this->update_via2_irq();
|
||||
}
|
||||
|
||||
void AMIC::ack_cpu_int(uint32_t irq_id, uint8_t irq_line_state) {
|
||||
if (this->int_ctrl & AMIC_INT_MODE) { // 68k interrupt emulation mode?
|
||||
if (this->int_ctrl & CPU_INT_MODE) { // 68k interrupt emulation mode?
|
||||
if (irq_line_state) {
|
||||
this->dev_irq_lines |= irq_id;
|
||||
} else {
|
||||
this->dev_irq_lines &= ~irq_id;
|
||||
}
|
||||
if (!(this->int_ctrl & 0x80)) {
|
||||
this->int_ctrl |= 0x80; // set CPU interrupt bit
|
||||
if (!(this->int_ctrl & CPU_INT_FLAG)) {
|
||||
this->int_ctrl |= CPU_INT_FLAG;
|
||||
ppc_assert_int();
|
||||
LOG_F(5, "AMIC: CPU INT asserted, source: %d", irq_id);
|
||||
} else {
|
||||
|
@ -445,7 +486,7 @@ void AMIC::ack_cpu_int(uint32_t irq_id, uint8_t irq_line_state) {
|
|||
}
|
||||
|
||||
} else {
|
||||
ABORT_F("AMIC: interrupt mode 0 not implemented");
|
||||
ABORT_F("AMIC: native interrupt mode not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-21 divingkatae and maximum
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -39,8 +39,39 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
#include <memory>
|
||||
|
||||
/** Interrupt related constants. */
|
||||
#define AMIC_INT_CLR 0x80 // clears CPU interrupt
|
||||
#define AMIC_INT_MODE 0x40 // interrupt mode: 0 - native, 1 - 68k-style
|
||||
|
||||
/** CPU interrupt register bits. */
|
||||
enum : uint8_t {
|
||||
CPU_INT_VIA1 = 1 << 0, // (R) VIA1 interrupts
|
||||
CPU_INT_VIA2 = 1 << 1, // (R) pseudo VIA2 interrupts
|
||||
CPU_INT_ESCC = 1 << 2, // (R) ESCC interrupt
|
||||
CPU_INT_ENET = 1 << 3, // (R) ethernet interrupt
|
||||
CPU_INT_ALL_DMA = 1 << 4, // (R) all DMA interrupts are signalled here
|
||||
CPU_INT_NMI = 1 << 5, // (R) non-maskable interrupt
|
||||
CPU_INT_MODE = 1 << 6, // (R/W) interrupt mode: 0 - native, 1 - 68k-style
|
||||
CPU_INT_CLEAR = 1 << 7, // (R/W) writing "1" clears CPU interrupt flag
|
||||
CPU_INT_FLAG = 1 << 7 // special constant for manipulating CPU int flag
|
||||
};
|
||||
|
||||
/** Pseudo VIA2 interrupt flag/enable register bits. */
|
||||
enum : uint8_t {
|
||||
VIA2_INT_SCSI_DRQ = 1 << 0, // (R) SCSI DRQ interrupt
|
||||
VIA2_INT_ALL_SLOT = 1 << 1, // (R) all slot interrupts are signalled here
|
||||
VIA2_INT_SCSI_IRQ = 1 << 3, // (R) SCSI IRQ interrupt
|
||||
VIA2_INT_SOUND = 1 << 4, // (R) sound chip (AWACS) interrupt
|
||||
VIA2_INT_SWIM3 = 1 << 5, // (R) floppy disk controller interrupt
|
||||
VIA2_INT_IRQ = 1 << 7, // (R) all VIA2 interrupts are signalled here
|
||||
};
|
||||
|
||||
/** Slot interrupt flag/enable register bits. */
|
||||
enum : uint8_t {
|
||||
SLOT_INT_SLOT_0 = 1 << 2, // (R) ColdFusion Nubus slot 0 interrupt
|
||||
SLOT_INT_SLOT_1 = 1 << 3, // (R) ColdFusion Nubus slot 1 interrupt
|
||||
SLOT_INT_SLOT_2 = 1 << 4, // (R) ColdFusion Nubus slot 2 interrupt
|
||||
SLOT_INT_SLOT_VDS = 1 << 5, // (R) ColdFusion video direct slot interrupt
|
||||
SLOT_INT_SLOT_PDS = 1 << 5, // (R) PDM processor direct slot interrupt
|
||||
SLOT_INT_VBL = 1 << 6, // (R) built-in video VBL interrupt
|
||||
};
|
||||
|
||||
/** AMIC sound buffers are located at fixed offsets from DMA base. */
|
||||
#define AMIC_SND_BUF0_OFFS 0x10000
|
||||
|
@ -158,6 +189,7 @@ enum AMICReg : uint32_t {
|
|||
Ariel_Config = 0x24002,
|
||||
|
||||
// VIA2 registers
|
||||
VIA2_Slot_IFR = 0x26002,
|
||||
VIA2_IFR = 0x26003,
|
||||
VIA2_Slot_IER = 0x26012,
|
||||
VIA2_IER = 0x26013,
|
||||
|
@ -233,8 +265,10 @@ public:
|
|||
void ack_dma_int(uint32_t irq_id, uint8_t irq_line_state);
|
||||
|
||||
protected:
|
||||
void ack_slot_int(uint32_t irq_id, uint8_t irq_line_state);
|
||||
void ack_via2_int(uint32_t irq_id, uint8_t irq_line_state);
|
||||
void ack_cpu_int(uint32_t irq_id, uint8_t irq_line_state);
|
||||
void update_via2_irq();
|
||||
|
||||
private:
|
||||
uint8_t imm_snd_regs[4]; // temporary storage for sound control registers
|
||||
|
@ -260,9 +294,12 @@ private:
|
|||
uint8_t dev_irq_lines = 0; // state of the IRQ lines
|
||||
|
||||
// pseudo VIA2 state
|
||||
uint8_t via2_ier = 0;
|
||||
uint8_t via2_ifr = 0;
|
||||
uint8_t via2_irq = 0;
|
||||
uint8_t via2_ier = 0;
|
||||
uint8_t via2_ifr = 0;
|
||||
uint8_t via2_irq = 0;
|
||||
uint8_t via2_slot_ier = 0; // normal logic
|
||||
uint8_t via2_slot_ifr = 0x7F; // reverse logic
|
||||
uint8_t via2_slot_irq = 0; // normal logic
|
||||
|
||||
uint32_t pseudo_vbl_tid; // ID for the pseudo-VBL timer
|
||||
|
||||
|
|
Loading…
Reference in New Issue