/* * UAE - The Un*x Amiga Emulator * * IDE HD/ATAPI CD emulation * * (c) 2006 - 2015 Toni Wilen */ #define IDE_LOG 2 #include "sysconfig.h" #include "sysdeps.h" #include "options.h" #include "blkdev.h" #include "filesys.h" #include "gui.h" #include "uae.h" #include "memory.h" #include "newcpu.h" #include "threaddep/thread.h" #include "debug.h" #include "savestate.h" #include "scsi.h" #include "ide.h" /* STATUS bits */ #define IDE_STATUS_ERR 0x01 // 0 #define IDE_STATUS_IDX 0x02 // 1 #define IDE_STATUS_DRQ 0x08 // 3 #define IDE_STATUS_DSC 0x10 // 4 #define IDE_STATUS_DRDY 0x40 // 6 #define IDE_STATUS_BSY 0x80 // 7 #define ATAPI_STATUS_CHK IDE_STATUS_ERR /* ERROR bits */ #define IDE_ERR_UNC 0x40 #define IDE_ERR_MC 0x20 #define IDE_ERR_IDNF 0x10 #define IDE_ERR_MCR 0x08 #define IDE_ERR_ABRT 0x04 #define IDE_ERR_NM 0x02 #define ATAPI_ERR_EOM 0x02 #define ATAPI_ERR_ILI 0x01 /* ATAPI interrupt reason (Sector Count) */ #define ATAPI_IO 0x02 #define ATAPI_CD 0x01 #define ATAPI_MAX_TRANSFER 32768 #define MAX_IDE_MULTIPLE_SECTORS 128 uae_u16 adide_decode_word(uae_u16 w) { uae_u16 o = 0; if (w & 0x8000) o |= 0x0001; if (w & 0x0001) o |= 0x0002; if (w & 0x4000) o |= 0x0004; if (w & 0x0002) o |= 0x0008; if (w & 0x2000) o |= 0x0010; if (w & 0x0004) o |= 0x0020; if (w & 0x1000) o |= 0x0040; if (w & 0x0008) o |= 0x0080; if (w & 0x0800) o |= 0x0100; if (w & 0x0010) o |= 0x0200; if (w & 0x0400) o |= 0x0400; if (w & 0x0020) o |= 0x0800; if (w & 0x0200) o |= 0x1000; if (w & 0x0040) o |= 0x2000; if (w & 0x0100) o |= 0x4000; if (w & 0x0080) o |= 0x8000; return o; } uae_u16 adide_encode_word(uae_u16 w) { uae_u16 o = 0; if (w & 0x0001) o |= 0x8000; if (w & 0x0002) o |= 0x0001; if (w & 0x0004) o |= 0x4000; if (w & 0x0008) o |= 0x0002; if (w & 0x0010) o |= 0x2000; if (w & 0x0020) o |= 0x0004; if (w & 0x0040) o |= 0x1000; if (w & 0x0080) o |= 0x0008; if (w & 0x0100) o |= 0x0800; if (w & 0x0200) o |= 0x0010; if (w & 0x0400) o |= 0x0400; if (w & 0x0800) o |= 0x0020; if (w & 0x1000) o |= 0x0200; if (w & 0x2000) o |= 0x0040; if (w & 0x4000) o |= 0x0100; if (w & 0x8000) o |= 0x0080; return o; } static void ide_grow_buffer(struct ide_hdf *ide, int newsize) { if (ide->secbuf_size >= newsize) return; uae_u8 *oldbuf = ide->secbuf; int oldsize = ide->secbuf_size; ide->secbuf_size = newsize + 16384; if (oldsize) write_log(_T("IDE%d buffer %d -> %d\n"), ide->num, oldsize, ide->secbuf_size); ide->secbuf = xmalloc(uae_u8, ide->secbuf_size); memcpy(ide->secbuf, oldbuf, oldsize); xfree(oldbuf); } static void pw (struct ide_hdf *ide, int offset, uae_u16 w) { if (ide->byteswap) { w = (w >> 8) | (w << 8); } if (ide->adide) w = adide_decode_word(w); ide->secbuf[offset * 2 + 0] = (uae_u8)w; ide->secbuf[offset * 2 + 1] = w >> 8; } static void ps (struct ide_hdf *ide, int offset, const TCHAR *src, int max) { int i, len; char *s; s = ua (src); len = strlen (s); for (i = 0; i < max; i += 2) { char c1 = ' '; if (i < len) c1 = s[i]; char c2 = ' '; if (i + 1 < len) c2 = s[i + 1]; uae_u16 w = (c1 << 0) | (c2 << 8); if (ide->byteswap) { w = (w >> 8) | (w << 8); } if (ide->adide) w = adide_decode_word(w); ide->secbuf[offset * 2 + 0] = w >> 8; ide->secbuf[offset * 2 + 1] = w >> 0; offset++; } xfree (s); } bool ide_isdrive (struct ide_hdf *ide) { return ide && (ide->hdhfd.size != 0 || ide->atapi); } static void ide_interrupt (struct ide_hdf *ide) { ide->regs.ide_status |= IDE_STATUS_BSY; ide->regs.ide_status &= ~IDE_STATUS_DRQ; ide->irq_delay = 2; } static void ide_fast_interrupt (struct ide_hdf *ide) { ide->regs.ide_status |= IDE_STATUS_BSY; ide->regs.ide_status &= ~IDE_STATUS_DRQ; ide->irq_delay = 1; } static bool ide_interrupt_do (struct ide_hdf *ide) { uae_u8 os = ide->regs.ide_status; ide->regs.ide_status &= ~IDE_STATUS_DRQ; if (ide->intdrq) ide->regs.ide_status |= IDE_STATUS_DRQ; ide->regs.ide_status &= ~IDE_STATUS_BSY; if (IDE_LOG > 1) write_log (_T("IDE INT %02X -> %02X\n"), os, ide->regs.ide_status); ide->intdrq = false; ide->irq_delay = 0; if (ide->regs.ide_devcon & 2) return false; ide->irq_new = true; ide->irq = 1; return true; } bool ide_drq_check(struct ide_hdf *idep) { for (int i = 0; idep && i < 2; i++) { struct ide_hdf *ide = i == 0 ? idep : idep->pair; if (ide) { if (ide->regs.ide_status & IDE_STATUS_DRQ) return true; } } return false; } bool ide_irq_check(struct ide_hdf *idep, bool edge_triggered) { for (int i = 0; idep && i < 2; i++) { struct ide_hdf *ide = i == 0 ? idep : idep->pair; if (ide->irq) { if (edge_triggered) { if (ide->irq_new) { ide->irq_new = false; return true; } continue; } return true; } } return false; } bool ide_interrupt_hsync(struct ide_hdf *idep) { bool irq = false; for (int i = 0; idep && i < 2; i++) { struct ide_hdf *ide = i == 0 ? idep : idep->pair; if (ide) { if (ide->irq_delay > 0) { ide->irq_delay--; if (ide->irq_delay == 0) { ide_interrupt_do (ide); } } if (ide->irq && !(ide->regs.ide_devcon & 2)) irq = true; } } return irq; } static void ide_fail_err (struct ide_hdf *ide, uae_u8 err) { ide->regs.ide_error |= err; if (ide->ide_drv == 1 && !ide_isdrive (ide->pair)) { ide->pair->regs.ide_status |= IDE_STATUS_ERR; } ide->regs.ide_status |= IDE_STATUS_ERR; ide_interrupt (ide); } static void ide_fail (struct ide_hdf *ide) { ide_fail_err (ide, IDE_ERR_ABRT); } static void ide_data_ready (struct ide_hdf *ide) { memset (ide->secbuf, 0, ide->blocksize); ide->data_offset = 0; ide->data_size = ide->blocksize; ide->data_multi = 1; ide->intdrq = true; ide_interrupt (ide); } static void ide_recalibrate (struct ide_hdf *ide) { write_log (_T("IDE%d recalibrate\n"), ide->num); ide->regs.ide_sector = 0; ide->regs.ide_lcyl = ide->regs.ide_hcyl = 0; ide_interrupt (ide); } static void ide_identify_drive (struct ide_hdf *ide) { uae_u64 totalsecs; int v; uae_u8 *buf = ide->secbuf; TCHAR tmp[100]; bool atapi = ide->atapi; bool cf = ide->media_type > 0; if (!ide_isdrive (ide)) { ide_fail (ide); return; } memset (buf, 0, ide->blocksize); ide->byteswapped_buffer = 1; if (IDE_LOG > 0) write_log (_T("IDE%d identify drive\n"), ide->num); ide_data_ready (ide); ide->direction = 0; pw (ide, 0, atapi ? 0x85c0 : (cf ? 0x848a : (1 << 6))); pw (ide, 1, ide->hdhfd.cyls_def); pw (ide, 2, 0xc837); pw (ide, 3, ide->hdhfd.heads_def); pw (ide, 4, ide->blocksize * ide->hdhfd.secspertrack_def); pw (ide, 5, ide->blocksize); pw (ide, 6, ide->hdhfd.secspertrack_def); ps (ide, 10, _T("68000"), 20); /* serial */ pw (ide, 20, 3); pw (ide, 21, ide->blocksize); pw (ide, 22, 4); ps (ide, 23, _T("0.7"), 8); /* firmware revision */ if (ide->atapi) _tcscpy (tmp, _T("UAE-ATAPI")); else _stprintf (tmp, _T("UAE-IDE %s"), ide->hdhfd.hfd.product_id); ps (ide, 27, tmp, 40); /* model */ pw (ide, 47, MAX_IDE_MULTIPLE_SECTORS >> (ide->blocksize / 512 - 1)); /* max sectors in multiple mode */ pw (ide, 48, 1); pw (ide, 49, (1 << 9) | (1 << 8)); /* LBA and DMA supported */ pw (ide, 51, 0x200); /* PIO cycles */ pw (ide, 52, 0x200); /* DMA cycles */ pw (ide, 53, 1 | 2 | 4); pw (ide, 54, ide->hdhfd.cyls); pw (ide, 55, ide->hdhfd.heads); pw (ide, 56, ide->hdhfd.secspertrack); totalsecs = ide->hdhfd.cyls * ide->hdhfd.heads * ide->hdhfd.secspertrack; pw (ide, 57, (uae_u16)totalsecs); pw (ide, 58, (uae_u16)(totalsecs >> 16)); v = ide->multiple_mode; pw (ide, 59, (v > 0 ? 0x100 : 0) | v); totalsecs = ide->blocksize ? ide->hdhfd.size / ide->blocksize : 0; if (totalsecs > 0x0fffffff) totalsecs = 0x0fffffff; pw (ide, 60, (uae_u16)totalsecs); pw (ide, 61, (uae_u16)(totalsecs >> 16)); pw (ide, 62, 0x0f); pw (ide, 63, 0x0f); if (ide->ata_level) { pw (ide, 64, ide->ata_level ? 0x03 : 0x00); /* PIO3 and PIO4 */ pw (ide, 65, 120); /* MDMA2 supported */ pw (ide, 66, 120); pw (ide, 67, 120); pw (ide, 68, 120); pw (ide, 80, (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)); /* ATA-1 to ATA-6 */ pw (ide, 81, 0x1c); /* ATA revision */ pw (ide, 82, (1 << 14) | (atapi ? 0x10 | 4 : 0)); /* NOP, ATAPI: PACKET and Removable media features supported */ pw (ide, 83, (1 << 14) | (1 << 13) | (1 << 12) | (ide->lba48 ? (1 << 10) : 0)); /* cache flushes, LBA 48 supported */ pw (ide, 84, 1 << 14); pw (ide, 85, 1 << 14); pw (ide, 86, (1 << 14) | (1 << 13) | (1 << 12) | (ide->lba48 ? (1 << 10) : 0)); /* cache flushes, LBA 48 enabled */ pw (ide, 87, 1 << 14); pw (ide, 88, (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); /* UDMA modes */ pw (ide, 93, (1 << 14) | (1 << 13) | (1 << 0)); if (ide->lba48) { totalsecs = ide->hdhfd.size / ide->blocksize; pw (ide, 100, (uae_u16)(totalsecs >> 0)); pw (ide, 101, (uae_u16)(totalsecs >> 16)); pw (ide, 102, (uae_u16)(totalsecs >> 32)); pw (ide, 103, (uae_u16)(totalsecs >> 48)); } } } static void set_signature (struct ide_hdf *ide) { if (ide->atapi) { ide->regs.ide_sector = 1; ide->regs.ide_nsector = 1; ide->regs.ide_lcyl = 0x14; ide->regs.ide_hcyl = 0xeb; ide->regs.ide_status = 0; ide->atapi_drdy = false; } else { ide->regs.ide_nsector = 1; ide->regs.ide_sector = 1; ide->regs.ide_lcyl = 0; ide->regs.ide_hcyl = 0; ide->regs.ide_status = 0; } ide->regs.ide_error = 0x01; // device ok ide->packet_state = 0; } static void reset_device (struct ide_hdf *ide, bool both) { set_signature (ide); if (both) set_signature (ide->pair); } void ide_reset_device(struct ide_hdf *ide) { reset_device(ide, true); } static void ide_execute_drive_diagnostics (struct ide_hdf *ide, bool irq) { reset_device (ide, irq); if (irq) ide_interrupt (ide); else ide->regs.ide_status &= ~IDE_STATUS_BSY; } static void ide_initialize_drive_parameters (struct ide_hdf *ide) { if (ide->hdhfd.size) { ide->hdhfd.secspertrack = ide->regs.ide_nsector == 0 ? 256 : ide->regs.ide_nsector; ide->hdhfd.heads = (ide->regs.ide_select & 15) + 1; if (ide->hdhfd.hfd.ci.pcyls) ide->hdhfd.cyls = ide->hdhfd.hfd.ci.pcyls; else ide->hdhfd.cyls = (ide->hdhfd.size / ide->blocksize) / (ide->hdhfd.secspertrack * ide->hdhfd.heads); if (ide->hdhfd.heads * ide->hdhfd.cyls * ide->hdhfd.secspertrack > 16515072 || ide->lba48) { if (ide->hdhfd.hfd.ci.pcyls) ide->hdhfd.cyls = ide->hdhfd.hfd.ci.pcyls; else ide->hdhfd.cyls = ide->hdhfd.cyls_def; ide->hdhfd.heads = ide->hdhfd.heads_def; ide->hdhfd.secspertrack = ide->hdhfd.secspertrack_def; } } else { ide->regs.ide_error |= IDE_ERR_ABRT; ide->regs.ide_status |= IDE_STATUS_ERR; } write_log (_T("IDE%d initialize drive parameters, CYL=%d,SPT=%d,HEAD=%d\n"), ide->num, ide->hdhfd.cyls, ide->hdhfd.secspertrack, ide->hdhfd.heads); ide_interrupt (ide); } static void ide_set_multiple_mode (struct ide_hdf *ide) { write_log (_T("IDE%d drive multiple mode = %d\n"), ide->num, ide->regs.ide_nsector); ide->multiple_mode = ide->regs.ide_nsector; ide_interrupt (ide); } static void ide_set_features (struct ide_hdf *ide) { int type = ide->regs.ide_nsector >> 3; int mode = ide->regs.ide_nsector & 7; write_log (_T("IDE%d set features %02X (%02X)\n"), ide->num, ide->regs.ide_feat, ide->regs.ide_nsector); switch (ide->regs.ide_feat) { // 8-bit mode case 1: ide->mode_8bit = true; ide_interrupt(ide); break; case 0x81: ide->mode_8bit = false; ide_interrupt(ide); break; // write cache case 2: case 0x82: ide_interrupt(ide); break; default: ide_fail (ide); break; } } static void get_lbachs (struct ide_hdf *ide, uae_u64 *lbap, unsigned int *cyl, unsigned int *head, unsigned int *sec) { if (ide->lba48 && ide->lba48cmd && (ide->regs.ide_select & 0x40)) { uae_u64 lba; lba = (ide->regs.ide_hcyl << 16) | (ide->regs.ide_lcyl << 8) | ide->regs.ide_sector; lba |= ((ide->regs.ide_hcyl2 << 16) | (ide->regs.ide_lcyl2 << 8) | ide->regs.ide_sector2) << 24; *lbap = lba; } else { if (ide->regs.ide_select & 0x40) { *lbap = ((ide->regs.ide_select & 15) << 24) | (ide->regs.ide_hcyl << 16) | (ide->regs.ide_lcyl << 8) | ide->regs.ide_sector; } else { *cyl = (ide->regs.ide_hcyl << 8) | ide->regs.ide_lcyl; *head = ide->regs.ide_select & 15; *sec = ide->regs.ide_sector; *lbap = (((*cyl) * ide->hdhfd.heads + (*head)) * ide->hdhfd.secspertrack) + (*sec) - 1; } } } static int get_nsec (struct ide_hdf *ide) { if (ide->lba48 && ide->lba48cmd) return (ide->regs.ide_nsector == 0 && ide->regs.ide_nsector2 == 0) ? 65536 : (ide->regs.ide_nsector2 * 256 + ide->regs.ide_nsector); else return ide->regs.ide_nsector == 0 ? 256 : ide->regs.ide_nsector; } static int dec_nsec (struct ide_hdf *ide, int v) { if (ide->lba48 && ide->lba48cmd) { uae_u16 nsec; nsec = (ide->regs.ide_nsector2 << 8) | ide->regs.ide_nsector; nsec -= v; ide->regs.ide_nsector2 = nsec >> 8; ide->regs.ide_nsector = nsec & 0xff; return (ide->regs.ide_nsector2 << 8) | ide->regs.ide_nsector; } else { ide->regs.ide_nsector -= v; return ide->regs.ide_nsector; } } static void put_lbachs (struct ide_hdf *ide, uae_u64 lba, unsigned int cyl, unsigned int head, unsigned int sec, unsigned int inc) { if (ide->lba48 && ide->lba48cmd) { lba += inc; ide->regs.ide_hcyl = (lba >> 16) & 0xff; ide->regs.ide_lcyl = (lba >> 8) & 0xff; ide->regs.ide_sector = lba & 0xff; lba >>= 24; ide->regs.ide_hcyl2 = (lba >> 16) & 0xff; ide->regs.ide_lcyl2 = (lba >> 8) & 0xff; ide->regs.ide_sector2 = lba & 0xff; } else { if (ide->regs.ide_select & 0x40) { lba += inc; ide->regs.ide_select &= ~15; ide->regs.ide_select |= (lba >> 24) & 15; ide->regs.ide_hcyl = (lba >> 16) & 0xff; ide->regs.ide_lcyl = (lba >> 8) & 0xff; ide->regs.ide_sector = lba & 0xff; } else { sec += inc; while (sec >= ide->hdhfd.secspertrack) { sec -= ide->hdhfd.secspertrack; head++; if (head >= ide->hdhfd.heads) { head -= ide->hdhfd.heads; cyl++; } } ide->regs.ide_select &= ~15; ide->regs.ide_select |= head; ide->regs.ide_sector = sec; ide->regs.ide_hcyl = cyl >> 8; ide->regs.ide_lcyl = (uae_u8)cyl; } } } static void check_maxtransfer (struct ide_hdf *ide, int state) { if (state == 1) { // transfer was started if (ide->maxtransferstate < 2 && ide->regs.ide_nsector == 0) { ide->maxtransferstate = 1; } else if (ide->maxtransferstate == 2) { // second transfer was started (part of split) write_log (_T("IDE maxtransfer check detected split >256 block transfer\n")); ide->maxtransferstate = 0; } else { ide->maxtransferstate = 0; } } else if (state == 2) { // address was read if (ide->maxtransferstate == 1) ide->maxtransferstate++; else ide->maxtransferstate = 0; } } static void setdrq (struct ide_hdf *ide) { ide->regs.ide_status |= IDE_STATUS_DRQ; ide->regs.ide_status &= ~IDE_STATUS_BSY; } static void setbsy (struct ide_hdf *ide) { ide->regs.ide_status |= IDE_STATUS_BSY; ide->regs.ide_status &= ~IDE_STATUS_DRQ; } static void process_rw_command (struct ide_hdf *ide) { setbsy (ide); write_comm_pipe_u32 (&ide->its->requests, ide->num, 1); } static void process_packet_command (struct ide_hdf *ide) { setbsy (ide); write_comm_pipe_u32 (&ide->its->requests, ide->num | 0x80, 1); } static void atapi_data_done (struct ide_hdf *ide) { ide->regs.ide_nsector = ATAPI_IO | ATAPI_CD; ide->regs.ide_status = IDE_STATUS_DRDY; ide->data_size = 0; ide->packet_data_offset = 0; ide->data_offset = 0; } static bool atapi_set_size (struct ide_hdf *ide) { int size; size = ide->data_size; ide->data_offset = 0; if (!size) { ide->packet_state = 0; ide->packet_transfer_size = 0; return false; } if (ide->packet_state == 2) { if (size > ide->packet_data_size) size = ide->packet_data_size; if (size > ATAPI_MAX_TRANSFER) size = ATAPI_MAX_TRANSFER; ide->packet_transfer_size = size & ~1; ide->regs.ide_lcyl = size & 0xff; ide->regs.ide_hcyl = size >> 8; } else { ide->packet_transfer_size = 12; } if (IDE_LOG > 1) write_log (_T("ATAPI data transfer %d/%d bytes\n"), ide->packet_transfer_size, ide->data_size); return true; } static void atapi_packet (struct ide_hdf *ide) { ide->packet_data_offset = 0; ide->packet_data_size = (ide->regs.ide_hcyl << 8) | ide->regs.ide_lcyl; if (ide->packet_data_size == 65535) ide->packet_data_size = 65534; ide->data_size = 12; if (IDE_LOG > 0) write_log (_T("ATAPI packet command. Data size = %d\n"), ide->packet_data_size); ide->packet_state = 1; ide->data_multi = 1; ide->data_offset = 0; ide->regs.ide_nsector = ATAPI_CD; ide->regs.ide_error = 0; if (atapi_set_size (ide)) setdrq (ide); } static void do_packet_command (struct ide_hdf *ide) { memcpy (ide->scsi->cmd, ide->secbuf, 12); ide->scsi->cmd_len = 12; if (IDE_LOG > 0) { uae_u8 *c = ide->scsi->cmd; write_log (_T("ATASCSI %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x\n"), c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11]); } ide->direction = 0; scsi_emulate_analyze (ide->scsi); if (ide->scsi->direction <= 0) { // data in scsi_emulate_cmd (ide->scsi); ide->data_size = ide->scsi->data_len; ide->regs.ide_status = 0; if (ide->scsi->status) { // error ide->regs.ide_error = (ide->scsi->sense[2] << 4) | 4; atapi_data_done (ide); ide->regs.ide_status |= ATAPI_STATUS_CHK; atapi_set_size (ide); return; } else if (ide->scsi->data_len) { // data in ide_grow_buffer(ide, ide->scsi->data_len); memcpy (ide->secbuf, ide->scsi->buffer, ide->scsi->data_len); ide->regs.ide_nsector = ATAPI_IO; } else { // no data atapi_data_done (ide); } } else { // data out ide->direction = 1; ide->regs.ide_nsector = 0; ide->data_size = ide->scsi->data_len; } ide->packet_state = 2; // data phase if (atapi_set_size (ide)) ide->intdrq = true; } static void do_process_packet_command (struct ide_hdf *ide) { if (ide->packet_state == 1) { do_packet_command (ide); } else { ide->packet_data_offset += ide->packet_transfer_size; if (!ide->direction) { // data still remaining, next transfer if (atapi_set_size (ide)) ide->intdrq = true; } else { if (atapi_set_size (ide)) { ide->intdrq = true; } else { if (IDE_LOG > 1) write_log(_T("IDE%d ATAPI write finished, %d bytes\n"), ide->num, ide->data_size); memcpy (&ide->scsi->buffer, ide->secbuf, ide->data_size); ide->scsi->data_len = ide->data_size; scsi_emulate_cmd (ide->scsi); } } } ide_fast_interrupt (ide); } static void do_process_rw_command (struct ide_hdf *ide) { unsigned int cyl, head, sec, nsec, nsec_total; uae_u64 lba; bool last; ide->data_offset = 0; nsec = get_nsec (ide); get_lbachs (ide, &lba, &cyl, &head, &sec); if (IDE_LOG > 1) write_log (_T("IDE%d off=%d, nsec=%d (%d) lba48=%d\n"), ide->num, (uae_u32)lba, nsec, ide->multiple_mode, ide->lba48 + ide->lba48cmd); if (nsec * ide->blocksize > ide->hdhfd.size - lba * ide->blocksize) { nsec = (ide->hdhfd.size - lba * ide->blocksize) / ide->blocksize; if (IDE_LOG > 1) write_log (_T("IDE%d nsec changed to %d\n"), ide->num, nsec); } if (nsec <= 0) { ide_data_ready (ide); ide_fail_err (ide, IDE_ERR_IDNF); return; } nsec_total = nsec; ide_grow_buffer(ide, nsec_total * ide->blocksize); if (nsec > ide->data_multi) nsec = ide->data_multi; if (ide->buffer_offset == 0) { // store initial lba and number of sectors to transfer ide->start_lba = lba; ide->start_nsec = nsec_total; } if (ide->direction) { if (IDE_LOG > 1) write_log (_T("IDE%d write, %d/%d bytes, buffer offset %d\n"), ide->num, nsec * ide->blocksize, nsec_total * ide->blocksize, ide->buffer_offset); } else { if (ide->buffer_offset == 0) { hdf_read(&ide->hdhfd.hfd, ide->secbuf, ide->start_lba * ide->blocksize, ide->start_nsec * ide->blocksize); if (IDE_LOG > 1) write_log(_T("IDE%d initial read, %d bytes\n"), ide->num, nsec_total * ide->blocksize); } if (IDE_LOG > 1) write_log (_T("IDE%d read, read %d/%d bytes, buffer offset=%d\n"), ide->num, nsec * ide->blocksize, nsec_total * ide->blocksize, ide->buffer_offset); } ide->intdrq = true; last = dec_nsec (ide, nsec) == 0; // ATA-2 spec says CHS/LBA does only need to be updated if error condition if (ide->ata_level != 2 || !last) { put_lbachs (ide, lba, cyl, head, sec, last ? nsec - 1 : nsec); } if (last && ide->direction) { if (IDE_LOG > 1) write_log(_T("IDE%d write finished, %d bytes\n"), ide->num, ide->start_nsec * ide->blocksize); ide->intdrq = false; hdf_write (&ide->hdhfd.hfd, ide->secbuf, ide->start_lba * ide->blocksize, ide->start_nsec * ide->blocksize); } if (ide->direction) { if (last) { ide_fast_interrupt(ide); } else { ide->irq_delay = 1; } } else { if (ide->buffer_offset == 0) { ide_fast_interrupt(ide); } else { ide->irq_delay = 1; } } } static void ide_read_sectors (struct ide_hdf *ide, int flags) { unsigned int cyl, head, sec, nsec; uae_u64 lba; int multi = flags & 1; ide->lba48cmd = (flags & 2) != 0; if (multi && ide->multiple_mode == 0) { ide_fail (ide); return; } check_maxtransfer (ide, 1); gui_flicker_led (LED_HD, ide->num, 1); nsec = get_nsec (ide); get_lbachs (ide, &lba, &cyl, &head, &sec); if (lba * ide->blocksize >= ide->hdhfd.size) { ide_data_ready (ide); ide_fail_err (ide, IDE_ERR_IDNF); return; } if (IDE_LOG > 0) write_log (_T("IDE%d %s off=%d, sec=%d (%d) lba48=%d\n"), ide->num, (flags & 4) ? _T("verify") : _T("read"), (uae_u32)lba, nsec, ide->multiple_mode, ide->lba48 + ide->lba48cmd); if (flags & 4) { // verify ide_interrupt(ide); return; } ide->data_multi = multi ? ide->multiple_mode : 1; ide->data_offset = 0; ide->data_size = nsec * ide->blocksize; ide->direction = 0; ide->buffer_offset = 0; // read start: preload sector(s), then trigger interrupt. process_rw_command (ide); } static void ide_write_sectors (struct ide_hdf *ide, int flags) { unsigned int cyl, head, sec, nsec; uae_u64 lba; int multi = flags & 1; ide->lba48cmd = (flags & 2) != 0; if (multi && ide->multiple_mode == 0) { ide_fail (ide); return; } check_maxtransfer (ide, 1); gui_flicker_led (LED_HD, ide->num, 2); nsec = get_nsec (ide); get_lbachs (ide, &lba, &cyl, &head, &sec); if (lba * ide->blocksize >= ide->hdhfd.size) { ide_data_ready (ide); ide_fail_err (ide, IDE_ERR_IDNF); return; } if (IDE_LOG > 0) write_log (_T("IDE%d write off=%d, sec=%d (%d) lba48=%d\n"), ide->num, (uae_u32)lba, nsec, ide->multiple_mode, ide->lba48 + ide->lba48cmd); if (nsec * ide->blocksize > ide->hdhfd.size - lba * ide->blocksize) nsec = (ide->hdhfd.size - lba * ide->blocksize) / ide->blocksize; if (nsec <= 0) { ide_data_ready (ide); ide_fail_err (ide, IDE_ERR_IDNF); return; } ide->data_multi = multi ? ide->multiple_mode : 1; ide->data_offset = 0; ide->data_size = nsec * ide->blocksize; ide->direction = 1; ide->buffer_offset = 0; // write start: set DRQ and clear BSY. No interrupt. ide->regs.ide_status |= IDE_STATUS_DRQ; ide->regs.ide_status &= ~IDE_STATUS_BSY; } static void ide_do_command (struct ide_hdf *ide, uae_u8 cmd) { int lba48 = ide->lba48; if (IDE_LOG > 1) write_log (_T("**** IDE%d command %02X\n"), ide->num, cmd); ide->regs.ide_status &= ~ (IDE_STATUS_DRDY | IDE_STATUS_DRQ | IDE_STATUS_ERR); ide->regs.ide_error = 0; ide->intdrq = false; ide->lba48cmd = false; ide->byteswapped_buffer = 0; if (ide->atapi) { gui_flicker_led (LED_CD, ide->num, 1); ide->atapi_drdy = true; if (cmd == 0x00) { /* nop */ ide_interrupt (ide); } else if (cmd == 0x08) { /* device reset */ ide_execute_drive_diagnostics (ide, true); } else if (cmd == 0xa1) { /* identify packet device */ ide_identify_drive (ide); } else if (cmd == 0xa0) { /* packet */ atapi_packet (ide); } else if (cmd == 0x90) { /* execute drive diagnostics */ ide_execute_drive_diagnostics (ide, true); } else { ide_execute_drive_diagnostics (ide, false); ide->atapi_drdy = false; ide_fail (ide); write_log (_T("IDE%d: unknown ATAPI command 0x%02x\n"), ide->num, cmd); } } else { if (cmd == 0x10) { /* recalibrate */ ide_recalibrate (ide); } else if (cmd == 0xec) { /* identify drive */ ide_identify_drive (ide); } else if (cmd == 0x90) { /* execute drive diagnostics */ ide_execute_drive_diagnostics (ide, true); } else if (cmd == 0x91) { /* initialize drive parameters */ ide_initialize_drive_parameters (ide); } else if (cmd == 0xc6) { /* set multiple mode */ ide_set_multiple_mode (ide); } else if (cmd == 0x20 || cmd == 0x21) { /* read sectors */ ide_read_sectors(ide, 0); } else if (cmd == 0x40 || cmd == 0x41) { /* verify sectors */ ide_read_sectors(ide, 4); } else if (cmd == 0x24 && lba48) { /* read sectors ext */ ide_read_sectors (ide, 2); } else if (cmd == 0xc4) { /* read multiple */ ide_read_sectors (ide, 1); } else if (cmd == 0x29 && lba48) { /* read multiple ext */ ide_read_sectors (ide, 1|2); } else if (cmd == 0x30 || cmd == 0x31) { /* write sectors */ ide_write_sectors (ide, 0); } else if (cmd == 0x34 && lba48) { /* write sectors ext */ ide_write_sectors (ide, 2); } else if (cmd == 0xc5) { /* write multiple */ ide_write_sectors (ide, 1); } else if (cmd == 0x39 && lba48) { /* write multiple ext */ ide_write_sectors (ide, 1|2); } else if (cmd == 0x50) { /* format track (nop) */ ide_interrupt (ide); } else if (cmd == 0xef) { /* set features */ ide_set_features (ide); } else if (cmd == 0x00) { /* nop */ ide_fail (ide); } else if (cmd == 0x70) { /* seek */ ide_interrupt (ide); } else if (cmd == 0xe0 || cmd == 0xe1 || cmd == 0xe7 || cmd == 0xea) { /* standby now/idle/flush cache/flush cache ext */ ide_interrupt (ide); } else if (cmd == 0xe5) { /* check power mode */ ide->regs.ide_nsector = 0xff; ide_interrupt (ide); } else { ide_fail (ide); write_log (_T("IDE%d: unknown ATA command 0x%02x\n"), ide->num, cmd); } } } static uae_u16 ide_get_data_2(struct ide_hdf *ide, int bussize) { bool irq = false; uae_u16 v; int inc = bussize ? 2 : 1; if (ide->data_size == 0) { if (IDE_LOG > 0) write_log (_T("IDE%d DATA but no data left!? %02X PC=%08X\n"), ide->num, ide->regs.ide_status, m68k_getpc ()); if (!ide_isdrive (ide)) return 0xffff; return 0; } if (ide->packet_state) { if (bussize) { v = ide->secbuf[ide->packet_data_offset + ide->data_offset + 1] | (ide->secbuf[ide->packet_data_offset + ide->data_offset + 0] << 8); } else { v = ide->secbuf[(ide->packet_data_offset + ide->data_offset)]; } if (IDE_LOG > 4) write_log (_T("IDE%d DATA read %04x\n"), ide->num, v); ide->data_offset += inc; if (ide->data_size < 0) ide->data_size += inc; else ide->data_size -= inc; if (ide->data_offset == ide->packet_transfer_size) { if (IDE_LOG > 1) write_log (_T("IDE%d ATAPI partial read finished, %d bytes remaining\n"), ide->num, ide->data_size); if (ide->data_size == 0) { ide->packet_state = 0; atapi_data_done (ide); if (IDE_LOG > 1) write_log (_T("IDE%d ATAPI read finished, %d bytes\n"), ide->num, ide->packet_data_offset + ide->data_offset); irq = true; } else { process_packet_command (ide); } } } else { if (bussize) { v = ide->secbuf[ide->buffer_offset + ide->data_offset + 1] | (ide->secbuf[ide->buffer_offset + ide->data_offset + 0] << 8); } else { v = ide->secbuf[(ide->buffer_offset + ide->data_offset)]; } if (IDE_LOG > 4) write_log (_T("IDE%d DATA read %04x %d/%d\n"), ide->num, v, ide->data_offset, ide->data_size); ide->data_offset += inc; if (ide->data_size < 0) { ide->data_size += inc; } else { ide->data_size -= inc; if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0) { if (ide->data_size) { ide->buffer_offset += ide->data_offset; do_process_rw_command(ide); } } } if (ide->data_size == 0) { if (!(ide->regs.ide_status & IDE_STATUS_DRQ)) { write_log (_T("IDE%d read finished but DRQ was not active?\n"), ide->num); } ide->regs.ide_status &= ~IDE_STATUS_DRQ; if (IDE_LOG > 1) write_log (_T("IDE%d read finished\n"), ide->num); } } if (irq) ide_fast_interrupt (ide); return v; } uae_u16 ide_get_data(struct ide_hdf *ide) { return ide_get_data_2(ide, 1); } uae_u8 ide_get_data_8bit(struct ide_hdf *ide) { return (uae_u8)ide_get_data_2(ide, 0); } static void ide_put_data_2(struct ide_hdf *ide, uae_u16 v, int bussize) { int inc = bussize ? 2 : 1; if (IDE_LOG > 4) write_log (_T("IDE%d DATA write %04x %d/%d\n"), ide->num, v, ide->data_offset, ide->data_size); if (ide->data_size == 0) { if (IDE_LOG > 0) write_log (_T("IDE%d DATA write without request!? %02X PC=%08X\n"), ide->num, ide->regs.ide_status, m68k_getpc ()); return; } ide_grow_buffer(ide, ide->packet_data_offset + ide->data_offset + 2); if (ide->packet_state) { if (bussize) { ide->secbuf[ide->packet_data_offset + ide->data_offset + 1] = v & 0xff; ide->secbuf[ide->packet_data_offset + ide->data_offset + 0] = v >> 8; } else { ide->secbuf[(ide->packet_data_offset + ide->data_offset) ^ 1] = v; } } else { if (bussize) { ide->secbuf[ide->buffer_offset + ide->data_offset + 1] = v & 0xff; ide->secbuf[ide->buffer_offset + ide->data_offset + 0] = v >> 8; } else { ide->secbuf[(ide->buffer_offset + ide->data_offset)] = v; } } ide->data_offset += inc; ide->data_size -= inc; if (ide->packet_state) { if (ide->data_offset == ide->packet_transfer_size) { if (IDE_LOG > 0) { uae_u16 v = (ide->regs.ide_hcyl << 8) | ide->regs.ide_lcyl; write_log (_T("Data size after command received = %d (%d)\n"), v, ide->packet_data_size); } process_packet_command (ide); } } else { if (ide->data_size == 0) { process_rw_command (ide); } else if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0) { int off = ide->data_offset; do_process_rw_command(ide); ide->buffer_offset += off; } } } void ide_put_data(struct ide_hdf *ide, uae_u16 v) { ide_put_data_2(ide, v, 1); } void ide_put_data_8bit(struct ide_hdf *ide, uae_u8 v) { ide_put_data_2(ide, v, 0); } uae_u32 ide_read_reg (struct ide_hdf *ide, int ide_reg) { uae_u8 v = 0; bool isdrv = ide_isdrive (ide); if (!ide) goto end; if (ide->regs.ide_status & IDE_STATUS_BSY) ide_reg = IDE_STATUS; if (!ide_isdrive (ide)) { if (ide_reg == IDE_STATUS) { if (ide->pair->irq) ide->pair->irq = 0; if (ide_isdrive (ide->pair)) v = 0x01; else v = 0xff; } else { v = 0; } goto end; } switch (ide_reg) { case IDE_SECONDARY: case IDE_SECONDARY + 1: case IDE_SECONDARY + 2: case IDE_SECONDARY + 3: case IDE_SECONDARY + 4: case IDE_SECONDARY + 5: v = 0xff; break; case IDE_DRVADDR: v = ((ide->ide_drv ? 2 : 1) | ((ide->regs.ide_select & 15) << 2)) ^ 0xff; break; case IDE_DATA: break; case IDE_ERROR: v = ide->regs.ide_error; break; case IDE_NSECTOR: if (isdrv) { if (ide->regs.ide_devcon & 0x80) v = ide->regs.ide_nsector2; else v = ide->regs.ide_nsector; } break; case IDE_SECTOR: if (isdrv) { if (ide->regs.ide_devcon & 0x80) v = ide->regs.ide_sector2; else v = ide->regs.ide_sector; check_maxtransfer (ide, 2); } break; case IDE_LCYL: if (isdrv) { if (ide->regs.ide_devcon & 0x80) v = ide->regs.ide_lcyl2; else v = ide->regs.ide_lcyl; } break; case IDE_HCYL: if (isdrv) { if (ide->regs.ide_devcon & 0x80) v = ide->regs.ide_hcyl2; else v = ide->regs.ide_hcyl; } break; case IDE_SELECT: v = ide->regs.ide_select; break; case IDE_STATUS: ide->irq = 0; ide->irq_new = false; /* fall through */ case IDE_DEVCON: /* ALTSTATUS when reading */ if (!isdrv) { v = 0; if (ide->regs.ide_error) v |= IDE_STATUS_ERR; } else { v = ide->regs.ide_status; if (!ide->atapi || (ide->atapi && ide->atapi_drdy)) v |= IDE_STATUS_DRDY | IDE_STATUS_DSC; } break; } end: if (IDE_LOG > 2 && ide_reg > 0 && (1 || ide->num > 0)) write_log (_T("IDE%d GET register %d->%02X (%08X)\n"), ide->num, ide_reg, (uae_u32)v & 0xff, m68k_getpc ()); return v; } void ide_write_reg (struct ide_hdf *ide, int ide_reg, uae_u32 val) { if (!ide) return; ide->regs1->ide_devcon &= ~0x80; /* clear HOB */ ide->regs0->ide_devcon &= ~0x80; /* clear HOB */ if (IDE_LOG > 2 && ide_reg > 0 && (1 || ide->num > 0)) write_log (_T("IDE%d PUT register %d=%02X (%08X)\n"), ide->num, ide_reg, (uae_u32)val & 0xff, m68k_getpc ()); switch (ide_reg) { case IDE_DRVADDR: break; case IDE_DEVCON: if ((ide->regs.ide_devcon & 4) == 0 && (val & 4) != 0) { reset_device (ide, true); if (IDE_LOG > 1) write_log (_T("IDE%d: SRST\n"), ide->num); } ide->regs0->ide_devcon = val; ide->regs1->ide_devcon = val; break; case IDE_DATA: break; case IDE_ERROR: ide->regs0->ide_feat2 = ide->regs0->ide_feat; ide->regs0->ide_feat = val; ide->regs1->ide_feat2 = ide->regs1->ide_feat; ide->regs1->ide_feat = val; break; case IDE_NSECTOR: ide->regs0->ide_nsector2 = ide->regs0->ide_nsector; ide->regs0->ide_nsector = val; ide->regs1->ide_nsector2 = ide->regs1->ide_nsector; ide->regs1->ide_nsector = val; break; case IDE_SECTOR: ide->regs0->ide_sector2 = ide->regs0->ide_sector; ide->regs0->ide_sector = val; ide->regs1->ide_sector2 = ide->regs1->ide_sector; ide->regs1->ide_sector = val; break; case IDE_LCYL: ide->regs0->ide_lcyl2 = ide->regs0->ide_lcyl; ide->regs0->ide_lcyl = val; ide->regs1->ide_lcyl2 = ide->regs1->ide_lcyl; ide->regs1->ide_lcyl = val; break; case IDE_HCYL: ide->regs0->ide_hcyl2 = ide->regs0->ide_hcyl; ide->regs0->ide_hcyl = val; ide->regs1->ide_hcyl2 = ide->regs1->ide_hcyl; ide->regs1->ide_hcyl = val; break; case IDE_SELECT: ide->regs0->ide_select = val; ide->regs1->ide_select = val; #if IDE_LOG > 2 if (ide->ide_drv != (val & 0x10) ? 1 : 0) write_log (_T("DRIVE=%d\n"), (val & 0x10) ? 1 : 0); #endif ide->pair->ide_drv = ide->ide_drv = (val & 0x10) ? 1 : 0; break; case IDE_STATUS: ide->irq = 0; ide->irq_new = false; if (ide_isdrive (ide)) { ide->regs.ide_status |= IDE_STATUS_BSY; ide_do_command (ide, val); } break; } } static void *ide_thread (void *idedata) { struct ide_thread_state *its = (struct ide_thread_state*)idedata; for (;;) { uae_u32 unit = read_comm_pipe_u32_blocking (&its->requests); struct ide_hdf *ide; if (its->state == 0 || unit == 0xfffffff) break; ide = its->idetable[unit & 0x7f]; if (unit & 0x80) do_process_packet_command (ide); else do_process_rw_command (ide); } its->state = -1; return 0; } void start_ide_thread(struct ide_thread_state *its) { if (!its->state) { its->state = 1; init_comm_pipe (&its->requests, 100, 1); uae_start_thread (_T("ide"), ide_thread, its, NULL); } } void stop_ide_thread(struct ide_thread_state *its) { if (its->state > 0) { its->state = 0; write_comm_pipe_u32 (&its->requests, 0xffffffff, 1); while(its->state == 0) sleep_millis (10); its->state = 0; } } void ide_initialize(struct ide_hdf **idetable, int chpair) { struct ide_hdf *ide0 = idetable[chpair * 2 + 0]; struct ide_hdf *ide1 = idetable[chpair * 2 + 1]; ide0->regs0 = &ide0->regs; ide0->regs1 = &ide1->regs; ide0->pair = ide1; ide1->regs1 = &ide1->regs; ide1->regs0 = &ide0->regs; ide1->pair = ide0; ide0->num = chpair * 2 + 0; ide1->num = chpair * 2 + 1; reset_device (ide0, true); } void alloc_ide_mem (struct ide_hdf **idetable, int max, struct ide_thread_state *its) { for (int i = 0; i < max; i++) { struct ide_hdf *ide; if (!idetable[i]) { ide = idetable[i] = xcalloc (struct ide_hdf, 1); ide->cd_unit_num = -1; } ide = idetable[i]; ide_grow_buffer(ide, 1024); if (its) ide->its = its; } } void remove_ide_unit(struct ide_hdf **idetable, int ch) { struct ide_hdf *ide; if (!idetable) return; ide = idetable[ch]; if (ide) { struct ide_thread_state *its; hdf_hd_close(&ide->hdhfd); scsi_free(ide->scsi); xfree(ide->secbuf); its = ide->its; memset(ide, 0, sizeof(struct ide_hdf)); ide->its = its; } } struct ide_hdf *add_ide_unit (struct ide_hdf **idetable, int max, int ch, struct uaedev_config_info *ci, struct romconfig *rc) { struct ide_hdf *ide; alloc_ide_mem(idetable, max, NULL); if (ch < 0) return NULL; ide = idetable[ch]; if (ci) memcpy (&ide->hdhfd.hfd.ci, ci, sizeof (struct uaedev_config_info)); if (ci->type == UAEDEV_CD && ci->device_emu_unit >= 0) { device_func_init (0); ide->scsi = scsi_alloc_cd (ch, ci->device_emu_unit, true); if (!ide->scsi) { write_log (_T("IDE: CD EMU unit %d failed to open\n"), ide->cd_unit_num); return NULL; } ide->cd_unit_num = ci->device_emu_unit; ide->atapi = true; ide->blocksize = 512; gui_flicker_led (LED_CD, ch, -1); write_log (_T("IDE%d CD %d\n"), ch, ide->cd_unit_num); } else if (ci->type == UAEDEV_HDF) { if (!hdf_hd_open (&ide->hdhfd)) return NULL; ide->blocksize = ide->hdhfd.hfd.ci.blocksize; ide->lba48 = (ide->hdhfd.hfd.ci.unit_special_flags & 1) || ide->hdhfd.size >= 128 * (uae_u64)0x40000000 ? 1 : 0; gui_flicker_led (LED_HD, ch, -1); ide->cd_unit_num = -1; ide->media_type = ci->controller_media_type; ide->ata_level = ci->unit_feature_level; if (!ide->ata_level && (ide->hdhfd.size >= 4 * (uae_u64)0x40000000 || ide->media_type)) ide->ata_level = 1; write_log (_T("IDE%d HD '%s', LCHS=%d/%d/%d. PCHS=%d/%d/%d %uM. LBA48=%d\n"), ch, ide->hdhfd.hfd.ci.rootdir, ide->hdhfd.cyls, ide->hdhfd.heads, ide->hdhfd.secspertrack, ide->hdhfd.hfd.ci.pcyls, ide->hdhfd.hfd.ci.pheads, ide->hdhfd.hfd.ci.psecs, (int)(ide->hdhfd.size / (1024 * 1024)), ide->lba48); } ide->regs.ide_status = 0; ide->data_offset = 0; ide->data_size = 0; return ide; } #ifdef SAVESTATE uae_u8 *ide_save_state(uae_u8 *dst, struct ide_hdf *ide) { save_u64 (ide->hdhfd.size); save_string (ide->hdhfd.hfd.ci.rootdir); save_u32 (ide->hdhfd.hfd.ci.blocksize); save_u32 (ide->hdhfd.hfd.ci.readonly); save_u8 (ide->multiple_mode); save_u32 (ide->hdhfd.cyls); save_u32 (ide->hdhfd.heads); save_u32 (ide->hdhfd.secspertrack); save_u8 (ide->regs.ide_select); save_u8 (ide->regs.ide_nsector); save_u8 (ide->regs.ide_nsector2); save_u8 (ide->regs.ide_sector); save_u8 (ide->regs.ide_sector2); save_u8 (ide->regs.ide_lcyl); save_u8 (ide->regs.ide_lcyl2); save_u8 (ide->regs.ide_hcyl); save_u8 (ide->regs.ide_hcyl2); save_u8 (ide->regs.ide_feat); save_u8 (ide->regs.ide_feat2); save_u8 (ide->regs.ide_error); save_u8 (ide->regs.ide_devcon); save_u64 (ide->hdhfd.hfd.virtual_size); save_u32 (ide->hdhfd.hfd.ci.sectors); save_u32 (ide->hdhfd.hfd.ci.surfaces); save_u32 (ide->hdhfd.hfd.ci.reserved); save_u32 (ide->hdhfd.hfd.ci.bootpri); return dst; } uae_u8 *ide_restore_state(uae_u8 *src, struct ide_hdf *ide) { ide->multiple_mode = restore_u8 (); ide->hdhfd.cyls = restore_u32 (); ide->hdhfd.heads = restore_u32 (); ide->hdhfd.secspertrack = restore_u32 (); ide->regs.ide_select = restore_u8 (); ide->regs.ide_nsector = restore_u8 (); ide->regs.ide_sector = restore_u8 (); ide->regs.ide_lcyl = restore_u8 (); ide->regs.ide_hcyl = restore_u8 (); ide->regs.ide_feat = restore_u8 (); ide->regs.ide_nsector2 = restore_u8 (); ide->regs.ide_sector2 = restore_u8 (); ide->regs.ide_lcyl2 = restore_u8 (); ide->regs.ide_hcyl2 = restore_u8 (); ide->regs.ide_feat2 = restore_u8 (); ide->regs.ide_error = restore_u8 (); ide->regs.ide_devcon = restore_u8 (); ide->hdhfd.hfd.virtual_size = restore_u64 (); ide->hdhfd.hfd.ci.sectors = restore_u32 (); ide->hdhfd.hfd.ci.surfaces = restore_u32 (); ide->hdhfd.hfd.ci.reserved = restore_u32 (); ide->hdhfd.hfd.ci.bootpri = restore_u32 (); return src; } #endif /* SAVESTATE */