diff --git a/MiSTer.vcxproj b/MiSTer.vcxproj index 8661e5e..1ec0716 100644 --- a/MiSTer.vcxproj +++ b/MiSTer.vcxproj @@ -95,6 +95,7 @@ + @@ -157,6 +158,7 @@ + diff --git a/MiSTer.vcxproj.filters b/MiSTer.vcxproj.filters index 677c412..b264194 100644 --- a/MiSTer.vcxproj.filters +++ b/MiSTer.vcxproj.filters @@ -202,6 +202,9 @@ Source Files\support + + Source Files\support + @@ -384,5 +387,8 @@ Header Files\support + + Header Files\support + \ No newline at end of file diff --git a/file_io.cpp b/file_io.cpp index d37fe18..b3197f4 100644 --- a/file_io.cpp +++ b/file_io.cpp @@ -588,7 +588,7 @@ int FileReadSec(fileTYPE *file, void *pBuffer) } // Write with offset advancing -int FileWriteAdv(fileTYPE *file, void *pBuffer, int length) +int FileWriteAdv(fileTYPE *file, void *pBuffer, int length, int failres) { int ret; @@ -600,18 +600,18 @@ int FileWriteAdv(fileTYPE *file, void *pBuffer, int length) if (ret < 0) { printf("FileWriteAdv error(%d).\n", ret); - return 0; + return failres; } } else if (file->zip) { printf("FileWriteAdv error(not supported for zip).\n"); - return 0; + return failres; } else { printf("FileWriteAdv error(unknown file type).\n"); - return 0; + return failres; } file->offset += ret; diff --git a/file_io.h b/file_io.h index 900722e..fdfe086 100644 --- a/file_io.h +++ b/file_io.h @@ -74,7 +74,7 @@ int FileSeekLBA(fileTYPE *file, uint32_t offset); int FileReadAdv(fileTYPE *file, void *pBuffer, int length, int failres = 0); int FileReadSec(fileTYPE *file, void *pBuffer); -int FileWriteAdv(fileTYPE *file, void *pBuffer, int length); +int FileWriteAdv(fileTYPE *file, void *pBuffer, int length, int failres = 0); int FileWriteSec(fileTYPE *file, void *pBuffer); int FileCreatePath(const char *dir); diff --git a/menu.cpp b/menu.cpp index a9a1434..d610fa0 100644 --- a/menu.cpp +++ b/menu.cpp @@ -262,7 +262,7 @@ static char SelectedDir[1024] = {}; static char SelectedLabel[1024] = {}; static char Selected_F[16][1024] = {}; -static char Selected_S[4][1024] = {}; +static char Selected_S[8][1024] = {}; static char Selected_tmp[1024] = {}; static char selPath[1024] = {}; @@ -1563,20 +1563,32 @@ void HandleUI(void) { if (p[0] == 'S') s_entry = selentry; substrcpy(s, p, 2); - if (strlen(s)) + int num = (p[1] >= '0' && p[1] <= '9') ? p[1] - '0' : 0; + + if (is_x86() && x86_get_image_name(num)) { strcpy(s, " "); substrcpy(s + 1, p, 2); - strcat(s, " *."); + strcat(s, " "); + strcat(s, x86_get_image_name(num)); } else { - if (p[0] == 'F') strcpy(s, " Load *."); - else strcpy(s, " Mount *."); + if (strlen(s)) + { + strcpy(s, " "); + substrcpy(s + 1, p, 2); + strcat(s, " *."); + } + else + { + if (p[0] == 'F') strcpy(s, " Load *."); + else strcpy(s, " Mount *."); + } + pos = s + strlen(s); + substrcpy(pos, p, 1); + strcpy(pos, GetExt(pos)); } - pos = s + strlen(s); - substrcpy(pos, p, 1); - strcpy(pos, GetExt(pos)); MenuWrite(entry, s, menusub == selentry, d); // add bit in menu mask @@ -1863,7 +1875,7 @@ void HandleUI(void) else if (p[0] == 'S' && (select || recent)) { ioctl_index = 0; - if (p[1] >= '0' && p[1] <= '3') ioctl_index = p[1] - '0'; + if ((p[1] >= '0' && p[1] <= '3') || is_x86()) ioctl_index = p[1] - '0'; substrcpy(ext, p, 1); while (strlen(ext) % 3) strcat(ext, " "); @@ -1872,11 +1884,11 @@ void HandleUI(void) fs_MenuCancel = MENU_8BIT_MAIN1; strcpy(fs_pFileExt, ext); - memcpy(Selected_tmp, Selected_S[ioctl_index & 3], sizeof(Selected_tmp)); + memcpy(Selected_tmp, Selected_S[(int)ioctl_index], sizeof(Selected_tmp)); if (is_pce() || is_megacd()) { int num = ScanDirectory(Selected_tmp, SCANF_INIT, fs_pFileExt, 0); - memcpy(Selected_tmp, Selected_S[ioctl_index & 3], sizeof(Selected_tmp)); + memcpy(Selected_tmp, Selected_S[(int)ioctl_index], sizeof(Selected_tmp)); if (num == 1) { @@ -2052,11 +2064,11 @@ void HandleUI(void) case MENU_8BIT_MAIN_IMAGE_SELECTED: { - menustate = selPath[0] ? MENU_NONE1 : MENU_8BIT_MAIN1; - HandleUI(); + menustate = MENU_8BIT_MAIN1; + if (selPath[0] && !is_x86()) MenuHide(); printf("Image selected: %s\n", selPath); - memcpy(Selected_S[ioctl_index & 3], selPath, sizeof(Selected_S[ioctl_index & 3])); + memcpy(Selected_S[(int)ioctl_index], selPath, sizeof(Selected_S[(int)ioctl_index])); char idx = user_io_ext_idx(selPath, fs_pFileExt) << 6 | ioctl_index; if (addon[0] == 'f' && addon[1] != '1') process_addon(addon, idx); @@ -2082,7 +2094,7 @@ void HandleUI(void) if (addon[0] == 'f' && addon[1] == '1') process_addon(addon, idx); - recent_update(SelectedDir, Selected_S[ioctl_index & 3], SelectedLabel, ioctl_index + 500); + recent_update(SelectedDir, Selected_S[(int)ioctl_index], SelectedLabel, ioctl_index + 500); } break; diff --git a/support/x86/x86.cpp b/support/x86/x86.cpp index 5df736b..f20c378 100644 --- a/support/x86/x86.cpp +++ b/support/x86/x86.cpp @@ -38,6 +38,7 @@ #include "../../file_io.h" #include "../../fpga_io.h" #include "x86_share.h" +#include "x86_ide.h" #define FLOPPY0_BASE_OLD 0x8800 #define HDD0_BASE_OLD 0x8840 @@ -67,16 +68,14 @@ #define BIOS_SIZE 0x10000 static int newcore = 0; +static int v3 = 0; -#define IOWR(base, reg, value) dma_set((base) + (newcore ? (reg) : ((reg)<<2)), value) +#define IOWR(base, reg, value) x86_dma_set((base) + (newcore ? (reg) : ((reg)<<2)), value) typedef struct { uint32_t ver; - char fdd0_name[1024]; - char fdd1_name[1024]; - char hdd0_name[1024]; - char hdd1_name[1024]; + char img_name[6][1024]; } x86_config; static x86_config config; @@ -117,16 +116,16 @@ static uint32_t dma_get(uint32_t address) } */ -static void dma_set(uint32_t address, uint32_t data) +void x86_dma_set(uint32_t address, uint32_t data) { EnableIO(); spi8(UIO_DMA_WRITE); spi32_w(address); - spi32_w(data); + if (v3) spi_w((uint16_t)data); else spi32_w(data); DisableIO(); } -static void dma_sendbuf(uint32_t address, uint32_t length, uint32_t *data) +void x86_dma_sendbuf(uint32_t address, uint32_t length, uint32_t *data) { EnableIO(); fpga_spi_fast(UIO_DMA_WRITE); @@ -139,14 +138,14 @@ static void dma_sendbuf(uint32_t address, uint32_t length, uint32_t *data) { uint8_t *buf = (uint8_t*)data; length *= 4; - while (length--) spi32_w(*buf++); + while (length--) if (v3) spi_w(*buf++); else spi32_w(*buf++); } } else while (length--) spi32_w(*data++); DisableIO(); } -static void dma_recvbuf(uint32_t address, uint32_t length, uint32_t *data) +void x86_dma_recvbuf(uint32_t address, uint32_t length, uint32_t *data) { EnableIO(); fpga_spi_fast(UIO_DMA_READ); @@ -154,12 +153,16 @@ static void dma_recvbuf(uint32_t address, uint32_t length, uint32_t *data) fpga_spi_fast(0); if (newcore) { - if (address <= FDD0_BASE_NEW) fpga_spi_fast_block_read((uint16_t*)data, length * 2); + if (address < FDD0_BASE_NEW || (!v3 && address == FDD0_BASE_NEW)) fpga_spi_fast_block_read((uint16_t*)data, length * 2); + else if (v3 && address == FDD0_BASE_NEW) + { + while (length--) *data++ = spi_w(0); + } else { uint8_t *buf = (uint8_t*)data; length *= 4; - while (length--) *buf++ = spi32_w(0); + while (length--) *buf++ = v3 ? spi_w(0) : spi32_w(0); } } else while (length--) *data++ = spi32_w(0); @@ -231,32 +234,26 @@ static uint8_t get_fdd_bios_type(char type) return 0x1; } -struct image_t -{ - fileTYPE f; -}; - -static image_t fdd0_image = {}; -static image_t fdd1_image = {}; -static image_t hdd0_image = {}; -static image_t hdd1_image = {}; +static fileTYPE fdd0_image = {}; +static fileTYPE fdd1_image = {}; +static fileTYPE ide_image[4] = {}; static bool boot_from_floppy = 1; -static int img_mount(image_t *img, char *name) +static int img_mount(fileTYPE *f, char *name) { - FileClose(&img->f); + FileClose(f); int writable = 0, ret = 0; if (strlen(name)) { writable = FileCanWrite(name); - ret = FileOpenEx(&img->f, name, writable ? (O_RDWR | O_SYNC) : O_RDONLY); + ret = FileOpenEx(f, name, writable ? (O_RDWR | O_SYNC) : O_RDONLY); if (!ret) printf("Failed to open file %s\n", name); } if (!ret) { - img->f.size = 0; + f->size = 0; return 0; } @@ -264,16 +261,16 @@ static int img_mount(image_t *img, char *name) return 1; } -static int img_read(image_t *img, uint32_t lba, void *buf, uint32_t cnt) +static int img_read(fileTYPE *f, uint32_t lba, void *buf, uint32_t cnt) { - if (!FileSeekLBA(&img->f, lba)) return 0; - return FileReadAdv(&img->f, buf, cnt * 512); + if (!FileSeekLBA(f, lba)) return 0; + return FileReadAdv(f, buf, cnt * 512); } -static uint32_t img_write(image_t *img, uint32_t lba, void *buf, uint32_t cnt) +static uint32_t img_write(fileTYPE *f, uint32_t lba, void *buf, uint32_t cnt) { - if (!FileSeekLBA(&img->f, lba)) return 0; - return FileWriteAdv(&img->f, buf, cnt * 512); + if (!FileSeekLBA(f, lba)) return 0; + return FileWriteAdv(f, buf, cnt * 512); } static int floppy_wait_cycles; @@ -333,14 +330,20 @@ static void fdd_set(int num, char* filename) floppy_type[num] = FDD_TYPE_1440; uint32_t base = newcore ? FDD0_BASE_NEW : FLOPPY0_BASE_OLD; - image_t *fdd_image = num ? &fdd1_image : &fdd0_image; + fileTYPE *fdd_image = num ? &fdd1_image : &fdd0_image; int floppy = img_mount(fdd_image, filename); - uint32_t size = fdd_image->f.size/512; + uint32_t size = fdd_image->size/512; printf("floppy size: %d blks\n", size); if (floppy && size) { - if (size >= 5760) floppy_type[num] = FDD_TYPE_2880; + if (size >= 8000) + { + floppy = 0; + FileClose(fdd_image); + printf("Image size is too large for floppy. Closing...\n"); + } + else if (size >= 5760) floppy_type[num] = FDD_TYPE_2880; else if (size >= 3360) floppy_type[num] = FDD_TYPE_1680; else if (size >= 2880) floppy_type[num] = FDD_TYPE_1440; else if (size >= 2400) floppy_type[num] = FDD_TYPE_1200; @@ -399,7 +402,7 @@ static void fdd_set(int num, char* filename) uint32_t subaddr = num << 7; IOWR(base + subaddr, 0x0, floppy ? 1 : 0); - IOWR(base + subaddr, 0x1, (floppy && (fdd_image->f.mode & O_RDWR)) ? 0 : 1); + IOWR(base + subaddr, 0x1, (floppy && (fdd_image->mode & O_RDWR)) ? 0 : 1); IOWR(base + subaddr, 0x2, floppy_cylinders); IOWR(base + subaddr, 0x3, floppy_spt); IOWR(base + subaddr, 0x4, floppy_total_sectors); @@ -410,159 +413,12 @@ static void fdd_set(int num, char* filename) if(!newcore) set_clock(); } -static int hdd_set(uint32_t num, char* filename) +static void hdd_set(int num, char* filename) { - uint32_t hd_cylinders; - uint32_t hd_heads; - uint32_t hd_spt; - uint32_t hd_total_sectors; - uint32_t present; - - uint32_t base = newcore ? (num ? HDD1_BASE_NEW : HDD0_BASE_NEW) : (num ? HDD1_BASE_OLD : HDD0_BASE_OLD); - image_t *img = num ? &hdd1_image : &hdd0_image; - - hd_cylinders = 0; - hd_heads = 0; - hd_spt = 0; - hd_total_sectors = 0; - - present = img_mount(img, filename); - if (!present) return 0; - - hd_heads = 16; - hd_spt = 63; - hd_cylinders = img->f.size / (hd_heads * hd_spt * 512); - - //Maximum 8GB images are supported. - if (hd_cylinders > 16383) hd_cylinders = 16383; - hd_total_sectors = hd_spt*hd_heads*hd_cylinders; - - /* - 0x00.[31:0]: identify write - 0x01.[16:0]: media cylinders - 0x02.[4:0]: media heads - 0x03.[8:0]: media spt - 0x04.[13:0]: media sectors per cylinder = spt * heads - 0x05.[31:0]: media sectors total - 0x06.[31:0]: media sd base - */ - - uint32_t identify[256] = - { - 0x0040, //word 0 - hd_cylinders, //word 1 - 0x0000, //word 2 reserved - hd_heads, //word 3 - (uint16_t)(512 * hd_spt), //word 4 - 512, //word 5 - hd_spt, //word 6 - 0x0000, //word 7 vendor specific - 0x0000, //word 8 vendor specific - 0x0000, //word 9 vendor specific - ('A' << 8) | 'O', //word 10 - ('H' << 8) | 'D', //word 11 - ('0' << 8) | '0', //word 12 - ('0' << 8) | '0', //word 13 - ('0' << 8) | ' ', //word 14 - (' ' << 8) | ' ', //word 15 - (' ' << 8) | ' ', //word 16 - (' ' << 8) | ' ', //word 17 - (' ' << 8) | ' ', //word 18 - (' ' << 8) | ' ', //word 19 - 3, //word 20 buffer type - 512, //word 21 cache size - 4, //word 22 number of ecc bytes - 0,0,0,0, //words 23..26 firmware revision - (' ' << 8) | ' ', //words 27..46 model number - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - (' ' << 8) | ' ', - 16, //word 47 max multiple sectors - 1, //word 48 dword io - 1 << 9, //word 49 lba supported - 0x0000, //word 50 reserved - 0x0200, //word 51 pio timing - 0x0200, //word 52 pio timing - 0x0007, //word 53 valid fields - hd_cylinders, //word 54 - hd_heads, //word 55 - hd_spt, //word 56 - hd_total_sectors & 0xFFFF, //word 57 - hd_total_sectors >> 16, //word 58 - 16, //word 59 multiple sectors - hd_total_sectors & 0xFFFF, //word 60 - hd_total_sectors >> 16, //word 61 - 0x0000, //word 62 single word dma modes - 0x0000, //word 63 multiple word dma modes - 0x0000, //word 64 pio modes - 120,120,120,120, //word 65..68 - 0,0,0,0,0,0,0,0,0,0,0, //word 69..79 - 0x007E, //word 80 ata modes - 0x0000, //word 81 minor version number - 1 << 14, //word 82 supported commands - (1 << 14) | (1 << 13) | (1 << 12) | (1 << 10), //word 83 - 1 << 14, //word 84 - 1 << 14, //word 85 - (1 << 14) | (1 << 13) | (1 << 12) | (1 << 10), //word 86 - 1 << 14, //word 87 - 0x0000, //word 88 - 0,0,0,0, //word 89..92 - 1 | (1 << 14) | 0x2000, //word 93 - 0,0,0,0,0,0, //word 94..99 - hd_total_sectors & 0xFFFF, //word 100 - hd_total_sectors >> 16, //word 101 - 0, //word 102 - 0, //word 103 - - 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 - - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //word 128..255 - 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, - 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, - 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, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - }; - - if (present) - { - char *name = img->f.name; - for (int i = 0; i < 20; i++) - { - if (*name) identify[27 + i] = ((*name++) << 8) | 0x20; - if (*name) identify[27 + i] = (identify[27 + i] & 0xFF00) | (*name++); - } - } - - for (int i = 0; i < 128; i++) IOWR(base, 0, present ? ((unsigned int)identify[2 * i + 1] << 16) | (unsigned int)identify[2 * i + 0] : 0); - - IOWR(base, 1, hd_cylinders); - IOWR(base, 2, hd_heads); - IOWR(base, 3, hd_spt); - IOWR(base, 4, hd_spt * hd_heads); - IOWR(base, 5, hd_spt * hd_heads * hd_cylinders); - IOWR(base, 6, 0); // 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", num, present, hd_cylinders, hd_heads, hd_spt, hd_total_sectors); - return present; + if (!v3 && num > 1) return; + uint32_t base = newcore ? ((num & (v3 ? 2 : 1)) ? HDD1_BASE_NEW : HDD0_BASE_NEW) : (num ? HDD1_BASE_OLD : HDD0_BASE_OLD); + int present = img_mount(&ide_image[num], filename); + x86_ide_set(num, base, present ? &ide_image[num] : 0, v3 ? 3 : newcore ? 2 : 0); } static uint8_t bin2bcd(unsigned val) @@ -570,6 +426,13 @@ static uint8_t bin2bcd(unsigned val) return ((val / 10) << 4) + (val % 10); } +static void check_ver() +{ + uint16_t flg = dma_sdio(0); + newcore = ((flg & 0xC000) == 0xC000); + v3 = ((flg & 0xF000) == 0xE000); +} + void x86_init() { user_io_8bit_set_status(UIO_STATUS_RESET, UIO_STATUS_RESET); @@ -579,7 +442,7 @@ void x86_init() load_bios(user_io_make_filepath(home, "boot0.rom"), 0); load_bios(user_io_make_filepath(home, "boot1.rom"), 1); - newcore = ((dma_sdio(0) & 0xC000) == 0xC000); + check_ver(); if (!newcore) { @@ -587,10 +450,10 @@ void x86_init() IOWR(PC_BUS_BASE_OLD, 1, 0x000000F0); } - fdd_set(0, config.fdd0_name); - fdd_set(1, config.fdd1_name); - hdd_set(0, config.hdd0_name); - hdd_set(1, config.hdd1_name); + x86_ide_reset(); + fdd_set(0, config.img_name[0]); + fdd_set(1, config.img_name[1]); + for (int i = 0; i < 4; i++) hdd_set(i, config.img_name[i + 2]); //-------------------------------------------------------------------------- rtc @@ -652,7 +515,7 @@ void x86_init() 0x00, //0x2B: hd 1 configuration 8/9; landing zone high 0x00, //0x2C: hd 1 configuration 9/9; sectors/track - (uint8_t)((fdd0_image.f.size && boot_from_floppy) ? 0x20 : 0x00), //0x2D: boot sequence + (uint8_t)((fdd0_image.size && boot_from_floppy) ? 0x20 : 0x00), //0x2D: boot sequence 0x00, //0x2E: checksum MSB 0x00, //0x2F: checksum LSB @@ -676,7 +539,7 @@ void x86_init() 0x00, //0x3B: ? 0x00, //0x3C: ? - 0x00, //0x3D: eltorito boot sequence; not used + (uint8_t)((fdd0_image.size && boot_from_floppy) ? 0x21 : 0x02), //0x3D: eltorito boot sequence 0x00, //0x3E: ? 0x00, //0x3F: ? @@ -687,8 +550,6 @@ void x86_init() 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - printf("cmos[0x2D] = %x\n", cmos[0x2d]); - //count checksum unsigned short sum = 0; for (int i = 0x10; i <= 0x2D; i++) sum += cmos[i]; @@ -701,7 +562,7 @@ void x86_init() user_io_8bit_set_status(0, UIO_STATUS_RESET); } -static void img_io(image_t *img, uint32_t basereg, uint8_t read, int sz) +static void img_io(fileTYPE *img, uint32_t basereg, uint8_t read, int sz) { struct sd_param_t { @@ -712,12 +573,12 @@ static void img_io(image_t *img, uint32_t basereg, uint8_t read, int sz) static struct sd_param_t sd_params = {}; static uint32_t secbuf[128 * 16]; - dma_recvbuf(basereg, sizeof(sd_params) >> 2, (uint32_t*)&sd_params); + x86_dma_recvbuf(basereg, sizeof(sd_params) >> 2, (uint32_t*)&sd_params); - if (sz == 1 && (sd_params.lba >> 31)) + if (sz == 1 && (sd_params.lba >> 15)) { // Floppy B: - sd_params.lba &= 0x7FFFFFFF; + sd_params.lba &= 0x7FFF; img = &fdd1_image; } @@ -726,11 +587,11 @@ static void img_io(image_t *img, uint32_t basereg, uint8_t read, int sz) { //printf("Read: 0x%08x, %d, %d\n", basereg, sd_params.lba, sd_params.cnt); - if (img->f.size) + if (img->size) { if (img_read(img, sd_params.lba, &secbuf, sz)) { - dma_sendbuf(basereg + 255, sz * 128, secbuf); + x86_dma_sendbuf(basereg + 255, sz * 128, secbuf); res = 1; } } @@ -742,19 +603,19 @@ static void img_io(image_t *img, uint32_t basereg, uint8_t read, int sz) if (!res) { memset(secbuf, 0, sz * 512); - dma_sendbuf(basereg + 255, sz * 128, secbuf); + x86_dma_sendbuf(basereg + 255, sz * 128, secbuf); } } else { //printf("Write: 0x%08x, 0x%08x, %d\n", basereg, sd_params.lba, sd_params.cnt); - dma_recvbuf(basereg + 255, sd_params.cnt * 128, secbuf); - if (img->f.size) + x86_dma_recvbuf(basereg + 255, sd_params.cnt * 128, secbuf); + if (img->size) { if (sd_params.cnt > 0 && sd_params.cnt <= 16) { - if (img->f.mode & O_RDWR) + if (img->mode & O_RDWR) { if (img_write(img, sd_params.lba, secbuf, sd_params.cnt)) { @@ -789,18 +650,18 @@ void img_io_old(uint8_t sd_req) static struct sd_param_t sd_params = {}; static uint32_t secbuf[128 * 4]; - dma_recvbuf(SD_BASE_OLD + (4 << 2), sizeof(sd_params) >> 2, (uint32_t*)&sd_params); + x86_dma_recvbuf(SD_BASE_OLD + (4 << 2), sizeof(sd_params) >> 2, (uint32_t*)&sd_params); - image_t *img; + fileTYPE *img; switch (sd_params.addr) { case IMG_TYPE_HDD0_OLD: //printf("HDD0 req\n"); - img = &hdd0_image; + img = &ide_image[0]; break; case IMG_TYPE_HDD1_OLD: //printf("HDD1 req\n"); - img = &hdd1_image; + img = &ide_image[1]; break; default: //printf("FDD req\n"); @@ -813,13 +674,13 @@ void img_io_old(uint8_t sd_req) { //printf("Read(old): 0x%08x, 0x%08x, %d\n", sd_params.addr, sd_params.lba, sd_params.bl_cnt); - if (img->f.size) + if (img->size) { if (sd_params.bl_cnt > 0 && sd_params.bl_cnt <= 4) { if (img_read(img, sd_params.lba, secbuf, sd_params.bl_cnt)) { - dma_sendbuf(sd_params.addr, sd_params.bl_cnt * 128, secbuf); + x86_dma_sendbuf(sd_params.addr, sd_params.bl_cnt * 128, secbuf); res = 1; } } @@ -839,13 +700,13 @@ void img_io_old(uint8_t sd_req) { //printf("Write(old): 0x%08x, 0x%08x, %d\n", sd_params.addr, sd_params.lba, sd_params.bl_cnt); - if (img->f.size) + if (img->size) { if (sd_params.bl_cnt > 0 && sd_params.bl_cnt <= 4) { - if (img->f.mode & O_RDWR) + if (img->mode & O_RDWR) { - dma_recvbuf(sd_params.addr, sd_params.bl_cnt * 128, secbuf); + x86_dma_recvbuf(sd_params.addr, sd_params.bl_cnt * 128, secbuf); if (img_write(img, sd_params.lba, secbuf, sd_params.bl_cnt)) { res = 1; @@ -885,9 +746,13 @@ void x86_poll() { if (sd_req & 0x8000) { - if (sd_req & 3) img_io(&hdd0_image, HDD0_BASE_NEW, sd_req & 1, 16); + if (v3) x86_ide_io(0, sd_req & 7); + else if (sd_req & 3) img_io(&ide_image[0], HDD0_BASE_NEW, sd_req & 1, 16); + sd_req >>= 3; - if (sd_req & 3) img_io(&hdd1_image, HDD1_BASE_NEW, sd_req & 1, 16); + if (v3) x86_ide_io(1, sd_req & 7); + else if (sd_req & 3) img_io(&ide_image[1], HDD1_BASE_NEW, sd_req & 1, 16); + sd_req >>= 3; if (sd_req & 3) img_io(&fdd0_image, FDD0_BASE_NEW, sd_req & 1, 1); } @@ -900,30 +765,10 @@ void x86_poll() void x86_set_image(int num, char *filename) { - switch (num) - { - case 0: - memset(config.fdd0_name, 0, sizeof(config.fdd0_name)); - strcpy(config.fdd0_name, filename); - fdd_set(0, filename); - break; - - case 1: - memset(config.fdd1_name, 0, sizeof(config.fdd1_name)); - strcpy(config.fdd1_name, filename); - fdd_set(1, filename); - break; - - case 2: - memset(config.hdd0_name, 0, sizeof(config.hdd0_name)); - strcpy(config.hdd0_name, filename); - break; - - case 3: - memset(config.hdd1_name, 0, sizeof(config.hdd1_name)); - strcpy(config.hdd1_name, filename); - break; - } + memset(config.img_name[num], 0, sizeof(config.img_name[0])); + strcpy(config.img_name[num], filename); + if (num < 2) fdd_set(num, filename); + else if (v3 && x86_ide_is_placeholder(num - 2)) hdd_set(num - 2, filename); } void x86_config_save() @@ -934,7 +779,8 @@ void x86_config_save() void x86_config_load() { - newcore = ((dma_sdio(0) & 0xC000) == 0xC000); + check_ver(); + static x86_config tmp; memset(&config, 0, sizeof(config)); if (FileLoadConfig("ao486sys.cfg", &tmp, sizeof(tmp)) && (tmp.ver == CFG_VER)) @@ -952,3 +798,24 @@ void x86_set_uart_mode(int mode) { dma_sdio(mode ? 0x40 : 0x80); } + +const char* x86_get_image_name(int num) +{ + static char res[32]; + + char *name = config.img_name[num]; + if (!name[0]) return NULL; + + char *p = strrchr(name, '/'); + if (!p) p = name; + else p++; + + if (strlen(p) < 19) strcpy(res, p); + else + { + strncpy(res, p, 19); + res[19] = 0; + } + + return res; +} diff --git a/support/x86/x86.h b/support/x86/x86.h index 73343f6..045a608 100644 --- a/support/x86/x86.h +++ b/support/x86/x86.h @@ -5,10 +5,15 @@ void x86_init(); void x86_poll(); void x86_set_image(int num, char *filename); +const char* x86_get_image_name(int num); void x86_config_load(); void x86_config_save(); void x86_set_fdd_boot(uint32_t boot); void x86_set_uart_mode(int mode); +void x86_dma_set(uint32_t address, uint32_t data); +void x86_dma_sendbuf(uint32_t address, uint32_t length, uint32_t *data); +void x86_dma_recvbuf(uint32_t address, uint32_t length, uint32_t *data); + #endif diff --git a/support/x86/x86_ide.cpp b/support/x86/x86_ide.cpp new file mode 100644 index 0000000..2a9b45c --- /dev/null +++ b/support/x86/x86_ide.cpp @@ -0,0 +1,1621 @@ +#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" + +#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 + + +#if 0 + #define dbg_printf printf + #define dbg_print_regs print_regs + #define dbg_hexdump hexdump +#else + #define dbg_printf(...) void() + #define dbg_print_regs void + #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]; + +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 +{ + uint32_t base; + struct + { + fileTYPE *f; + uint32_t hd_cylinders; + uint32_t hd_heads; + uint32_t hd_spt; + uint32_t hd_total_sectors; + uint32_t present; + uint32_t placeholder; + uint32_t cd; + + struct + { + uint32_t start; + uint32_t length; + uint32_t skip; + uint16_t sectorSize; + uint8_t attr; + uint8_t mode2; + } + tracks[2]; + + uint8_t load_state; + uint16_t id[256]; + } drive[2]; + + uint32_t state; + uint32_t null; + uint32_t prepcnt; + regs_t regs; +} ide_config; + +static ide_config ide_inst[2] = {}; + +static void print_regs(regs_t *regs) +{ + printf("\nIDE regs:\n"); + printf(" io_done: %02X\n", regs->io_done); + printf(" features: %02X\n", regs->features); + printf(" sec_cnt: %02X\n", regs->sector_count); + printf(" sector: %02X\n", regs->sector); + printf(" cylinder: %04X\n", regs->cylinder); + printf(" head: %02X\n", regs->head); + printf(" drv: %02X\n", regs->drv); + printf(" lba: %02X\n", regs->lba); + printf(" command: %02X\n", regs->cmd); +} + +static void get_regs(ide_config *ide) +{ + uint32_t data[3]; + x86_dma_recvbuf(ide->base, 3, data); + + ide->regs.io_done = (uint8_t)(data[0] & 1); + ide->regs.features = (uint8_t)(data[0] >> 8); + ide->regs.sector_count = (uint8_t)(data[0] >> 16); + ide->regs.sector = (uint8_t)(data[0] >> 24); + + ide->regs.cylinder = data[1] & 0xFFFF; + ide->regs.head = (data[2] >> 16) & 0xF; + ide->regs.drv = (data[2] >> 20) & 1; + ide->regs.lba = (data[2] >> 22) & 1; + ide->regs.cmd = data[2] >> 24; + + ide->regs.error = 0; + ide->regs.status = 0; + + dbg_print_regs(&ide->regs); +} + +static void set_regs(ide_config *ide) +{ + uint32_t data[3]; + + if(!(ide->regs.status & (ATA_STATUS_BSY | ATA_STATUS_ERR))) ide->regs.status |= ATA_STATUS_SKC; + + data[0] = (ide->drive[ide->regs.drv].cd) ? 0x80 : ide->regs.io_size; + data[0] |= ide->regs.error << 8; + data[0] |= ide->regs.sector_count << 16; + data[0] |= ide->regs.sector << 24; + + data[1] = ide->regs.cylinder; + + data[2] = (ide->drive[ide->regs.drv].cd) ? ide->regs.pkt_io_size : 0; + data[2] |= ide->regs.head << 16; + data[2] |= ide->regs.drv << 20; + data[2] |= (ide->regs.lba ? 7 : 5) << 21; + 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 void ParseIsoFile(ide_config *ide, int drv) +{ + memset(ide->drive[drv].tracks, 0, sizeof(ide->drive[drv].tracks)); + ide->drive[drv].tracks[0].attr = 0x40; //data track + if (!ide->drive[drv].f) + { + printf("No CD!\n"); + return; + } + + if(!x86_check_iso_file(ide->drive[drv].f, &ide->drive[drv].tracks[0].mode2, &ide->drive[drv].tracks[0].sectorSize)) + { + printf("Fail to parse ISO!\n"); + return; + } + + ide->drive[drv].tracks[0].length = ide->drive[drv].f->size / ide->drive[drv].tracks[0].sectorSize; + + // lead-out track (track 2) + ide->drive[drv].tracks[1].start = ide->drive[drv].tracks[0].length; + + printf("ISO: mode2 = %d, sectorSize = %d, sectors = %d\n", ide->drive[drv].tracks[0].mode2, ide->drive[drv].tracks[0].sectorSize, ide->drive[drv].tracks[0].length); +} + +typedef struct SMSF +{ + //! \brief Time, minutes field + unsigned char min; + //! \brief Time, seconds field + unsigned char sec; + //! \brief Time, frame field + unsigned char fr; +} TMSF; + +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(ide_config *ide, int& start_track_num, int& end_track_num, TMSF& lead_out_msf) +{ + if (!ide->drive[ide->regs.drv].tracks[0].length) return 0; + + start_track_num = 1; + end_track_num = 1; + lead_out_msf = frames_to_msf(ide->drive[ide->regs.drv].tracks[1].start + REDBOOK_FRAME_PADDING); + return 1; +} + +static int GetCDTrackInfo(ide_config *ide, int requested_track_num, TMSF& start_msf, unsigned char& attr) +{ + if (!ide->drive[ide->regs.drv].tracks[0].length + || requested_track_num < 1 + || (unsigned int)requested_track_num >= (sizeof(ide->drive[ide->regs.drv].tracks)/sizeof(ide->drive[ide->regs.drv].tracks[0]))) + { + return 0; + } + + start_msf = frames_to_msf(ide->drive[ide->regs.drv].tracks[requested_track_num - 1].start + REDBOOK_FRAME_PADDING); + attr = ide->drive[ide->regs.drv].tracks[requested_track_num - 1].attr; + return 1; +} + +static int read_toc(ide_config *ide, 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(ide, 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(ide, 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(ide, 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(ide_config *ide, unsigned char& attr, unsigned char& track_num, unsigned char& index, TMSF& relative_msf, TMSF& absolute_msf) +{ + attr = ide->drive[ide->regs.drv].tracks[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(ide_config *ide, 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, pause; + 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(ide, attr, track, index, rel, abs)) + { + printf("ATAPI READ SUBCHANNEL unable to read current pos\n"); + memset(buf, 0, 8); + return 8; + } + + playing = pause = false; + + 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; + } + + hexdump(buf, write - buf); + return write - buf; +} + +void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver) +{ + int drv = (ver == 3) ? (num & 1) : 0; + if (ver == 3) num >>= 1; + + ide_inst[num].base = baseaddr; + ide_inst[num].drive[drv].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; + + ide_inst[num].drive[drv].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 && x86_check_iso_file(f, 0, 0); + if (ide_inst[num].drive[drv].cd) printf("Image recognized as ISO file\n"); + + if (ver == 3) + { + if (f && ide_inst[num].drive[drv].placeholder && !ide_inst[num].drive[drv].cd) + { + printf("Cannot hot-mount HDD image to CD!\n"); + FileClose(ide_inst[num].drive[drv].f); + ide_inst[num].drive[drv].f = 0; + f = 0; + ide_inst[num].drive[drv].present = 0; + } + + ide_inst[num].drive[drv].placeholder = (num && !drv); + 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; + + 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, 0x200, 1); + } + else if (!ide_inst[num].drive[drv].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); + + //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) + { + uint32_t identify[256] = + { + 0x0040, //word 0 + ide_inst[num].drive[drv].hd_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 + 0x0000, //word 7 vendor specific + 0x0000, //word 8 vendor specific + 0x0000, //word 9 vendor specific + ('A' << 8) | 'O', //word 10 + ('H' << 8) | 'D', //word 11 + ('0' << 8) | '0', //word 12 + ('0' << 8) | '0', //word 13 + ('0' << 8) | ' ', //word 14 + (' ' << 8) | ' ', //word 15 + (' ' << 8) | ' ', //word 16 + (' ' << 8) | ' ', //word 17 + (' ' << 8) | ' ', //word 18 + (' ' << 8) | ' ', //word 19 + 3, //word 20 buffer type + 512, //word 21 cache size + 4, //word 22 number of ecc bytes + 0,0,0,0, //words 23..26 firmware revision + (' ' << 8) | ' ', //words 27..46 model number + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + 0x8010, //word 47 max multiple sectors + 1, //word 48 dword io + 1 << 9, //word 49 lba supported + 0x4001, //word 50 reserved + 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 + 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 + 0x0000, //word 62 single word dma modes + 0x0000, //word 63 multiple word dma modes + 0x0000, //word 64 pio modes + 120,120,120,120, //word 65..68 + 0,0,0,0,0,0,0,0,0,0,0, //word 69..79 + 0x007E, //word 80 ata modes + 0x0000, //word 81 minor version number + (1 << 14) | (1 << 9), //word 82 supported commands + (1 << 14) | (1 << 13) | (1 << 12) | (1 << 10), //word 83 + 1 << 14, //word 84 + (1 << 14) | (1 << 9), //word 85 + (1 << 14) | (1 << 13) | (1 << 12) | (1 << 10), //word 86 + 1 << 14, //word 87 + 0x0000, //word 88 + 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 + + 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 + + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //word 128..255 + 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, + 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, + 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, + 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]; + } + else + { + uint32_t identify[256] = + { + 0x8580, //word 0 + 0x0000, //word 1 + 0x0000, //word 2 reserved + 0x0000, //word 3 + 0x0000, //word 4 + 0x0000, //word 5 + 0x0000, //word 6 + 0x0000, //word 7 vendor specific + 0x0000, //word 8 vendor specific + 0x0000, //word 9 vendor specific + ('A' << 8) | 'O', //word 10 + ('C' << 8) | 'D', //word 11 + ('0' << 8) | '0', //word 12 + ('0' << 8) | '0', //word 13 + ('0' << 8) | ' ', //word 14 + (' ' << 8) | ' ', //word 15 + (' ' << 8) | ' ', //word 16 + (' ' << 8) | ' ', //word 17 + (' ' << 8) | ' ', //word 18 + (' ' << 8) | ' ', //word 19 + 0x0000, //word 20 buffer type + 0x0000, //word 21 cache size + 0x0000, //word 22 number of ecc bytes + 0,0,0,0, //words 23..26 firmware revision + (' ' << 8) | ' ', //words 27..46 model number + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + (' ' << 8) | ' ', + 0x0000, //word 47 + 0x0000, //word 48 + 1 << 9, //word 49 lba supported + 0x0000, //word 50 + 0x0000, //word 51 + 0x0000, //word 52 + 0x0007, //word 53 valid fields + 0x0000, //word 54 + 0x0000, //word 55 + 0x0000, //word 56 + 0x0000, //word 57 + 0x0000, //word 58 + 0x0000, //word 59 + 0x0000, //word 60 + 0x0000, //word 61 + 0x0000, //word 62 + 0x0000, //word 63 multiple word dma modes + 0x0000, //word 64 pio modes + 120,120,120,120, //word 65..68 + 0,0,0,0,0,0,0,0,0,0,0, //word 69..79 + 0x007E, //word 80 ata modes + 0x0000, //word 81 minor version number + (1 << 9) | (1 << 4), //word 82 supported commands + (1 << 14), //word 83 + 1 << 14, //word 84 + (1 << 14) | (1 << 9) | (1 << 4), //word 85 + 0, //word 86 + 1 << 14, //word 87 + 0x0000, //word 88 + 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 + 0x0000, //word 100 + 0x0000, //word 101 + 0x0000, //word 102 + 0x0000, //word 103 + + 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 + + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //word 128..255 + 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, + 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, + 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, + 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]; + + ParseIsoFile(&ide_inst[num], drv); + ide_inst[num].drive[drv].load_state = ide_inst[num].drive[drv].f ? 1 : 3; + } + + if (ide_inst[num].drive[drv].present) + { + 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 (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); + + 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, 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); +} + +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 (ide->state == IDE_STATE_INIT_RW) + { + //printf("Read from LBA: %d\n", lba); + 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); + + ide_send_data(buf, cnt * 128); + + lba += cnt; + ide->regs.sector_count -= cnt; + + ide->regs.sector = lba; + lba >>= 8; + ide->regs.cylinder = lba; + lba >>= 16; + ide->regs.head = lba & 0xF; + + ide->state = ide->regs.sector_count ? IDE_STATE_WAIT_RD : IDE_STATE_WAIT_END; + + ide->regs.io_size = cnt; + ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ; + 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; + + ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ; + + if (ide->state == IDE_STATE_INIT_RW) + { + uint32_t lba = ide->regs.sector | (ide->regs.cylinder << 8) | (ide->regs.head << 24); + //printf("Write to LBA: %d\n", lba); + ide->null = !FileSeekLBA(ide->drive[ide->regs.drv].f, lba); + ide->regs.status &= ~ATA_STATUS_IRQ; + } + + ide->state = IDE_STATE_WAIT_WR; + ide->regs.io_size = ide->prepcnt; + 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); + + uint32_t lba = ide->regs.sector | (ide->regs.cylinder << 8) | (ide->regs.head << 24); + lba += ide->prepcnt; + ide->regs.sector_count -= ide->prepcnt; + + ide->regs.sector = lba; + lba >>= 8; + ide->regs.cylinder = lba; + lba >>= 16; + ide->regs.head = lba & 0xF; +} + +static int handle_ide(ide_config *ide) +{ + switch (ide->regs.cmd) + { + case 0xEC: // identify + { + //print_regs(&ide->regs); + ide_send_data(ide->drive[ide->regs.drv].id, 128); + + uint8_t drv = ide->regs.drv; + memset(&ide->regs, 0, sizeof(ide->regs)); + 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->state = IDE_STATE_WAIT_END; + } + break; + + case 0x20: // read with retry + case 0x21: // read + case 0xC4: // read multiple + { + if (!ide->regs.lba) + { + printf("(!) Unsupported Non-LBA read!\n"); + return 1; + } + + ide->state = IDE_STATE_INIT_RW; + process_read(ide); + } + break; + + case 0x30: // write with retry + case 0x31: // write + case 0xC5: // write multiple + { + if (!ide->regs.lba) + { + printf("(!) Unsupported Non-LBA write!\n"); + return 1; + } + + ide->state = IDE_STATE_INIT_RW; + prep_write(ide); + } + break; + + case 0xC6: // set multople + { + if (!ide->regs.sector_count || ide->regs.sector_count > io_max_size) + { + return 1; + } + + ide->regs.status = ATA_STATUS_RDY; + set_regs(ide); + } + break; + + case 0x08: // reset (fail) + printf("reset for ide 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].tracks[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].tracks[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].tracks[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; +} + +static void cd_err_nomedium(ide_config *ide) +{ + ide->state = IDE_STATE_IDLE; + ide->regs.sector_count = 3; + ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_ERR | ATA_STATUS_IRQ; + ide->regs.error = (2 << 4) | ATA_ERR_ABRT; + 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(ide_config *ide) +{ + switch (ide->drive[ide->regs.drv].load_state) + { + case 3: + set_sense(2, 0x3A); + break; + + case 2: + set_sense(2, 4, 1); + ide->drive[ide->regs.drv].load_state--; + break; + + case 1: + set_sense(2, 28, 0); + ide->drive[ide->regs.drv].load_state--; + break; + + default: + set_sense(0); + break; + } + + cddbg_hexdump(buf, 18); + return 18; +} + +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 0x28: // read sectors + //printf("** Read Sectors\n"); + //hexdump(cmdbuf, 12, 0); + ide->regs.pkt_cnt = ((cmdbuf[7] << 8) | cmdbuf[8]); + ide->regs.pkt_lba = ((cmdbuf[2] << 24) | (cmdbuf[3] << 16) | (cmdbuf[4] << 8) | cmdbuf[5]); + if (!ide->regs.pkt_cnt) + { + ide->state = IDE_STATE_IDLE; + ide->regs.sector_count = 3; + ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_IRQ; + ide->regs.error = 0; + set_regs(ide); + break; + } + ide->state = IDE_STATE_INIT_RW; + if(!ide->drive[ide->regs.drv].load_state) process_cd_read(ide); + else cd_err_nomedium(ide); + 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_err_nomedium(ide); + break; + + case 0x5A: // mode sense + pkt_send(ide, buf, mode_sense(cmdbuf[2])); + break; + + case 0x42: // read sub + pkt_send(ide, buf, read_subchannel(ide, cmdbuf)); + break; + + case 0x43: // read TOC + pkt_send(ide, buf, read_toc(ide, cmdbuf)); + break; + + case 0x12: // inquiry + pkt_send(ide, buf, cd_inquiry(cmdbuf[4])); + break; + + case 0x03: // request sense + cddbg_printf("get sense:\n"); + pkt_send(ide, buf, get_sense(ide)); + break; + + case 0x00: // test unit ready + if (!ide->drive[ide->regs.drv].load_state) + { + ide->state = IDE_STATE_IDLE; + ide->regs.sector_count = 3; + ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_IRQ; + ide->regs.error = 0; + set_regs(ide); + } + else cd_err_nomedium(ide); + break; + + default: + err = 1; + break; + } + + if (err) + { + printf("(!) Error in packet command %02X\n", cmdbuf[0]); + hexdump(cmdbuf, 12, 0); + ide->state = IDE_STATE_IDLE; + ide->regs.sector_count = 3; + ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_ERR | ATA_STATUS_IRQ; + ide->regs.error = ATA_ERR_ABRT; + set_regs(ide); + } +} + +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->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); + return 1; + } + + return 0; +} + +void x86_ide_io(int num, int req) +{ + ide_config *ide = &ide_inst[num]; + + if (req == 0) // no request + { + if (ide->state == IDE_STATE_RESET) + { + ide->state = IDE_STATE_IDLE; + + ide->regs.status = ATA_STATUS_RDY; + set_regs(ide); + + printf("IDE %04X reset finish\n", ide->base); + } + } + else if (req == 4) // command + { + ide->state = IDE_STATE_IDLE; + get_regs(ide); + + int err = 0; + + if (ide->drive[ide->regs.drv].cd) err = handle_cd(ide); + else if (!ide->drive[ide->regs.drv].present) err = 1; + else err = handle_ide(ide); + + if (err) + { + printf("** err (drv=%d)!\n", ide->regs.drv); + ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_ERR | ATA_STATUS_IRQ; + ide->regs.error = ATA_ERR_ABRT; + set_regs(ide); + } + } + else if (req == 5) // data request + { + dbg_printf("IDE data request\n"); + if (ide->state == IDE_STATE_WAIT_END) + { + ide->state = IDE_STATE_IDLE; + ide->regs.status = ATA_STATUS_RDY; + set_regs(ide); + } + else if (ide->state == IDE_STATE_WAIT_RD) + { + process_read(ide); + } + else if (ide->state == IDE_STATE_WAIT_WR) + { + process_write(ide); + if (ide->regs.sector_count) + { + prep_write(ide); + } + else + { + ide->state = IDE_STATE_IDLE; + ide->regs.status = ATA_STATUS_RDY; + set_regs(ide); + } + } + else if (ide->state == IDE_STATE_WAIT_PKT_CMD) + { + cddbg_printf("packet cmd received\n"); + process_pkt_cmd(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 + { + ide->state = IDE_STATE_IDLE; + ide->regs.sector_count = 3; + ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_IRQ; + ide->regs.error = 0; + set_regs(ide); + } + } + else + { + printf("(!) IDE unknown state!\n"); + 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); + } + } + else if (req == 6) // reset + { + if (ide->state != IDE_STATE_RESET) + { + printf("IDE %04X reset start\n", ide->base); + } + + get_regs(ide); + ide->regs.head = 0; + ide->regs.error = 0; + ide->regs.sector = 1; + ide->regs.sector_count = 1; + 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->state = IDE_STATE_RESET; + } +} + +int x86_ide_is_placeholder(int num) +{ + return ide_inst[num / 2].drive[num & 1].placeholder; +} + +void x86_ide_reset() +{ + ide_inst[0].drive[0].placeholder = 0; + ide_inst[0].drive[1].placeholder = 0; + ide_inst[1].drive[0].placeholder = 0; + ide_inst[1].drive[1].placeholder = 0; +} \ No newline at end of file diff --git a/support/x86/x86_ide.h b/support/x86/x86_ide.h new file mode 100644 index 0000000..63cbf7c --- /dev/null +++ b/support/x86/x86_ide.h @@ -0,0 +1,11 @@ +#ifndef X86_IDE_H +#define X86_IDE_H + +#include "x86.h" + +void x86_ide_set(uint32_t num, uint32_t baseaddr, fileTYPE *f, int ver); +void x86_ide_io(int num, int req); +int x86_ide_is_placeholder(int num); +void x86_ide_reset(); + +#endif diff --git a/user_io.cpp b/user_io.cpp index dc087db..abed8e4 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -418,7 +418,8 @@ static void parse_config() if (i>=2 && p && p[0]) { //skip Disable/Hide masks - while((p[0] == 'H' || p[0] == 'D') && strlen(p)>=2) p += 2; + while((p[0] == 'H' || p[0] == 'D' || p[0] == 'h' || p[0] == 'd') && strlen(p)>=2) p += 2; + if (p[0] == 'P') p += 2; if (p[0] == 'J') {