Skip to content

Commit

Permalink
idechannel: split MacIO specific stuff into a separate class.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
maximumspatium committed Aug 20, 2024
1 parent 4e78ac3 commit f5c9196
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 48 deletions.
77 changes: 46 additions & 31 deletions devices/common/ata/idechannel.cpp
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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>
Expand Down Expand Up @@ -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());
Expand All @@ -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)
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)
{
// 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);
}
}

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 the channel config", this->name.c_str());
LOG_F(WARNING, "%s: non-DWORD read from TIME_CONFIG", this->name.c_str());
}
return this->ch_config;
} else {
return this->devices[this->cur_dev]->read(reg_addr);
}
} else
return IdeChannel::read(reg_addr, size);
}

void IdeChannel::write(const uint8_t reg_addr, const uint32_t val, const int 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 the channel config", this->name.c_str());
LOG_F(WARNING, "%s: non-DWORD write to TIME_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;
}

// redirect register writes to both devices
for (auto& dev : this->devices) {
dev->write(reg_addr, 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);
Expand Down
55 changes: 38 additions & 17 deletions devices/common/ata/idechannel.h
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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>

Expand All @@ -38,43 +39,63 @@ class IdeChannel : public HWComponent
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);
void write(const uint8_t reg_addr, const uint32_t val, const int size);

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 set_irq_callback(std::function<void(const uint8_t intrq_state)> cb) {
this->irq_callback = cb;
}

void report_intrq(uint8_t intrq_state) {
this->int_ctrl->ack_int(this->irq_id, 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
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;

std::unique_ptr<AtaInterface> device_stub;
};

#endif // IDE_CHANNEL_H

0 comments on commit f5c9196

Please sign in to comment.