Rework ATA reset logic.

This commit is contained in:
Maxim Poliakovski 2023-04-24 23:02:32 +02:00
parent c543d0936c
commit 0f8e68d4bf
6 changed files with 75 additions and 32 deletions

View File

@ -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)
@ -30,32 +30,39 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
using namespace ata_interface;
AtaBaseDevice::AtaBaseDevice(const std::string name)
{
AtaBaseDevice::AtaBaseDevice(const std::string name, uint8_t type) {
this->set_name(name);
supports_types(HWCompType::IDE_DEV);
device_reset();
this->device_type = type;
device_reset(false);
device_set_signature();
}
void AtaBaseDevice::device_reset(const uint8_t dev_head_val)
{
this->r_error = 1; // Device 0 passed, Device 1 passed or not present
void AtaBaseDevice::device_reset(bool is_soft_reset) {
LOG_F(INFO, "%s: %s-reset triggered", this->name.c_str(),
is_soft_reset ? "soft" : "hard");
// Diagnostic code
this->r_error = 1; // device 0 passed, device 1 passed or not present
}
void AtaBaseDevice::device_set_signature() {
this->r_sect_count = 1;
this->r_sect_num = 1;
this->r_dev_ctrl = 0;
this->r_dev_head = 0;
// set protocol signature
if (this->device_type == DEVICE_TYPE_ATAPI) {
this->r_cylinder_lo = 0x14;
this->r_cylinder_hi = 0xEB;
this->r_dev_head = dev_head_val;
this->r_status_save = this->r_status; // for restoring on the first command
this->r_status = 0;
} else { // assume ATA by default
this->r_cylinder_lo = 0;
this->r_cylinder_hi = 0;
this->r_dev_head = 0;
this->r_status = DRDY | DSC;
this->r_status = DRDY | DSC; // DSC=1 is required for ATA devices
}
}
@ -117,13 +124,29 @@ void AtaBaseDevice::write(const uint8_t reg_addr, const uint16_t value) {
}
break;
case ATA_Reg::DEV_CTRL:
if (!(this->r_dev_ctrl & SRST) && (value & SRST)) {
LOG_F(INFO, "%s: soft reset triggered", this->name.c_str());
this->r_status |= BSY;
}
this->r_dev_ctrl = value;
this->device_control(value);
break;
default:
LOG_F(WARNING, "Attempted to write unknown IDE register: %x", reg_addr);
}
}
void AtaBaseDevice::device_control(const uint8_t new_ctrl) {
// perform ATA Soft Reset if requested
if ((this->r_dev_ctrl ^ new_ctrl) & SRST) {
if (new_ctrl & SRST) { // SRST set -> phase 0 aka self-test
this->r_status |= BSY;
this->device_reset(true);
} else { // SRST cleared -> phase 1 aka signature and error report
if (!this->my_dev_id && this->host_obj->is_device1_present())
this->r_error |= 0x80;
this->device_set_signature();
this->r_status &= ~BSY;
if (this->my_dev_id && this->r_error == 1) {
this->host_obj->assert_pdiag();
}
}
}
this->r_dev_ctrl = new_ctrl;
}

View File

@ -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)
@ -25,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#define ATA_BASE_DEVICE_H
#include <devices/common/ata/atadefs.h>
#include <devices/common/ata/idechannel.h>
#include <devices/common/hwcomponent.h>
#include <cinttypes>
@ -32,17 +33,25 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
class AtaBaseDevice : public HWComponent, public AtaInterface
{
public:
AtaBaseDevice(const std::string name);
AtaBaseDevice(const std::string name, uint8_t type);
~AtaBaseDevice() = default;
uint16_t read(const uint8_t reg_addr);
void write(const uint8_t reg_addr, const uint16_t value);
void set_host(IdeChannel* host) { this->host_obj = host; };
uint16_t read(const uint8_t reg_addr) override;
void write(const uint8_t reg_addr, const uint16_t value) override;
virtual int perform_command() = 0;
int get_device_id() { return this->my_dev_id; };
int get_device_id() override { return this->my_dev_id; };
void device_reset(const uint8_t dev_head_val = 0);
void pdiag_callback() override {
this->r_error &= 0x7F;
};
void device_reset(bool is_soft_reset);
void device_set_signature();
void device_control(const uint8_t new_ctrl);
private:
bool is_selected() { return ((this->r_dev_head >> 4) & 1) == this->my_dev_id; };
@ -51,6 +60,8 @@ protected:
uint8_t my_dev_id = 0; // my IDE device ID configured by the host
uint8_t device_type = ata_interface::DEVICE_TYPE_UNKNOWN;
IdeChannel* host_obj = nullptr;
// IDE aka task file registers
uint8_t r_error;
uint8_t r_features;
@ -61,7 +72,8 @@ protected:
uint8_t r_dev_head;
uint8_t r_command;
uint8_t r_status;
uint8_t r_dev_ctrl;
uint8_t r_status_save;
uint8_t r_dev_ctrl = 0x08;
};
#endif // ATA_BASE_DEVICE_H

View File

@ -128,7 +128,8 @@ public:
virtual uint16_t read(const uint8_t reg_addr) = 0;
virtual void write(const uint8_t reg_addr, const uint16_t val) = 0;
virtual int get_device_id() = 0;
virtual int get_device_id() = 0;
virtual void pdiag_callback() {};
};
/** Dummy ATA device. */
@ -137,7 +138,7 @@ public:
AtaNullDevice() = default;
~AtaNullDevice() = default;
uint16_t read(const uint8_t reg_addr) {
uint16_t read(const uint8_t reg_addr) override {
// return all one's except DD7 if no device is present
// DD7 corresponds to the BSY bit of the status register
// The host should have a pull-down resistor on DD7
@ -146,10 +147,10 @@ public:
return 0xFF7FU;
};
void write(const uint8_t reg_addr, const uint16_t val) {};
void write(const uint8_t reg_addr, const uint16_t val) override {};
// invalid device ID means no real device is present
int get_device_id() { return ata_interface::DEVICE_ID_INVALID; };
int get_device_id() override { return ata_interface::DEVICE_ID_INVALID; };
};
#endif // ATA_INTERFACE_H

View File

@ -31,8 +31,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
using namespace ata_interface;
AtaHardDisk::AtaHardDisk() : AtaBaseDevice("ATA-HD")
{
AtaHardDisk::AtaHardDisk() : AtaBaseDevice("ATA-HD", DEVICE_TYPE_ATA) {
}
void AtaHardDisk::insert_image(std::string filename) {
@ -57,7 +56,7 @@ int AtaHardDisk::perform_command()
case NOP:
break;
case RESET_ATAPI:
device_reset();
device_reset(true);
break;
case RECALIBRATE:
hdd_img.seekg(0, std::ios::beg);

View File

@ -37,7 +37,7 @@ public:
~AtaHardDisk() = default;
void insert_image(std::string filename);
int perform_command();
int perform_command() override;
private:
std::fstream hdd_img;

View File

@ -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)
@ -48,6 +48,14 @@ public:
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;
}
private:
int cur_dev = 0;
uint32_t ch_config = 0; // timing configuration for this channel