/* Copyright 2008, 2009 Jakub Bednarski Copyright 2017, 2018 Sorgelig This file is part of Minimig Minimig is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Minimig is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ // 2009-11-22 - read/write multiple implemented // 2018-05-13 - 4xIDE implemented // 2018-05-xx - Use RDB CHS values if valid // 2018-05-29 - LBA mode implemented #include #include #include "../../hardware.h" #include "../../file_io.h" #include "minimig_hdd.h" #include "../../menu.h" #include "minimig_config.h" #include "../../debug.h" #include "../../user_io.h" #define CMD_IDECMD 0x04 #define CMD_IDEDAT 0x08 #define CMD_IDE_REGS_RD 0x80 #define CMD_IDE_REGS_WR 0x90 #define CMD_IDE_DATA_WR 0xA0 #define CMD_IDE_DATA_RD 0xB0 #define CMD_IDE_STATUS_WR 0xF0 #define IDE_STATUS_END 0x80 #define IDE_STATUS_IRQ 0x10 #define IDE_STATUS_RDY 0x08 #define IDE_STATUS_REQ 0x04 #define IDE_STATUS_ERR 0x01 #define ACMD_RECALIBRATE 0x10 #define ACMD_DIAGNOSTIC 0x90 #define ACMD_IDENTIFY_DEVICE 0xEC #define ACMD_INITIALIZE_PARAMETERS 0x91 #define ACMD_READ_SECTORS 0x20 #define ACMD_WRITE_SECTORS 0x30 #define ACMD_READ_MULTIPLE 0xC4 #define ACMD_WRITE_MULTIPLE 0xC5 #define ACMD_SET_MULTIPLE_MODE 0xC6 #define SWAP(a) ((((a)&0x000000ff)<<24)|(((a)&0x0000ff00)<<8)|(((a)&0x00ff0000)>>8)|(((a)&0xff000000)>>24)) #define SWAPW(a) ((((a)<<8)&0xff00)|(((a)>>8)&0x00ff)) // hardfile structure typedef struct { int unit; int enabled; fileTYPE file; uint32_t cylinders; uint16_t heads; uint16_t sectors; uint16_t sectors_per_block; int32_t offset; // if a partition, the lba offset of the partition. Can be negative if we've synthesized an RDB. uint8_t lu; int32_t lba, nextlba; uint16_t sector; uint16_t cylinder; uint16_t head; uint16_t sector_count; } hdfTYPE; static hdfTYPE HDF[4] = {}; static uint8_t sector_buffer[512]; static void CalcGeometry(hdfTYPE *hdf) { uint32_t head = 0, cyl = 0, spt = 0; uint32_t sptt[] = { 63, 127, 255, 0 }; uint32_t total = hdf->file.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; } hdf->cylinders = cyl; hdf->heads = (uint16_t)head; hdf->sectors = (uint16_t)spt; } static void GetRDBGeometry(hdfTYPE *hdf) { struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer; hdf->heads = SWAP(rdb->rdb_Heads); hdf->sectors = SWAP(rdb->rdb_Sectors); hdf->cylinders = SWAP(rdb->rdb_Cylinders); if (hdf->sectors > 255 || hdf->heads > 16) { printf("ATTN: Illegal CHS value(s)."); if (!(hdf->sectors & 1) && (hdf->sectors < 512) && (hdf->heads <= 8)) { printf(" Translate: sectors %d->%d, heads %d->%d.\n", hdf->sectors, hdf->sectors / 2, hdf->heads, hdf->heads * 2); hdf->sectors /= 2; hdf->heads *= 2; return; } printf(" DANGEROUS: Cannot translate to legal CHS values. Re-calculate the CHS.\n"); CalcGeometry(hdf); } } static void SetHardfileGeometry(hdfTYPE *hdf, int isHDF) { struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer; uint8_t flg = 0; hdf->offset = 0; for (int i = 0; i<16; ++i) { if (!FileReadSec(&hdf->file, sector_buffer)) break; for (int i = 0; i < 512; i++) flg |= sector_buffer[i]; if (rdb->rdb_ID == RDB_MAGIC) { printf("Found RDB header -> native Amiga image.\n"); GetRDBGeometry(hdf); return; } } if (isHDF && flg) { //use UAE settings. hdf->heads = 1; hdf->sectors = 32; int spc = hdf->heads * hdf->sectors; hdf->cylinders = hdf->file.size / (512 * spc) + 1; hdf->offset = -spc; printf("No RDB header found in HDF image. Assume it's image of single partition. Use Virtual RDB header.\n"); } else { CalcGeometry(hdf); printf("No RDB header found. Possible non-Amiga or empty image.\n"); } } static uint8_t GetDiskStatus(void) { uint8_t status; EnableFpga(); status = (uint8_t)(spi_w(0) >> 8); spi_w(0); spi_w(0); DisableFpga(); return status; } static uint32_t RDBChecksum(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; ilba) { case 0: { // RDB struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer; 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 = hdf->cylinders; rdb->rdb_Sectors = hdf->sectors; rdb->rdb_Heads = hdf->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*)(sector_buffer); for (int i = 0; i < 40; i++) p[i] = SWAP(p[i]); RDBChecksum(p, 1); break; } case 1: { // Partition struct PartitionBlock *pb = (struct PartitionBlock *)sector_buffer; 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] = hdf->unit + '0'; pb->pb_Environment.de_TableSize = 0x10; pb->pb_Environment.de_SizeBlock = 0x80; pb->pb_Environment.de_Surfaces = hdf->heads; pb->pb_Environment.de_SectorPerBlock = 1; pb->pb_Environment.de_BlocksPerTrack = hdf->sectors; pb->pb_Environment.de_Reserved = 2; pb->pb_Environment.de_LowCyl = 1; pb->pb_Environment.de_HighCyl = hdf->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*)(sector_buffer); for (int i = 0; i < 64; i++) p[i] = SWAP(p[i]); RDBChecksum(p, 1); break; } } } // builds Identify Device struct static void IdentifyDevice(uint16_t *pBuffer, hdfTYPE *hdf) { char *p, x; int i; uint32_t total_sectors = hdf->cylinders * hdf->heads * hdf->sectors; memset(pBuffer, 0, 512); if(hdf->enabled) { pBuffer[0] = 1 << 6; // hard disk pBuffer[1] = hdf->cylinders; // cyl count pBuffer[3] = hdf->heads; // head count pBuffer[6] = hdf->sectors; // sectors per track // FIXME - can get serial no from card itself. memcpy((char*)&pBuffer[10], "MiniMigHardfile0000 ", 20); // serial number - byte swapped p = (char*)&pBuffer[27]; if (hdf->offset < 0) { memcpy((char*)&pBuffer[23], ".000 ", 8); // firmware version - byte swapped memcpy(p, "DON'T REPARTITION! ", 40); } else { memcpy((char*)&pBuffer[23], ".100 ", 8); // firmware version - byte swapped memcpy(p, "MiSTer ", 40); // model name - byte swapped p += 8; char *s = strrchr(minimig_config.hardfile[hdf->unit].filename, '/'); if (s) s++; else s = minimig_config.hardfile[hdf->unit].filename; i = strlen(s); if (i > 32) s += i - 32; for (i = 0; (x = s[i]) && i < 16; i++) p[i] = x; // copy file name as model name } p = (char*)&pBuffer[27]; for (i = 0; i < 40; i += 2) { char c = p[i]; p[i] = p[i + 1]; p[i + 1] = c; } } pBuffer[47] = 0x8010; // maximum sectors per block in Read/Write Multiple command pBuffer[49] = 1<<9; // LBA support pBuffer[53] = 1; pBuffer[54] = hdf->cylinders; pBuffer[55] = hdf->heads; pBuffer[56] = hdf->sectors; pBuffer[57] = (uint16_t)total_sectors; pBuffer[58] = (uint16_t)(total_sectors >> 16); pBuffer[60] = (uint16_t)total_sectors; pBuffer[61] = (uint16_t)(total_sectors >> 16); } static void WriteTaskFile(uint8_t error, uint8_t sector_count, uint8_t sector_number, uint8_t cylinder_low, uint8_t cylinder_high, uint8_t drive_head) { EnableFpga(); spi_w(CMD_IDE_REGS_WR<<8); // write task file registers command spi_w(0); // dummy spi_w(0); // dummy spi_w(0); // data (dummy) spi_w(error); // error spi_w(sector_count); // sector count spi_w(sector_number); // sector number spi_w(cylinder_low); // cylinder low spi_w(cylinder_high); // cylinder high spi_w(drive_head); // drive/head DisableFpga(); } static void WriteStatus(uint8_t status) { EnableFpga(); spi_w((CMD_IDE_STATUS_WR<<8) | status); spi_w(0); spi_w(0); DisableFpga(); } static void ATA_Recalibrate(uint8_t* tfr, hdfTYPE *hdf) { // Recalibrate 0x10-0x1F (class 3 command: no data) (void)hdf; hdd_debugf("IDE%d: Recalibrate", hdf->unit); WriteTaskFile(0, 0, tfr[6] & 0x40 ? 0 : 1, 0, 0, tfr[6] & 0xF0); WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ); } static void ATA_Diagnostic(uint8_t* tfr, hdfTYPE *hdf) { // Execute Drive Diagnostic (0x90) (void)hdf; (void)tfr; hdd_debugf("IDE: Drive Diagnostic"); WriteTaskFile(1, 0, 0, 0, 0, 0); WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ); } static void ATA_IdentifyDevice(uint8_t* tfr, hdfTYPE *hdf) { // Identify Device (0xec) hdd_debugf("IDE%d: Identify Device", hdf->unit); IdentifyDevice((uint16_t*)sector_buffer, hdf); WriteTaskFile(0, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]); WriteStatus(IDE_STATUS_RDY); // pio in (class 1) command type EnableFpga(); spi_w(CMD_IDE_DATA_WR<<8); // write data command spi_w(0); spi_w(0); spi_block_write_16be((uint16_t*)sector_buffer); DisableFpga(); WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ); } static void ATA_Initialize(uint8_t* tfr, hdfTYPE *hdf) { // Initialize Device Parameters (0x91) (void)hdf; hdd_debugf("Initialize Device Parameters"); hdd_debugf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X", hdf->unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]); WriteTaskFile(0, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]); WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ); } static void ATA_SetMultipleMode(uint8_t* tfr, hdfTYPE *hdf) { // Set Multiple Mode (0xc6) hdd_debugf("Set Multiple Mode"); hdd_debugf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X", hdf->unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]); hdf->sectors_per_block = tfr[2]; WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ); } static int Preface(uint8_t* tfr, hdfTYPE *hdf) { hdf->sector = tfr[3]; hdf->cylinder = tfr[4] | (tfr[5] << 8); hdf->head = tfr[6] & 0x0F; hdf->lu = tfr[6] & 0xF0; hdf->sector_count = tfr[2]; if (hdf->sector_count == 0) hdf->sector_count = 256; uint8_t uselba = hdf->lu & 0x40; if (uselba) { hdf->lba = (hdf->head << 24) | (hdf->cylinder << 8) | hdf->sector; } else { hdf->lba = hdf->cylinder; hdf->lba *= hdf->heads; hdf->lba += hdf->head; hdf->lba *= hdf->sectors; hdf->lba += hdf->sector - 1; } //printf("setCHS: %s: %d,%d,%d -> %d\n", uselba ? "LBA" : "CHS", hdf->cylinder, hdf->head, hdf->sector, hdf->lba); hdf->nextlba = hdf->lba; if (hdf->enabled && hdf->lba >= 0 && hdf->file.size) { FileSeekLBA(&hdf->file, (hdf->lba + hdf->offset) < 0 ? 0 : hdf->lba + hdf->offset); return 1; } return 0; } static void nextCHS(hdfTYPE *hdf) { // do not increment after last sector if (hdf->sector_count) { hdf->nextlba++; if (hdf->lu & 0x40) { hdf->sector = (uint8_t)hdf->nextlba; hdf->cylinder = (uint16_t)(hdf->nextlba >> 8); hdf->head = 0xF & (uint8_t)(hdf->nextlba >> 24); } else { if (hdf->sector == hdf->sectors) { hdf->sector = 1; hdf->head++; if (hdf->head == hdf->heads) { hdf->head = 0; hdf->cylinder++; } } else { hdf->sector++; } } } } static void updateTaskFile(hdfTYPE *hdf) { WriteTaskFile(0, hdf->sector_count, hdf->sector, (uint8_t)hdf->cylinder, (uint8_t)(hdf->cylinder >> 8), (uint8_t)(hdf->lu | hdf->head)); } static void ReadSector(hdfTYPE *hdf) { // sector outside limit (fake rdb header) if ((hdf->lba + hdf->offset) < 0) FakeRDB(hdf); else FileReadSec(&hdf->file, sector_buffer); } static void SendSector() { EnableFpga(); spi_w(CMD_IDE_DATA_WR << 8); // write data command spi_w(0); spi_w(0); spi_block_write_16be((uint16_t*)sector_buffer); DisableFpga(); } static void RecvSector() { EnableFpga(); spi_w(CMD_IDE_DATA_RD << 8); // read data command spi_w(0); spi_w(0); spi_block_read_16be((uint16_t*)sector_buffer); DisableFpga(); } static void WriteSector(hdfTYPE *hdf) { //Do not write to fake RDB header if ((hdf->lba + hdf->offset) < 0) return; //Write RDB header, grab the CHS! if (!hdf->offset && hdf->lba < 16 && (*(uint32_t*)sector_buffer) == RDB_MAGIC) { printf("Writing RDB header, LBA=%d: ", hdf->lba); uint32_t sum = RDBChecksum((uint32_t*)sector_buffer, 0); if (sum) { printf("Checksumm is incorrect(0x%08X)! Ignore the RDB parameters.\n", sum); } else { GetRDBGeometry(hdf); printf("Using new CHS: %u/%u/%u (%llu MB)\n", hdf->cylinders, hdf->heads, hdf->sectors, ((((uint64_t)hdf->cylinders) * hdf->heads * hdf->sectors) >> 11)); } } FileWriteSec(&hdf->file, sector_buffer); } // Read Sectors (0x20) static void ATA_ReadSectors(uint8_t* tfr, hdfTYPE *hdf) { WriteStatus(IDE_STATUS_RDY); // pio in (class 1) command type if(Preface(tfr, hdf)) { while (hdf->sector_count) { while (!(GetDiskStatus() & CMD_IDECMD)); // wait for empty sector buffer WriteStatus(IDE_STATUS_IRQ); ReadSector(hdf); // to be modified sector of first partition if (!hdf->unit && !hdf->lba) { struct RigidDiskBlock *rdb = (struct RigidDiskBlock *)sector_buffer; if (rdb->rdb_ID == RDB_MAGIC) { // adjust checksum by the difference between old and new flag value rdb->rdb_ChkSum = SWAP(SWAP(rdb->rdb_ChkSum) + SWAP(rdb->rdb_Flags) - 0x12); rdb->rdb_Flags = SWAP(0x12); } } SendSector(); hdf->lba++; hdf->sector_count--; nextCHS(hdf); updateTaskFile(hdf); } } WriteStatus(IDE_STATUS_END); } // multiple sector transfer per IRQ static void ATA_ReadMultiple(uint8_t* tfr, hdfTYPE *hdf) { WriteStatus(IDE_STATUS_RDY); // pio in (class 1) command type if (Preface(tfr, hdf)) { while (hdf->sector_count) { while (!(GetDiskStatus() & CMD_IDECMD)); // wait for empty sector buffer uint16_t block_count = hdf->sector_count; if (block_count > hdf->sectors_per_block) block_count = hdf->sectors_per_block; WriteStatus(IDE_STATUS_IRQ); while (block_count--) { ReadSector(hdf); SendSector(); hdf->lba++; hdf->sector_count--; nextCHS(hdf); } updateTaskFile(hdf); } } WriteStatus(IDE_STATUS_END); } static void ATA_WriteSectors(uint8_t* tfr, hdfTYPE *hdf) { WriteStatus(IDE_STATUS_REQ); // pio out (class 2) command type if (Preface(tfr, hdf)) { hdf->lba += hdf->offset; while (hdf->sector_count) { while (!(GetDiskStatus() & CMD_IDEDAT)); // wait for full write buffer RecvSector(); hdf->sector_count--; nextCHS(hdf); updateTaskFile(hdf); WriteStatus(hdf->sector_count ? IDE_STATUS_IRQ : IDE_STATUS_END | IDE_STATUS_IRQ); WriteSector(hdf); hdf->lba++; } } } static void ATA_WriteMultiple(uint8_t* tfr, hdfTYPE *hdf) { WriteStatus(IDE_STATUS_REQ); // pio out (class 2) command type if (Preface(tfr, hdf)) { hdf->lba += hdf->offset; while (hdf->sector_count) { uint16_t block_count = hdf->sector_count; if (block_count > hdf->sectors_per_block) block_count = hdf->sectors_per_block; while (block_count) { while (!(GetDiskStatus() & CMD_IDEDAT)); // wait for full write buffer RecvSector(); WriteSector(hdf); hdf->lba++; block_count--; hdf->sector_count--; nextCHS(hdf); } updateTaskFile(hdf); WriteStatus(hdf->sector_count ? IDE_STATUS_IRQ : IDE_STATUS_END | IDE_STATUS_IRQ); } } } void HandleHDD(uint8_t c1, uint8_t c2) { (void)c2; if (c1 & CMD_IDECMD) { uint8_t unit = 0; uint8_t tfr[8]; DISKLED_ON; EnableFpga(); spi_w(CMD_IDE_REGS_RD<<8); // read task file registers spi_w(0); spi_w(0); for (int i = 0; i < 8; i++) { uint16_t tmp = spi_w(0); tfr[i] = (uint8_t)tmp; if (i == 6) unit = ((tmp >> 7) & 2) | ((tmp >> 4) & 1); } DisableFpga(); //printf("IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X\n", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]); hdfTYPE *hdf = &HDF[unit]; if (hdf->enabled) { if ((tfr[7] & 0xF0) == ACMD_RECALIBRATE) ATA_Recalibrate (tfr, hdf); else if (tfr[7] == ACMD_DIAGNOSTIC) ATA_Diagnostic (tfr, hdf); else if (tfr[7] == ACMD_IDENTIFY_DEVICE) ATA_IdentifyDevice (tfr, hdf); else if (tfr[7] == ACMD_INITIALIZE_PARAMETERS) ATA_Initialize (tfr, hdf); else if (tfr[7] == ACMD_SET_MULTIPLE_MODE) ATA_SetMultipleMode (tfr, hdf); else if (tfr[7] == ACMD_READ_SECTORS) ATA_ReadSectors (tfr, hdf); else if (tfr[7] == ACMD_READ_MULTIPLE) ATA_ReadMultiple (tfr, hdf); else if (tfr[7] == ACMD_WRITE_SECTORS) ATA_WriteSectors (tfr, hdf); else if (tfr[7] == ACMD_WRITE_MULTIPLE) ATA_WriteMultiple (tfr, hdf); else { printf("Unknown ATA command: IDE%d: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X\n", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]); WriteTaskFile(0x04, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]); WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ | IDE_STATUS_ERR); } } else { printf("IDE%d not enabled: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X\n", unit, tfr[0], tfr[1], tfr[2], tfr[3], tfr[4], tfr[5], tfr[6], tfr[7]); WriteTaskFile(0x04, tfr[2], tfr[3], tfr[4], tfr[5], tfr[6]); WriteStatus(IDE_STATUS_END | IDE_STATUS_IRQ | IDE_STATUS_ERR); } DISKLED_OFF; } } uint8_t OpenHardfile(uint8_t unit) { hdfTYPE *hdf = &HDF[unit]; hdf->unit = unit; hdf->enabled = 0; if (minimig_config.enable_ide && minimig_config.hardfile[unit].enabled) { printf("\nChecking HDD %d\n", unit); if (minimig_config.hardfile[unit].filename[0]) { if (FileOpenEx(&hdf->file, minimig_config.hardfile[unit].filename, FileCanWrite(minimig_config.hardfile[unit].filename) ? O_RDWR : O_RDONLY)) { hdf->enabled = 1; printf("file: \"%s\": ", hdf->file.name); SetHardfileGeometry(hdf, !strcasecmp(".hdf", minimig_config.hardfile[unit].filename + strlen(minimig_config.hardfile[unit].filename) - 4)); printf("size: %llu (%llu MB)\n", hdf->file.size, hdf->file.size >> 20); printf("CHS: %u/%u/%u", hdf->cylinders, hdf->heads, hdf->sectors); printf(" (%llu MB), ", ((((uint64_t)hdf->cylinders) * hdf->heads * hdf->sectors) >> 11)); printf("Offset: %d\n", hdf->offset); return 1; } } printf("HDD %d: not present\n", unit); } // close if opened earlier. FileClose(&hdf->file); return 0; } int checkHDF(const char* name, struct RigidDiskBlock **rdb) { fileTYPE file = {}; *rdb = NULL; if (FileOpenEx(&file, name, O_RDONLY)) { *rdb = (struct RigidDiskBlock *)sector_buffer; for (int i = 0; i<16; ++i) { if (!FileReadSec(&file, sector_buffer)) break; if ((*rdb)->rdb_ID == RDB_MAGIC) { FileClose(&file); (*rdb)->rdb_Heads = SWAP((*rdb)->rdb_Heads); (*rdb)->rdb_Sectors = SWAP((*rdb)->rdb_Sectors); (*rdb)->rdb_Cylinders = SWAP((*rdb)->rdb_Cylinders); return ((*rdb)->rdb_Heads <= 16 && (*rdb)->rdb_Sectors <= 255 && (*rdb)->rdb_Cylinders <= 65536); } } FileClose(&file); return 1; // non-HDF file } return 0; }