diff --git a/MiSTer.vcxproj b/MiSTer.vcxproj
index 1ec0716..01f275a 100644
--- a/MiSTer.vcxproj
+++ b/MiSTer.vcxproj
@@ -95,6 +95,7 @@
+
@@ -158,6 +159,7 @@
+
diff --git a/MiSTer.vcxproj.filters b/MiSTer.vcxproj.filters
index b264194..b17a54d 100644
--- a/MiSTer.vcxproj.filters
+++ b/MiSTer.vcxproj.filters
@@ -205,6 +205,9 @@
Source Files\support
+
+ Source Files\support
+
@@ -390,5 +393,8 @@
Header Files\support
+
+ Header Files\support
+
\ No newline at end of file
diff --git a/support/x86/x86.cpp b/support/x86/x86.cpp
index b5c0286..bc882a9 100644
--- a/support/x86/x86.cpp
+++ b/support/x86/x86.cpp
@@ -39,6 +39,7 @@
#include "../../fpga_io.h"
#include "x86_share.h"
#include "x86_ide.h"
+#include "x86_cdrom.h"
#define FLOPPY0_BASE_OLD 0x8800
#define HDD0_BASE_OLD 0x8840
@@ -426,7 +427,7 @@ static void hdd_set(int num, char* filename)
if (num > 1 && !vhd)
{
- const char *img_name = x86_ide_parse_cd(num, filename);
+ const char *img_name = cdrom_parse(num, filename);
if (img_name) present = img_mount(&ide_image[num], img_name);
if (present) cd = 1;
}
diff --git a/support/x86/x86_cdrom.cpp b/support/x86/x86_cdrom.cpp
new file mode 100644
index 0000000..e1510d7
--- /dev/null
+++ b/support/x86/x86_cdrom.cpp
@@ -0,0 +1,1287 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../../spi.h"
+#include "../../user_io.h"
+#include "../../file_io.h"
+#include "../../hardware.h"
+#include "x86.h"
+#include "x86_ide.h"
+#include "x86_cdrom.h"
+
+#if 0
+ #define dbg_printf printf
+ #define dbg_print_regs ide_print_regs
+ #define dbg_hexdump hexdump
+#else
+ #define dbg_printf(...) void()
+ #define dbg_print_regs void
+ #define dbg_hexdump(...) void()
+#endif
+
+#define IOWR(base, reg, value, ver) x86_dma_set((base) + ((ver) ? (reg) : ((reg)<<2)), value)
+
+#define ide_send_data(databuf, size) x86_dma_sendbuf(ide->base + 255, (size), (uint32_t*)(databuf))
+#define ide_recv_data(databuf, size) x86_dma_recvbuf(ide->base + 255, (size), (uint32_t*)(databuf))
+#define ide_reset_buf() x86_dma_set(ide->base + 3, 0)
+
+#define BYTES_PER_RAW_REDBOOK_FRAME 2352
+#define BYTES_PER_COOKED_REDBOOK_FRAME 2048
+#define REDBOOK_FRAMES_PER_SECOND 75
+#define REDBOOK_FRAME_PADDING 150
+
+#define CD_FPS 75
+#define MSF_TO_FRAMES(M, S, F) ((M)*60*CD_FPS+(S)*CD_FPS+(F))
+
+#define CD_ERR_NO_DISK ((2 << 4) | ATA_ERR_ABRT)
+
+typedef struct
+{
+ unsigned char min;
+ unsigned char sec;
+ unsigned char fr;
+} TMSF;
+
+static int check_magic(fileTYPE *file, int sectorSize, int mode2)
+{
+ // Initialize our array in the event file->read() doesn't fully write it
+ static uint8_t pvd[BYTES_PER_COOKED_REDBOOK_FRAME];
+ memset(pvd, 0, sizeof(pvd));
+
+ uint32_t seek = 16 * sectorSize; // first vd is located at sector 16
+ if (sectorSize == BYTES_PER_RAW_REDBOOK_FRAME && !mode2) seek += 16;
+ if (mode2) seek += 24;
+ FileSeek(file, seek, SEEK_SET);
+ if (!FileReadAdv(file, pvd, BYTES_PER_COOKED_REDBOOK_FRAME)) return 0;
+
+ // pvd[0] = descriptor type, pvd[1..5] = standard identifier,
+ // pvd[6] = iso version (+8 for High Sierra)
+ return ((pvd[0] == 1 && !strncmp((char*)(&pvd[1]), "CD001", 5) && pvd[6] == 1) ||
+ (pvd[8] == 1 && !strncmp((char*)(&pvd[9]), "CDROM", 5) && pvd[14] == 1));
+}
+
+static int check_iso_file(fileTYPE *f, uint8_t *mode2, uint16_t *sectorSize)
+{
+ if (check_magic(f, BYTES_PER_COOKED_REDBOOK_FRAME, false))
+ {
+ if (sectorSize) *sectorSize = BYTES_PER_COOKED_REDBOOK_FRAME;
+ if (mode2) *mode2 = 0;
+ return 1;
+ }
+ else if (check_magic(f, BYTES_PER_RAW_REDBOOK_FRAME, false))
+ {
+ if (sectorSize) *sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
+ if (mode2) *mode2 = 0;
+ return 1;
+ }
+ else if (check_magic(f, 2336, true))
+ {
+ if (sectorSize) *sectorSize = 2336;
+ if (mode2) *mode2 = 1;
+ return 1;
+ }
+ else if (check_magic(f, BYTES_PER_RAW_REDBOOK_FRAME, true))
+ {
+ if (sectorSize) *sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
+ if (mode2) *mode2 = 1;
+ return 1;
+ }
+
+ if (sectorSize) *sectorSize = 0;
+ if (mode2) *mode2 = 0;
+ return 0;
+}
+
+static const char * load_iso_file(drive_t *drv, const char* filename)
+{
+ fileTYPE f;
+ memset(drv->track, 0, sizeof(drv->track));
+ drv->track_cnt = 0;
+
+ strcpy(drv->track[0].filename, filename);
+ if (!FileOpen(&f, filename))
+ {
+ printf("Cannot open ISO file!\n");
+ return 0;
+ }
+
+ if(!check_iso_file(&f, &drv->track[0].mode2, &drv->track[0].sectorSize))
+ {
+ printf("Fail to parse ISO!\n");
+ FileClose(&f);
+ return 0;
+ }
+
+ drv->track[0].attr = 0x40; //data track
+ drv->track[0].length = f.size / drv->track[0].sectorSize;
+ drv->track[0].number = 1;
+
+ FileClose(&f);
+
+ // lead-out track (track 2)
+ drv->track[1].start = drv->track[0].length;
+ drv->track[2].number = 2;
+
+ printf("ISO: mode2 = %d, sectorSize = %d, sectors = %d\n", drv->track[0].mode2, drv->track[0].sectorSize, drv->track[0].length);
+ drv->track_cnt = 2;
+
+ drv->data_num = 0;
+ return drv->track[0].filename;
+}
+
+static int get_word(std::string &keyword, std::istream &in)
+{
+ in >> keyword;
+ for (uint32_t i = 0; i < keyword.size(); i++) keyword[i] = (char)toupper(keyword[i]);
+ return keyword.size();
+}
+
+static int get_timecode(uint32_t &frames, std::istream &in)
+{
+ std::string msf;
+ in >> msf;
+ TMSF tmp = { 0, 0, 0 };
+ int success = sscanf(msf.c_str(), "%hhu:%hhu:%hhu", &tmp.min, &tmp.sec, &tmp.fr) == 3;
+ frames = (int)MSF_TO_FRAMES(tmp.min, tmp.sec, tmp.fr);
+ return success;
+}
+
+static int add_track(drive_t *drv, track_t *curr, uint32_t &shift, const int32_t prestart, uint32_t &totalPregap, uint32_t currPregap)
+{
+ uint32_t skip = 0;
+ if (drv->track_cnt >= sizeof(drv->track) / sizeof(drv->track[0]))
+ {
+ printf("CDROM: too many tracks(%d)\n", drv->track_cnt);
+ return 0;
+ }
+
+ // frames between index 0(prestart) and 1(curr.start) must be skipped
+ if (prestart >= 0)
+ {
+ if (prestart > static_cast(curr->start))
+ {
+ printf("CDROM: add_track => prestart %d cannot be > curr.start %u\n", prestart, curr->start);
+ return 0;
+ }
+ skip = static_cast(static_cast(curr->start) - prestart);
+ }
+
+ // Add the first track, if our vector is empty
+ if (!drv->track_cnt)
+ {
+ //assertm(curr.number == 1, "The first track must be labelled number 1 [BUG!]");
+ curr->skip = skip * curr->sectorSize;
+ curr->start += currPregap;
+ totalPregap = currPregap;
+
+ memcpy(&drv->track[drv->track_cnt], curr, sizeof(track_t));
+ drv->track_cnt++;
+ return 1;
+ }
+
+ // Guard against undefined behavior in subsequent tracks.back() call
+ //assert(!tracks.empty());
+ track_t *prev = &drv->track[drv->track_cnt - 1];
+
+ // current track consumes data from the same file as the previous
+ if (!strcmp(prev->filename, curr->filename))
+ {
+ curr->start += shift;
+ if (!prev->length)
+ {
+ prev->length = curr->start + totalPregap - prev->start - skip;
+ }
+ curr->skip += prev->skip + prev->length * prev->sectorSize + skip * curr->sectorSize;
+ totalPregap += currPregap;
+ curr->start += totalPregap;
+ // current track uses a different file as the previous track
+ }
+ else
+ {
+ uint32_t size = FileLoad(prev->filename, 0, 0);
+ const uint32_t tmp = size - prev->skip;
+ prev->length = tmp / prev->sectorSize;
+
+ if (tmp % prev->sectorSize != 0) prev->length++; // padding
+
+ curr->start += prev->start + prev->length + currPregap;
+ curr->skip = skip * curr->sectorSize;
+ shift += prev->start + prev->length;
+ totalPregap = currPregap;
+ }
+
+ // error checks
+ if (curr->number <= 1
+ || prev->number + 1 != curr->number
+ || curr->start < prev->start + prev->length) {
+ printf("add_track: failed consistency checks\n"
+ "\tcurr.number (%d) <= 1\n"
+ "\tprev.number (%d) + 1 != curr.number (%d)\n"
+ "\tcurr.start (%d) < prev.start (%d) + prev.length (%d)\n",
+ curr->number, prev->number, curr->number,
+ curr->start, prev->start, prev->length);
+ return 0;
+ }
+
+ memcpy(&drv->track[drv->track_cnt], curr, sizeof(track_t));
+ drv->track_cnt++;
+ return 1;
+}
+
+static const char* load_cue_file(drive_t *drv, const char *cuefile)
+{
+ memset(drv->track, 0, sizeof(drv->track));
+ drv->track_cnt = 0;
+
+ track_t track = {};
+ uint32_t shift = 0;
+ uint32_t currPregap = 0;
+ uint32_t totalPregap = 0;
+ int32_t prestart = -1;
+ int track_number;
+ int success;
+ int canAddTrack = 0;
+
+ std::string pathname(cuefile);
+ std::size_t found = pathname.find_last_of('/');
+ if (found == std::string::npos) return 0; // no folder name?
+ pathname.resize(found + 1);
+
+ std::ifstream in;
+ in.open(cuefile, std::ios::in);
+ if (in.fail()) return 0;
+
+ while (!in.eof())
+ {
+ // get next line
+ std::string buf;
+ std::getline(in, buf);
+
+ if (in.fail() && !in.eof()) return 0; // probably a binary file
+
+ std::istringstream line(buf);
+
+ std::string command;
+ get_word(command, line);
+
+ //printf("command: %s\n", command.c_str());
+
+ if (command == "TRACK")
+ {
+ if (canAddTrack) success = add_track(drv, &track, shift, prestart, totalPregap, currPregap);
+ else success = 1;
+
+ track.start = 0;
+ track.skip = 0;
+ currPregap = 0;
+ prestart = -1;
+
+ line >> track_number; // (cin) read into a true int first
+
+ track.number = static_cast(track_number);
+
+ std::string type;
+ get_word(type, line);
+
+ //printf(" type: %s\n", type.c_str());
+
+ if (type == "AUDIO")
+ {
+ track.sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
+ track.attr = 0;
+ track.mode2 = false;
+ }
+ else if (type == "MODE1/2048")
+ {
+ track.sectorSize = BYTES_PER_COOKED_REDBOOK_FRAME;
+ track.attr = 0x40;
+ track.mode2 = false;
+ }
+ else if (type == "MODE1/2352")
+ {
+ track.sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
+ track.attr = 0x40;
+ track.mode2 = false;
+ }
+ else if (type == "MODE2/2336")
+ {
+ track.sectorSize = 2336;
+ track.attr = 0x40;
+ track.mode2 = true;
+ }
+ else if (type == "MODE2/2352")
+ {
+ track.sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
+ track.attr = 0x40;
+ track.mode2 = true;
+ }
+ else success = 0;
+
+ canAddTrack = 1;
+ }
+ else if (command == "INDEX")
+ {
+ int index;
+ line >> index;
+ uint32_t frame;
+ success = get_timecode(frame, line);
+
+ if (index == 1) track.start = frame;
+ else if (index == 0) prestart = static_cast(frame);
+ // ignore other indices
+ }
+ else if (command == "FILE")
+ {
+ if (canAddTrack) success = add_track(drv, &track, shift, prestart, totalPregap, currPregap);
+ else success = 1;
+ canAddTrack = 0;
+
+ std::string filename;
+ std::getline(std::getline(line, filename, '"'), filename, '"');
+
+ strcpy(track.filename, pathname.c_str());
+ strcat(track.filename, filename.c_str());
+
+ printf("cue: got new file name: %s\n", track.filename);
+ }
+ else if (command == "PREGAP") success = get_timecode(currPregap, line);
+ // ignored commands
+ else if (command == "CATALOG" || command == "CDTEXTFILE" || command == "FLAGS" || command == "ISRC" ||
+ command == "PERFORMER" || command == "POSTGAP" || command == "REM" ||
+ command == "SONGWRITER" || command == "TITLE" || command.empty())
+ {
+ success = 1;
+ }
+ // failure
+ else
+ {
+ success = 0;
+ }
+
+ if (!success)
+ {
+ return 0;
+ }
+ }
+
+ // add last track
+ if (!add_track(drv, &track, shift, prestart, totalPregap, currPregap))
+ {
+ return 0;
+ }
+
+ // add lead-out track
+ track.number++;
+ track.attr = 0;//sync with load iso
+ track.start = 0;
+ track.length = 0;
+
+ if (!add_track(drv, &track, shift, -1, totalPregap, 0))
+ {
+ return 0;
+ }
+
+ for (uint8_t i = 0; i < drv->track_cnt; i++)
+ {
+ if (drv->track[i].attr == 0x40)
+ {
+ drv->data_num = i;
+ return drv->track[i].filename;
+ }
+ }
+
+ return 0;
+}
+
+inline TMSF frames_to_msf(uint32_t frames)
+{
+ TMSF msf = { 0, 0, 0 };
+ msf.fr = frames % REDBOOK_FRAMES_PER_SECOND;
+ frames /= REDBOOK_FRAMES_PER_SECOND;
+ msf.sec = frames % 60;
+ frames /= 60;
+ msf.min = static_cast(frames);
+ return msf;
+}
+
+static int get_tracks(drive_t *drv, int& start_track_num, int& end_track_num, TMSF& lead_out_msf)
+{
+ if (drv->track_cnt < 2 || !drv->track[0].length) return 0;
+
+ start_track_num = drv->track[0].number;
+ end_track_num = drv->track[drv->track_cnt - 2].number;
+ lead_out_msf = frames_to_msf(drv->track[drv->track_cnt - 1].start + REDBOOK_FRAME_PADDING);
+ return 1;
+}
+
+static int get_track_info(drive_t *drv, int requested_track_num, TMSF& start_msf, unsigned char& attr)
+{
+ if (drv->track_cnt < 2 || requested_track_num < 1 || requested_track_num >= drv->track_cnt)
+ {
+ return 0;
+ }
+
+ start_msf = frames_to_msf(drv->track[requested_track_num - 1].start + REDBOOK_FRAME_PADDING);
+ attr = drv->track[requested_track_num - 1].attr;
+ return 1;
+}
+
+static int read_toc(drive_t *drv, uint8_t *cmdbuf)
+{
+ /* NTS: The SCSI MMC standards say we're allowed to indicate the return data
+ * is longer than it's allocation length. But here's the thing: some MS-DOS
+ * CD-ROM drivers will ask for the TOC but only provide enough room for one
+ * entry (OAKCDROM.SYS) and if we signal more data than it's buffer, it will
+ * reject our response and render the CD-ROM drive inaccessible. So to make
+ * this emulation work, we have to cut our response short to the driver's
+ * allocation length */
+ unsigned int AllocationLength = ((unsigned int)cmdbuf[7] << 8) + cmdbuf[8];
+ unsigned char Format = cmdbuf[2] & 0xF;
+ unsigned char Track = cmdbuf[6];
+ bool TIME = !!(cmdbuf[1] & 2);
+ unsigned char *write;
+ int first, last, track;
+ TMSF leadOut;
+
+ printf("read_toc in:\n");
+ hexdump(cmdbuf, 12);
+
+ memset(ide_buf, 0, 8);
+
+ if (!get_tracks(drv, first, last, leadOut))
+ {
+ printf("WARNING: ATAPI READ TOC failed to get track info\n");
+ return 8;
+ }
+
+ /* start 2 bytes out. we'll fill in the data length later */
+ write = ide_buf + 2;
+
+ if (Format == 1) /* Read multisession info */
+ {
+ unsigned char attr;
+ TMSF start;
+
+ *write++ = (unsigned char)1; /* @+2 first complete session */
+ *write++ = (unsigned char)1; /* @+3 last complete session */
+
+ if (!get_track_info(drv, first, start, attr))
+ {
+ printf("WARNING: ATAPI READ TOC unable to read track %u information\n", first);
+ attr = 0x41; /* ADR=1 CONTROL=4 */
+ start.min = 0;
+ start.sec = 0;
+ start.fr = 0;
+ }
+
+ printf("Track %u attr=0x%02x %02u:%02u:%02u\n", first, attr, start.min, start.sec, start.fr);
+
+ *write++ = 0x00; /* entry+0 RESERVED */
+ *write++ = (attr >> 4) | 0x10; /* entry+1 ADR=1 CONTROL=4 (DATA) */
+ *write++ = (unsigned char)first;/* entry+2 TRACK */
+ *write++ = 0x00; /* entry+3 RESERVED */
+
+ /* then, start address of first track in session */
+ if (TIME)
+ {
+ *write++ = 0x00;
+ *write++ = start.min;
+ *write++ = start.sec;
+ *write++ = start.fr;
+ }
+ else
+ {
+ uint32_t sec = (start.min * 60u * 75u) + (start.sec * 75u) + start.fr - 150u;
+ *write++ = (unsigned char)(sec >> 24u);
+ *write++ = (unsigned char)(sec >> 16u);
+ *write++ = (unsigned char)(sec >> 8u);
+ *write++ = (unsigned char)(sec >> 0u);
+ }
+ }
+ else if (Format == 0) /* Read table of contents */
+ {
+ *write++ = (unsigned char)first; /* @+2 */
+ *write++ = (unsigned char)last; /* @+3 */
+
+ for (track = first; track <= last; track++)
+ {
+ unsigned char attr;
+ TMSF start;
+
+ if (!get_track_info(drv, track, start, attr))
+ {
+ printf("WARNING: ATAPI READ TOC unable to read track %u information\n", track);
+ attr = 0x41; /* ADR=1 CONTROL=4 */
+ start.min = 0;
+ start.sec = 0;
+ start.fr = 0;
+ }
+
+ if (track < Track) continue;
+ if ((write + 8) > (ide_buf + AllocationLength)) break;
+
+ printf("Track %u attr=0x%02x %02u:%02u:%02u\n", first, attr, start.min, start.sec, start.fr);
+
+ *write++ = 0x00; /* entry+0 RESERVED */
+ *write++ = (attr >> 4) | 0x10; /* entry+1 ADR=1 CONTROL=4 (DATA) */
+ *write++ = (unsigned char)track;/* entry+2 TRACK */
+ *write++ = 0x00; /* entry+3 RESERVED */
+ if (TIME)
+ {
+ *write++ = 0x00;
+ *write++ = start.min;
+ *write++ = start.sec;
+ *write++ = start.fr;
+ }
+ else
+ {
+ uint32_t sec = (start.min * 60u * 75u) + (start.sec * 75u) + start.fr - 150u;
+ *write++ = (unsigned char)(sec >> 24u);
+ *write++ = (unsigned char)(sec >> 16u);
+ *write++ = (unsigned char)(sec >> 8u);
+ *write++ = (unsigned char)(sec >> 0u);
+ }
+ }
+
+ if ((write + 8) <= (ide_buf + AllocationLength))
+ {
+ *write++ = 0x00;
+ *write++ = 0x14;
+ *write++ = 0xAA;/*TRACK*/
+ *write++ = 0x00;
+ if (TIME)
+ {
+ *write++ = 0x00;
+ *write++ = leadOut.min;
+ *write++ = leadOut.sec;
+ *write++ = leadOut.fr;
+ }
+ else
+ {
+ uint32_t sec = (leadOut.min * 60u * 75u) + (leadOut.sec * 75u) + leadOut.fr - 150u;
+ *write++ = (unsigned char)(sec >> 24u);
+ *write++ = (unsigned char)(sec >> 16u);
+ *write++ = (unsigned char)(sec >> 8u);
+ *write++ = (unsigned char)(sec >> 0u);
+ }
+ }
+ }
+ else
+ {
+ printf("WARNING: ATAPI READ TOC Format=%u not supported\n", Format);
+ return 8;
+ }
+
+ /* update the TOC data length field */
+ unsigned int x = (unsigned int)(write - ide_buf) - 2;
+ ide_buf[0] = x >> 8;
+ ide_buf[1] = x & 0xFF;
+
+ printf("read_toc result:\n");
+ hexdump(ide_buf, write - ide_buf);
+
+ return write - ide_buf;
+}
+
+static uint16_t mode_sense(int page)
+{
+ uint8_t *write = ide_buf;
+ uint8_t *plen;
+
+ uint32_t x;
+
+ int valid = 0;
+
+ printf("mode_sense page: %X\n", page);
+
+ /* Mode Parameter List MMC-3 Table 340 */
+ /* - Mode parameter header */
+ /* - Page(s) */
+
+ /* Mode Parameter Header (response for 10-byte MODE SENSE) SPC-2 Table 148 */
+ *write++ = 0x00; /* MODE DATA LENGTH (MSB) */
+ *write++ = 0x00; /* (LSB) */
+ *write++ = 0x00; /* MEDIUM TYPE */
+ *write++ = 0x00; /* DEVICE-SPECIFIC PARAMETER */
+ *write++ = 0x00; /* Reserved */
+ *write++ = 0x00; /* Reserved */
+ *write++ = 0x00; /* BLOCK DESCRIPTOR LENGTH (MSB) */
+ *write++ = 0x00; /* (LSB) */
+ /* NTS: MMC-3 Table 342 says that BLOCK DESCRIPTOR LENGTH is zero, where it would be 8 for legacy units */
+
+ /* Mode Page Format MMC-3 Table 341 */
+ if (page == 0x01 || page == 0x3F)
+ {
+ valid = 1;
+ *write++ = 1; /* PS|reserved|Page Code */
+ plen = write;
+ *write++ = 0x00; /* Page Length (n - 1) ... Length in bytes of the mode parameters that follow */
+
+ *write++ = 0x00; /* +2 Error recovery Parameter AWRE|ARRE|TB|RC|Reserved|PER|DTE|DCR */
+ *write++ = 3; /* +3 Read Retry Count */
+ *write++ = 0x00; /* +4 Reserved */
+ *write++ = 0x00; /* +5 Reserved */
+ *write++ = 0x00; /* +6 Reserved */
+ *write++ = 0x00; /* +7 Reserved */
+
+ *plen = write - plen - 1;
+ }
+
+ /* CD-ROM audio control MMC-3 Section 6.3.7 table 354 */
+ /* also MMC-1 Section 5.2.3.1 table 97 */
+ if (page == 0x0E || page == 0x3F)
+ {
+ valid = 1;
+ *write++ = 0x0E; /* PS|reserved|Page Code */
+ plen = write;
+ *write++ = 0x00; /* Page Length (n - 1) ... Length in bytes of the mode parameters that follow */
+
+ *write++ = 0x04; /* +2 Reserved|IMMED=1|SOTC=0|Reserved */
+ *write++ = 0x00; /* +3 Reserved */
+ *write++ = 0x00; /* +4 Reserved */
+ *write++ = 0x00; /* +5 Reserved */
+ *write++ = 0x00; /* +6 Obsolete (75) */
+ *write++ = 75; /* +7 Obsolete (75) */
+ *write++ = 0x01; /* +8 output port 0 selection (0001b = channel 0) */
+ *write++ = 0xFF; /* +9 output port 0 volume (0xFF = 0dB atten.) */
+ *write++ = 0x02; /* +10 output port 1 selection (0010b = channel 1) */
+ *write++ = 0xFF; /* +11 output port 1 volume (0xFF = 0dB atten.) */
+ *write++ = 0x00; /* +12 output port 2 selection (none) */
+ *write++ = 0x00; /* +13 output port 2 volume (0x00 = mute) */
+ *write++ = 0x00; /* +14 output port 3 selection (none) */
+ *write++ = 0x00; /* +15 output port 3 volume (0x00 = mute) */
+
+ *plen = write - plen - 1;
+ }
+
+ /* CD-ROM mechanical status MMC-3 Section 6.3.11 table 361 */
+ if (page == 0x2A || page == 0x3F)
+ {
+ valid = 1;
+ *write++ = 0x2A; /* PS|reserved|Page Code */
+ plen = write;
+ *write++ = 0x00; /* Page Length (n - 1) ... Length in bytes of the mode parameters that follow */
+
+ /* MSB | | | | | | | LSB */
+ *write++ = 0x07; /* +2 Reserved |Reserved |DVD-RAM read |DVD-R read |DVD-ROM read | Method 2 | CD-RW read | CD-R read */
+ *write++ = 0x00; /* +3 Reserved |Reserved |DVD-RAM write|DVD-R write | Reserved | Test Write | CD-RW write | CD-R write */
+ *write++ = 0x71; /* +4 Buffer Underrun|Multisession |Mode 2 form 2|Mode 2 form 1|Digital Port 2|Digital Port 1 | Composite | Audio play */
+ *write++ = 0xFF; /* +5 Read code bar |UPC |ISRC |C2 Pointers |R-W deintcorr | R-W supported |CDDA accurate |CDDA support */
+ *write++ = 0x2F; /* +6 Loading mechanism type |Reserved |Eject |Prevent Jumper |Lock state |Lock */
+ /* 0 (0x00) = Caddy
+ * 1 (0x20) = Tray
+ * 2 (0x40) = Popup
+ * 3 (0x60) = Reserved
+ * 4 (0x80) = Changer with indivually changeable discs
+ * 5 (0xA0) = Changer using a magazine mechanism
+ * 6 (0xC0) = Reserved
+ * 6 (0xE0) = Reserved */
+ *write++ = 0x03; /* +7 Reserved |Reserved |R-W in leadin|Side chg cap |S/W slot sel |Changer disc pr|Sep. ch. mute |Sep. volume levels */
+
+ x = 176 * 8; /* +8 maximum speed supported in kB: 8X (obsolete in MMC-3) */
+ *write++ = x >> 8;
+ *write++ = x;
+
+ x = 256; /* +10 Number of volume levels supported */
+ *write++ = x >> 8;
+ *write++ = x;
+
+ x = 6 * 256; /* +12 buffer size supported by drive in kB */
+ *write++ = x >> 8;
+ *write++ = x;
+
+ x = 176 * 8; /* +14 current read speed selected in kB: 8X (obsolete in MMC-3) */
+ *write++ = x >> 8;
+ *write++ = x;
+
+ *plen = write - plen - 1;
+ }
+
+ if (!valid)
+ {
+ *write++ = page; /* PS|reserved|Page Code */
+ *write++ = 0x06; /* Page Length (n - 1) ... Length in bytes of the mode parameters that follow */
+
+ memset(write, 0, 6); write += 6;
+ printf("WARNING: MODE SENSE on page 0x%02x not supported\n", page);
+ }
+
+ /* mode param header, data length */
+ x = (uint32_t)(write - ide_buf) - 2;
+ ide_buf[0] = (unsigned char)(x >> 8u);
+ ide_buf[1] = (unsigned char)x;
+
+ hexdump(ide_buf, x + 2);
+ return x + 2;
+}
+
+static int get_subchan(drive_t *drv, unsigned char& attr, unsigned char& track_num, unsigned char& index, TMSF& relative_msf, TMSF& absolute_msf)
+{
+ attr = 0;
+ track_num = 1;
+ index = 1;
+ relative_msf.min = relative_msf.fr = 0; relative_msf.sec = 2;
+ absolute_msf.min = absolute_msf.fr = 0; absolute_msf.sec = 2;
+
+ if (drv->play_start_lba == 0xFFFFFFFF) return 0;
+
+ //TODO: use current play position when audio playback will be implemented
+ uint32_t cur_pos = drv->play_start_lba;
+
+ for (int i = 0; i < drv->track_cnt; i++)
+ {
+ if (cur_pos >= drv->track[i].start && cur_pos < (drv->track[i].start + drv->track[i].length))
+ {
+ track_num = drv->track[i].number;
+ attr = drv->track[i].attr;
+ absolute_msf = frames_to_msf(cur_pos + REDBOOK_FRAME_PADDING);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int read_subchannel(drive_t *drv, uint8_t* cmdbuf)
+{
+ unsigned char paramList = cmdbuf[3];
+ unsigned char attr, track, index;
+ bool SUBQ = !!(cmdbuf[2] & 0x40);
+ bool TIME = !!(cmdbuf[1] & 2);
+ unsigned char *write;
+ TMSF rel, abs;
+
+ if (paramList == 0 || paramList > 3)
+ {
+ printf("ATAPI READ SUBCHANNEL unknown param list\n");
+ memset(ide_buf, 0, 8);
+ return 8;
+ }
+ else if (paramList == 2)
+ {
+ printf("ATAPI READ SUBCHANNEL Media Catalog Number not supported\n");
+ memset(ide_buf, 0, 8);
+ return 8;
+ }
+ else if (paramList == 3) {
+ printf("ATAPI READ SUBCHANNEL ISRC not supported\n");
+ memset(ide_buf, 0, 8);
+ return 8;
+ }
+
+ /* get current subchannel position */
+ if (!get_subchan(drv, attr, track, index, rel, abs))
+ {
+ printf("ATAPI READ SUBCHANNEL unable to read current pos\n");
+ memset(ide_buf, 0, 8);
+ return 8;
+ }
+
+ memset(ide_buf, 0, 8);
+ write = ide_buf;
+ *write++ = 0x00;
+ *write++ = (!drv->playing) ? 0x13 : drv->paused ? 0x12 : 0x11; /* AUDIO STATUS */
+ *write++ = 0x00; /* SUBCHANNEL DATA LENGTH */
+ *write++ = 0x00;
+
+ if (SUBQ)
+ {
+ *write++ = 0x01; /* subchannel data format code */
+ *write++ = (attr >> 4) | 0x10; /* ADR/CONTROL */
+ *write++ = track;
+ *write++ = index;
+ if (TIME)
+ {
+ *write++ = 0x00;
+ *write++ = abs.min;
+ *write++ = abs.sec;
+ *write++ = abs.fr;
+ *write++ = 0x00;
+ *write++ = rel.min;
+ *write++ = rel.sec;
+ *write++ = rel.fr;
+ }
+ else
+ {
+ uint32_t sec;
+
+ sec = (abs.min * 60u * 75u) + (abs.sec * 75u) + abs.fr - 150u;
+ *write++ = (unsigned char)(sec >> 24u);
+ *write++ = (unsigned char)(sec >> 16u);
+ *write++ = (unsigned char)(sec >> 8u);
+ *write++ = (unsigned char)(sec >> 0u);
+
+ sec = (rel.min * 60u * 75u) + (rel.sec * 75u) + rel.fr - 150u;
+ *write++ = (unsigned char)(sec >> 24u);
+ *write++ = (unsigned char)(sec >> 16u);
+ *write++ = (unsigned char)(sec >> 8u);
+ *write++ = (unsigned char)(sec >> 0u);
+ }
+ }
+
+ unsigned int x = (unsigned int)(write - ide_buf) - 4;
+ ide_buf[2] = x >> 8;
+ ide_buf[3] = x;
+
+ dbg_hexdump(ide_buf, write - ide_buf);
+ return write - ide_buf;
+}
+
+static void pkt_send(ide_config *ide, void *data, uint16_t size)
+{
+ ide_send_data(data, (size + 3) / 4);
+
+ ide->regs.pkt_io_size = (size+1)/2;
+ ide->regs.cylinder = size;
+ ide->regs.sector_count = 2;
+
+ ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ;
+ ide_set_regs(ide);
+ ide->state = IDE_STATE_WAIT_PKT_RD;
+}
+
+static void read_cd_sectors(ide_config *ide, int cnt)
+{
+ drive_t *drv = &ide->drive[ide->regs.drv];
+ uint32_t sz = drv->track[drv->data_num].sectorSize;
+
+ if (sz == 2048)
+ {
+ if (!ide->null) ide->null = (FileReadAdv(drv->f, ide_buf, cnt * sz, -1) <= 0);
+ if (ide->null) memset(ide_buf, 0, cnt * sz);
+ return;
+ }
+
+ uint32_t pre = drv->track[drv->data_num].mode2 ? 24 : 16;
+ uint32_t post = sz - pre - 2048;
+ uint32_t off = 0;
+
+ while (cnt--)
+ {
+ if (!ide->null) ide->null = !FileSeek(drv->f, pre, SEEK_CUR);
+ if (!ide->null) ide->null = (FileReadAdv(drv->f, ide_buf + off, 2048, -1) <= 0);
+ if (ide->null) memset(ide_buf + off, 0, 2048);
+ if (!ide->null) ide->null = !FileSeek(drv->f, post, SEEK_CUR);
+ off += 2048;
+ }
+}
+
+void cdrom_read(ide_config *ide)
+{
+ uint32_t cnt = ide->regs.pkt_cnt;
+ if ((cnt * 4) > ide_io_max_size) cnt = ide_io_max_size / 4;
+
+ while ((cnt * 2048) > ide->regs.pkt_size_limit)
+ {
+ if (cnt <= 1) break;
+ cnt--;
+ }
+
+ if (cnt != ide->regs.pkt_cnt)
+ {
+ dbg_printf("** partial CD read\n");
+ }
+
+ if (ide->state == IDE_STATE_INIT_RW)
+ {
+ uint32_t pos = ide->regs.pkt_lba * ide->drive[ide->regs.drv].track[ide->drive[ide->regs.drv].data_num].sectorSize;
+
+ //printf("Read from pos: %d\n", pos);
+ ide->null = (FileSeek(ide->drive[ide->regs.drv].f, pos, SEEK_SET) < 0);
+ }
+
+ read_cd_sectors(ide, cnt);
+
+ dbg_printf("\nsector:\n");
+ dbg_hexdump(ide_buf, 512, 0);
+
+ ide->regs.pkt_cnt -= cnt;
+ pkt_send(ide, ide_buf, cnt * 2048);
+}
+
+static int cd_inquiry(uint8_t maxlen)
+{
+ static const char vendor[] = "MiSTer ";
+ static const char product[] = "CDROM ";
+
+ memset(ide_buf, 0, 47);
+ ide_buf[0] = (0 << 5) | 5; /* Peripheral qualifier=0 device type=5 (CDROM) */
+ ide_buf[1] = 0x80; /* RMB=1 removable media */
+ ide_buf[2] = 0x00; /* ANSI version */
+ ide_buf[3] = 0x21;
+ ide_buf[4] = maxlen - 5; /* additional length */
+
+ for (int i = 0; i < 8; i++) ide_buf[i + 8] = (unsigned char)vendor[i];
+ for (int i = 0; i < 16; i++) ide_buf[i + 16] = (unsigned char)product[i];
+ for (int i = 0; i < 4; i++) ide_buf[i + 32] = ' ';
+ for (int i = 0; i < 11; i++) ide_buf[i + 36] = ' ';
+
+ hexdump(ide_buf, maxlen);
+ return maxlen;
+}
+
+static void set_sense(uint8_t SK, uint8_t ASC = 0, uint8_t ASCQ = 0)
+{
+ int len = 18;
+ memset(ide_buf, 0, len);
+
+ ide_buf[0] = 0x70; /* RESPONSE CODE */
+ ide_buf[2] = SK & 0xF; /* SENSE KEY */
+ ide_buf[7] = len - 18; /* additional sense length */
+ ide_buf[12] = ASC;
+ ide_buf[13] = ASCQ;
+}
+
+static int get_sense(drive_t *drv)
+{
+ switch (drv->load_state)
+ {
+ case 3:
+ set_sense(2, 0x3A);
+ break;
+
+ case 2:
+ set_sense(2, 4, 1);
+ drv->load_state--;
+ break;
+
+ case 1:
+ set_sense(2, 28, 0);
+ drv->load_state--;
+ break;
+
+ default:
+ set_sense(0);
+ break;
+ }
+
+ dbg_hexdump(ide_buf, 18);
+ return 18;
+}
+
+static void pause_resume(drive_t *drv, uint8_t *cmdbuf)
+{
+ bool resume = !!(cmdbuf[8] & 1);
+ if(drv->playing) drv->paused = !resume;
+}
+
+static void play_audio_msf(drive_t *drv, uint8_t *cmdbuf)
+{
+ if (cmdbuf[3] == 0xFF && cmdbuf[4] == 0xFF && cmdbuf[5] == 0xFF)
+ {
+ drv->play_start_lba = 0xFFFFFFFF;
+ }
+ else
+ {
+ drv->play_start_lba = (cmdbuf[3] * 60u * 75u) + (cmdbuf[4] * 75u) + cmdbuf[5];
+
+ if (drv->play_start_lba >= 150u) drv->play_start_lba -= 150u; /* LBA sector 0 == M:S:F sector 0:2:0 */
+ else drv->play_end_lba = 0;
+ }
+
+ if (cmdbuf[6] == 0xFF && cmdbuf[7] == 0xFF && cmdbuf[8] == 0xFF)
+ {
+ drv->play_end_lba = 0xFFFFFFFF;
+ }
+ else
+ {
+ drv->play_end_lba = (cmdbuf[6] * 60u * 75u) + (cmdbuf[7] * 75u) + cmdbuf[8];
+ if (drv->play_end_lba >= 150u) drv->play_end_lba -= 150u; /* LBA sector 0 == M:S:F sector 0:2:0 */
+ else drv->play_end_lba = 0;
+ }
+
+ if (drv->play_start_lba == drv->play_end_lba)
+ {
+ drv->playing = 0;
+ drv->paused = 0;
+ return;
+ }
+
+ /* LBA 0xFFFFFFFF means start playing wherever the optics of the CD sit */
+ if (drv->play_start_lba != 0xFFFFFFFF)
+ {
+ drv->playing = 1;
+ drv->paused = 0;
+ }
+ else
+ {
+ drv->playing = 0;
+ drv->paused = 1;
+ }
+}
+
+void play_audio10(drive_t *drv, uint8_t *cmdbuf)
+{
+ uint16_t play_length;
+
+ drv->play_start_lba = ((uint32_t)cmdbuf[2] << 24) + ((uint32_t)cmdbuf[3] << 16) + ((uint32_t)cmdbuf[4] << 8) + ((uint32_t)cmdbuf[5] << 0);
+ play_length = ((uint16_t)cmdbuf[7] << 8) + ((uint16_t)cmdbuf[8] << 0);
+ drv->play_end_lba = drv->play_start_lba + play_length;
+
+ if (play_length == 0)
+ {
+ drv->playing = 0;
+ drv->paused = 0;
+ return;
+ }
+
+ /* LBA 0xFFFFFFFF means start playing wherever the optics of the CD sit */
+ if (drv->play_start_lba != 0xFFFFFFFF)
+ {
+ drv->playing = 1;
+ drv->paused = 0;
+ }
+ else
+ {
+ drv->playing = 0;
+ drv->paused = 1;
+ }
+}
+
+static void cdrom_nodisk(ide_config *ide)
+{
+ drive_t *drv = &ide->drive[ide->regs.drv];
+ drv->last_load_state = drv->load_state;
+ if (drv->load_state && drv->load_state < 3) drv->load_state--;
+
+ cdrom_reply(ide, CD_ERR_NO_DISK);
+}
+
+void cdrom_handle_pkt(ide_config *ide)
+{
+ drive_t *drv = &ide->drive[ide->regs.drv];
+ uint8_t cmdbuf[16];
+
+ ide_recv_data(cmdbuf, 3);
+ ide_reset_buf();
+ dbg_hexdump(cmdbuf, 12, 0);
+
+ ide->regs.pkt_cnt = 0;
+ int err = 0;
+
+ switch (cmdbuf[0])
+ {
+ case 0xA8: // read(12) sectors
+ case 0x28: // read(10) sectors
+ dbg_printf("** Read Sectors\n");
+ //hexdump(cmdbuf, 12, 0);
+ ide->regs.pkt_cnt = (cmdbuf[0] == 0x28) ? ((cmdbuf[7] << 8) | cmdbuf[8]) : ((cmdbuf[6] << 24) | (cmdbuf[7] << 16) | (cmdbuf[8] << 8) | cmdbuf[9]);
+ ide->regs.pkt_lba = ((cmdbuf[2] << 24) | (cmdbuf[3] << 16) | (cmdbuf[4] << 8) | cmdbuf[5]);
+ if (!ide->regs.pkt_cnt)
+ {
+ dbg_printf(" length 0 is not a error.\n");
+ cdrom_reply(ide, 0);
+ break;
+ }
+
+ dbg_printf("** par: lba = %d, cnt = %d, load_state = %d\n", ide->regs.pkt_lba, ide->regs.pkt_cnt, drv->load_state);
+ ide->state = IDE_STATE_INIT_RW;
+ if(!drv->load_state) cdrom_read(ide);
+ else cdrom_nodisk(ide);
+ break;
+
+ case 0x25: // read capacity
+ //printf("** Read Capacity\n");
+ if (!drv->load_state)
+ {
+ uint32_t tmp = drv->f->size / 2048;;
+ ide_buf[0] = tmp >> 24;
+ ide_buf[1] = tmp >> 16;
+ ide_buf[2] = tmp >> 8;
+ ide_buf[3] = tmp;
+
+ tmp = 2048;
+ ide_buf[4] = tmp >> 24;
+ ide_buf[5] = tmp >> 16;
+ ide_buf[6] = tmp >> 8;
+ ide_buf[7] = tmp;
+ //hexdump(buf, 8, 0);
+ pkt_send(ide, ide_buf, 8);
+ }
+ else cdrom_nodisk(ide);
+ break;
+
+ case 0x2B: // seek
+ drv->playing = 0;
+ drv->paused = 0;
+ cdrom_reply(ide, 0);
+ break;
+
+ case 0x1E: // lock the cd door - doing nothing.
+ cdrom_reply(ide, 0);
+ break;
+
+ case 0x5A: // mode sense
+ pkt_send(ide, ide_buf, mode_sense(cmdbuf[2]));
+ break;
+
+ case 0x42: // read sub
+ dbg_printf("read sub:\n");
+ pkt_send(ide, ide_buf, read_subchannel(drv, cmdbuf));
+ break;
+
+ case 0x43: // read TOC
+ pkt_send(ide, ide_buf, read_toc(drv, cmdbuf));
+ break;
+
+ case 0x12: // inquiry
+ pkt_send(ide, ide_buf, cd_inquiry(cmdbuf[4]));
+ break;
+
+ case 0x03: // mode sense
+ dbg_printf("get sense:\n");
+ pkt_send(ide, ide_buf, get_sense(drv));
+ break;
+
+ case 0x55: // mode select
+ printf("mode select\n");
+ ide->regs.cylinder = (cmdbuf[7] << 8) | cmdbuf[8];
+ if (ide->regs.cylinder > 512) ide->regs.cylinder = 512;
+ ide->regs.pkt_io_size = (ide->regs.cylinder + 1) / 2;
+ ide->regs.sector_count = 0;
+ ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ;
+ ide_set_regs(ide);
+ ide->state = IDE_STATE_WAIT_PKT_MODE;
+ break;
+
+ case 0x00: // test unit ready
+ if (!drv->load_state) cdrom_reply(ide, 0);
+ else cdrom_nodisk(ide);
+ break;
+
+ case 0x45: // play lba
+ printf("CD PLAY AUDIO(10)\n");
+ play_audio10(drv, cmdbuf);
+ cdrom_reply(ide, 0);
+ break;
+
+ case 0x47: // play msf
+ printf("CD PLAY AUDIO MSF\n");
+ play_audio_msf(drv, cmdbuf);
+ cdrom_reply(ide, 0);
+ break;
+
+ case 0x4B: // pause/resume
+ printf("CD PAUSE/RESUME\n");
+ pause_resume(drv, cmdbuf);
+ cdrom_reply(ide, 0);
+ break;
+
+ default:
+ err = 1;
+ break;
+ }
+
+ if (err)
+ {
+ printf("(!) Error in packet command %02X\n", cmdbuf[0]);
+ hexdump(cmdbuf, 12, 0);
+ cdrom_reply(ide, ATA_ERR_ABRT);
+ }
+}
+
+int cdrom_handle_cmd(ide_config *ide)
+{
+ uint8_t drv;
+
+ switch (ide->regs.cmd)
+ {
+ case 0xA1: // identify packet
+ //print_regs(&ide->regs);
+ dbg_printf("identify packet\n");
+ ide_send_data(ide->drive[ide->regs.drv].id, 128);
+ drv = ide->regs.drv;
+ memset(&ide->regs, 0, sizeof(ide->regs));
+ ide->regs.drv = drv;
+ ide->regs.pkt_io_size = 256;
+ ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ;
+ ide_set_regs(ide);
+ ide->state = IDE_STATE_WAIT_END;
+ break;
+
+ case 0xEC: // identify (fail)
+ dbg_printf("identify (CD)\n");
+ ide->regs.sector = 1;
+ ide->regs.sector_count = 1;
+ ide->regs.cylinder = 0xEB14;
+ ide->regs.head = 0;
+ ide->regs.io_size = 0;
+ return 1;
+
+ case 0xA0: // packet
+ dbg_printf("cmd A0: %02X\n", ide->regs.features);
+ if (ide->regs.features & 1)
+ {
+ dbg_printf("Cancel A0 DMA transfer\n");
+ return 1;
+ }
+ ide->regs.pkt_size_limit = ide->regs.cylinder;
+ if (!ide->regs.pkt_size_limit) ide->regs.pkt_size_limit = ide_io_max_size * 512;
+ ide->regs.pkt_io_size = 6;
+ ide->regs.sector_count = 1;
+ ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ; // | ATA_STATUS_IRQ;
+ ide_set_regs(ide);
+ ide->state = IDE_STATE_WAIT_PKT_CMD;
+ break;
+
+ case 0x08: // reset
+ dbg_printf("cmd 08\n");
+ ide->drive[ide->regs.drv].playing = 0;
+ ide->drive[ide->regs.drv].paused = 0;
+ ide->regs.sector = 1;
+ ide->regs.sector_count = 1;
+ ide->regs.cylinder = 0xEB14;
+ ide->regs.head = 0;
+ ide->regs.io_size = 0;
+ ide->regs.status = ATA_STATUS_RDY;
+ ide_set_regs(ide);
+ break;
+
+ case 0x00: // nop
+ dbg_printf("cmd 00\n");
+ return 1; // must always fail
+
+ default:
+ printf("(!) Unsupported command\n");
+ ide_print_regs(&ide->regs);
+ return 1;
+ }
+
+ return 0;
+}
+
+void cdrom_reply(ide_config *ide, uint8_t error)
+{
+ ide->state = IDE_STATE_IDLE;
+ ide->regs.sector_count = 3;
+ ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_IRQ | (error ? ATA_STATUS_ERR : 0);
+ ide->regs.error = error;
+ ide_set_regs(ide);
+}
+
+const char* cdrom_parse(uint32_t num, const char *filename)
+{
+ const char *res = 0;
+
+ if (strlen(filename))
+ {
+ const char *path = getFullPath(filename);
+ int drv = num & 1;
+ num >>= 1;
+ res = load_cue_file(&ide_inst[num].drive[drv], path);
+ if (!res) res = load_iso_file(&ide_inst[num].drive[drv], path);
+ }
+ return res;
+}
diff --git a/support/x86/x86_cdrom.h b/support/x86/x86_cdrom.h
new file mode 100644
index 0000000..d2250c4
--- /dev/null
+++ b/support/x86/x86_cdrom.h
@@ -0,0 +1,11 @@
+#ifndef X86_CDROM_H
+#define X86_CDROM_H
+
+int cdrom_handle_cmd(ide_config *ide);
+void cdrom_handle_pkt(ide_config *ide);
+void cdrom_reply(ide_config *ide, uint8_t error);
+void cdrom_read(ide_config *ide);
+
+const char* cdrom_parse(uint32_t num, const char *filename);
+
+#endif
diff --git a/support/x86/x86_ide.cpp b/support/x86/x86_ide.cpp
index ea2a912..f8e3aac 100644
--- a/support/x86/x86_ide.cpp
+++ b/support/x86/x86_ide.cpp
@@ -18,43 +18,12 @@
#include "../../file_io.h"
#include "../../hardware.h"
#include "x86.h"
-
-#define ATA_STATUS_BSY 0x80 // busy
-#define ATA_STATUS_RDY 0x40 // ready
-#define ATA_STATUS_DF 0x20 // device fault
-#define ATA_STATUS_WFT 0x20 // write fault (old name)
-#define ATA_STATUS_SKC 0x10 // seek complete
-#define ATA_STATUS_SERV 0x10 // service
-#define ATA_STATUS_DRQ 0x08 // data request
-#define ATA_STATUS_IRQ 0x04 // rise IRQ
-#define ATA_STATUS_IDX 0x02 // index
-#define ATA_STATUS_ERR 0x01 // error (ATA)
-#define ATA_STATUS_CHK 0x01 // check (ATAPI)
-
-#define ATA_ERR_ICRC 0x80 // ATA Ultra DMA bad CRC
-#define ATA_ERR_BBK 0x80 // ATA bad block
-#define ATA_ERR_UNC 0x40 // ATA uncorrected error
-#define ATA_ERR_MC 0x20 // ATA media change
-#define ATA_ERR_IDNF 0x10 // ATA id not found
-#define ATA_ERR_MCR 0x08 // ATA media change request
-#define ATA_ERR_ABRT 0x04 // ATA command aborted
-#define ATA_ERR_NTK0 0x02 // ATA track 0 not found
-#define ATA_ERR_NDAM 0x01 // ATA address mark not found
-
-#define IDE_STATE_IDLE 0
-#define IDE_STATE_RESET 1
-#define IDE_STATE_INIT_RW 2
-#define IDE_STATE_WAIT_RD 3
-#define IDE_STATE_WAIT_WR 4
-#define IDE_STATE_WAIT_END 5
-#define IDE_STATE_WAIT_PKT_CMD 6
-#define IDE_STATE_WAIT_PKT_RD 7
-#define IDE_STATE_WAIT_PKT_END 8
-#define IDE_STATE_WAIT_PKT_MODE 9
+#include "x86_ide.h"
+#include "x86_cdrom.h"
#if 0
#define dbg_printf printf
- #define dbg_print_regs print_regs
+ #define dbg_print_regs ide_print_regs
#define dbg_hexdump hexdump
#else
#define dbg_printf(...) void()
@@ -62,95 +31,17 @@
#define dbg_hexdump(...) void()
#endif
-#if 0
- #define cddbg_printf printf
- #define cddbg_print_regs print_regs
- #define cddbg_hexdump hexdump
-#else
- #define cddbg_printf(...) void()
- #define cddbg_print_regs void
- #define cddbg_hexdump(...) void()
-#endif
-
#define IOWR(base, reg, value, ver) x86_dma_set((base) + ((ver) ? (reg) : ((reg)<<2)), value)
#define ide_send_data(databuf, size) x86_dma_sendbuf(ide->base + 255, (size), (uint32_t*)(databuf))
#define ide_recv_data(databuf, size) x86_dma_recvbuf(ide->base + 255, (size), (uint32_t*)(databuf))
-#define ide_reset_buf() x86_dma_set(ide->base + 3, 0)
-static const uint32_t io_max_size = 32;
-static uint8_t buf[io_max_size * 512];
+const uint32_t ide_io_max_size = 32;
+uint8_t ide_buf[ide_io_max_size * 512];
-typedef struct
-{
- uint8_t io_done;
- uint8_t features;
- uint8_t sector_count;
- uint8_t sector;
- uint16_t cylinder;
- uint8_t head;
- uint8_t drv;
- uint8_t lba;
- uint8_t cmd;
+ide_config ide_inst[2] = {};
- uint16_t pkt_size_limit;
- uint16_t pkt_io_size;
- uint32_t pkt_lba;
- uint32_t pkt_cnt;
-
- uint8_t io_size;
- uint8_t error;
- uint8_t status;
-} regs_t;
-
-typedef struct
-{
- char filename[1024];
- uint32_t start;
- uint32_t length;
- uint32_t skip;
- uint16_t sectorSize;
- uint8_t attr;
- uint8_t mode2;
- uint8_t number;
-} track_t;
-
-typedef struct
-{
- fileTYPE *f;
- uint32_t hd_cylinders;
- uint32_t hd_heads;
- uint32_t hd_spt;
- uint32_t hd_total_sectors;
- uint8_t present;
-
- uint8_t placeholder;
- uint8_t allow_placeholder;
- uint8_t cd;
- uint8_t load_state;
- uint8_t playing;
- uint8_t paused;
- uint8_t track_cnt;
- uint8_t data_num;
- track_t track[50];
-
- uint16_t id[256];
-} drive_t;
-
-typedef struct
-{
- uint32_t base;
- uint32_t state;
- uint32_t null;
- uint32_t prepcnt;
- regs_t regs;
-
- drive_t drive[2];
-} ide_config;
-
-static ide_config ide_inst[2] = {};
-
-static void print_regs(regs_t *regs)
+void ide_print_regs(regs_t *regs)
{
printf("\nIDE regs:\n");
printf(" io_done: %02X\n", regs->io_done);
@@ -164,7 +55,7 @@ static void print_regs(regs_t *regs)
printf(" command: %02X\n", regs->cmd);
}
-static void get_regs(ide_config *ide)
+void ide_get_regs(ide_config *ide)
{
uint32_t data[3];
x86_dma_recvbuf(ide->base, 3, data);
@@ -186,7 +77,7 @@ static void get_regs(ide_config *ide)
dbg_print_regs(&ide->regs);
}
-static void set_regs(ide_config *ide)
+void ide_set_regs(ide_config *ide)
{
uint32_t data[3];
@@ -206,878 +97,6 @@ static void set_regs(ide_config *ide)
data[2] |= ide->regs.status << 24;
x86_dma_sendbuf(ide->base, 3, data);
-
- if (ide->drive[ide->regs.drv].cd)
- {
- if (ide->regs.status & 1)
- {
- cddbg_printf(" status: %02X, error %02X\n", ide->regs.status, ide->regs.error);
- }
- else
- {
- cddbg_printf(" status: %02X\n", ide->regs.status);
- }
- }
-}
-
-#define BYTES_PER_RAW_REDBOOK_FRAME 2352
-#define BYTES_PER_COOKED_REDBOOK_FRAME 2048
-#define REDBOOK_FRAMES_PER_SECOND 75
-#define REDBOOK_FRAME_PADDING 150
-
-static int CanReadPVD(fileTYPE *file, int sectorSize, int mode2)
-{
- // Initialize our array in the event file->read() doesn't fully write it
- static uint8_t pvd[BYTES_PER_COOKED_REDBOOK_FRAME];
- memset(pvd, 0, sizeof(pvd));
-
- uint32_t seek = 16 * sectorSize; // first vd is located at sector 16
- if (sectorSize == BYTES_PER_RAW_REDBOOK_FRAME && !mode2) seek += 16;
- if (mode2) seek += 24;
- FileSeek(file, seek, SEEK_SET);
- if (!FileReadAdv(file, pvd, BYTES_PER_COOKED_REDBOOK_FRAME)) return 0;
-
- // pvd[0] = descriptor type, pvd[1..5] = standard identifier,
- // pvd[6] = iso version (+8 for High Sierra)
- return ((pvd[0] == 1 && !strncmp((char*)(&pvd[1]), "CD001", 5) && pvd[6] == 1) ||
- (pvd[8] == 1 && !strncmp((char*)(&pvd[9]), "CDROM", 5) && pvd[14] == 1));
-}
-
-static int x86_check_iso_file(fileTYPE *f, uint8_t *mode2, uint16_t *sectorSize)
-{
- if (CanReadPVD(f, BYTES_PER_COOKED_REDBOOK_FRAME, false))
- {
- if (sectorSize) *sectorSize = BYTES_PER_COOKED_REDBOOK_FRAME;
- if (mode2) *mode2 = 0;
- return 1;
- }
- else if (CanReadPVD(f, BYTES_PER_RAW_REDBOOK_FRAME, false))
- {
- if (sectorSize) *sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
- if (mode2) *mode2 = 0;
- return 1;
- }
- else if (CanReadPVD(f, 2336, true))
- {
- if (sectorSize) *sectorSize = 2336;
- if (mode2) *mode2 = 1;
- return 1;
- }
- else if (CanReadPVD(f, BYTES_PER_RAW_REDBOOK_FRAME, true))
- {
- if (sectorSize) *sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
- if (mode2) *mode2 = 1;
- return 1;
- }
-
- if (sectorSize) *sectorSize = 0;
- if (mode2) *mode2 = 0;
- return 0;
-}
-
-static const char * LoadIsoFile(drive_t *drv, const char* filename)
-{
- fileTYPE f;
- memset(drv->track, 0, sizeof(drv->track));
- drv->track_cnt = 0;
-
- strcpy(drv->track[0].filename, filename);
- if (!FileOpen(&f, filename))
- {
- printf("No CD!\n");
- return 0;
- }
-
- if(!x86_check_iso_file(&f, &drv->track[0].mode2, &drv->track[0].sectorSize))
- {
- printf("Fail to parse ISO!\n");
- FileClose(&f);
- return 0;
- }
-
- drv->track[0].attr = 0x40; //data track
- drv->track[0].length = f.size / drv->track[0].sectorSize;
- drv->track[0].number = 1;
-
- FileClose(&f);
-
- // lead-out track (track 2)
- drv->track[1].start = drv->track[0].length;
- drv->track[2].number = 2;
-
- printf("ISO: mode2 = %d, sectorSize = %d, sectors = %d\n", drv->track[0].mode2, drv->track[0].sectorSize, drv->track[0].length);
- drv->track_cnt = 2;
-
- drv->data_num = 0;
- return drv->track[0].filename;
-}
-
-#define CD_FPS 75
-#define MSF_TO_FRAMES(M, S, F) ((M)*60*CD_FPS+(S)*CD_FPS+(F))
-
-typedef struct
-{
- unsigned char min;
- unsigned char sec;
- unsigned char fr;
-} TMSF;
-
-static int GetCueKeyword(std::string &keyword, std::istream &in)
-{
- in >> keyword;
- for (uint32_t i = 0; i < keyword.size(); i++)
- {
- keyword[i] = static_cast(toupper(keyword[i]));
- }
- return 1;
-}
-
-static int GetCueFrame(uint32_t &frames, std::istream &in)
-{
- std::string msf;
- in >> msf;
- TMSF tmp = { 0, 0, 0 };
- int success = sscanf(msf.c_str(), "%hhu:%hhu:%hhu", &tmp.min, &tmp.sec, &tmp.fr) == 3;
- frames = (int)MSF_TO_FRAMES(tmp.min, tmp.sec, tmp.fr);
- return success;
-}
-
-static int GetCueString(std::string &str, std::istream &in)
-{
- int pos = (int)in.tellg();
- in >> str;
- if (str[0] == '\"') {
- if (str[str.size() - 1] == '\"')
- {
- str.assign(str, 1, str.size() - 2);
- }
- else
- {
- in.seekg(pos, std::ios::beg);
- static char buffer[1024];
- in.getline(buffer, 1024, '\"'); // skip
- in.getline(buffer, 1024, '\"');
- str = buffer;
- }
- }
- return 1;
-}
-
-static int AddTrack(drive_t *drv, track_t *curr, uint32_t &shift, const int32_t prestart, uint32_t &totalPregap, uint32_t currPregap)
-{
- uint32_t skip = 0;
- if (drv->track_cnt >= sizeof(drv->track) / sizeof(drv->track[0]))
- {
- printf("CDROM: too many tracks(%d)\n", drv->track_cnt);
- return 0;
- }
-
- // frames between index 0(prestart) and 1(curr.start) must be skipped
- if (prestart >= 0)
- {
- if (prestart > static_cast(curr->start))
- {
- printf("CDROM: AddTrack => prestart %d cannot be > curr.start %u\n", prestart, curr->start);
- return 0;
- }
- skip = static_cast(static_cast(curr->start) - prestart);
- }
-
- // Add the first track, if our vector is empty
- if (!drv->track_cnt)
- {
- //assertm(curr.number == 1, "The first track must be labelled number 1 [BUG!]");
- curr->skip = skip * curr->sectorSize;
- curr->start += currPregap;
- totalPregap = currPregap;
-
- memcpy(&drv->track[drv->track_cnt], curr, sizeof(track_t));
- drv->track_cnt++;
- return 1;
- }
-
- // Guard against undefined behavior in subsequent tracks.back() call
- //assert(!tracks.empty());
- track_t *prev = &drv->track[drv->track_cnt - 1];
-
- // current track consumes data from the same file as the previous
- if (!strcmp(prev->filename, curr->filename))
- {
- curr->start += shift;
- if (!prev->length)
- {
- prev->length = curr->start + totalPregap - prev->start - skip;
- }
- curr->skip += prev->skip + prev->length * prev->sectorSize + skip * curr->sectorSize;
- totalPregap += currPregap;
- curr->start += totalPregap;
- // current track uses a different file as the previous track
- }
- else
- {
- uint32_t size = FileLoad(prev->filename, 0, 0);
- const uint32_t tmp = size - prev->skip;
- prev->length = tmp / prev->sectorSize;
-
- if (tmp % prev->sectorSize != 0) prev->length++; // padding
-
- curr->start += prev->start + prev->length + currPregap;
- curr->skip = skip * curr->sectorSize;
- shift += prev->start + prev->length;
- totalPregap = currPregap;
- }
-
- // error checks
- if (curr->number <= 1
- || prev->number + 1 != curr->number
- || curr->start < prev->start + prev->length) {
- printf("AddTrack: failed consistency checks\n"
- "\tcurr.number (%d) <= 1\n"
- "\tprev.number (%d) + 1 != curr.number (%d)\n"
- "\tcurr.start (%d) < prev.start (%d) + prev.length (%d)\n",
- curr->number, prev->number, curr->number,
- curr->start, prev->start, prev->length);
- return 0;
- }
-
- memcpy(&drv->track[drv->track_cnt], curr, sizeof(track_t));
- drv->track_cnt++;
- return 1;
-}
-
-static std::string dirname(char * file)
-{
- char * sep = strrchr(file, '/');
- if (!sep) return "";
-
- int len = (int)(sep - file);
- static char tmp[1024];
- strncpy(tmp, file, len + 1);
- tmp[len + 1] = 0;
- return tmp;
-}
-
-static int GetRealFileName(std::string &filename, std::string &pathname)
-{
- // check if file exists
- struct stat test;
- if (stat(filename.c_str(), &test) == 0)
- {
- return 1;
- }
-
- // check if file with path relative to cue file exists
- std::string tmpstr(pathname + "/" + filename);
- if (stat(tmpstr.c_str(), &test) == 0)
- {
- filename = tmpstr;
- return 1;
- }
-
- return 0;
-}
-
-static const char * LoadCueSheet(drive_t *drv, const char *cuefile)
-{
- printf("LoadCueSheet(%s)\n", cuefile);
-
- memset(drv->track, 0, sizeof(drv->track));
- drv->track_cnt = 0;
-
- track_t track = {};
- uint32_t shift = 0;
- uint32_t currPregap = 0;
- uint32_t totalPregap = 0;
- int32_t prestart = -1;
- int track_number;
- int success;
- int canAddTrack = 0;
- static char tmp[1024];
-
- strcpy(tmp, cuefile);
- std::string pathname(dirname(tmp));
-
- std::ifstream in;
- in.open(cuefile, std::ios::in);
- if (in.fail()) return 0;
-
- while (!in.eof())
- {
- // get next line
- char buf[256];
- in.getline(buf, sizeof(buf));
- if (in.fail() && !in.eof())
- {
- return 0; // probably a binary file
- }
-
- std::istringstream line(buf);
-
- std::string command;
- GetCueKeyword(command, line);
-
- //printf("command: %s\n", command.c_str());
-
- if (command == "TRACK")
- {
- if (canAddTrack) success = AddTrack(drv, &track, shift, prestart, totalPregap, currPregap);
- else success = 1;
-
- track.start = 0;
- track.skip = 0;
- currPregap = 0;
- prestart = -1;
-
- line >> track_number; // (cin) read into a true int first
-
- track.number = static_cast(track_number);
-
- std::string type;
- GetCueKeyword(type, line);
-
- //printf(" type: %s\n", type.c_str());
-
- if (type == "AUDIO")
- {
- track.sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
- track.attr = 0;
- track.mode2 = false;
- }
- else if (type == "MODE1/2048")
- {
- track.sectorSize = BYTES_PER_COOKED_REDBOOK_FRAME;
- track.attr = 0x40;
- track.mode2 = false;
- }
- else if (type == "MODE1/2352")
- {
- track.sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
- track.attr = 0x40;
- track.mode2 = false;
- }
- else if (type == "MODE2/2336")
- {
- track.sectorSize = 2336;
- track.attr = 0x40;
- track.mode2 = true;
- }
- else if (type == "MODE2/2352")
- {
- track.sectorSize = BYTES_PER_RAW_REDBOOK_FRAME;
- track.attr = 0x40;
- track.mode2 = true;
- }
- else success = 0;
-
- canAddTrack = true;
- }
- else if (command == "INDEX")
- {
- int index;
- line >> index;
- uint32_t frame;
- success = GetCueFrame(frame, line);
-
- if (index == 1) track.start = frame;
- else if (index == 0) prestart = static_cast(frame);
- // ignore other indices
- }
- else if (command == "FILE")
- {
- if (canAddTrack) success = AddTrack(drv, &track, shift, prestart, totalPregap, currPregap);
- else success = 1;
- canAddTrack = 0;
-
- std::string filename;
- GetCueString(filename, line);
- GetRealFileName(filename, pathname);
- std::string type;
- GetCueKeyword(type, line);
- strcpy(track.filename, filename.c_str());
- }
- else if (command == "PREGAP") success = GetCueFrame(currPregap, line);
- // ignored commands
- else if (command == "CATALOG" || command == "CDTEXTFILE" || command == "FLAGS" || command == "ISRC" ||
- command == "PERFORMER" || command == "POSTGAP" || command == "REM" ||
- command == "SONGWRITER" || command == "TITLE" || command.empty())
- {
- success = 1;
- }
- // failure
- else
- {
- success = 0;
- }
-
- if (!success)
- {
- return 0;
- }
- }
-
- // add last track
- if (!AddTrack(drv, &track, shift, prestart, totalPregap, currPregap))
- {
- return 0;
- }
-
- // add lead-out track
- track.number++;
- track.attr = 0;//sync with load iso
- track.start = 0;
- track.length = 0;
-
- if (!AddTrack(drv, &track, shift, -1, totalPregap, 0))
- {
- return 0;
- }
-
- for (uint8_t i = 0; i < drv->track_cnt; i++)
- {
- if (drv->track[i].attr == 0x40)
- {
- drv->data_num = i;
- return drv->track[i].filename;
- }
- }
-
- return 0;
-}
-
-inline TMSF frames_to_msf(uint32_t frames)
-{
- TMSF msf = { 0, 0, 0 };
- msf.fr = frames % REDBOOK_FRAMES_PER_SECOND;
- frames /= REDBOOK_FRAMES_PER_SECOND;
- msf.sec = frames % 60;
- frames /= 60;
- msf.min = static_cast(frames);
- return msf;
-}
-
-static int GetCDTracks(drive_t *drv, int& start_track_num, int& end_track_num, TMSF& lead_out_msf)
-{
- if (drv->track_cnt < 2 || !drv->track[0].length) return 0;
-
- start_track_num = drv->track[0].number;
- end_track_num = drv->track[drv->track_cnt - 2].number;
- lead_out_msf = frames_to_msf(drv->track[drv->track_cnt - 1].start + REDBOOK_FRAME_PADDING);
- return 1;
-}
-
-static int GetCDTrackInfo(drive_t *drv, int requested_track_num, TMSF& start_msf, unsigned char& attr)
-{
- if (drv->track_cnt < 2 || requested_track_num < 1 || requested_track_num >= drv->track_cnt)
- {
- return 0;
- }
-
- start_msf = frames_to_msf(drv->track[requested_track_num - 1].start + REDBOOK_FRAME_PADDING);
- attr = drv->track[requested_track_num - 1].attr;
- return 1;
-}
-
-static int read_toc(drive_t *drv, uint8_t *cmdbuf)
-{
- /* NTS: The SCSI MMC standards say we're allowed to indicate the return data
- * is longer than it's allocation length. But here's the thing: some MS-DOS
- * CD-ROM drivers will ask for the TOC but only provide enough room for one
- * entry (OAKCDROM.SYS) and if we signal more data than it's buffer, it will
- * reject our response and render the CD-ROM drive inaccessible. So to make
- * this emulation work, we have to cut our response short to the driver's
- * allocation length */
- unsigned int AllocationLength = ((unsigned int)cmdbuf[7] << 8) + cmdbuf[8];
- unsigned char Format = cmdbuf[2] & 0xF;
- unsigned char Track = cmdbuf[6];
- bool TIME = !!(cmdbuf[1] & 2);
- unsigned char *write;
- int first, last, track;
- TMSF leadOut;
-
- printf("read_toc in:\n");
- hexdump(cmdbuf, 12);
-
- memset(buf, 0, 8);
-
- if (!GetCDTracks(drv, first, last, leadOut))
- {
- printf("WARNING: ATAPI READ TOC failed to get track info\n");
- return 8;
- }
-
- /* start 2 bytes out. we'll fill in the data length later */
- write = buf + 2;
-
- if (Format == 1) /* Read multisession info */
- {
- unsigned char attr;
- TMSF start;
-
- *write++ = (unsigned char)1; /* @+2 first complete session */
- *write++ = (unsigned char)1; /* @+3 last complete session */
-
- if (!GetCDTrackInfo(drv, first, start, attr))
- {
- printf("WARNING: ATAPI READ TOC unable to read track %u information\n", first);
- attr = 0x41; /* ADR=1 CONTROL=4 */
- start.min = 0;
- start.sec = 0;
- start.fr = 0;
- }
-
- printf("Track %u attr=0x%02x %02u:%02u:%02u\n", first, attr, start.min, start.sec, start.fr);
-
- *write++ = 0x00; /* entry+0 RESERVED */
- *write++ = (attr >> 4) | 0x10; /* entry+1 ADR=1 CONTROL=4 (DATA) */
- *write++ = (unsigned char)first;/* entry+2 TRACK */
- *write++ = 0x00; /* entry+3 RESERVED */
-
- /* then, start address of first track in session */
- if (TIME)
- {
- *write++ = 0x00;
- *write++ = start.min;
- *write++ = start.sec;
- *write++ = start.fr;
- }
- else
- {
- uint32_t sec = (start.min * 60u * 75u) + (start.sec * 75u) + start.fr - 150u;
- *write++ = (unsigned char)(sec >> 24u);
- *write++ = (unsigned char)(sec >> 16u);
- *write++ = (unsigned char)(sec >> 8u);
- *write++ = (unsigned char)(sec >> 0u);
- }
- }
- else if (Format == 0) /* Read table of contents */
- {
- *write++ = (unsigned char)first; /* @+2 */
- *write++ = (unsigned char)last; /* @+3 */
-
- for (track = first; track <= last; track++)
- {
- unsigned char attr;
- TMSF start;
-
- if (!GetCDTrackInfo(drv, track, start, attr))
- {
- printf("WARNING: ATAPI READ TOC unable to read track %u information\n", track);
- attr = 0x41; /* ADR=1 CONTROL=4 */
- start.min = 0;
- start.sec = 0;
- start.fr = 0;
- }
-
- if (track < Track) continue;
- if ((write + 8) > (buf + AllocationLength)) break;
-
- printf("Track %u attr=0x%02x %02u:%02u:%02u\n", first, attr, start.min, start.sec, start.fr);
-
- *write++ = 0x00; /* entry+0 RESERVED */
- *write++ = (attr >> 4) | 0x10; /* entry+1 ADR=1 CONTROL=4 (DATA) */
- *write++ = (unsigned char)track;/* entry+2 TRACK */
- *write++ = 0x00; /* entry+3 RESERVED */
- if (TIME)
- {
- *write++ = 0x00;
- *write++ = start.min;
- *write++ = start.sec;
- *write++ = start.fr;
- }
- else
- {
- uint32_t sec = (start.min * 60u * 75u) + (start.sec * 75u) + start.fr - 150u;
- *write++ = (unsigned char)(sec >> 24u);
- *write++ = (unsigned char)(sec >> 16u);
- *write++ = (unsigned char)(sec >> 8u);
- *write++ = (unsigned char)(sec >> 0u);
- }
- }
-
- if ((write + 8) <= (buf + AllocationLength))
- {
- *write++ = 0x00;
- *write++ = 0x14;
- *write++ = 0xAA;/*TRACK*/
- *write++ = 0x00;
- if (TIME)
- {
- *write++ = 0x00;
- *write++ = leadOut.min;
- *write++ = leadOut.sec;
- *write++ = leadOut.fr;
- }
- else
- {
- uint32_t sec = (leadOut.min * 60u * 75u) + (leadOut.sec * 75u) + leadOut.fr - 150u;
- *write++ = (unsigned char)(sec >> 24u);
- *write++ = (unsigned char)(sec >> 16u);
- *write++ = (unsigned char)(sec >> 8u);
- *write++ = (unsigned char)(sec >> 0u);
- }
- }
- }
- else
- {
- printf("WARNING: ATAPI READ TOC Format=%u not supported\n", Format);
- return 8;
- }
-
- /* update the TOC data length field */
- unsigned int x = (unsigned int)(write - buf) - 2;
- buf[0] = x >> 8;
- buf[1] = x & 0xFF;
-
- printf("read_toc result:\n");
- hexdump(buf, write - buf);
-
- return write - buf;
-}
-
-static uint16_t mode_sense(int page)
-{
- uint8_t *write = buf;
- uint8_t *plen;
-
- uint32_t x;
-
- int valid = 0;
-
- printf("mode_sense page: %X\n", page);
-
- /* Mode Parameter List MMC-3 Table 340 */
- /* - Mode parameter header */
- /* - Page(s) */
-
- /* Mode Parameter Header (response for 10-byte MODE SENSE) SPC-2 Table 148 */
- *write++ = 0x00; /* MODE DATA LENGTH (MSB) */
- *write++ = 0x00; /* (LSB) */
- *write++ = 0x00; /* MEDIUM TYPE */
- *write++ = 0x00; /* DEVICE-SPECIFIC PARAMETER */
- *write++ = 0x00; /* Reserved */
- *write++ = 0x00; /* Reserved */
- *write++ = 0x00; /* BLOCK DESCRIPTOR LENGTH (MSB) */
- *write++ = 0x00; /* (LSB) */
- /* NTS: MMC-3 Table 342 says that BLOCK DESCRIPTOR LENGTH is zero, where it would be 8 for legacy units */
-
- /* Mode Page Format MMC-3 Table 341 */
- if (page == 0x01 || page == 0x3F)
- {
- valid = 1;
- *write++ = 1; /* PS|reserved|Page Code */
- plen = write;
- *write++ = 0x00; /* Page Length (n - 1) ... Length in bytes of the mode parameters that follow */
-
- *write++ = 0x00; /* +2 Error recovery Parameter AWRE|ARRE|TB|RC|Reserved|PER|DTE|DCR */
- *write++ = 3; /* +3 Read Retry Count */
- *write++ = 0x00; /* +4 Reserved */
- *write++ = 0x00; /* +5 Reserved */
- *write++ = 0x00; /* +6 Reserved */
- *write++ = 0x00; /* +7 Reserved */
-
- *plen = write - plen - 1;
- }
-
- /* CD-ROM audio control MMC-3 Section 6.3.7 table 354 */
- /* also MMC-1 Section 5.2.3.1 table 97 */
- if (page == 0x0E || page == 0x3F)
- {
- valid = 1;
- *write++ = 0x0E; /* PS|reserved|Page Code */
- plen = write;
- *write++ = 0x00; /* Page Length (n - 1) ... Length in bytes of the mode parameters that follow */
-
- *write++ = 0x04; /* +2 Reserved|IMMED=1|SOTC=0|Reserved */
- *write++ = 0x00; /* +3 Reserved */
- *write++ = 0x00; /* +4 Reserved */
- *write++ = 0x00; /* +5 Reserved */
- *write++ = 0x00; /* +6 Obsolete (75) */
- *write++ = 75; /* +7 Obsolete (75) */
- *write++ = 0x01; /* +8 output port 0 selection (0001b = channel 0) */
- *write++ = 0xFF; /* +9 output port 0 volume (0xFF = 0dB atten.) */
- *write++ = 0x02; /* +10 output port 1 selection (0010b = channel 1) */
- *write++ = 0xFF; /* +11 output port 1 volume (0xFF = 0dB atten.) */
- *write++ = 0x00; /* +12 output port 2 selection (none) */
- *write++ = 0x00; /* +13 output port 2 volume (0x00 = mute) */
- *write++ = 0x00; /* +14 output port 3 selection (none) */
- *write++ = 0x00; /* +15 output port 3 volume (0x00 = mute) */
-
- *plen = write - plen - 1;
- }
-
- /* CD-ROM mechanical status MMC-3 Section 6.3.11 table 361 */
- if (page == 0x2A || page == 0x3F)
- {
- valid = 1;
- *write++ = 0x2A; /* PS|reserved|Page Code */
- plen = write;
- *write++ = 0x00; /* Page Length (n - 1) ... Length in bytes of the mode parameters that follow */
-
- /* MSB | | | | | | | LSB */
- *write++ = 0x07; /* +2 Reserved |Reserved |DVD-RAM read |DVD-R read |DVD-ROM read | Method 2 | CD-RW read | CD-R read */
- *write++ = 0x00; /* +3 Reserved |Reserved |DVD-RAM write|DVD-R write | Reserved | Test Write | CD-RW write | CD-R write */
- *write++ = 0x71; /* +4 Buffer Underrun|Multisession |Mode 2 form 2|Mode 2 form 1|Digital Port 2|Digital Port 1 | Composite | Audio play */
- *write++ = 0xFF; /* +5 Read code bar |UPC |ISRC |C2 Pointers |R-W deintcorr | R-W supported |CDDA accurate |CDDA support */
- *write++ = 0x2F; /* +6 Loading mechanism type |Reserved |Eject |Prevent Jumper |Lock state |Lock */
- /* 0 (0x00) = Caddy
- * 1 (0x20) = Tray
- * 2 (0x40) = Popup
- * 3 (0x60) = Reserved
- * 4 (0x80) = Changer with indivually changeable discs
- * 5 (0xA0) = Changer using a magazine mechanism
- * 6 (0xC0) = Reserved
- * 6 (0xE0) = Reserved */
- *write++ = 0x03; /* +7 Reserved |Reserved |R-W in leadin|Side chg cap |S/W slot sel |Changer disc pr|Sep. ch. mute |Sep. volume levels */
-
- x = 176 * 8; /* +8 maximum speed supported in kB: 8X (obsolete in MMC-3) */
- *write++ = x >> 8;
- *write++ = x;
-
- x = 256; /* +10 Number of volume levels supported */
- *write++ = x >> 8;
- *write++ = x;
-
- x = 6 * 256; /* +12 buffer size supported by drive in kB */
- *write++ = x >> 8;
- *write++ = x;
-
- x = 176 * 8; /* +14 current read speed selected in kB: 8X (obsolete in MMC-3) */
- *write++ = x >> 8;
- *write++ = x;
-
- *plen = write - plen - 1;
- }
-
- if (!valid)
- {
- *write++ = page; /* PS|reserved|Page Code */
- *write++ = 0x06; /* Page Length (n - 1) ... Length in bytes of the mode parameters that follow */
-
- memset(write, 0, 6); write += 6;
- printf("WARNING: MODE SENSE on page 0x%02x not supported\n", page);
- }
-
- /* mode param header, data length */
- x = (uint32_t)(write - buf) - 2;
- buf[0] = (unsigned char)(x >> 8u);
- buf[1] = (unsigned char)x;
-
- hexdump(buf, x + 2);
- return x + 2;
-}
-
-static int GetCDSub(drive_t *drv, unsigned char& attr, unsigned char& track_num, unsigned char& index, TMSF& relative_msf, TMSF& absolute_msf)
-{
- attr = drv->track[0].attr;
- track_num = 1;
- index = 1;
- relative_msf.min = relative_msf.fr = 0; relative_msf.sec = 2;
- absolute_msf.min = absolute_msf.fr = 0; absolute_msf.sec = 2;
- return 1;
-}
-
-static int read_subchannel(drive_t *drv, uint8_t* cmdbuf)
-{
- unsigned char paramList = cmdbuf[3];
- unsigned char attr, track, index;
- bool SUBQ = !!(cmdbuf[2] & 0x40);
- bool TIME = !!(cmdbuf[1] & 2);
- unsigned char *write;
- unsigned char astat;
- bool playing = drv->playing, pause = drv->paused;
- TMSF rel, abs;
-
- if (paramList == 0 || paramList > 3)
- {
- printf("ATAPI READ SUBCHANNEL unknown param list\n");
- memset(buf, 0, 8);
- return 8;
- }
- else if (paramList == 2)
- {
- printf("ATAPI READ SUBCHANNEL Media Catalog Number not supported\n");
- memset(buf, 0, 8);
- return 8;
- }
- else if (paramList == 3) {
- printf("ATAPI READ SUBCHANNEL ISRC not supported\n");
- memset(buf, 0, 8);
- return 8;
- }
-
- /* get current subchannel position */
- if (!GetCDSub(drv, attr, track, index, rel, abs))
- {
- printf("ATAPI READ SUBCHANNEL unable to read current pos\n");
- memset(buf, 0, 8);
- return 8;
- }
-
- if (playing)
- astat = pause ? 0x12 : 0x11;
- else
- astat = 0x13;
-
- memset(buf, 0, 8);
- write = buf;
- *write++ = 0x00;
- *write++ = astat;/* AUDIO STATUS */
- *write++ = 0x00;/* SUBCHANNEL DATA LENGTH */
- *write++ = 0x00;
-
- if (SUBQ) {
- *write++ = 0x01; /* subchannel data format code */
- *write++ = (attr >> 4) | 0x10; /* ADR/CONTROL */
- *write++ = track;
- *write++ = index;
- if (TIME) {
- *write++ = 0x00;
- *write++ = abs.min;
- *write++ = abs.sec;
- *write++ = abs.fr;
- *write++ = 0x00;
- *write++ = rel.min;
- *write++ = rel.sec;
- *write++ = rel.fr;
- }
- else {
- uint32_t sec;
-
- sec = (abs.min * 60u * 75u) + (abs.sec * 75u) + abs.fr - 150u;
- *write++ = (unsigned char)(sec >> 24u);
- *write++ = (unsigned char)(sec >> 16u);
- *write++ = (unsigned char)(sec >> 8u);
- *write++ = (unsigned char)(sec >> 0u);
-
- sec = (rel.min * 60u * 75u) + (rel.sec * 75u) + rel.fr - 150u;
- *write++ = (unsigned char)(sec >> 24u);
- *write++ = (unsigned char)(sec >> 16u);
- *write++ = (unsigned char)(sec >> 8u);
- *write++ = (unsigned char)(sec >> 0u);
- }
- }
-
- {
- unsigned int x = (unsigned int)(write - buf) - 4;
- buf[2] = x >> 8;
- buf[3] = x;
- }
-
- cddbg_hexdump(buf, write - buf);
- return write - buf;
-}
-
-const char * x86_ide_parse_cd(uint32_t num, const char *filename)
-{
- int drv = num & 1;
- num >>= 1;
-
- const char *path = getFullPath(filename);
-
- const char *res = LoadCueSheet(&ide_inst[num].drive[drv], path);
- if(!res) res = LoadIsoFile(&ide_inst[num].drive[drv], path);
-
- return res;
}
void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver, int cd)
@@ -1085,64 +104,69 @@ void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver, int cd)
int drv = (ver == 3) ? (num & 1) : 0;
if (ver == 3) num >>= 1;
+ drive_t *drive = &ide_inst[num].drive[drv];
+
ide_inst[num].base = baseaddr;
- ide_inst[num].drive[drv].f = f;
+ drive->f = f;
- ide_inst[num].drive[drv].hd_cylinders = 0;
- ide_inst[num].drive[drv].hd_heads = 0;
- ide_inst[num].drive[drv].hd_spt = 0;
- ide_inst[num].drive[drv].hd_total_sectors = 0;
+ drive->cylinders = 0;
+ drive->heads = 0;
+ drive->spt = 0;
+ drive->total_sectors = 0;
- ide_inst[num].drive[drv].present = f ? 1 : 0;
+ drive->present = f ? 1 : 0;
ide_inst[num].state = IDE_STATE_RESET;
- ide_inst[num].drive[drv].cd = ide_inst[num].drive[drv].present && (ver == 3) && num && cd;
+ drive->cd = drive->present && (ver == 3) && num && cd;
if (ver == 3)
{
- if (f && ide_inst[num].drive[drv].placeholder && !ide_inst[num].drive[drv].cd)
+ if (f && drive->placeholder && !drive->cd)
{
printf("Cannot hot-mount HDD image to CD!\n");
- FileClose(ide_inst[num].drive[drv].f);
- ide_inst[num].drive[drv].f = 0;
+ FileClose(drive->f);
+ drive->f = 0;
f = 0;
- ide_inst[num].drive[drv].present = 0;
+ drive->present = 0;
}
- ide_inst[num].drive[drv].placeholder = ide_inst[num].drive[drv].allow_placeholder;
- if (ide_inst[num].drive[drv].placeholder && ide_inst[num].drive[drv].present && !ide_inst[num].drive[drv].cd) ide_inst[num].drive[drv].placeholder = 0;
- if (ide_inst[num].drive[drv].placeholder) ide_inst[num].drive[drv].cd = 1;
+ drive->placeholder = drive->allow_placeholder;
+ if (drive->placeholder && drive->present && !drive->cd) drive->placeholder = 0;
+ if (drive->placeholder) drive->cd = 1;
- IOWR(ide_inst[num].base, 6, ((ide_inst[num].drive[drv].present || ide_inst[num].drive[drv].placeholder) ? 9 : 8) << (drv * 4), 1);
+ IOWR(ide_inst[num].base, 6, ((drive->present || drive->placeholder) ? 9 : 8) << (drv * 4), 1);
IOWR(ide_inst[num].base, 6, 0x200, 1);
}
- else if (!ide_inst[num].drive[drv].present)
+ else if (!drive->present)
{
return;
}
- if (ide_inst[num].drive[drv].present && !ide_inst[num].drive[drv].cd)
- {
- ide_inst[num].drive[drv].hd_heads = 16;
- ide_inst[num].drive[drv].hd_spt = (ver == 3) ? 256 : 63;
- ide_inst[num].drive[drv].hd_cylinders = ide_inst[num].drive[drv].f->size / (ide_inst[num].drive[drv].hd_heads * ide_inst[num].drive[drv].hd_spt * 512);
+ if(drive->f) drive->total_sectors = (drive->f->size / 512);
- //Maximum 137GB images are supported.
- if (ide_inst[num].drive[drv].hd_cylinders > 65535) ide_inst[num].drive[drv].hd_cylinders = 65535;
- ide_inst[num].drive[drv].hd_total_sectors = (ide_inst[num].drive[drv].f->size / 512); // ide_inst[num].drive[drv].hd_spt * ide_inst[num].drive[drv].hd_heads * ide_inst[num].drive[drv].hd_cylinders;
- }
-
- if (!ide_inst[num].drive[drv].cd)
+ if (!drive->cd)
{
- uint32_t identify[256] =
+ if (drive->present)
+ {
+ drive->heads = 16;
+ drive->spt = (ver == 3) ? 256 : 63;
+
+ uint32_t cylinders = drive->f->size / (drive->heads * drive->spt * 512);
+ if (cylinders > 65535) cylinders = 65535;
+
+ //Maximum 137GB images are supported.
+ drive->cylinders = cylinders;
+ }
+
+ uint16_t identify[256] =
{
0x0040, //word 0
- ide_inst[num].drive[drv].hd_cylinders, //word 1
+ drive->cylinders, //word 1
0x0000, //word 2 reserved
- ide_inst[num].drive[drv].hd_heads, //word 3
- (uint16_t)(512 * ide_inst[num].drive[drv].hd_spt), //word 4
- 512, //word 5
- ide_inst[num].drive[drv].hd_spt, //word 6
+ drive->heads, //word 3
+ 0x0000, //word 4 obsolete
+ 0x0000, //word 5 obsolete
+ drive->spt, //word 6
0x0000, //word 7 vendor specific
0x0000, //word 8 vendor specific
0x0000, //word 9 vendor specific
@@ -1187,14 +211,14 @@ void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver, int cd)
0x0200, //word 51 pio timing
0x0200, //word 52 pio timing
0x0007, //word 53 valid fields
- ide_inst[num].drive[drv].hd_cylinders, //word 54
- ide_inst[num].drive[drv].hd_heads, //word 55
- ide_inst[num].drive[drv].hd_spt, //word 56
- ide_inst[num].drive[drv].hd_total_sectors & 0xFFFF, //word 57
- ide_inst[num].drive[drv].hd_total_sectors >> 16, //word 58
+ drive->cylinders, //word 54
+ drive->heads, //word 55
+ drive->spt, //word 56
+ (uint16_t)(drive->total_sectors & 0xFFFF), //word 57
+ (uint16_t)(drive->total_sectors >> 16), //word 58
0x110, //word 59 multiple sectors
- ide_inst[num].drive[drv].hd_total_sectors & 0xFFFF, //word 60
- ide_inst[num].drive[drv].hd_total_sectors >> 16, //word 61
+ (uint16_t)(drive->total_sectors & 0xFFFF), //word 60 LBA-28
+ (uint16_t)(drive->total_sectors >> 16), //word 61 LBA-28
0x0000, //word 62 single word dma modes
0x0000, //word 63 multiple word dma modes
0x0000, //word 64 pio modes
@@ -1212,10 +236,10 @@ void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver, int cd)
0,0,0,0, //word 89..92
1 | (1 << 14) | (1 << 13) | (1 << 9) | (1 << 8) | (1 << 3) | (1 << 1) | (1 << 0), //word 93
0,0,0,0,0,0, //word 94..99
- ide_inst[num].drive[drv].hd_total_sectors & 0xFFFF, //word 100
- ide_inst[num].drive[drv].hd_total_sectors >> 16, //word 101
- 0, //word 102
- 0, //word 103
+ (uint16_t)(drive->total_sectors & 0xFFFF), //word 100 LBA-48
+ (uint16_t)(drive->total_sectors >> 16), //word 101 LBA-48
+ 0, //word 102 LBA-48
+ 0, //word 103 LBA-48
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //word 104..127
@@ -1228,11 +252,11 @@ void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver, int cd)
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
- for (int i = 0; i < 256; i++) ide_inst[num].drive[drv].id[i] = (uint16_t)identify[i];
+ for (int i = 0; i < 256; i++) drive->id[i] = identify[i];
}
else
{
- uint32_t identify[256] =
+ uint16_t identify[256] =
{
0x8580, //word 0
0x0000, //word 1
@@ -1327,9 +351,8 @@ void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver, int cd)
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
- for (int i = 0; i < 256; i++) ide_inst[num].drive[drv].id[i] = (uint16_t)identify[i];
-
- ide_inst[num].drive[drv].load_state = ide_inst[num].drive[drv].f ? 1 : 3;
+ for (int i = 0; i < 256; i++) drive->id[i] = identify[i];
+ drive->load_state = drive->f ? 1 : 3;
}
if (ide_inst[num].drive[drv].present)
@@ -1337,24 +360,25 @@ void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver, int cd)
char *name = ide_inst[num].drive[drv].f->name;
for (int i = 0; i < 20; i++)
{
- if (*name) ide_inst[num].drive[drv].id[27 + i] = ((*name++) << 8) | 0x20;
- if (*name) ide_inst[num].drive[drv].id[27 + i] = (ide_inst[num].drive[drv].id[27 + i] & 0xFF00) | (*name++);
+ if (*name) drive->id[27 + i] = ((*name++) << 8) | 0x20;
+ if (*name) drive->id[27 + i] = (drive->id[27 + i] & 0xFF00) | (*name++);
}
}
if (ver < 3)
{
- for (int i = 0; i < 128; i++) IOWR(ide_inst[num].base, 0, ide_inst[num].drive[drv].present ? (ide_inst[num].drive[drv].id[2 * i + 1] << 16) | ide_inst[num].drive[drv].id[2 * i + 0] : 0, ver);
+ for (int i = 0; i < 128; i++) IOWR(ide_inst[num].base, 0, drive->present ? (drive->id[2 * i + 1] << 16) | drive->id[2 * i + 0] : 0, ver);
- IOWR(ide_inst[num].base, 1, ide_inst[num].drive[drv].hd_cylinders, ver);
- IOWR(ide_inst[num].base, 2, ide_inst[num].drive[drv].hd_heads, ver);
- IOWR(ide_inst[num].base, 3, ide_inst[num].drive[drv].hd_spt, ver);
- IOWR(ide_inst[num].base, 4, ide_inst[num].drive[drv].hd_spt * ide_inst[num].drive[drv].hd_heads, ver);
- IOWR(ide_inst[num].base, 5, ide_inst[num].drive[drv].hd_spt * ide_inst[num].drive[drv].hd_heads * ide_inst[num].drive[drv].hd_cylinders, ver);
+ IOWR(ide_inst[num].base, 1, drive->cylinders, ver);
+ IOWR(ide_inst[num].base, 2, drive->heads, ver);
+ IOWR(ide_inst[num].base, 3, drive->spt, ver);
+ IOWR(ide_inst[num].base, 4, drive->spt * drive->heads, ver);
+ IOWR(ide_inst[num].base, 5, drive->spt * drive->heads * drive->cylinders, ver);
IOWR(ide_inst[num].base, 6, 0, ver); // base LBA
}
- printf("HDD%d:\n present %d\n hd_cylinders %d\n hd_heads %d\n hd_spt %d\n hd_total_sectors %d\n\n", (ver < 3) ? num : (num * 2 + drv), ide_inst[num].drive[drv].present, ide_inst[num].drive[drv].hd_cylinders, ide_inst[num].drive[drv].hd_heads, ide_inst[num].drive[drv].hd_spt, ide_inst[num].drive[drv].hd_total_sectors);
+ printf("HDD%d:\n present %d\n hd_cylinders %d\n hd_heads %d\n hd_spt %d\n hd_total_sectors %d\n\n",
+ (ver < 3) ? num : (num * 2 + drv), drive->present, drive->cylinders, drive->heads, drive->spt, drive->total_sectors);
}
static void process_read(ide_config *ide)
@@ -1362,7 +386,7 @@ static void process_read(ide_config *ide)
uint32_t lba = ide->regs.sector | (ide->regs.cylinder << 8) | (ide->regs.head << 24);
uint16_t cnt = ide->regs.sector_count;
- if (!cnt || cnt > io_max_size) cnt = io_max_size;
+ if (!cnt || cnt > ide_io_max_size) cnt = ide_io_max_size;
if (ide->state == IDE_STATE_INIT_RW)
{
@@ -1370,10 +394,10 @@ static void process_read(ide_config *ide)
ide->null = !FileSeekLBA(ide->drive[ide->regs.drv].f, lba);
}
- if (!ide->null) ide->null = (FileReadAdv(ide->drive[ide->regs.drv].f, buf, cnt * 512, -1) <= 0);
- if (ide->null) memset(buf, 0, cnt * 512);
+ if (!ide->null) ide->null = (FileReadAdv(ide->drive[ide->regs.drv].f, ide_buf, cnt * 512, -1) <= 0);
+ if (ide->null) memset(ide_buf, 0, cnt * 512);
- ide_send_data(buf, cnt * 128);
+ ide_send_data(ide_buf, cnt * 128);
lba += cnt;
ide->regs.sector_count -= cnt;
@@ -1388,13 +412,13 @@ static void process_read(ide_config *ide)
ide->regs.io_size = cnt;
ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ;
- set_regs(ide);
+ ide_set_regs(ide);
}
static void prep_write(ide_config *ide)
{
ide->prepcnt = ide->regs.sector_count;
- if (!ide->prepcnt || ide->prepcnt > io_max_size) ide->prepcnt = io_max_size;
+ if (!ide->prepcnt || ide->prepcnt > ide_io_max_size) ide->prepcnt = ide_io_max_size;
ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ;
@@ -1408,13 +432,13 @@ static void prep_write(ide_config *ide)
ide->state = IDE_STATE_WAIT_WR;
ide->regs.io_size = ide->prepcnt;
- set_regs(ide);
+ ide_set_regs(ide);
}
static void process_write(ide_config *ide)
{
- ide_recv_data(buf, ide->prepcnt * 128);
- if (!ide->null) ide->null = (FileWriteAdv(ide->drive[ide->regs.drv].f, buf, ide->prepcnt * 512, -1) <= 0);
+ ide_recv_data(ide_buf, ide->prepcnt * 128);
+ if (!ide->null) ide->null = (FileWriteAdv(ide->drive[ide->regs.drv].f, ide_buf, ide->prepcnt * 512, -1) <= 0);
uint32_t lba = ide->regs.sector | (ide->regs.cylinder << 8) | (ide->regs.head << 24);
lba += ide->prepcnt;
@@ -1441,7 +465,7 @@ static int handle_hdd(ide_config *ide)
ide->regs.drv = drv;
ide->regs.io_size = 1;
ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ;
- set_regs(ide);
+ ide_set_regs(ide);
ide->state = IDE_STATE_WAIT_END;
}
break;
@@ -1478,487 +502,23 @@ static int handle_hdd(ide_config *ide)
case 0xC6: // set multople
{
- if (!ide->regs.sector_count || ide->regs.sector_count > io_max_size)
+ if (!ide->regs.sector_count || ide->regs.sector_count > ide_io_max_size)
{
return 1;
}
ide->regs.status = ATA_STATUS_RDY;
- set_regs(ide);
+ ide_set_regs(ide);
}
break;
case 0x08: // reset (fail)
- printf("reset for ide not supported\n");
+ printf("Reset command (08h) for HDD not supported\n");
return 1;
default:
printf("(!) Unsupported command\n");
- print_regs(&ide->regs);
- return 1;
- }
-
- return 0;
-}
-
-static void pkt_send(ide_config *ide, void *data, uint16_t size)
-{
- ide_send_data(data, (size + 3) / 4);
-
- ide->regs.pkt_io_size = (size+1)/2;
- ide->regs.cylinder = size;
- ide->regs.sector_count = 2;
-
- ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ;
- set_regs(ide);
- ide->state = IDE_STATE_WAIT_PKT_RD;
-}
-
-static void read_cd_sectors(ide_config *ide, int cnt)
-{
- uint32_t sz = ide->drive[ide->regs.drv].track[0].sectorSize;
- fileTYPE *f = ide->drive[ide->regs.drv].f;
-
- if (sz == 2048)
- {
- if (!ide->null) ide->null = (FileReadAdv(f, buf, cnt * sz, -1) <= 0);
- if (ide->null) memset(buf, 0, cnt * sz);
- return;
- }
-
- uint32_t pre = ide->drive[ide->regs.drv].track[0].mode2 ? 24 : 16;
- uint32_t post = sz - pre - 2048;
- uint32_t off = 0;
-
- while (cnt--)
- {
- if (!ide->null) ide->null = !FileSeek(f, pre, SEEK_CUR);
- if (!ide->null) ide->null = (FileReadAdv(f, buf + off, 2048, -1) <= 0);
- if (ide->null) memset(buf + off, 0, 2048);
- if (!ide->null) ide->null = !FileSeek(f, post, SEEK_CUR);
- off += 2048;
- }
-}
-
-static void process_cd_read(ide_config *ide)
-{
- uint32_t cnt = ide->regs.pkt_cnt;
- if ((cnt * 4) > io_max_size) cnt = io_max_size / 4;
-
- while ((cnt * 2048) > ide->regs.pkt_size_limit)
- {
- if (cnt <= 1) break;
- cnt--;
- }
-
- if (cnt != ide->regs.pkt_cnt)
- {
- cddbg_printf("** partial CD read\n");
- }
-
- if (ide->state == IDE_STATE_INIT_RW)
- {
- uint32_t pos = ide->regs.pkt_lba * ide->drive[ide->regs.drv].track[0].sectorSize;
-
- //printf("Read from pos: %d\n", pos);
- ide->null = (FileSeek(ide->drive[ide->regs.drv].f, pos, SEEK_SET) < 0);
- }
-
- read_cd_sectors(ide, cnt);
-
- //printf("\nsector:\n");
- //hexdump(buf, 512, 0);
-
- ide->regs.pkt_cnt -= cnt;
- pkt_send(ide, buf, cnt * 2048);
-}
-
-static int cd_inquiry(uint8_t maxlen)
-{
- static const char vendor[] = "MiSTer ";
- static const char product[] = "CDROM ";
-
- memset(buf, 0, 47);
- buf[0] = (0 << 5) | 5; /* Peripheral qualifier=0 device type=5 (CDROM) */
- buf[1] = 0x80; /* RMB=1 removable media */
- buf[2] = 0x00; /* ANSI version */
- buf[3] = 0x21;
- buf[4] = maxlen - 5; /* additional length */
-
- for (int i = 0; i < 8; i++) buf[i + 8] = (unsigned char)vendor[i];
- for (int i = 0; i < 16; i++) buf[i + 16] = (unsigned char)product[i];
- for (int i = 0; i < 4; i++) buf[i + 32] = ' ';
- for (int i = 0; i < 11; i++) buf[i + 36] = ' ';
-
- hexdump(buf, maxlen);
- return maxlen;
-}
-
-#define CD_ERR_NO_MEDIUM ((2 << 4) | ATA_ERR_ABRT)
-
-static void cd_reply(ide_config *ide, uint8_t error)
-{
- ide->state = IDE_STATE_IDLE;
- ide->regs.sector_count = 3;
- ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_IRQ | (error ? ATA_STATUS_ERR : 0);
- ide->regs.error = error;
- set_regs(ide);
-}
-
-static void set_sense(uint8_t SK, uint8_t ASC = 0, uint8_t ASCQ = 0)
-{
- int len = 18;
- memset(buf, 0, len);
-
- buf[0] = 0x70; /* RESPONSE CODE */
- buf[2] = SK & 0xF; /* SENSE KEY */
- buf[7] = len - 18; /* additional sense length */
- buf[12] = ASC;
- buf[13] = ASCQ;
-}
-
-static int get_sense(drive_t *drv)
-{
- switch (drv->load_state)
- {
- case 3:
- set_sense(2, 0x3A);
- break;
-
- case 2:
- set_sense(2, 4, 1);
- drv->load_state--;
- break;
-
- case 1:
- set_sense(2, 28, 0);
- drv->load_state--;
- break;
-
- default:
- set_sense(0);
- break;
- }
-
- cddbg_hexdump(buf, 18);
- return 18;
-}
-
-
-static void pause_resume(drive_t *drv, uint8_t *cmdbuf)
-{
- bool resume = !!(cmdbuf[8] & 1);
- if(drv->playing) drv->paused = !resume;
-}
-
-static void play_audio_msf(drive_t *drv, uint8_t *cmdbuf)
-{
- uint32_t start_lba, end_lba;
-
- if (cmdbuf[3] == 0xFF && cmdbuf[4] == 0xFF && cmdbuf[5] == 0xFF)
- {
- start_lba = 0xFFFFFFFF;
- }
- else
- {
- start_lba = (cmdbuf[3] * 60u * 75u) + (cmdbuf[4] * 75u) + cmdbuf[5];
-
- if (start_lba >= 150u) start_lba -= 150u; /* LBA sector 0 == M:S:F sector 0:2:0 */
- else end_lba = 0;
- }
-
- if (cmdbuf[6] == 0xFF && cmdbuf[7] == 0xFF && cmdbuf[8] == 0xFF)
- {
- end_lba = 0xFFFFFFFF;
- }
- else
- {
- end_lba = (cmdbuf[6] * 60u * 75u) + (cmdbuf[7] * 75u) + cmdbuf[8];
- if (end_lba >= 150u) end_lba -= 150u; /* LBA sector 0 == M:S:F sector 0:2:0 */
- else end_lba = 0;
- }
-
- if (start_lba == end_lba)
- {
- drv->playing = 0;
- drv->paused = 0;
- return;
- }
-
- /* LBA 0xFFFFFFFF means start playing wherever the optics of the CD sit */
- if (start_lba != 0xFFFFFFFF)
- {
- drv->playing = 1;
- drv->paused = 0;
- }
- else
- {
- drv->playing = 0;
- drv->paused = 1;
- }
-}
-
-void play_audio10(drive_t *drv, uint8_t *cmdbuf)
-{
- uint16_t play_length;
- uint32_t start_lba;
-
- start_lba = ((uint32_t)cmdbuf[2] << 24) + ((uint32_t)cmdbuf[3] << 16) + ((uint32_t)cmdbuf[4] << 8) + ((uint32_t)cmdbuf[5] << 0);
- play_length = ((uint16_t)cmdbuf[7] << 8) + ((uint16_t)cmdbuf[8] << 0);
-
- if (play_length == 0)
- {
- drv->playing = 0;
- drv->paused = 0;
- return;
- }
-
- /* LBA 0xFFFFFFFF means start playing wherever the optics of the CD sit */
- if (start_lba != 0xFFFFFFFF)
- {
- drv->playing = 1;
- drv->paused = 0;
- }
- else
- {
- drv->playing = 0;
- drv->paused = 1;
- }
-}
-
-static void process_pkt_cmd(ide_config *ide)
-{
- uint8_t cmdbuf[16];
- ide_recv_data(cmdbuf, 3);
- ide_reset_buf();
-
- cddbg_hexdump(cmdbuf, 12, 0);
-
- ide->regs.pkt_cnt = 0;
- int err = 0;
-
- switch (cmdbuf[0])
- {
- case 0xA8: // read(12) sectors
- case 0x28: // read(10) sectors
- //printf("** Read Sectors\n");
- //hexdump(cmdbuf, 12, 0);
- ide->regs.pkt_cnt = (cmdbuf[0] == 0x28) ? ((cmdbuf[7] << 8) | cmdbuf[8]) : ((cmdbuf[6] << 24) | (cmdbuf[7] << 16) | (cmdbuf[8] << 8) | cmdbuf[9]);
- ide->regs.pkt_lba = ((cmdbuf[2] << 24) | (cmdbuf[3] << 16) | (cmdbuf[4] << 8) | cmdbuf[5]);
- if (!ide->regs.pkt_cnt)
- {
- // length 0 is not a error.
- cd_reply(ide, 0);
- break;
- }
- ide->state = IDE_STATE_INIT_RW;
- if(!ide->drive[ide->regs.drv].load_state) process_cd_read(ide);
- else cd_reply(ide, CD_ERR_NO_MEDIUM);
- break;
-
- case 0x25: // read capacity
- //printf("** Read Capacity\n");
- if (!ide->drive[ide->regs.drv].load_state)
- {
- uint32_t tmp = ide->drive[ide->regs.drv].f->size / 2048;;
- buf[0] = tmp >> 24;
- buf[1] = tmp >> 16;
- buf[2] = tmp >> 8;
- buf[3] = tmp;
-
- tmp = 2048;
- buf[4] = tmp >> 24;
- buf[5] = tmp >> 16;
- buf[6] = tmp >> 8;
- buf[7] = tmp;
- //hexdump(buf, 8, 0);
- pkt_send(ide, buf, 8);
- }
- else cd_reply(ide, CD_ERR_NO_MEDIUM);
- break;
-
- case 0x2B: // seek
- ide->drive[ide->regs.drv].playing = 0;
- ide->drive[ide->regs.drv].paused = 0;
- cd_reply(ide, 0);
- break;
-
- case 0x1E: // lock the cd door - doing nothing.
- cd_reply(ide, 0);
- break;
-
- case 0x5A: // mode sense
- pkt_send(ide, buf, mode_sense(cmdbuf[2]));
- break;
-
- case 0x42: // read sub
- cddbg_printf("read sub:\n");
- pkt_send(ide, buf, read_subchannel(&ide->drive[ide->regs.drv], cmdbuf));
- break;
-
- case 0x43: // read TOC
- pkt_send(ide, buf, read_toc(&ide->drive[ide->regs.drv], cmdbuf));
- break;
-
- case 0x12: // inquiry
- pkt_send(ide, buf, cd_inquiry(cmdbuf[4]));
- break;
-
- case 0x03: // mode sense
- cddbg_printf("get sense:\n");
- pkt_send(ide, buf, get_sense(&ide->drive[ide->regs.drv]));
- break;
-
- case 0x55: // mode select
- printf("mode select\n");
- ide->regs.cylinder = (cmdbuf[7] << 8) | cmdbuf[8];
- if (ide->regs.cylinder > 512) ide->regs.cylinder = 512;
- ide->regs.pkt_io_size = (ide->regs.cylinder + 1) / 2;
- ide->regs.sector_count = 0;
- ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ;
- set_regs(ide);
- ide->state = IDE_STATE_WAIT_PKT_MODE;
- break;
-
- case 0x00: // test unit ready
- if (!ide->drive[ide->regs.drv].load_state) cd_reply(ide, 0);
- else cd_reply(ide, CD_ERR_NO_MEDIUM);
- break;
-
- case 0x45: // play lba
- printf("CD PLAY AUDIO(10)\n");
- play_audio10(&ide->drive[ide->regs.drv], cmdbuf);
- cd_reply(ide, 0);
- break;
-
- case 0x47: // play msf
- printf("CD PLAY AUDIO MSF\n");
- play_audio_msf(&ide->drive[ide->regs.drv], cmdbuf);
- cd_reply(ide, 0);
- break;
-
- case 0x4B: // pause/resume
- printf("CD PAUSE/RESUME\n");
- pause_resume(&ide->drive[ide->regs.drv], cmdbuf);
- cd_reply(ide, 0);
- break;
-
- default:
- err = 1;
- break;
- }
-
- if (err)
- {
- printf("(!) Error in packet command %02X\n", cmdbuf[0]);
- hexdump(cmdbuf, 12, 0);
- cd_reply(ide, ATA_ERR_ABRT);
- }
-}
-
-static int handle_cd(ide_config *ide)
-{
- uint8_t drv;
-
- /*
- static uint32_t delay = 0;
- if (delay && !CheckTimer(delay)) return 0;
- uint32_t afterdelay = delay;
- delay = 0;
- */
-
- switch (ide->regs.cmd)
- {
- case 0xA1: // identify packet
- //print_regs(&ide->regs);
- cddbg_printf("identify packet\n");
- /*
- if (!afterdelay)
- {
- delay = GetTimer(3);
- cddbg_printf("wait..\n");
- return 0;
- }
- */
- ide_send_data(ide->drive[ide->regs.drv].id, 128);
- drv = ide->regs.drv;
- memset(&ide->regs, 0, sizeof(ide->regs));
- ide->regs.drv = drv;
- ide->regs.pkt_io_size = 256;
- ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ;
- set_regs(ide);
- ide->state = IDE_STATE_WAIT_END;
- break;
-
- case 0xEC: // identify (fail)
- cddbg_printf("identify (CD)\n");
- /*
- if (!afterdelay)
- {
- delay = GetTimer(3);
- cddbg_printf("wait..\n");
- return 0;
- }
- */
- ide->regs.sector = 1;
- ide->regs.sector_count = 1;
- ide->regs.cylinder = 0xEB14;
- ide->regs.head = 0;
- ide->regs.io_size = 0;
- return 1;
-
- case 0xA0: // packet
- cddbg_printf("cmd A0: %02X\n", ide->regs.features);
- if (ide->regs.features & 1)
- {
- cddbg_printf("Cancel A0 DMA transfer\n");
- return 1;
- }
- /*
- if (!afterdelay)
- {
- delay = GetTimer(3);
- cddbg_printf("wait..\n");
- return 0;
- }
- */
- ide->regs.pkt_size_limit = ide->regs.cylinder;
- if (!ide->regs.pkt_size_limit) ide->regs.pkt_size_limit = io_max_size * 512;
- ide->regs.pkt_io_size = 6;
- ide->regs.sector_count = 1;
- ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ; // | ATA_STATUS_IRQ;
- set_regs(ide);
- ide->state = IDE_STATE_WAIT_PKT_CMD;
- break;
-
- case 0x08: // reset
- cddbg_printf("cmd 08\n");
- /*
- if (!afterdelay)
- {
- delay = GetTimer(3);
- cddbg_printf("wait..\n");
- return 0;
- }
- */
- ide->drive[ide->regs.drv].playing = 0;
- ide->drive[ide->regs.drv].paused = 0;
- ide->regs.sector = 1;
- ide->regs.sector_count = 1;
- ide->regs.cylinder = 0xEB14;
- ide->regs.head = 0;
- ide->regs.io_size = 0;
- ide->regs.status = ATA_STATUS_RDY;
- set_regs(ide);
- break;
-
- case 0x00: // nop
- cddbg_printf("cmd 00\n");
- return 1; // must always fail
-
- default:
- printf("(!) Unsupported command\n");
- print_regs(&ide->regs);
+ ide_print_regs(&ide->regs);
return 1;
}
@@ -1976,7 +536,7 @@ void x86_ide_io(int num, int req)
ide->state = IDE_STATE_IDLE;
ide->regs.status = ATA_STATUS_RDY;
- set_regs(ide);
+ ide_set_regs(ide);
printf("IDE %04X reset finish\n", ide->base);
}
@@ -1984,11 +544,11 @@ void x86_ide_io(int num, int req)
else if (req == 4) // command
{
ide->state = IDE_STATE_IDLE;
- get_regs(ide);
+ ide_get_regs(ide);
int err = 0;
- if (ide->drive[ide->regs.drv].cd) err = handle_cd(ide);
+ if (ide->drive[ide->regs.drv].cd) err = cdrom_handle_cmd(ide);
else if (!ide->drive[ide->regs.drv].present) err = 1;
else err = handle_hdd(ide);
@@ -1996,7 +556,7 @@ void x86_ide_io(int num, int req)
{
ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_ERR | ATA_STATUS_IRQ;
ide->regs.error = ATA_ERR_ABRT;
- set_regs(ide);
+ ide_set_regs(ide);
}
}
else if (req == 5) // data request
@@ -2006,7 +566,7 @@ void x86_ide_io(int num, int req)
{
ide->state = IDE_STATE_IDLE;
ide->regs.status = ATA_STATUS_RDY;
- set_regs(ide);
+ ide_set_regs(ide);
}
else if (ide->state == IDE_STATE_WAIT_RD)
{
@@ -2023,26 +583,24 @@ void x86_ide_io(int num, int req)
{
ide->state = IDE_STATE_IDLE;
ide->regs.status = ATA_STATUS_RDY;
- set_regs(ide);
+ ide_set_regs(ide);
}
}
else if (ide->state == IDE_STATE_WAIT_PKT_CMD)
{
- cddbg_printf("packet cmd received\n");
- process_pkt_cmd(ide);
+ cdrom_handle_pkt(ide);
}
else if (ide->state == IDE_STATE_WAIT_PKT_RD)
{
- cddbg_printf("packet was read\n");
- if (ide->regs.pkt_cnt) process_cd_read(ide);
- else cd_reply(ide, 0);
+ if (ide->regs.pkt_cnt) cdrom_read(ide);
+ else cdrom_reply(ide, 0);
}
else if (ide->state == IDE_STATE_WAIT_PKT_MODE)
{
- ide_recv_data(buf, 128);
+ ide_recv_data(ide_buf, 128);
printf("mode select data:\n");
- hexdump(buf, ide->regs.cylinder);
- cd_reply(ide, 0);
+ hexdump(ide_buf, ide->regs.cylinder);
+ cdrom_reply(ide, 0);
}
else
{
@@ -2050,7 +608,7 @@ void x86_ide_io(int num, int req)
ide->state = IDE_STATE_IDLE;
ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_ERR | ATA_STATUS_IRQ;
ide->regs.error = ATA_ERR_ABRT;
- set_regs(ide);
+ ide_set_regs(ide);
}
}
else if (req == 6) // reset
@@ -2065,7 +623,7 @@ void x86_ide_io(int num, int req)
ide->drive[1].playing = 0;
ide->drive[1].paused = 0;
- get_regs(ide);
+ ide_get_regs(ide);
ide->regs.head = 0;
ide->regs.error = 0;
ide->regs.sector = 1;
@@ -2073,7 +631,7 @@ void x86_ide_io(int num, int req)
ide->regs.cylinder = (!ide->drive[ide->regs.drv].present) ? 0xFFFF : ide->drive[ide->regs.drv].cd ? 0xEB14 : 0x0000;
if (ide->drive[ide->regs.drv].placeholder) ide->regs.cylinder = 0xEB14;
ide->regs.status = ATA_STATUS_BSY;
- set_regs(ide);
+ ide_set_regs(ide);
ide->state = IDE_STATE_RESET;
}
diff --git a/support/x86/x86_ide.h b/support/x86/x86_ide.h
index a8b7bdf..5626cd1 100644
--- a/support/x86/x86_ide.h
+++ b/support/x86/x86_ide.h
@@ -1,9 +1,119 @@
#ifndef X86_IDE_H
#define X86_IDE_H
-#include "x86.h"
+#define ATA_STATUS_BSY 0x80 // busy
+#define ATA_STATUS_RDY 0x40 // ready
+#define ATA_STATUS_DF 0x20 // device fault
+#define ATA_STATUS_WFT 0x20 // write fault (old name)
+#define ATA_STATUS_SKC 0x10 // seek complete
+#define ATA_STATUS_SERV 0x10 // service
+#define ATA_STATUS_DRQ 0x08 // data request
+#define ATA_STATUS_IRQ 0x04 // rise IRQ
+#define ATA_STATUS_IDX 0x02 // index
+#define ATA_STATUS_ERR 0x01 // error (ATA)
+#define ATA_STATUS_CHK 0x01 // check (ATAPI)
+
+#define ATA_ERR_ICRC 0x80 // ATA Ultra DMA bad CRC
+#define ATA_ERR_BBK 0x80 // ATA bad block
+#define ATA_ERR_UNC 0x40 // ATA uncorrected error
+#define ATA_ERR_MC 0x20 // ATA media change
+#define ATA_ERR_IDNF 0x10 // ATA id not found
+#define ATA_ERR_MCR 0x08 // ATA media change request
+#define ATA_ERR_ABRT 0x04 // ATA command aborted
+#define ATA_ERR_NTK0 0x02 // ATA track 0 not found
+#define ATA_ERR_NDAM 0x01 // ATA address mark not found
+
+#define IDE_STATE_IDLE 0
+#define IDE_STATE_RESET 1
+#define IDE_STATE_INIT_RW 2
+#define IDE_STATE_WAIT_RD 3
+#define IDE_STATE_WAIT_WR 4
+#define IDE_STATE_WAIT_END 5
+#define IDE_STATE_WAIT_PKT_CMD 6
+#define IDE_STATE_WAIT_PKT_RD 7
+#define IDE_STATE_WAIT_PKT_END 8
+#define IDE_STATE_WAIT_PKT_MODE 9
+
+typedef struct
+{
+ uint8_t io_done;
+ uint8_t features;
+ uint8_t sector_count;
+ uint8_t sector;
+ uint16_t cylinder;
+ uint8_t head;
+ uint8_t drv;
+ uint8_t lba;
+ uint8_t cmd;
+
+ uint16_t pkt_size_limit;
+ uint16_t pkt_io_size;
+ uint32_t pkt_lba;
+ uint32_t pkt_cnt;
+
+ uint8_t io_size;
+ uint8_t error;
+ uint8_t status;
+} regs_t;
+
+typedef struct
+{
+ char filename[1024];
+ uint32_t start;
+ uint32_t length;
+ uint32_t skip;
+ uint16_t sectorSize;
+ uint8_t attr;
+ uint8_t mode2;
+ uint8_t number;
+} track_t;
+
+typedef struct
+{
+ fileTYPE *f;
+ uint8_t present;
+
+ uint16_t cylinders;
+ uint16_t heads;
+ uint16_t spt;
+ uint32_t total_sectors;
+
+ uint8_t placeholder;
+ uint8_t allow_placeholder;
+ uint8_t cd;
+ uint8_t load_state;
+ uint8_t last_load_state;
+ uint8_t track_cnt;
+ uint8_t data_num;
+ track_t track[50];
+
+ uint8_t playing;
+ uint8_t paused;
+ uint32_t play_start_lba;
+ uint32_t play_end_lba;
+
+ uint16_t id[256];
+} drive_t;
+
+typedef struct
+{
+ uint32_t base;
+ uint32_t state;
+ uint32_t null;
+ uint32_t prepcnt;
+ regs_t regs;
+
+ drive_t drive[2];
+} ide_config;
+
+extern ide_config ide_inst[2];
+extern const uint32_t ide_io_max_size;
+extern uint8_t ide_buf[];
+
+void ide_print_regs(regs_t *regs);
+void ide_get_regs(ide_config *ide);
+void ide_set_regs(ide_config *ide);
-const char * x86_ide_parse_cd(uint32_t num, const char *filename);
void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver, int cd);
void x86_ide_io(int num, int req);
int x86_ide_is_placeholder(int num);