/* * ide.c - Emulate IDE interface * * Copyright (C) 2010 Ivo van Poorten * * Based on QEMU IDE disk emulator (hw/ide/{core.c,microdrive.c,mmio.c}) * Copyright (C) 2003 Fabrice Bellard * Copyright (C) 2006 Openhand Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* Emulate 8-bit and 16-bit IDE interface at $D500-$D50F * * Eight 16-bit IDE registers: * * LSB (bit 0..7) at 0xd500-0xd507 * MSB (bit 8..15) at 0xd508-0xd50f * * Full 16-bits are read and written when accessing the LSB (latch) * * If the MSB is ignored, the interface is compatible with MyIDE * * 16-bit operations: * * Read: read LSB (device returns 1 16-bit word), read MSB * Write: write MSB, write LSB (16-bit word is written to device) */ #define _XOPEN_SOURCE 600 #include "config.h" /* allow non-ansi fseek/ftell functions */ #ifdef __STRICT_ANSI__ # undef __STRICT_ANSI__ # include # define __STRICT_ANSI__ 1 #else # include #endif #include "ide.h" #include "atari.h" #include "log.h" #include "util.h" #include "ide_internal.h" #include #ifdef HAVE_INTTYPES_H # include #endif #include #include #ifdef HAVE_UNISTD_H # include #endif #define SECTOR_SIZE 512 #define STD_HEADS 16 #define STD_SECTORS 63 #if defined (HAVE_WINDOWS_H) # define fseeko fseeko64 # define ftello ftello64 #elif defined (__BEOS__) # define fseeko _fseek # define ftello _ftell # define PRId64 "lld" #elif defined (__DJGPP__) # define fseeko fseek # define ftello ftell # define PRId64 "lld" #endif int IDE_enabled = 0, IDE_debug = 0; struct ide_device device; static int count = 0; /* for debug stuff */ static inline void padstr(uint8_t *str, const char *src, int len) { int i; for(i = 0; i < len; i++) str[i^1] = *src ? *src++ : ' '; } #define LE16(x,y,z) (x)[(y)<<1] = (z)&0xff; (x)[((y)<<1)+1] = ((uint16_t)z)>>8; static void ide_identify(struct ide_device *s) { unsigned int oldsize; uint8_t *p = s->io_buffer; memset(p, 0, 512); LE16(p, 0, GCBI_FIXED_DRIVE); LE16(p, 1, s->cylinders); LE16(p, 3, s->heads); /* LE16(p, 4, 512 * s->sectors); // bytes per track, obsolete ATA2 LE16(p, 5, 512); // bytes per sector, obsolete ATA2 */ LE16(p, 6, s->sectors); /* sectors per track */ padstr(p+10*2, s->drive_serial_str, 20); /* LE16(p, 20, 3); // buffer type, obsolete ATA2 LE16(p, 21, 16); // cache size in sectors, obsolete ATA2 */ LE16(p, 22, 4); /* number of ECC bytes */ padstr(p+23*2, PACKAGE_VERSION, 8); padstr(p+27*2, "ATARI800 HARDDISK", 40); if (MAX_MULT_SECTORS > 1) LE16(p, 47, 0x8000 | MAX_MULT_SECTORS); LE16(p, 48, 0); /* cannot perform double word I/O */ LE16(p, 49, CAP_LBA_SUPPORTED); LE16(p, 51, 0x0200); /* PIO transfer cycle */ /* LE16(p, 52, 0x0200); // DMA transfer cycle, obsolete ATA3 */ LE16(p, 53, 1/*+2+4*/); /* words 54-58[,64-70,88] are valid */ LE16(p, 54, s->cylinders); LE16(p, 55, s->heads); LE16(p, 56, s->sectors); oldsize = s->cylinders * s->heads * s->sectors; LE16(p, 57, oldsize); LE16(p, 58, oldsize >> 16); if (s->mult_sectors) LE16(p, 59, 0x100 | s->mult_sectors); LE16(p, 60, s->nb_sectors); /* total number of LBA sectors */ LE16(p, 61, s->nb_sectors >> 16); if (s->is_cf) { LE16(p, 0, 0x848a); /* CF Storage Card signature */ padstr(p+27*2, "ATARI800 MICRODRIVE", 40); LE16(p, 49, CAP_LBA_SUPPORTED); LE16(p, 51, 2); LE16(p, 52, 1); } if (s->is_cdrom) { LE16(p, 0, GCBI_HAS_PACKET_FEAT_SET | GCBI_CDROM_DEVICE | GCBI_HAS_REMOVABLE_MEDIA | GCBI_50US_TILL_DRQ | GCBI_12BYTE_PACKETS); padstr(p+27*2, "ATARI800 DVD-ROM", 40); LE16(p, 49, CAP_LBA_SUPPORTED); } } static void ide_dummy_transfer_stop(struct ide_device *s) { s->data_ptr = s->data_end = s->io_buffer; s->io_buffer[0] = s->io_buffer[1] = s->io_buffer[2] = s->io_buffer[3] = 0xff; count = 0; } static void ide_reset(struct ide_device *s) { if (IDE_debug) fprintf(stderr, "ide: reset\n"); if (s->is_cf) s->mult_sectors = 0; else s->mult_sectors = MAX_MULT_SECTORS; /* ide regs */ s->feature = s->error = s->nsector = s->sector = s->lcyl = s->hcyl = 0; /* lba48 */ s->hob_feature = s->hob_sector = s->hob_nsector = s->hob_lcyl = s->hob_hcyl = s->lba48 = 0; s->select = 0xa0; s->status = READY_STAT | SEEK_STAT; /* ATAPI stuff skipped */ s->select &= 0xf0; /* clear head */ s->nsector = s->sector = 1; if (s->is_cdrom) { s->lcyl = 0x14; s->hcyl = 0xeb; } else { s->lcyl = s->hcyl = 0; } s->end_transfer_func = ide_dummy_transfer_stop; ide_dummy_transfer_stop(s); s->media_changed = 0; } static int ide_init_drive(struct ide_device *s, char *filename) { if (!(s->file = fopen(filename, "rb+"))) { Log_print("%s: %s", filename, strerror(errno)); return FALSE; } s->blocksize = SECTOR_SIZE; fseeko(s->file, 0, SEEK_END); s->filesize = ftello(s->file); if (IDE_debug) fprintf(stderr, "ide: filesize: %"PRId64"\n", (int64_t)s->filesize); if (!s->io_buffer) { s->io_buffer_size = SECTOR_SIZE * MAX_MULT_SECTORS; s->io_buffer = Util_malloc(s->io_buffer_size); } s->nb_sectors = s->filesize / SECTOR_SIZE; /* use standard physical disk geometry */ s->cylinders = s->nb_sectors / (STD_HEADS * STD_SECTORS); if (s->cylinders > 16383) s->cylinders = 16383; else if (s->cylinders < 2) { Log_print("%s: image file too small\n", filename); fclose(s->file); return FALSE; } s->heads = STD_HEADS; s->sectors = STD_SECTORS; if (IDE_debug) fprintf(stderr, "ide: cyls/heads/secs - %d/%d/%d\n", s->cylinders, s->heads, s->sectors); s->drive_serial = 1; snprintf(s->drive_serial_str, sizeof(s->drive_serial_str), "QM%05d", s->drive_serial); ide_reset(s); return TRUE; } static uint32_t ide_ioport_read(struct ide_device *s, uint16_t addr) { int ret = 0xff, hob = 0; addr &= 7; /* hob = s->select & (1<<7); */ /* FIXME: HOB stuff is broken/disabled in QEMU */ switch(addr) { case 0: /* bottom ide layer does nothing here */ break; case 1: ret = hob ? s->hob_feature : s->error; break; case 2: ret = hob ? s->hob_nsector : s->nsector & 0xff; break; case 3: ret = hob ? s->hob_sector : s->sector; break; case 4: ret = hob ? s->hob_lcyl : s->lcyl; break; case 5: ret = hob ? s->hob_hcyl : s->hcyl; break; case 6: ret = s->select; break; default: case 7: ret = s->status; break; } if (IDE_debug) fprintf(stderr, "ide: get: addr: %04x, ret: %02x\n", addr, ret); return ret; } static inline void not_implemented(uint8_t val) { if (IDE_debug) fprintf(stderr, "\tIDE: %02x not implemented\n", val); } static inline void ide_abort_command(struct ide_device *s) { s->status = READY_STAT | ERR_STAT; s->error = ABRT_ERR; } static void ide_cmd_lba48_transform(struct ide_device *s, int lba48) { s->lba48 = lba48; if (!s->lba48) { if (!s->nsector) s->nsector = 256; } else { if (!s->nsector && !s->hob_nsector) s->nsector = 65536; else s->nsector = (s->hob_nsector << 8) | s->nsector; } } static int64_t ide_get_sector(struct ide_device *s) { int64_t sector_num; if (s->select & 0x40) { /* lba */ if (IDE_debug) fprintf(stderr, "get_sector: lba\n"); if (!s->lba48) { sector_num = ((s->select & 0x0f) << 24) | ( s->hcyl << 16) | ( s->lcyl << 8) | s->sector; } else { sector_num = ((int64_t) s->hob_hcyl << 40) | ((int64_t) s->hob_lcyl << 32) | ((int64_t) s->hob_sector << 24) | ((int64_t) s->hcyl << 16) | ((int64_t) s->lcyl << 8) | s->sector; } } else { sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors + (s->select & 0x0f) * s->sectors + (s->sector - 1); if (IDE_debug) fprintf(stderr, "get_sector: large: hcyl %02x lcyl %02x heads %02x sectors %02x select&f %1x sector-1 %d sector_num %"PRId64"\n", s->hcyl, s->lcyl, s->heads, s->sectors, s->select&0x0f, s->sector-1, sector_num); } return sector_num; } static void ide_transfer_start(struct ide_device *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func) { if (IDE_debug) fprintf(stderr, "transfer start\n"); s->end_transfer_func = end_transfer_func; s->data_ptr = buf; s->data_end = buf + size; s->cycle = 0; if (!(s->status & ERR_STAT)) s->status |= DRQ_STAT; } static void ide_transfer_stop(struct ide_device *s) { if (IDE_debug) fprintf(stderr, "transfer stop\n"); s->end_transfer_func = ide_transfer_stop; s->data_ptr = s->io_buffer; s->data_end = s->io_buffer; s->status &= ~DRQ_STAT; count = 0; } static void ide_set_sector(struct ide_device *s, int64_t sector_num) { unsigned int cyl, r; if (s->select & 0x40) { if (!s->lba48) { s->select = (s->select & 0xf0) | (sector_num >> 24); s->hcyl = sector_num >> 16 ; s->lcyl = sector_num >> 8 ; s->sector = sector_num ; } else { s->sector = sector_num ; s->lcyl = sector_num >> 8; s->hcyl = sector_num >> 16; s->hob_sector = sector_num >> 24; s->hob_lcyl = sector_num >> 32; s->hob_hcyl = sector_num >> 40; } } else { cyl = sector_num / (s->heads * s->sectors); r = sector_num % (s->heads * s->sectors); s->hcyl = cyl >> 8; s->lcyl = cyl ; s->select = (s->select & 0xf0) | ((r / s->sectors) & 0x0f); s->sector = (r % s->sectors) + 1; } } static void ide_sector_read(struct ide_device *s) { int64_t sector_num; int n; s->status = READY_STAT | SEEK_STAT; s->error = 0; sector_num = ide_get_sector(s); n = s->nsector; if (!n) { ide_transfer_stop(s); } else { if (IDE_debug) fprintf(stderr, "IDE: read sector=%" PRId64 "\n", sector_num); if (n > s->req_nb_sectors) n = s->req_nb_sectors; if (fseeko(s->file, sector_num * SECTOR_SIZE, SEEK_SET) < 0) goto fail; if (fread(s->io_buffer, n * SECTOR_SIZE, 1, s->file) != 1) goto fail; if (IDE_debug) fprintf(stderr, "sector read OK\n"); ide_transfer_start(s, s->io_buffer, 512*n, ide_sector_read); s->nsector -= n; ide_set_sector(s, sector_num + n + (s->nsector ? 0 : -1)); } return; fail: ide_abort_command(s); if (IDE_debug) fprintf(stderr, "sector read FAILED\n"); } static void ide_sector_write(struct ide_device *s) { int64_t sector_num; int n, n1; s->status = READY_STAT | SEEK_STAT; sector_num = ide_get_sector(s); if (IDE_debug) fprintf(stderr, "IDE: write sector=%" PRId64 "\n", sector_num); n = s->nsector; if (n > s->req_nb_sectors) n = s->req_nb_sectors; if (fseeko(s->file, sector_num * SECTOR_SIZE, SEEK_SET) < 0) { fprintf(stderr, "FSEEKO FAILED\n"); goto fail; } if (fwrite(s->io_buffer, n * SECTOR_SIZE, 1, s->file) != 1) { fprintf(stderr, "FWRITE FAILED\n"); goto fail; } fflush(s->file); s->nsector -= n; if (s->nsector == 0) { ide_transfer_stop(s); } else { n1 = s->nsector; if (n1 > s->req_nb_sectors) n1 = s->req_nb_sectors; ide_transfer_start(s, s->io_buffer, 512 * n1, ide_sector_write); } ide_set_sector(s, sector_num + n + (s->nsector ? 0 : -1)); return; fail: ide_abort_command(s); } static void ide_command(struct ide_device *s, uint8_t val) { int lba48 = 0, n; switch(val) { case WIN_IDENTIFY: ide_identify(s); s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); break; case WIN_SPECIFY: case WIN_RECAL: s->error = 0; s->status = READY_STAT | SEEK_STAT; break; case WIN_SETMULT: if (s->is_cf && !s->nsector) { s->mult_sectors = 0; s->status = READY_STAT | SEEK_STAT; } else if ((s->nsector & 0xff) != 0 && ((s->nsector & 0xff) > MAX_MULT_SECTORS || (s->nsector & (s->nsector - 1)) != 0)) { ide_abort_command(s); } else { s->mult_sectors = s->nsector & 0xff; s->status = READY_STAT | SEEK_STAT; } break; case WIN_VERIFY_EXT: lba48 = 1; case WIN_VERIFY: case WIN_VERIFY_ONCE: /* do sector number check ? */ ide_cmd_lba48_transform(s, lba48); s->status = READY_STAT | SEEK_STAT; break; case WIN_READ_EXT: lba48 = 1; case WIN_READ: case WIN_READ_ONCE: ide_cmd_lba48_transform(s, lba48); s->req_nb_sectors = 1; ide_sector_read(s); break; case WIN_WRITE_EXT: lba48 = 1; case WIN_WRITE: case WIN_WRITE_ONCE: case CFA_WRITE_SECT_WO_ERASE: case WIN_WRITE_VERIFY: ide_cmd_lba48_transform(s, lba48); s->error = 0; s->status = SEEK_STAT | READY_STAT; s->req_nb_sectors = 1; ide_transfer_start(s, s->io_buffer, 512, ide_sector_write); s->media_changed = 1; break; case WIN_MULTREAD_EXT: lba48 = 1; case WIN_MULTREAD: if (!s->mult_sectors) goto abort_cmd; ide_cmd_lba48_transform(s, lba48); s->req_nb_sectors = s->mult_sectors; ide_sector_read(s); break; case WIN_MULTWRITE_EXT: lba48 = 1; case WIN_MULTWRITE: case CFA_WRITE_MULTI_WO_ERASE: if (!s->mult_sectors) goto abort_cmd; ide_cmd_lba48_transform(s, lba48); s->error = 0; s->status = SEEK_STAT | READY_STAT; s->req_nb_sectors = s->mult_sectors; n = s->nsector; if (n > s->req_nb_sectors) n = s->req_nb_sectors; ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write); s->media_changed = 1; break; case WIN_READDMA_EXT: case WIN_READDMA: case WIN_READDMA_ONCE: case WIN_WRITEDMA_EXT: case WIN_WRITEDMA: case WIN_WRITEDMA_ONCE: not_implemented(val); goto abort_cmd; break; case WIN_READ_NATIVE_MAX_EXT: lba48 = 1; case WIN_READ_NATIVE_MAX: ide_cmd_lba48_transform(s, lba48); ide_set_sector(s, s->nb_sectors - 1); s->status = READY_STAT | SEEK_STAT; break; case WIN_CHECKPOWERMODE1: case WIN_CHECKPOWERMODE2: s->nsector = 0xff; /* device active or idle */ s->status = READY_STAT | SEEK_STAT; break; case WIN_SETFEATURES: switch(s->feature) { case FEAT_ENABLE_REVERTING_TO_DEFAULTS: case FEAT_DISABLE_REVERTING_TO_DEFAULTS: case FEAT_ENABLE_WRITE_CACHE: case FEAT_DISABLE_WRITE_CACHE: case FEAT_ENABLE_READ_LOOKAHEAD: case FEAT_DISABLE_READ_LOOKAHEAD: case FEAT_ENABLE_ADVANCED_PM: case FEAT_DISABLE_ADVANCED_PM: case FEAT_ENABLE_AUTO_ACOUSTIC_MNGMNT: case FEAT_DISABLE_AUTO_ACOUSTIC_MNGMNT: s->status = READY_STAT | SEEK_STAT; break; case FEAT_SET_TRANSFER_MODE: if ( (s->nsector >> 3) <= 1) { /* 0: pio default, 1: pio mode */ /* set identify_data accordingly */ } else { /* single word dma, mdma and udma mode */ goto abort_cmd; } s->status = READY_STAT | SEEK_STAT; break; case FEAT_ENABLE_8BIT_DATA_TRANSFERS: if (IDE_debug) fprintf(stderr, "ide: enable 8-bit mode\n"); s->do_8bit = 1; s->cycle = 0; s->status = READY_STAT | SEEK_STAT; break; case FEAT_DISABLE_8BIT_DATA_TRANSFERS: if (IDE_debug) fprintf(stderr, "ide: disable 8-bit mode\n"); s->do_8bit = 0; s->status = READY_STAT | SEEK_STAT; break; default: goto abort_cmd; break; } break; case WIN_FLUSH_CACHE: case WIN_FLUSH_CACHE_EXT: fflush(s->file); break; case WIN_STANDBY: case WIN_STANDBY2: case WIN_STANDBYNOW1: case WIN_STANDBYNOW2: case WIN_IDLEIMMEDIATE: case CFA_IDLEIMMEDIATE: case WIN_SETIDLE1: case WIN_SETIDLE2: case WIN_SLEEPNOW1: case WIN_SLEEPNOW2: s->status = READY_STAT; break; case WIN_SEEK: if(s->is_cdrom) goto abort_cmd; /* XXX: Check that seek is within bounds and return error if not */ s->status = READY_STAT | SEEK_STAT; break; /* ATAPI Commands SKIPPED */ /* CF-ATA commands */ case CFA_REQ_EXT_ERROR_CODE: if (!s->is_cf) goto abort_cmd; s->error = 0x09; /* miscellaneous error, MARK_ERR | MCR_ERR */ s->status = READY_STAT | SEEK_STAT; break; case CFA_ERASE_SECTORS: case CFA_WEAR_LEVEL: if (!s->is_cf) goto abort_cmd; if (val == CFA_WEAR_LEVEL) s->nsector = 0; if (val == CFA_ERASE_SECTORS) s->media_changed = 1; s->error = 0; s->status = READY_STAT | SEEK_STAT; break; case CFA_TRANSLATE_SECTOR: if (!s->is_cf) goto abort_cmd; s->error = 0; s->status = READY_STAT | SEEK_STAT; memset(s->io_buffer, 0, 0x200); s->io_buffer[0x00] = s->hcyl; /* Cyl MSB */ s->io_buffer[0x01] = s->lcyl; /* Cyl LSB */ s->io_buffer[0x02] = s->select; /* Head */ s->io_buffer[0x03] = s->sector; /* Sector */ s->io_buffer[0x04] = ide_get_sector(s) >> 16; /* LBA MSB */ s->io_buffer[0x05] = ide_get_sector(s) >> 8; /* LBA */ s->io_buffer[0x06] = ide_get_sector(s) >> 0; /* LBA LSB */ s->io_buffer[0x13] = 0x00; /* Erase flag */ s->io_buffer[0x18] = 0x00; /* Hot count */ s->io_buffer[0x19] = 0x00; /* Hot count */ s->io_buffer[0x1a] = 0x01; /* Hot count */ ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); break; case CFA_ACCESS_METADATA_STORAGE: if (!s->is_cf) goto abort_cmd; not_implemented(val); /* FIXME: ... not yet */ goto abort_cmd; break; case IBM_SENSE_CONDITION: if (!s->is_cf) goto abort_cmd; switch (s->feature) { case 0x01: /* sense temperature in device */ s->nsector = 0x50; /* +20 C */ break; default: goto abort_cmd; } s->status = READY_STAT | SEEK_STAT; break; case WIN_SMART: not_implemented(val); /* FIXME: ... not yet */ goto abort_cmd; break; default: abort_cmd: ide_abort_command(s); break; } } static void ide_clear_hob(struct ide_device *s) { s->select &= ~(1<<7); } static void ide_ioport_write(struct ide_device *s, uint16_t addr, uint8_t val){ if (IDE_debug) fprintf(stderr, "ide: put: addr: %04x, byte: %02x\n", addr, val); addr &= 7; /* ignore writes to command block while busy */ if (addr != 7 && s->bus_status & (BUSY_STAT|DRQ_STAT)) return; switch(addr) { case 0: /* bottom ide layer does nothing here */ break; case 1: ide_clear_hob(s); s->hob_feature = s->feature; s->feature = val; break; case 2: ide_clear_hob(s); s->hob_nsector = s->nsector; s->nsector = val; break; case 3: ide_clear_hob(s); s->hob_sector = s->sector; s->sector = val; break; case 4: ide_clear_hob(s); s->hob_lcyl = s->lcyl; s->lcyl = val; break; case 5: ide_clear_hob(s); s->hob_hcyl = s->hcyl; s->hcyl = val; break; case 6: /* FIXME: HOB readback uses bit 7 */ s->select = (val & ~0x10) | 0xa0; s->bus_unit = (val>>4)&1; break; default: case 7: if (IDE_debug) fprintf(stderr, "\tIDE: CMD=%02x\n", val); ide_transfer_stop(s); /* if ( (s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET) break; */ ide_command(s, val); break; } } static uint16_t ide_data_readw(struct ide_device *s, int addr) { uint8_t *p; uint16_t ret; /* PIO data access only when DRQ bit is set */ if (!(s->status & DRQ_STAT)) return 0; /* LE16 */ p = s->data_ptr; ret = p[0]; ret |= p[1] << 8; p += 2; s->data_ptr = p; if (IDE_debug) { fprintf(stderr, "data_readw: %d, %04x (count: %d)\n", addr, ret, count); count++; count &= 0xff; } if (p >= s->data_end) s->end_transfer_func(s); return ret; } static void ide_data_writew(struct ide_device *s, int addr, uint16_t val) { uint8_t *p; if (IDE_debug) fprintf(stderr, "data_writew: %d, %04x\n", addr, val); /* PIO data access only when DRQ bit is set */ if (!(s->status & DRQ_STAT)) return; /* LE16 */ p = s->data_ptr; p[0] = val & 0xff; p[1] = val >> 8; p += 2; s->data_ptr = p; if (p >= s->data_end) s->end_transfer_func(s); } static uint8_t mmio_ide_read(struct ide_device *s, int addr) { uint16_t ret; /* will be cast at return */ addr &= 15; if (addr == 0) { if (!s->do_8bit) { ret = ide_data_readw(s, 0); s->upperhalf[0] = ret & 0xff00; return ret; } if (!s->cycle) { s->data = ide_data_readw(s, 0); ret = s->data & 0xff; } else { ret = s->data >> 8; } s->cycle = !s->cycle; return ret; } else if (addr >= 8) { return s->upperhalf[addr-8] >> 8; } else { ret = ide_ioport_read(s, addr); s->upperhalf[addr] = ret & 0xff00; return ret; } } static void mmio_ide_write(struct ide_device *s, int addr, uint8_t val) { addr &= 15; if (addr == 0) { if (!s->do_8bit) { ide_data_writew(s, 0, s->upperhalf[0] | val); return; } if (!s->cycle) { s->data = val & 0xff; } else { ide_data_writew(s, 0, s->data | (val << 8)); } s->cycle = !s->cycle; } else if (addr >= 8) { s->upperhalf[addr-8] = val << 8; } else { ide_ioport_write(s, addr, s->upperhalf[addr] | val); } } void IDE_PutByte(uint16_t addr, uint8_t val) { struct ide_device *s = &device; mmio_ide_write(s, addr, val); } uint8_t IDE_GetByte(uint16_t addr, int no_side_effects) { struct ide_device *s = &device; return mmio_ide_read(s, addr); } int IDE_Initialise(int *argc, char *argv[]) { int i, j, ret = TRUE; char *filename = NULL; if (IDE_debug) fprintf(stderr, "ide: init\n"); for (i = j = 1; i < *argc; i++) { int available = i + 1 < *argc; if (!strcmp(argv[i], "-ide" )) { if (!available) { Log_print("Missing argument for '%s'", argv[i]); return FALSE; } filename = Util_strdup(argv[++i]); } else if (!strcmp(argv[i], "-ide_debug")) { IDE_debug = 1; } else if (!strcmp(argv[i], "-ide_cf")) { device.is_cf = 1; } else { if (!strcmp(argv[i], "-help")) { Log_print("\t-ide Enable IDE emulation"); Log_print("\t-ide_debug Enable IDE Debug Output"); Log_print("\t-ide_cf Enable CF emulation"); } argv[j++] = argv[i]; } } *argc = j; if (filename) { IDE_enabled = ret = ide_init_drive(&device, filename); free(filename); } return ret; } void IDE_Exit(void) { if (IDE_enabled) { fclose(device.file); IDE_enabled = FALSE; } }