Slightly more refined interrupt methods

This commit is contained in:
dingusdev 2021-11-27 19:49:24 -07:00
parent 87054deb3d
commit 7fbbde0750
9 changed files with 330 additions and 178 deletions

View File

@ -37,6 +37,7 @@ enum HWCompType : int {
I2C_DEV = 61, /* I2C device */
ADB_HOST = 70, /* ADB host */
ADB_DEV = 71, /* ADB device */
INT_CTRL = 80, /* interrupt controller */
SND_SERVER = 100, /* host sound server */
};

View File

@ -1,80 +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/>.
*/
#include <cpu/ppc/ppcemu.h>
#include "hwinterrupt.h"
#include <cinttypes>
#include <string>
#include <loguru.hpp>
using namespace std;
HWInterrupt::HWInterrupt(std::string sys_type) {
if (sys_type.compare("Heathrow") == 0) {
scsi0_dma_mask_bit = (1 << 0);
scca_dma_out_mask_bit = (1 << 4);
scca_dma_in_mask_bit = (1 << 5);
sccb_dma_out_mask_bit = (1 << 6);
sccb_dma_in_mask_bit = (1 << 7);
dav_dma_out_mask_bit = (1 << 8);
dav_dma_in_mask_bit = (1 << 9);
scsi0_mask_bit = (1 << 12);
scca_mask_bit = (1 << 15);
sccb_mask_bit = (1 << 16);
davbus_mask_bit = (1 << 17);
cuda_mask_bit = (1 << 18);
nmi_mask_bit = (1 << 20);
}
else if (sys_type.compare("AMIC") == 0) {
LOG_F(WARNING, "Missing interrupt handling for %s!", sys_type.c_str());
}
else {
LOG_F(ERROR, "Cannot determine interrupt layout for %s!", sys_type.c_str());
}
}
uint32_t HWInterrupt::update_int1_flags(uint32_t bit_setting) {
pending_int1_flags = bit_setting;
}
uint32_t HWInterrupt::clear_int1_flags(uint32_t bit_setting) {
pending_int1_flags &= ~(bit_setting);
}
uint32_t HWInterrupt::update_int2_flags(uint32_t bit_setting) {
pending_int2_flags = bit_setting;
}
uint32_t HWInterrupt::clear_int2_flags(uint32_t bit_setting) {
pending_int2_flags &= ~(bit_setting);
}
uint32_t HWInterrupt::ack_interrupts() {
}
uint32_t HWInterrupt::pending_interrupts(uint32_t device_bits) {
call_ppc_handler(device_bits);
}
void HWInterrupt::call_ppc_handler(uint32_t device_bits) {
ppc_exception_handler(Except_Type::EXC_EXT_INT, device_bits);
}

View File

@ -1,65 +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 HWINTERRUPT_H
#define HWINTERRUPT_H
#include <cinttypes>
#include <memory>
#include <string>
class HWInterrupt {
public:
HWInterrupt(std::string sys_type = "None");
~HWInterrupt() = default;
uint32_t ack_interrupts();
//for all (Old World) PCI-based Macs
uint32_t update_int1_flags(uint32_t bit_setting);
uint32_t clear_int1_flags(uint32_t bit_setting);
//for Heathrow
uint32_t update_int2_flags(uint32_t bit_setting);
uint32_t clear_int2_flags(uint32_t bit_setting);
uint32_t pending_interrupts(uint32_t device_bits);
void call_ppc_handler(uint32_t device_bits);
private:
uint32_t pending_int1_flags;
uint32_t pending_int2_flags;
uint32_t scsi0_dma_mask_bit = 0;
uint32_t scca_dma_out_mask_bit = 0;
uint32_t scca_dma_in_mask_bit = 0;
uint32_t sccb_dma_out_mask_bit = 0;
uint32_t sccb_dma_in_mask_bit = 0;
uint32_t dav_dma_out_mask_bit = 0;
uint32_t dav_dma_in_mask_bit = 0;
uint32_t scsi0_mask_bit = 0;
uint32_t scca_mask_bit = 0;
uint32_t sccb_mask_bit = 0;
uint32_t davbus_mask_bit = 0;
uint32_t cuda_mask_bit = 0;
uint32_t nmi_mask_bit = 0;
};
#endif /* HWINTERRUPT_H */

View File

@ -0,0 +1,35 @@
/*
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

@ -0,0 +1,127 @@
/*
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

@ -21,9 +21,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cpu/ppc/ppcemu.h>
#include <devices/common/dbdma.h>
#include <devices/common/hwinterrupt.h>
#include <devices/common/viacuda.h>
#include <devices/ioctrl/macio.h>
#include <devices/ioctrl/heathintctrl.h>
#include <devices/serial/escc.h>
#include <devices/sound/awacs.h>
#include <endianswap.h>
@ -59,7 +59,7 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow") {
this->mesh = std::unique_ptr<MESHController> (new MESHController(HeathrowMESHID));
this->escc = std::unique_ptr<EsccController>(new EsccController());
this->hwinterrupt = std::unique_ptr<HWInterrupt>(new HWInterrupt());
this->int_ctrl = std::unique_ptr<HeathIntCtrl>(new HeathIntCtrl());
}
uint32_t HeathrowIC::pci_cfg_read(uint32_t reg_offs, uint32_t size) {
@ -95,6 +95,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);
break;
default:
LOG_F(WARNING, "Unsupported DMA channel read, offset=0x%X", offset);
@ -177,6 +178,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);
break;
case 0x11: // BMAC
LOG_F(WARNING, "Attempted to write to BMAC: %x \n", (offset - 0x11000));
@ -189,6 +191,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);
break;
case 0x15: // SWIM 3
LOG_F(WARNING, "Attempted to write to SWIM 3: %x \n", (offset - 0x15000));
@ -196,6 +199,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);
break;
case 0x20: // IDE
case 0x21:
@ -216,35 +220,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_events2;
res = this->int_ctrl->retrieve_events2_flags();
break;
case 0x14:
LOG_F(0, "read from MIO:Int_Mask2 register \n");
res = this->int_mask2;
res = this->int_ctrl->retrieve_mask2_flags();
break;
case 0x18:
LOG_F(0, "read from MIO:Int_Clear2 register \n");
res = this->int_clear2;
res = this->int_ctrl->retrieve_clear2_flags();
break;
case 0x1C:
LOG_F(0, "read from MIO:Int_Levels2 register \n");
res = this->int_levels2;
res = this->int_ctrl->retrieve_levels2_flags();
break;
case 0x20:
LOG_F(0, "read from MIO:Int_Events1 register \n");
res = this->int_events2;
res = this->int_ctrl->retrieve_events_flags();
break;
case 0x24:
LOG_F(0, "read from MIO:Int_Mask1 register \n");
res = this->int_mask1;
res = this->int_ctrl->retrieve_mask_flags();
break;
case 0x28:
LOG_F(0, "read from MIO:Int_Clear1 register \n");
res = this->int_clear1;
res = this->int_ctrl->retrieve_clear_flags();
break;
case 0x2C:
LOG_F(0, "read from MIO:Int_Levels1 register \n");
res = this->int_levels1;
res = this->int_ctrl->retrieve_levels_flags();
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);
@ -266,39 +270,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_events2 = value;
this->int_ctrl->update_events2_flags(value);
break;
case 0x14:
LOG_F(0, "write %x to MIO:Int_Mask2 register \n", value);
this->int_mask2 = value;
this->hwinterrupt->update_int2_flags(this->int_mask2);
this->int_ctrl->update_mask2_flags(value);
break;
case 0x18:
LOG_F(0, "write %x to MIO:Int_Clear2 register \n", value);
this->int_clear2 = value;
this->hwinterrupt->clear_int2_flags(this->int_clear2);
this->int_ctrl->update_clear2_flags(value);
break;
case 0x1C:
LOG_F(0, "write %x to MIO:Int_Levels2 register \n", value);
this->int_levels2 = value;
this->int_ctrl->update_levels2_flags(value);
break;
case 0x20:
LOG_F(0, "write %x to MIO:Int_Events1 register \n", value);
this->int_events1 = value;
this->int_ctrl->update_events_flags(value);
break;
case 0x24:
LOG_F(0, "write %x to MIO:Int_Mask1 register \n", value);
this->int_mask1 = value;
this->hwinterrupt->update_int1_flags(this->int_mask1);
this->int_ctrl->update_mask_flags(value);
break;
case 0x28:
LOG_F(0, "write %x to MIO:Int_Clear1 register \n", value);
this->int_clear1 = value;
this->hwinterrupt->clear_int1_flags(this->int_clear1);
this->int_ctrl->update_clear_flags(value);
break;
case 0x2C:
LOG_F(0, "write %x to MIO:Int_Levels1 register \n", value);
this->int_levels1 = value;
this->int_ctrl->update_levels_flags(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

@ -0,0 +1,81 @@
/*
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/>.
*/
#include <cpu/ppc/ppcemu.h>
#include "interruptctrl.h"
#include <cinttypes>
#include <string>
#include <loguru.hpp>
using namespace std;
InterruptCtrl::InterruptCtrl() {
}
void InterruptCtrl::update_mask_flags(uint32_t bit_setting) {
int_mask1 = bit_setting;
}
void InterruptCtrl::update_events_flags(uint32_t bit_setting) {
int_events1 = bit_setting;
}
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){
bool confirm_interrupt = false;
int_events1 |= device_bits;
if (int_events1 & int_mask1)
confirm_interrupt = true;
if (confirm_interrupt)
call_ppc_handler();
return 0;
}
void InterruptCtrl::call_ppc_handler() {
ppc_exception_handler(Except_Type::EXC_EXT_INT, 0x0);
}

View File

@ -0,0 +1,61 @@
/*
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 HWINTERRUPT_H
#define HWINTERRUPT_H
#include <devices/common/hwcomponent.h>
#include <cinttypes>
#include <memory>
class InterruptCtrl : public HWComponent {
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();
//for Heathrow
void call_ppc_handler();
private:
uint32_t int_events1 = 0;
uint32_t int_mask1 = 0;
uint32_t int_clear1 = 0;
uint32_t int_levels1 = 0;
};
#endif /* HWINTERRUPT_H */

View File

@ -53,13 +53,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/dbdma.h>
#include <devices/common/hwcomponent.h>
#include <devices/common/hwinterrupt.h>
#include <devices/common/mmiodevice.h>
#include <devices/common/nvram.h>
#include <devices/common/pci/pcidevice.h>
#include <devices/common/pci/pcihost.h>
#include <devices/common/scsi/mesh.h>
#include <devices/common/viacuda.h>
#include <devices/ioctrl/heathintctrl.h>
#include <devices/memctrl/memctrlbase.h>
#include <devices/serial/escc.h>
#include <devices/sound/awacs.h>
@ -143,15 +143,7 @@ private:
0x00,
0x00 // unknown defaults
};
uint32_t int_events2 = 0;
uint32_t int_mask2 = 0;
uint32_t int_clear2 = 0;
uint32_t int_levels2 = 0;
uint32_t int_events1 = 0;
uint32_t int_mask1 = 0;
uint32_t int_clear1 = 0;
uint32_t int_levels1 = 0;
uint32_t macio_id = 0xF0700008UL;
uint32_t feat_ctrl = 0; // features control register
uint32_t aux_ctrl = 0; // aux features control register
@ -162,7 +154,7 @@ 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<HWInterrupt> hwinterrupt; // ESCC serial controller
std::unique_ptr<HeathIntCtrl> int_ctrl; // Interrupt controller
std::unique_ptr<DMAChannel> snd_out_dma;
};