#include #include #include #include #include #include #include #include #include #include #include #include #include #include "support/x86/x86.h" #include "support/minimig/minimig_hdd.h" #include "support/minimig/minimig_config.h" #include "spi.h" #include "user_io.h" #include "file_io.h" #include "hardware.h" #include "ide.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 #if 0 #define dbg2_printf printf #else #define dbg2_printf(...) void() #endif #define IDE0_BASE 0xF000 #define IDE1_BASE 0xF100 #define ide_send_data(databuf, size) ide_sendbuf(ide, 255, (size), (uint16_t*)(databuf)) #define ide_recv_data(databuf, size) ide_recvbuf(ide, 255, (size), (uint16_t*)(databuf)) #define SWAP(a) ((((a)&0x000000ff)<<24)|(((a)&0x0000ff00)<<8)|(((a)&0x00ff0000)>>8)|(((a)&0xff000000)>>24)) void ide_reg_set(ide_config *ide, uint16_t reg, uint16_t value) { EnableIO(); spi8(UIO_DMA_WRITE); spi32_w(ide->base + reg); spi_w(value); DisableIO(); } void ide_sendbuf(ide_config *ide, uint16_t reg, uint32_t length, uint16_t *data) { EnableIO(); fpga_spi_fast(UIO_DMA_WRITE); fpga_spi_fast(ide->base + reg); fpga_spi_fast(0); fpga_spi_fast_block_write(data, length); DisableIO(); } void ide_recvbuf(ide_config *ide, uint16_t reg, uint32_t length, uint16_t *data) { EnableIO(); fpga_spi_fast(UIO_DMA_READ); fpga_spi_fast(ide->base + reg); fpga_spi_fast(0); fpga_spi_fast_block_read(data, length); DisableIO(); } const uint32_t ide_io_max_size = 32; uint8_t ide_buf[ide_io_max_size * 512]; ide_config ide_inst[2] = {}; uint16_t ide_check(int status) { uint16_t res; EnableIO(); res = spi_w(UIO_DMA_SDIO); if (status || !res) res = (uint8_t)spi_w((uint16_t)status); DisableIO(); return res; } int ide_img_mount(fileTYPE *f, const char *name, int rw) { FileClose(f); int writable = 0, ret = 0; int len = strlen(name); if (len) { const char *ext = name + len - 4; if (!strncasecmp(".chd", ext, 4)) { ret = 1; } else { writable = rw && FileCanWrite(name); ret = FileOpenEx(f, name, writable ? (O_RDWR | O_SYNC) : O_RDONLY); if (!ret) printf("Failed to open file %s\n", name); } } if (!ret) { f->size = 0; return 0; } printf("Mount %s as %s\n", name, writable ? "read-write" : "read-only"); return 1; } void ide_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); } void ide_get_regs(ide_config *ide) { uint32_t data[3]; ide_recvbuf(ide, 0, 6, (uint16_t*)data); ide->regs.io_done = (uint8_t)(data[0] & 1); ide->regs.io_fast = (uint8_t)(data[0] & 2); 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); } void ide_set_regs(ide_config *ide) { if (!(ide->regs.status & (ATA_STATUS_BSY | ATA_STATUS_ERR))) ide->regs.status |= ATA_STATUS_DSC; uint8_t data[12] = { (uint8_t)((ide->drive[ide->regs.drv].cd) ? 0x80 : ide->regs.io_size), (uint8_t)(ide->regs.error), (uint8_t)(ide->regs.sector_count), (uint8_t)(ide->regs.sector), (uint8_t)(ide->regs.cylinder), (uint8_t)(ide->regs.cylinder >> 8), (uint8_t)(ide->regs.cylinder >> 16), (uint8_t)(ide->regs.cylinder >> 24), (uint8_t)(ide->drive[ide->regs.drv].cd ? ide->regs.pkt_io_size : 0), (uint8_t)(ide->drive[ide->regs.drv].cd ? ide->regs.pkt_io_size >> 8 : 0), (uint8_t)((ide->regs.lba ? 0xE0 : 0xA0) | (ide->regs.drv ? 0x10 : 0x00) | ide->regs.head), (uint8_t)(ide->regs.status) }; //hexdump(data, 12, ide->base); ide_sendbuf(ide, 0, 6, (uint16_t*)data); } static void calc_geometry(chs_t *chs, uint64_t size) { uint32_t head = 0, cyl = 0, spt = 0; uint32_t sptt[] = { 63, 127, 255, 0 }; uint32_t total = size / 512; for (int i = 0; sptt[i] != 0; i++) { spt = sptt[i]; for (head = 4; head <= 16; head++) { cyl = total / (head * spt); if (total <= 1024 * 1024) { if (cyl <= 1023) break; } else { if (cyl < 16383) break; if (cyl < 32767 && head >= 5) break; if (cyl <= 65536) break; } } if (head <= 16) break; } chs->cylinders = cyl; chs->heads = (uint16_t)head; chs->sectors = (uint16_t)spt; } static void get_rdb_geometry(RigidDiskBlock *rdb, chs_t *chs, uint64_t size) { chs->heads = SWAP(rdb->rdb_Heads); chs->sectors = SWAP(rdb->rdb_Sectors); chs->cylinders = SWAP(rdb->rdb_Cylinders); if (chs->sectors > 255 || chs->heads > 16) { printf("ATTN: Illegal CHS value(s)."); if (!(chs->sectors & 1) && (chs->sectors < 512) && (chs->heads <= 8)) { printf(" Translate: sectors %d->%d, heads %d->%d.\n", chs->sectors, chs->sectors / 2, chs->heads, chs->heads * 2); chs->sectors /= 2; chs->heads *= 2; return; } printf(" DANGEROUS: Cannot translate to legal CHS values. Re-calculate the CHS.\n"); calc_geometry(chs, size); } } static void guess_geometry(fileTYPE *f, chs_t *chs, int allow_vrdb) { uint8_t flg = 0; chs->offset = 0; for (int i = 0; i < 16; ++i) { struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)ide_buf; if (!FileReadSec(f, ide_buf)) break; for (int i = 0; i < 512; i++) flg |= ide_buf[i]; if (rdb->rdb_ID == RDB_MAGIC) { printf("Found RDB header -> native Amiga image.\n"); get_rdb_geometry(rdb, chs, f->size); return; } } if (allow_vrdb && flg) { chs->heads = 16; chs->sectors = 128; for (int i = 32; i <= 2048; i <<= 1) { int cylinders = f->size / (512 * i) + 1; if (cylinders < 65536) { chs->sectors = (i < 128) ? i : 128; chs->heads = i / chs->sectors; break; } } int spc = chs->heads * chs->sectors; chs->cylinders = f->size / (512 * spc) + 1; if (chs->cylinders > 65535) chs->cylinders = 65535; chs->offset = -spc; printf("No RDB header found in HDF image. Assume it's image of single partition. Use Virtual RDB header.\n"); } else { calc_geometry(chs, f->size); if(allow_vrdb) printf("No RDB header found. Possible non-Amiga or empty image.\n"); } } static void ide_set_geometry(drive_t *drive, uint16_t sectors, uint16_t heads) { int info = 0; if (drive->heads != heads || drive->spt != sectors) { info = 1; printf("SPT=%d, Heads=%d\n", sectors, heads); } drive->heads = heads ? heads : 16; drive->spt = sectors ? sectors : 256; uint32_t cylinders = drive->f->size / (drive->heads * drive->spt * 512); if (drive->offset) { cylinders++; drive->offset = drive->heads * drive->spt; } if (cylinders > 65535) cylinders = 65535; //Maximum 137GB images are supported. drive->cylinders = cylinders; if(info) printf("New SPT=%d, Heads=%d, Cylinders=%d\n", drive->spt, drive->heads, drive->cylinders); } static uint32_t checksum_rdb(uint32_t *p, int set) { uint32_t count = SWAP(p[1]); uint32_t result = 0; if (set) p[2] = 0; for (uint32_t i = 0; i < count; ++i) result += SWAP(p[i]); if (!set) return result; result = 0 - result; p[2] = SWAP(result); return 0; } static void fill_fake_rdb(drive_t *drive, uint32_t sector, int cnt) { printf("fill_fake_rdb(%u,%d)\n", sector, cnt); uint8_t *buff = ide_buf; memset(ide_buf, 0, sizeof(ide_buf)); while (cnt) { // if we're asked for LBA 0 we create an RDSK block, and if LBA 1, a PART block if (sector == 0) { // RDB struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)buff; rdb->rdb_ID = 'R' << 24 | 'D' << 16 | 'S' << 8 | 'K'; rdb->rdb_Summedlongs = 0x40; rdb->rdb_HostID = 0x07; rdb->rdb_BlockBytes = 0x200; rdb->rdb_Flags = 0x12; // (Disk ID valid, no LUNs after this one) rdb->rdb_BadBlockList = 0xffffffff; // We don't provide a bad block list rdb->rdb_PartitionList = 1; rdb->rdb_FileSysHeaderList = 0xffffffff; rdb->rdb_DriveInit = 0xffffffff; rdb->rdb_Reserved1[0] = 0xffffffff; rdb->rdb_Reserved1[1] = 0xffffffff; rdb->rdb_Reserved1[2] = 0xffffffff; rdb->rdb_Reserved1[3] = 0xffffffff; rdb->rdb_Reserved1[4] = 0xffffffff; rdb->rdb_Reserved1[5] = 0xffffffff; rdb->rdb_Cylinders = drive->cylinders; rdb->rdb_Sectors = drive->spt; rdb->rdb_Heads = drive->heads; rdb->rdb_Interleave = 1; rdb->rdb_Park = rdb->rdb_Cylinders; rdb->rdb_WritePreComp = rdb->rdb_Cylinders; rdb->rdb_ReducedWrite = rdb->rdb_Cylinders; rdb->rdb_StepRate = 3; rdb->rdb_RDBBlocksLo = 0; rdb->rdb_RDBBlocksHi = 1; rdb->rdb_LoCylinder = 1; rdb->rdb_HiCylinder = rdb->rdb_Cylinders - 1; rdb->rdb_CylBlocks = rdb->rdb_Heads * rdb->rdb_Sectors; rdb->rdb_AutoParkSeconds = 0; rdb->rdb_HighRDSKBlock = 1; strcpy(rdb->rdb_DiskVendor, "DON'T REPARTITION! 0.00"); uint32_t *p = (uint32_t*)buff; for (int i = 0; i < 40; i++) p[i] = SWAP(p[i]); } else if(sector == 1) { // Partition struct PartitionBlock *pb = (struct PartitionBlock *)buff; pb->pb_ID = 'P' << 24 | 'A' << 16 | 'R' << 8 | 'T'; pb->pb_Summedlongs = 0x40; pb->pb_HostID = 0x07; pb->pb_Next = 0xffffffff; pb->pb_Flags = 0x1; // bootable pb->pb_DevFlags = 0; strcpy(pb->pb_DriveName, "0HD\003"); // "DHx" BCPL string pb->pb_DriveName[0] = drive->drvnum + '0'; pb->pb_Environment.de_TableSize = 0x10; pb->pb_Environment.de_SizeBlock = 0x80; pb->pb_Environment.de_Surfaces = drive->heads; pb->pb_Environment.de_SectorPerBlock = 1; pb->pb_Environment.de_BlocksPerTrack = drive->spt; pb->pb_Environment.de_Reserved = 2; pb->pb_Environment.de_LowCyl = 1; pb->pb_Environment.de_HighCyl = drive->cylinders - 1; pb->pb_Environment.de_NumBuffers = 30; pb->pb_Environment.de_MaxTransfer = 0xffffff; pb->pb_Environment.de_Mask = 0x7ffffffe; pb->pb_Environment.de_DosType = 0x444f5301; uint32_t *p = (uint32_t*)buff; for (int i = 0; i < 64; i++) p[i] = SWAP(p[i]); } else { break; } checksum_rdb((uint32_t*)buff, 1); //hexdump(buff, 256); cnt--; sector++; buff += 512; } } void ide_img_set(uint32_t drvnum, fileTYPE *f, int cd, int sectors, int heads, int offset, int type) { int drv = (drvnum & 1); int port = (drvnum >> 1); drive_t *drive = &ide_inst[port].drive[drv]; ide_inst[port].base = port ? IDE1_BASE : IDE0_BASE; ide_inst[port].drive[drv].drvnum = drvnum; drive->f = f; drive->cylinders = 0; drive->heads = 0; drive->spt = 0; drive->spb = 0; drive->offset = 0; drive->type = 0; drive->present = f ? 1 : 0; ide_inst[port].state = IDE_STATE_RESET; ide_inst[port].bitoff = port * 3; drive->cd = drive->present && cd; if (f && drive->placeholder && !drive->cd) { printf("Cannot hot-mount HDD image to CD!\n"); FileClose(drive->f); drive->f = 0; f = 0; drive->present = 0; } drive->placeholder = drive->allow_placeholder; if (drive->placeholder && drive->present && !drive->cd) drive->placeholder = 0; if (drive->placeholder) drive->cd = 1; ide_reg_set(&ide_inst[port], 6, ((drive->present || drive->placeholder) ? 9 : 8) << (drv * 4)); ide_reg_set(&ide_inst[port], 6, 0x200); if(drive->f) { if (!drive->chd_f) drive->total_sectors = (drive->f->size / 512); } else { drive->total_sectors = 0; } if (!drive->cd) { if (drive->present) { ide_set_geometry(drive, sectors, heads); if (offset && drive->cylinders < 65535) drive->cylinders++; drive->offset = offset; drive->type = type; } uint16_t identify[256] = { 0x0040, //word 0 drive->cylinders, //word 1 0x0000, //word 2 reserved 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 ('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) | ' ', 0x8020, //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 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 (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 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), //word 83 1 << 14, //word 84 (1 << 14) | (1 << 9), //word 85 (1 << 14) | (1 << 13) | (1 << 12), //word 86 1 << 14, //word 87 0x0000, //word 88 0,0,0,0, //word 89..92 (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 (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 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++) drive->id[i] = identify[i]; } else { uint16_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++) drive->id[i] = identify[i]; drive->load_state = (drive->f || drive->chd_f) ? 1 : 3; } if (ide_inst[port].drive[drv].present) { char *name = ide_inst[port].drive[drv].f->name; for (int i = 0; i < 20; i++) { if (*name) drive->id[27 + i] = ((*name++) << 8) | 0x20; if (*name) drive->id[27 + i] = (drive->id[27 + i] & 0xFF00) | (*name++); } //hexdump(drive->id, 256); } 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", drvnum, drive->present, drive->cylinders, drive->heads, drive->spt, drive->total_sectors); } static uint32_t get_lba(ide_config *ide) { uint32_t lba; if (ide->regs.lba) { lba = ide->regs.sector | (ide->regs.cylinder << 8) | (ide->regs.head << 24); } else { drive_t *drive = &ide->drive[ide->regs.drv]; dbg2_printf(" CHS: %d/%d/%d (%d/%d)\n", ide->regs.cylinder, ide->regs.head, ide->regs.sector, drive->heads, drive->spt); lba = ide->regs.cylinder; lba *= drive->heads; lba += ide->regs.head; lba *= drive->spt; lba += ide->regs.sector - 1; } dbg2_printf(" LBA: %u\n", lba); return lba; } static void put_lba(ide_config *ide, uint32_t lba) { lba--; dbg2_printf(" putLBA: %u\n", lba); if (ide->regs.lba) { ide->regs.sector = lba; lba >>= 8; ide->regs.cylinder = lba; lba >>= 16; ide->regs.head = lba & 0xF; } else { drive_t *drive = &ide->drive[ide->regs.drv]; uint32_t hspt = drive->heads * drive->spt; ide->regs.cylinder = lba / hspt; lba = lba % hspt; ide->regs.head = lba / drive->spt; lba = lba % drive->spt; ide->regs.sector = lba + 1; } } inline uint16_t get_cnt(ide_config *ide) { drive_t *drive = &ide->drive[ide->regs.drv]; dbg2_printf(" Cnt: %d (max = %d)\n", ide->regs.sector_count, drive->spb); uint16_t cnt = ide->regs.sector_count; if (!cnt || cnt > drive->spb) { cnt = drive->spb; dbg2_printf(" New cnt: %d\n", cnt); } return cnt; } inline int readhdd(drive_t *drive, uint32_t lba, int cnt) { if (lba < drive->offset) { if (!drive->type) fill_fake_rdb(drive, lba, cnt); else memset(ide_buf, 0, sizeof(ide_buf)); return 1; } else { return FileReadAdv(drive->f, ide_buf, cnt * 512, -1); } } static void process_read(ide_config *ide, int multi) { uint32_t lba = get_lba(ide); uint16_t ide_req = 0; dbg2_printf(" sector_count: %d\n", ide->regs.sector_count); uint32_t cnt = multi ? get_cnt(ide) : 1; ide->null = !FileSeekLBA(ide->drive[ide->regs.drv].f, (lba <= ide->drive[ide->regs.drv].offset) ? 0 : (lba - ide->drive[ide->regs.drv].offset)); if (!ide->null) ide->null = (readhdd(&ide->drive[ide->regs.drv], lba, cnt) <= 0); if (ide->null) memset(ide_buf, 0, cnt * 512); while (1) { lba += cnt; ide->regs.sector_count -= cnt; put_lba(ide, lba); ide->regs.io_size = cnt; ide->regs.status = ATA_STATUS_RDP | ATA_STATUS_RDY | ATA_STATUS_DRQ | ATA_STATUS_IRQ; if (!ide->regs.sector_count) ide->regs.status |= ATA_STATUS_END; if (ide->regs.io_fast) { ide_set_regs(ide); ide_send_data(ide_buf, cnt * 256); } else { ide_send_data(ide_buf, cnt * 256); ide->regs.status &= ~ATA_STATUS_RDP; ide_set_regs(ide); } if (!ide->regs.sector_count) { //ATA_STATUS_END will set ATA_STATUS_RDY at the end ide->state = IDE_STATE_IDLE; break; } cnt = multi ? get_cnt(ide) : 1; if (!ide->null) ide->null = (readhdd(&ide->drive[ide->regs.drv], lba, cnt) <= 0); if (ide->null) memset(ide_buf, 0, cnt * 512); ide_req = 0; while (!ide_req) ide_req = (ide_check() >> ide->bitoff) & 7; if (ide_req != 5) { ide->state = IDE_STATE_IDLE; break; } } dbg2_printf(" finish\n"); } static void process_write(ide_config *ide, int multi) { uint32_t lba = get_lba(ide); uint32_t cnt = 1; uint16_t ide_req; ide->null = (ide->regs.cmd != 0xFA) ? !FileSeekLBA(ide->drive[ide->regs.drv].f, (lba <= ide->drive[ide->regs.drv].offset) ? 0 : (lba - ide->drive[ide->regs.drv].offset)) : 1; uint8_t irq = 0; while (1) { cnt = multi ? get_cnt(ide) : 1; ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_DRQ | irq; irq = ATA_STATUS_IRQ; ide->regs.io_size = cnt; ide_set_regs(ide); ide_req = 0; while (!ide_req) ide_req = (ide_check() >> ide->bitoff) & 7; if (ide_req != 5) { ide->state = IDE_STATE_IDLE; break; } ide_recv_data(ide_buf, cnt * 256); if (ide->regs.cmd == 0xFA) { ide->regs.sector_count = 0; char* filename = user_io_make_filepath(HomeDir(), (char*)ide_buf); int drvnum = (ide->regs.head == 1) ? 0 : (ide->regs.head == 2) ? 1 : (ide->drive[ide->regs.drv].drvnum + 2); static const char* names[6] = { "fdd0", "fdd1", "ide00", "ide01", "ide10", "ide11" }; printf("Request for new image for drive %s: %s\n", names[drvnum], filename); if(is_x86()) x86_set_image(drvnum, filename); } else { if (!ide->null) ide->null = (lba < ide->drive[ide->regs.drv].offset) ? 0 : (FileWriteAdv(ide->drive[ide->regs.drv].f, ide_buf, cnt * 512, -1) <= 0); lba += cnt; ide->regs.sector_count -= cnt; put_lba(ide, lba); } if (!ide->regs.sector_count) { ide->state = IDE_STATE_IDLE; ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_IRQ; ide_set_regs(ide); break; } } } static int handle_hdd(ide_config *ide) { switch (ide->regs.cmd) { case 0xEC: // identify { 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 | ATA_STATUS_END; ide_send_data(ide->drive[ide->regs.drv].id, 256); ide_set_regs(ide); break; case 0xC4: // read multiple if (!ide->drive[ide->regs.drv].spb) { printf("(!) Read multiple is disabled!\n"); return 1; } process_read(ide, 1); break; case 0x20: // read with retry case 0x21: // read process_read(ide, 0); break; case 0xC5: // write multiple if (!ide->drive[ide->regs.drv].spb) { printf("(!) Read multiple is disabled!\n"); return 1; } process_write(ide, 1); break; case 0x30: // write with retry case 0x31: // write process_write(ide, 0); break; case 0xFA: // mount image ide->regs.pkt_io_size = 256; process_write(ide, 0); break; case 0xC6: // set multople if (ide->regs.sector_count > ide_io_max_size) { return 1; } ide->drive[ide->regs.drv].spb = ide->regs.sector_count; printf("New block size: %d\n", ide->drive[ide->regs.drv].spb); ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_IRQ; ide_set_regs(ide); break; case 0x08: // reset (fail) printf("Reset command (08h) for HDD not supported\n"); return 1; case 0x10 ... 0x1F: // recalibrate ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_IRQ; ide->regs.cylinder = 0; ide_set_regs(ide); break; case 0x91: // initialize device parameters ide_set_geometry(&ide->drive[ide->regs.drv], ide->regs.sector_count, ide->regs.head + 1); ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_IRQ; ide_set_regs(ide); break; default: printf("(!) Unsupported command (%04X)\n", ide->base); ide_print_regs(&ide->regs); return 1; } return 0; } void ide_io(int num, int req) { ide_config *ide = &ide_inst[num]; //printf("req: %d, disk: %d\n", req, num); if (req == 0) // no request { if (ide->state == IDE_STATE_RESET) { ide->state = IDE_STATE_IDLE; ide->regs.status = ATA_STATUS_RDY; ide_set_regs(ide); printf("IDE %04X reset finish\n", ide->base); } } else if (req == 4) // command { ide->state = IDE_STATE_IDLE; ide_get_regs(ide); dbg2_printf("IDE command: %02X (on %d)\n", ide->regs.cmd, ide->regs.drv); int err = 0; if(ide->regs.cmd == 0xFA) err = handle_hdd(ide); else 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); if (err) { ide->regs.status = ATA_STATUS_RDY | ATA_STATUS_ERR | ATA_STATUS_IRQ; ide->regs.error = ATA_ERR_ABRT; ide_set_regs(ide); } } else if (req == 5) // data request { dbg2_printf("IDE data request (on %d)\n", ide->regs.drv); if (ide->state == IDE_STATE_WAIT_PKT_CMD) { cdrom_handle_pkt(ide); } else if (ide->state == IDE_STATE_WAIT_PKT_RD) { 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(ide_buf, 256); printf("mode select data:\n"); hexdump(ide_buf, ide->regs.cylinder); cdrom_reply(ide, 0); } 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; ide_set_regs(ide); } } else if (req == 6) // reset { if (ide->state != IDE_STATE_RESET) { printf("IDE %04X reset start\n", ide->base); } ide->drive[0].playing = 0; ide->drive[0].paused = 0; ide->drive[1].playing = 0; ide->drive[1].paused = 0; ide_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; ide_set_regs(ide); ide->state = IDE_STATE_RESET; } } int ide_is_placeholder(int num) { return ide_inst[num / 2].drive[num & 1].placeholder; } void ide_reset(uint8_t hotswap[4]) { 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; ide_inst[0].drive[0].allow_placeholder = hotswap[0]; ide_inst[0].drive[1].allow_placeholder = hotswap[1]; ide_inst[1].drive[0].allow_placeholder = hotswap[2]; ide_inst[1].drive[1].allow_placeholder = hotswap[3]; } int ide_open(uint8_t unit, const char* filename) { static fileTYPE hdd_file[4] = {}; chs_t chs = {}; if (!is_minimig() || ((minimig_config.ide_cfg & 1) && minimig_config.hardfile[unit].cfg)) { printf("\nChecking HDD %d\n", unit); if (filename[0] && FileOpenEx(&hdd_file[unit], filename, FileCanWrite(filename) ? O_RDWR : O_RDONLY)) { printf("file: \"%s\": ", hdd_file[unit].name); guess_geometry(&hdd_file[unit], &chs, is_minimig() && !strcasecmp(".hdf", filename + strlen(filename) - 4)); printf("size: %llu (%llu MB)\n", hdd_file[unit].size, hdd_file[unit].size >> 20); printf("CHS: %u/%u/%u", chs.cylinders, chs.heads, chs.sectors); printf(" (%llu MB), ", ((((uint64_t)chs.cylinders) * chs.heads * chs.sectors) >> 11)); printf("Offset: %d\n", chs.offset); int present = 0; int cd = 0; int len = strlen(filename); const char *ext = filename + len - 4; int vhd = (len > 4 && (!strcasecmp(ext, ".hdf") || (!strcasecmp(ext, ".vhd")))); if (!vhd) { const char *img_name = cdrom_parse(unit, filename); if (img_name) present = ide_img_mount(&hdd_file[unit], img_name, 0); if (present) cd = 1; else vhd = 1; } if (!present && vhd) present = ide_img_mount(&hdd_file[unit], filename, 1); ide_img_set(unit, present ? &hdd_file[unit] : 0, cd, chs.sectors, chs.heads, cd ? 0 : -chs.offset); if (present) return 1; } printf("HDD %d: not present\n", unit); } // close if opened earlier. ide_img_set(unit, 0, 0); FileClose(&hdd_file[unit]); return 0; }