diff --git a/devices/common/ata/atabasedevice.h b/devices/common/ata/atabasedevice.h
index 7bcbe7c..497783c 100644
--- a/devices/common/ata/atabasedevice.h
+++ b/devices/common/ata/atabasedevice.h
@@ -27,6 +27,7 @@ along with this program. If not, see .
#include
#include
#include
+#include "endianswap.h"
#include
#include
@@ -55,6 +56,14 @@ public:
void device_control(const uint8_t new_ctrl);
void update_intrq(uint8_t new_intrq_state);
+ bool has_data() {
+ return data_ptr && xfer_cnt;
+ }
+
+ uint16_t get_data() {
+ return BYTESWAP_16(*this->data_ptr++);
+ }
+
protected:
bool is_selected() { return ((this->r_dev_head >> 4) & 1) == this->my_dev_id; };
diff --git a/devices/common/ata/atapibasedevice.cpp b/devices/common/ata/atapibasedevice.cpp
index 95e376c..e34c73f 100644
--- a/devices/common/ata/atapibasedevice.cpp
+++ b/devices/common/ata/atapibasedevice.cpp
@@ -50,28 +50,26 @@ void AtapiBaseDevice::device_set_signature() {
uint16_t AtapiBaseDevice::read(const uint8_t reg_addr) {
switch (reg_addr) {
case ATA_Reg::DATA:
- if (this->data_ptr && this->xfer_cnt) {
+ if (has_data()) {
+ uint16_t ret_data = get_data();
this->xfer_cnt -= 2;
if (this->xfer_cnt <= 0) {
this->r_status &= ~DRQ;
if ((this->r_int_reason & ATAPI_Int_Reason::IO) &&
- !(this->r_int_reason & ATAPI_Int_Reason::CoD)) {
- uint16_t ret_data = BYTESWAP_16(*this->data_ptr++);
+ !(this->r_int_reason & ATAPI_Int_Reason::CoD)
+ ) {
if (this->data_available()) {
this->r_status &= ~DRQ;
this->r_status |= BSY;
- this->update_intrq(1);
- return ret_data;
+ this->update_intrq(1); // Is this going to work here? The interrupt happens before returning ret_data.
}
-
- if (this->status_expected) {
+ else if (this->status_expected) {
this->present_status();
}
- return ret_data;
}
}
- return BYTESWAP_16(*this->data_ptr++);
+ return ret_data;
} else {
return 0xFFFFU;
}
@@ -105,7 +103,7 @@ uint16_t AtapiBaseDevice::read(const uint8_t reg_addr) {
void AtapiBaseDevice::write(const uint8_t reg_addr, const uint16_t value) {
switch (reg_addr) {
case ATA_Reg::DATA:
- if (this->data_ptr && this->xfer_cnt) {
+ if (has_data()) {
*this->data_ptr++ = BYTESWAP_16(value);
this->xfer_cnt -= 2;
if (this->xfer_cnt <= 0) {
diff --git a/devices/common/ata/atapicdrom.cpp b/devices/common/ata/atapicdrom.cpp
index 94e4e67..2cb9e34 100644
--- a/devices/common/ata/atapicdrom.cpp
+++ b/devices/common/ata/atapicdrom.cpp
@@ -68,6 +68,11 @@ void AtapiCdrom::perform_packet_command() {
uint32_t lba, xfer_len;
this->r_status |= BSY;
+ this->sector_areas = 0;
+ if (this->doing_sector_areas) {
+ this->doing_sector_areas = false;
+ LOG_F(WARNING, "%s: doing_sector_areas reset", this->name.c_str());
+ }
switch (this->cmd_pkt[0]) {
case ScsiCommand::TEST_UNIT_READY:
@@ -183,20 +188,70 @@ void AtapiCdrom::perform_packet_command() {
this->present_status();
break;
case ScsiCommand::READ_CD:
- lba = READ_DWORD_BE_U(&this->cmd_pkt[2]);
+ {
+ lba = READ_DWORD_BE_U(&this->cmd_pkt[2]);
xfer_len = (this->cmd_pkt[6] << 16) | READ_WORD_BE_U(&this->cmd_pkt[7]);
- if (this->cmd_pkt[1] || this->cmd_pkt[9] != 0x10 || this->cmd_pkt[10])
- LOG_F(WARNING, "%s: unsupported READ_CD params", this->name.c_str());
+ if (this->cmd_pkt[1] || (this->cmd_pkt[9] & ~0xf8) || ((this->cmd_pkt[9] & 0xf8) == 0) || this->cmd_pkt[10])
+ LOG_F(WARNING, "%s: unsupported READ_CD params: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
+ this->name.c_str(),
+ this->cmd_pkt[0], this->cmd_pkt[1], this->cmd_pkt[2], this->cmd_pkt[3], this->cmd_pkt[4], this->cmd_pkt[5],
+ this->cmd_pkt[6], this->cmd_pkt[7], this->cmd_pkt[8], this->cmd_pkt[9], this->cmd_pkt[10], this->cmd_pkt[11]
+ );
if (this->r_features & ATAPI_Features::DMA) {
LOG_F(WARNING, "ATAPI DMA transfer requsted");
}
this->set_fpos(lba);
- this->xfer_cnt = this->read_begin(xfer_len, this->r_byte_count);
- this->r_byte_count = this->xfer_cnt;
+ this->sector_areas = cmd_pkt[9];
+ this->doing_sector_areas = true;
+ this->current_block = lba;
+ this->current_block_byte = 0;
+
+ int bytes_prolog = 0;
+ int bytes_data = 0;
+ int bytes_epilog = 0;
+
+ // For Mode 1 CD-ROMs:
+ if (this->sector_areas & 0x80) bytes_prolog += 12; // Sync
+ if (this->sector_areas & 0x20) bytes_prolog += 4; // Header
+ if (this->sector_areas & 0x40) bytes_prolog += 0; // SubHeader
+ if (this->sector_areas & 0x10) bytes_data += 2048; // User
+ if (this->sector_areas & 0x08) bytes_epilog += 288; // Auxiliary
+ if (this->sector_areas & 0x02) bytes_epilog += 294; // ErrorFlags
+ if (this->sector_areas & 0x01) bytes_epilog += 96; // SubChannel
+
+ int bytes_per_block = bytes_prolog + bytes_data + bytes_epilog;
+
+ int disk_image_byte_count;
+
+ if (bytes_per_block == 0) {
+ disk_image_byte_count = 0xffff;
+ }
+ else {
+ disk_image_byte_count = (this->r_byte_count / bytes_per_block) * this->block_size; // whole blocks
+ if ((this->r_byte_count % bytes_per_block) > bytes_prolog) { // partial block
+ int disk_image_byte_count_partial_block = (this->r_byte_count % bytes_per_block) - bytes_prolog; // remove prolog from partial block
+ if (disk_image_byte_count_partial_block > this->block_size) { // partial block includes some epilog?
+ disk_image_byte_count_partial_block = this->block_size; // // remove epilog from partial block
+ }
+ // add partial block
+ disk_image_byte_count += disk_image_byte_count_partial_block;
+ }
+ }
+
+ int disk_image_bytes_received = this->read_begin(xfer_len, disk_image_byte_count);
+
+ int bytes_received = (disk_image_bytes_received / this->block_size) * bytes_per_block + bytes_prolog + (disk_image_bytes_received % this->block_size); // whole blocks + prolog + partial block
+ if (bytes_received > this->r_byte_count) { // if partial epilog or partial prolog
+ bytes_received = this->r_byte_count; // confine to r_byte_count
+ }
+ this->r_byte_count = bytes_received;
+ this->xfer_cnt = bytes_received;
+
this->data_ptr = (uint16_t*)this->data_cache.get();
this->status_good();
this->data_out_phase();
break;
+ }
default:
LOG_F(ERROR, "%s: unsupported ATAPI command 0x%X", this->name.c_str(),
this->cmd_pkt[0]);
@@ -205,6 +260,101 @@ void AtapiCdrom::perform_packet_command() {
}
}
+int AtapiCdrom::request_data() {
+ // continuation of READ_CD above
+
+ this->data_ptr = (uint16_t*)this->data_cache.get();
+
+ this->xfer_cnt = this->read_more();
+
+ return this->xfer_cnt;
+}
+
+static const uint8_t mode_1_sync[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+
+uint16_t AtapiCdrom::get_data() {
+ uint16_t ret_data;
+
+ if (doing_sector_areas) {
+ // For Mode 1 CD-ROMs:
+ int area_start;
+ int area_end = 0;
+
+ ret_data = 0xffff;
+
+ if (this->sector_areas & 0x80) {
+ area_start = area_end;
+ area_end += 12; // Sync
+ if (this->current_block_byte >= area_start && this->current_block_byte < area_end) {
+ ret_data = BYTESWAP_16(*((uint16_t*)(&mode_1_sync[current_block_byte - area_start])));
+ }
+ }
+
+ if (this->sector_areas & 0x20) {
+ area_start = area_end;
+ area_end += 4; // Header
+ if (this->current_block_byte >= area_start && this->current_block_byte < area_end) {
+ AddrMsf msf = lba_to_msf(this->current_block + 150);
+ uint8_t header[4];
+ header[0] = msf.min;
+ header[1] = msf.sec;
+ header[2] = msf.frm;
+ header[3] = 0x01; // Mode 1
+ ret_data = BYTESWAP_16(*((uint16_t*)(&header[current_block_byte - area_start])));
+ }
+ }
+
+ if (this->sector_areas & 0x40) {
+ area_start = area_end;
+ area_end += 0; // SubHeader
+ if (this->current_block_byte >= area_start && this->current_block_byte < area_end) {
+ ret_data = 0;
+ }
+ }
+
+ if (this->sector_areas & 0x10) {
+ area_start = area_end;
+ area_end += 2048; // User
+ if (this->current_block_byte >= area_start && this->current_block_byte < area_end) {
+ ret_data = AtaBaseDevice::get_data();
+ }
+ }
+
+ if (this->sector_areas & 0x08) {
+ area_start = area_end;
+ area_end += 288; // Auxiliary
+ if (this->current_block_byte >= area_start && this->current_block_byte < area_end) {
+ ret_data = 0;
+ }
+ }
+
+ if (this->sector_areas & 0x02) {
+ area_start = area_end;
+ area_end += 294; // ErrorFlags
+ if (this->current_block_byte >= area_start && this->current_block_byte < area_end) {
+ ret_data = 0;
+ }
+ }
+
+ if (this->sector_areas & 0x01) {
+ area_start = area_end;
+ area_end += 96; // SubChannel
+ if (this->current_block_byte >= area_start && this->current_block_byte < area_end) {
+ ret_data = 0;
+ }
+ }
+
+ current_block_byte += 2;
+ if (current_block_byte >= area_end)
+ current_block_byte = 0;
+ }
+ else {
+ ret_data = AtaBaseDevice::get_data();
+ }
+
+ return ret_data;
+}
+
void AtapiCdrom::status_good() {
this->status_expected = true;
this->r_error = 0;
diff --git a/devices/common/ata/atapicdrom.h b/devices/common/ata/atapicdrom.h
index 84bd50b..c67b8a8 100644
--- a/devices/common/ata/atapicdrom.h
+++ b/devices/common/ata/atapicdrom.h
@@ -42,11 +42,7 @@ public:
void perform_packet_command() override;
- int request_data() override {
- this->data_ptr = (uint16_t*)this->data_cache.get();
- this->xfer_cnt = this->read_more();
- return this->xfer_cnt;
- }
+ int request_data() override;
bool data_available() override {
return this->data_left() != 0;
@@ -55,10 +51,16 @@ public:
void status_good();
void status_error(uint8_t sense_key, uint8_t asc);
+ uint16_t get_data();
private:
uint8_t sense_key = 0;
uint8_t asc = 0;
uint8_t ascq = 0;
+
+ bool doing_sector_areas = false;
+ uint8_t sector_areas;
+ uint32_t current_block;
+ uint16_t current_block_byte;
};
#endif // ATAPI_CDROM_H
diff --git a/devices/storage/cdromdrive.h b/devices/storage/cdromdrive.h
index 60b20ee..b5f59ad 100644
--- a/devices/storage/cdromdrive.h
+++ b/devices/storage/cdromdrive.h
@@ -67,7 +67,6 @@ public:
uint32_t report_capacity(uint8_t *data_ptr);
uint32_t read_toc(uint8_t *cmd_ptr, uint8_t *data_ptr);
-private:
AddrMsf lba_to_msf(const int lba);
protected: