idechannel: split MacIO specific stuff into a separate class.

Now we got two classes: IdeChannel and MacioIdeChannel.
The former models a generic IDE channel so it can be used elsewhere.
The latter implements MacIO specific configuration register(s)
and interrupt signaling.
This commit is contained in:
Maxim Poliakovski 2024-08-20 16:06:03 +02:00
parent 4e78ac33c5
commit f5c91968a2
2 changed files with 92 additions and 56 deletions

View File

@ -1,6 +1,6 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-23 divingkatae and maximum
Copyright (C) 2018-24 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@ -22,8 +22,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
/** IDE Channel (aka IDE port) emulation.
One IDE channel is capable of controlling up to two IDE devices.
This class handles device registration and passing of messages
IdeChannel class handles device registration and passing of messages
from and to the host.
MacioIdeChannel class implements MacIO specific registers
and interrupt handling.
*/
#include <devices/common/ata/atabasedevice.h>
@ -51,15 +55,6 @@ IdeChannel::IdeChannel(const std::string name)
this->devices[1] = this->device_stub.get();
}
int IdeChannel::device_postinit() {
this->int_ctrl = dynamic_cast<InterruptCtrl*>(
gMachineObj->get_comp_by_type(HWCompType::INT_CTRL));
this->irq_id = this->int_ctrl->register_dev_int(
this->name == "IDE0" ? IntSrc::IDE0 : IntSrc::IDE1);
return 0;
}
void IdeChannel::register_device(int id, AtaInterface* dev_obj) {
if (id < 0 || id >= 2)
ABORT_F("%s: invalid device ID", this->name.c_str());
@ -69,44 +64,64 @@ void IdeChannel::register_device(int id, AtaInterface* dev_obj) {
((AtaBaseDevice*)dev_obj)->set_host(this, id);
}
uint32_t IdeChannel::read(const uint8_t reg_addr, const int size)
{
if (reg_addr == TIME_CONFIG) {
if (size != 4) {
LOG_F(WARNING, "%s: non-DWORD read from the channel config", this->name.c_str());
}
return this->ch_config;
} else {
return this->devices[this->cur_dev]->read(reg_addr);
}
uint32_t IdeChannel::read(const uint8_t reg_addr, const int size) {
return this->devices[this->cur_dev]->read(reg_addr);
}
void IdeChannel::write(const uint8_t reg_addr, const uint32_t val, const int size)
{
if (reg_addr == TIME_CONFIG) {
if (size != 4) {
LOG_F(WARNING, "%s: non-DWORD write to the channel config", this->name.c_str());
}
this->ch_config = val;
} else {
// keep track of the currently selected device
if (reg_addr == DEVICE_HEAD) {
this->cur_dev = (val >> 4) & 1;
}
// keep track of the currently selected device
if (reg_addr == DEVICE_HEAD) {
this->cur_dev = (val >> 4) & 1;
}
// redirect register writes to both devices
for (auto& dev : this->devices) {
dev->write(reg_addr, val);
}
// redirect register writes to both devices
for (auto& dev : this->devices) {
dev->write(reg_addr, val);
}
}
int MacioIdeChannel::device_postinit() {
this->int_ctrl = dynamic_cast<InterruptCtrl*>(
gMachineObj->get_comp_by_type(HWCompType::INT_CTRL));
this->irq_id = this->int_ctrl->register_dev_int(
this->name == "IDE0" ? IntSrc::IDE0 : IntSrc::IDE1);
this->irq_callback = [this](const uint8_t intrq_state) {
this->int_ctrl->ack_int(this->irq_id, intrq_state);
};
return 0;
}
uint32_t MacioIdeChannel::read(const uint8_t reg_addr, const int size)
{
if (reg_addr == TIME_CONFIG) {
if (size != 4) {
LOG_F(WARNING, "%s: non-DWORD read from TIME_CONFIG", this->name.c_str());
}
return this->ch_config;
} else
return IdeChannel::read(reg_addr, size);
}
void MacioIdeChannel::write(const uint8_t reg_addr, const uint32_t val, const int size)
{
if (reg_addr == TIME_CONFIG) {
if (size != 4) {
LOG_F(WARNING, "%s: non-DWORD write to TIME_CONFIG", this->name.c_str());
}
this->ch_config = val;
} else
IdeChannel::write(reg_addr, val, size);
}
static const DeviceDescription Ide0_Descriptor = {
IdeChannel::create_first, {}, {}
MacioIdeChannel::create_first, {}, {}
};
static const DeviceDescription Ide1_Descriptor = {
IdeChannel::create_second, {}, {}
MacioIdeChannel::create_second, {}, {}
};
REGISTER_DEVICE(Ide0, Ide0_Descriptor);

View File

@ -1,6 +1,6 @@
/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-23 divingkatae and maximum
Copyright (C) 2018-24 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@ -29,6 +29,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <devices/common/hwinterrupt.h>
#include <cinttypes>
#include <functional>
#include <memory>
#include <string>
@ -38,16 +39,6 @@ public:
IdeChannel(const std::string name);
~IdeChannel() = default;
static std::unique_ptr<HWComponent> create_first() {
return std::unique_ptr<IdeChannel>(new IdeChannel("IDE0"));
}
static std::unique_ptr<HWComponent> create_second() {
return std::unique_ptr<IdeChannel>(new IdeChannel("IDE1"));
}
int device_postinit() override;
void register_device(int id, AtaInterface* dev_obj);
uint32_t read(const uint8_t reg_addr, const int size);
@ -55,26 +46,56 @@ public:
void assert_pdiag() {
this->devices[0]->pdiag_callback();
};
}
bool is_device1_present() {
return this->devices[1]->get_device_id() != ata_interface::DEVICE_ID_INVALID;
}
void report_intrq(uint8_t intrq_state) {
this->int_ctrl->ack_int(this->irq_id, intrq_state);
void set_irq_callback(std::function<void(const uint8_t intrq_state)> cb) {
this->irq_callback = cb;
}
void report_intrq(uint8_t intrq_state) {
this->irq_callback(intrq_state);
}
protected:
std::function<void(const uint8_t intrq_state)> irq_callback = nullptr;
private:
int cur_dev = 0;
uint32_t ch_config = 0; // timing configuration for this channel
AtaInterface* devices[2];
// interrupt related stuff
InterruptCtrl* int_ctrl = nullptr;
uint32_t irq_id = 0;
std::unique_ptr<AtaInterface> device_stub;
};
/** This class models an IDE channel specific to MacIO ASICs. */
class MacioIdeChannel : public IdeChannel
{
public:
MacioIdeChannel(const std::string name) : IdeChannel(name) {};
~MacioIdeChannel() = default;
static std::unique_ptr<HWComponent> create_first() {
return std::unique_ptr<IdeChannel>(new MacioIdeChannel("IDE0"));
}
static std::unique_ptr<HWComponent> create_second() {
return std::unique_ptr<IdeChannel>(new MacioIdeChannel("IDE1"));
}
int device_postinit() override;
uint32_t read(const uint8_t reg_addr, const int size);
void write(const uint8_t reg_addr, const uint32_t val, const int size);
private:
uint32_t ch_config = 0; // timing configuration for this channel
// interrupt stuff
InterruptCtrl* int_ctrl = nullptr;
uint32_t irq_id = 0;
};
#endif // IDE_CHANNEL_H