Interrupt refactor in progress

This commit is contained in:
dingusdev 2021-12-01 20:46:16 -07:00
parent 7fbbde0750
commit 3db6d07060
8 changed files with 175 additions and 240 deletions

View File

@ -61,7 +61,8 @@ AMIC::AMIC()
}
bool AMIC::supports_type(HWCompType type) {
if (type == HWCompType::MMIO_DEV) {
if ((type == HWCompType::MMIO_DEV)
|| (type == HWCompType::INT_CTRL)) {
return true;
} else {
return false;

View File

@ -28,6 +28,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/scsi/ncr53c94.h>
#include <devices/common/viacuda.h>
#include <devices/ethernet/mace.h>
#include <devices/ioctrl/interruptctrl.h>
#include <devices/serial/escc.h>
#include <devices/sound/awacs.h>
@ -121,7 +122,7 @@ enum AMICReg : uint32_t {
};
/** Apple Memory-mapped I/O controller device. */
class AMIC : public MMIODevice {
class AMIC : public MMIODevice, public InterruptCtrl {
public:
AMIC();
~AMIC() = default;

View File

@ -1,35 +0,0 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-21 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef HEATHINTCTRL_H
#define HEATHINTCTRL_H
#include <cinttypes>
#include <devices/ioctrl/interruptctrl.h>
#include <memory>
enum AMIC_Int {
};
class AMICIntCtrl : public InterruptCtrl {
};
#endif /* HEATHINTCTRL_H */

View File

@ -1,127 +0,0 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-21 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef HEATHINTCTRL_H
#define HEATHINTCTRL_H
#include <devices/ioctrl/interruptctrl.h>
#include <cinttypes>
#include <memory>
enum Heathrow_Int1 {
SCSI_DMA = (1 << 0),
Swim3_DMA = (1 << 1),
IDE0_DMA = (1 << 2),
IDE1_DMA = (1 << 3),
SCC_A_DMA_OUT = (1 << 4),
SCC_A_DMA_IN = (1 << 5),
SCC_B_DMA_OUT = (1 << 6),
SCC_B_DMA_IN = (1 << 7),
DAVBUS_DMA_OUT = (1 << 8),
DAVBUS_DMA_IN = (1 << 9),
SCSI0 = (1 << 12),
IDE0 = (1 << 13),
IDE1 = (1 << 14),
SCC_A = (1 << 15),
SCC_B = (1 << 16),
DAVBUS = (1 << 17),
VIA_CUDA = (1 << 18),
SWIM3 = (1 << 19),
NMI = (1 << 20),
PERCH_DMA = (1 << 21),
PERCH = (1 << 26),
};
enum Heathrow_Int2 {
BMAC_DMA_OUT = (1 << 32),
BMAC_DMA_IN = (1 << 33),
BMAC = (1 << 42),
};
class HeathIntCtrl : public InterruptCtrl {
public:
HeathIntCtrl(){};
~HeathIntCtrl() = default;
void update_mask2_flags(uint32_t bit_setting) {
int_mask2 = bit_setting;
}
void update_events2_flags(uint32_t bit_setting) {
int_events2 = bit_setting;
}
void update_levels2_flags(uint32_t bit_setting) {
int_levels2 = bit_setting;
}
void update_clear2_flags(uint32_t bit_setting) {
int_clear2 = bit_setting;
int_events2 &= ~int_clear2;
}
uint32_t retrieve_mask2_flags() {
return int_mask2;
}
uint32_t retrieve_events2_flags() {
return int_events2;
}
uint32_t retrieve_levels2_flags() {
return int_levels2;
}
uint32_t retrieve_clear2_flags() {
return int_clear2;
}
uint32_t ack_interrupt(uint32_t device_bits, bool is_int2) {
bool confirm_interrupt = false;
if (is_int2) {
int_events2 |= device_bits;
uint32_t mask2_check = retrieve_mask2_flags();
if (mask2_check && int_events2)
confirm_interrupt = true;
} else {
//slightly nasty workaround
uint32_t mask1_check = retrieve_mask_flags() + device_bits;
update_mask_flags(mask1_check + device_bits);
uint32_t events1_check = retrieve_events_flags();
if (mask1_check && events1_check)
confirm_interrupt = true;
}
if (confirm_interrupt)
call_ppc_handler();
return 0;
}
private:
uint32_t int_events2 = 0;
uint32_t int_mask2 = 0;
uint32_t int_clear2 = 0;
uint32_t int_levels2 = 0;
};
#endif /* HEATHINTCTRL_H */

View File

@ -23,7 +23,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/dbdma.h>
#include <devices/common/viacuda.h>
#include <devices/ioctrl/macio.h>
#include <devices/ioctrl/heathintctrl.h>
#include <devices/ioctrl/interruptctrl.h>
#include <devices/serial/escc.h>
#include <devices/sound/awacs.h>
#include <endianswap.h>
@ -59,9 +59,38 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow") {
this->mesh = std::unique_ptr<MESHController> (new MESHController(HeathrowMESHID));
this->escc = std::unique_ptr<EsccController>(new EsccController());
this->int_ctrl = std::unique_ptr<HeathIntCtrl>(new HeathIntCtrl());
}
uint32_t HeathrowIC::register_device(DEV_ID dev_id){
return (1 << dev_id);
};
void HeathrowIC::process_interrupt(uint32_t cookie) {
bool confirm_interrupt = false;
uint32_t bit_field = 0;
switch (cookie) {
case DEV_ID::DAVBUS_DMA_OUT:
bit_field = (1 << DEV_ID::DAVBUS_DMA_OUT);
break;
case DEV_ID::SCSI0:
bit_field = (1 << DEV_ID::SCSI0);
break;
case DEV_ID::VIA_CUDA:
bit_field = (1 << DEV_ID::VIA_CUDA);
break;
case DEV_ID::SWIM3:
bit_field = (1 << DEV_ID::SWIM3);
break;
case DEV_ID::DAVBUS:
bit_field = (1 << DEV_ID::DAVBUS);
break;
}
if (confirm_interrupt)
ack_interrupt(bit_field);
};
uint32_t HeathrowIC::pci_cfg_read(uint32_t reg_offs, uint32_t size) {
return this->pci_cfg_hdr[reg_offs & 0xFF];
}
@ -95,7 +124,7 @@ uint32_t HeathrowIC::dma_read(uint32_t offset, int size) {
switch (offset >> 8) {
case 8:
res = this->snd_out_dma->reg_read(offset & 0xFF, size);
this->int_ctrl->ack_interrupt(Heathrow_Int1::DAVBUS_DMA_OUT, 0);
this->ack_interrupt(DEV_ID::DAVBUS_DMA_OUT);
break;
default:
LOG_F(WARNING, "Unsupported DMA channel read, offset=0x%X", offset);
@ -178,7 +207,7 @@ void HeathrowIC::write(uint32_t reg_start, uint32_t offset, uint32_t value, int
break;
case 0x10:
this->mesh->write((offset >> 4) & 0xF, value);
this->int_ctrl->ack_interrupt(Heathrow_Int1::SCSI0, 0);
this->process_interrupt(DEV_ID::SCSI0);
break;
case 0x11: // BMAC
LOG_F(WARNING, "Attempted to write to BMAC: %x \n", (offset - 0x11000));
@ -191,7 +220,7 @@ void HeathrowIC::write(uint32_t reg_start, uint32_t offset, uint32_t value, int
break;
case 0x14:
this->screamer->snd_ctrl_write(offset - 0x14000, value, size);
this->int_ctrl->ack_interrupt(Heathrow_Int1::DAVBUS, 0);
this->process_interrupt(DEV_ID::DAVBUS);
break;
case 0x15: // SWIM 3
LOG_F(WARNING, "Attempted to write to SWIM 3: %x \n", (offset - 0x15000));
@ -199,7 +228,7 @@ void HeathrowIC::write(uint32_t reg_start, uint32_t offset, uint32_t value, int
case 0x16:
case 0x17:
this->viacuda->write((offset - 0x16000) >> 9, value);
this->int_ctrl->ack_interrupt(Heathrow_Int1::VIA_CUDA, 0);
this->process_interrupt(DEV_ID::VIA_CUDA);
break;
case 0x20: // IDE
case 0x21:
@ -220,35 +249,35 @@ uint32_t HeathrowIC::mio_ctrl_read(uint32_t offset, int size) {
switch (offset & 0xFC) {
case 0x10:
LOG_F(0, "read from MIO:Int_Events2 register \n");
res = this->int_ctrl->retrieve_events2_flags();
res = this->retrieve_reg(REG_ID::events2);
break;
case 0x14:
LOG_F(0, "read from MIO:Int_Mask2 register \n");
res = this->int_ctrl->retrieve_mask2_flags();
res = this->retrieve_reg(REG_ID::mask2);
break;
case 0x18:
LOG_F(0, "read from MIO:Int_Clear2 register \n");
res = this->int_ctrl->retrieve_clear2_flags();
res = this->retrieve_reg(REG_ID::clear2);
break;
case 0x1C:
LOG_F(0, "read from MIO:Int_Levels2 register \n");
res = this->int_ctrl->retrieve_levels2_flags();
res = this->retrieve_reg(REG_ID::levels2);
break;
case 0x20:
LOG_F(0, "read from MIO:Int_Events1 register \n");
res = this->int_ctrl->retrieve_events_flags();
res = this->retrieve_reg(REG_ID::events1);
break;
case 0x24:
LOG_F(0, "read from MIO:Int_Mask1 register \n");
res = this->int_ctrl->retrieve_mask_flags();
res = this->retrieve_reg(REG_ID::mask1);
break;
case 0x28:
LOG_F(0, "read from MIO:Int_Clear1 register \n");
res = this->int_ctrl->retrieve_clear_flags();
res = this->retrieve_reg(REG_ID::clear1);
break;
case 0x2C:
LOG_F(0, "read from MIO:Int_Levels1 register \n");
res = this->int_ctrl->retrieve_levels_flags();
res = this->retrieve_reg(REG_ID::levels1);
break;
case 0x34: /* heathrowIDs / HEATHROW_MBCR (Linux): media bay config reg? */
LOG_F(9, "read from MIO:ID register at Address %x \n", ppc_state.pc);
@ -270,35 +299,35 @@ void HeathrowIC::mio_ctrl_write(uint32_t offset, uint32_t value, int size) {
switch (offset & 0xFC) {
case 0x10:
LOG_F(0, "write %x to MIO:Int_Events2 register \n", value);
this->int_ctrl->update_events2_flags(value);
this->update_reg(REG_ID::events2, value);
break;
case 0x14:
LOG_F(0, "write %x to MIO:Int_Mask2 register \n", value);
this->int_ctrl->update_mask2_flags(value);
this->update_reg(REG_ID::mask2, value);
break;
case 0x18:
LOG_F(0, "write %x to MIO:Int_Clear2 register \n", value);
this->int_ctrl->update_clear2_flags(value);
this->update_reg(REG_ID::clear2, value);
break;
case 0x1C:
LOG_F(0, "write %x to MIO:Int_Levels2 register \n", value);
this->int_ctrl->update_levels2_flags(value);
this->update_reg(REG_ID::levels2, value);
break;
case 0x20:
LOG_F(0, "write %x to MIO:Int_Events1 register \n", value);
this->int_ctrl->update_events_flags(value);
this->update_reg(REG_ID::events1, value);
break;
case 0x24:
LOG_F(0, "write %x to MIO:Int_Mask1 register \n", value);
this->int_ctrl->update_mask_flags(value);
this->update_reg(REG_ID::mask1, value);
break;
case 0x28:
LOG_F(0, "write %x to MIO:Int_Clear1 register \n", value);
this->int_ctrl->update_clear_flags(value);
this->update_reg(REG_ID::clear1, value);
break;
case 0x2C:
LOG_F(0, "write %x to MIO:Int_Levels1 register \n", value);
this->int_ctrl->update_levels_flags(value);
this->update_reg(REG_ID::levels1, value);
break;
case 0x34:
LOG_F(WARNING, "Attempted to write %x to MIO:ID at %x; Address : %x \n", value, offset, ppc_state.pc);

View File

@ -20,7 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cpu/ppc/ppcemu.h>
#include "interruptctrl.h"
#include <devices/ioctrl/interruptctrl.h>
#include <cinttypes>
#include <string>
#include <loguru.hpp>
@ -30,40 +30,67 @@ using namespace std;
InterruptCtrl::InterruptCtrl() {
}
void InterruptCtrl::update_mask_flags(uint32_t bit_setting) {
int_mask1 = bit_setting;
void InterruptCtrl::update_reg(REG_ID retrieve_reg, uint32_t bit_setting) {
switch (retrieve_reg) {
case mask1:
int_mask1 = bit_setting;
break;
case clear1:
int_clear1 = bit_setting;
int_events1 &= ~int_clear1;
break;
case events1:
int_events1 = bit_setting;
break;
case levels1:
int_levels1 = bit_setting;
break;
case mask2:
int_mask2 = bit_setting;
break;
case clear2:
int_clear2 = bit_setting;
int_events2 &= ~int_clear2;
break;
case events2:
int_events2 = bit_setting;
break;
case levels2:
int_levels2 = bit_setting;
break;
}
}
void InterruptCtrl::update_events_flags(uint32_t bit_setting) {
int_events1 = bit_setting;
uint32_t InterruptCtrl::retrieve_reg(REG_ID retrieve_reg) {
switch (retrieve_reg) {
case mask1:
return int_mask1;
break;
case clear1:
return int_clear1;
break;
case events1:
return int_events1;
break;
case levels1:
return int_levels1;
break;
case mask2:
return int_mask2;
break;
case clear2:
return int_clear2;
break;
case events2:
return int_events2;
break;
case levels2:
return int_levels2;
break;
}
}
void InterruptCtrl::update_levels_flags(uint32_t bit_setting) {
int_levels1 = bit_setting;
}
void InterruptCtrl::update_clear_flags(uint32_t bit_setting) {
int_clear1 = bit_setting;
int_events1 &= ~int_clear1;
}
uint32_t InterruptCtrl::retrieve_mask_flags() {
return int_mask1;
}
uint32_t InterruptCtrl::retrieve_events_flags() {
return int_events1;
}
uint32_t InterruptCtrl::retrieve_levels_flags() {
return int_levels1;
}
uint32_t InterruptCtrl::retrieve_clear_flags() {
return int_clear1;
}
uint32_t InterruptCtrl::ack_interrupt(uint32_t device_bits){
uint32_t InterruptCtrl::ack_interrupt(uint32_t device_bits) {
bool confirm_interrupt = false;
int_events1 |= device_bits;
@ -71,11 +98,11 @@ uint32_t InterruptCtrl::ack_interrupt(uint32_t device_bits){
confirm_interrupt = true;
if (confirm_interrupt)
call_ppc_handler();
ack_cpu_interrupt();
return 0;
}
void InterruptCtrl::call_ppc_handler() {
void InterruptCtrl::ack_cpu_interrupt() {
ppc_exception_handler(Except_Type::EXC_EXT_INT, 0x0);
}

View File

@ -26,36 +26,71 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cinttypes>
#include <memory>
class InterruptCtrl : public HWComponent {
enum DEV_ID {
SCSI_DMA = 0,
Swim3_DMA = 1,
IDE0_DMA = 2,
IDE1_DMA = 3,
SCC_A_DMA_OUT = 4,
SCC_A_DMA_IN = 5,
SCC_B_DMA_OUT = 6,
SCC_B_DMA_IN = 7,
DAVBUS_DMA_OUT = 8,
DAVBUS_DMA_IN = 9,
VIDEO_OUT = 10,
VIDEO_IN = 11,
SCSI0 = 12,
IDE0 = 13,
IDE1 = 14,
SCC_A = 15,
SCC_B = 16,
DAVBUS = 17,
VIA_CUDA = 18,
SWIM3 = 19,
NMI = 20,
PERCH_DMA = 21,
PERCH = 22,
BMAC_DMA_OUT = 0,
BMAC_DMA_IN = 1,
BMAC = 10,
};
enum REG_ID {
mask1 = 0,
clear1 = 1,
events1 = 2,
levels1 = 3,
mask2 = 4,
clear2 = 5,
events2 = 6,
levels2 = 7,
};
class InterruptCtrl {
public:
InterruptCtrl();
~InterruptCtrl() = default;
bool supports_type(HWCompType type) {
return (type == HWCompType::INT_CTRL);
};
uint32_t ack_interrupt(uint32_t device_bits);
//for all (Old World) PCI-based Macs (is_int is only for Heathrow)
void update_mask_flags(uint32_t bit_setting);
void update_events_flags(uint32_t bit_setting);
void update_levels_flags(uint32_t bit_setting);
void update_clear_flags(uint32_t bit_setting);
uint32_t retrieve_mask_flags();
uint32_t retrieve_events_flags();
uint32_t retrieve_levels_flags();
uint32_t retrieve_clear_flags();
virtual uint32_t register_device(DEV_ID dev_id){
return 0;
};
//for Heathrow
void update_reg(REG_ID retrieve_reg, uint32_t bit_setting);
uint32_t retrieve_reg(REG_ID retrieve_reg);
void call_ppc_handler();
void ack_cpu_interrupt();
private:
protected:
uint32_t int_events1 = 0;
uint32_t int_mask1 = 0;
uint32_t int_clear1 = 0;
uint32_t int_levels1 = 0;
uint32_t int_events2 = 0;
uint32_t int_mask2 = 0;
uint32_t int_clear2 = 0;
uint32_t int_levels2 = 0;
};
#endif /* HWINTERRUPT_H */

View File

@ -59,7 +59,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/pci/pcihost.h>
#include <devices/common/scsi/mesh.h>
#include <devices/common/viacuda.h>
#include <devices/ioctrl/heathintctrl.h>
#include <devices/ioctrl/interruptctrl.h>
#include <devices/memctrl/memctrlbase.h>
#include <devices/serial/escc.h>
#include <devices/sound/awacs.h>
@ -93,13 +93,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
VIA-CUDA register space: 0x00016000, size: 0x00002000
*/
class HeathrowIC : public PCIDevice {
class HeathrowIC : public PCIDevice,
public InterruptCtrl {
public:
HeathrowIC();
~HeathrowIC() = default;
bool supports_type(HWCompType type) {
return type == HWCompType::MMIO_DEV;
return (type == HWCompType::MMIO_DEV) || (type == HWCompType::INT_CTRL);
};
/* PCI device methods */
@ -114,6 +116,9 @@ public:
uint32_t read(uint32_t reg_start, uint32_t offset, int size);
void write(uint32_t reg_start, uint32_t offset, uint32_t value, int size);
uint32_t register_device(DEV_ID dev_id);
void process_interrupt(uint32_t cookie);
protected:
uint32_t dma_read(uint32_t offset, int size);
void dma_write(uint32_t offset, uint32_t value, int size);
@ -154,7 +159,6 @@ private:
std::unique_ptr<AwacsScreamer> screamer; // Screamer audio codec instance
std::unique_ptr<MESHController> mesh; // MESH SCSI cell instance
std::unique_ptr<EsccController> escc; // ESCC serial controller
std::unique_ptr<HeathIntCtrl> int_ctrl; // Interrupt controller
std::unique_ptr<DMAChannel> snd_out_dma;
};