initial commit

This commit is contained in:
R-type
2015-12-14 14:00:35 +01:00
commit 5a96c0ca66
377 changed files with 149124 additions and 0 deletions
+910
View File
@@ -0,0 +1,910 @@
/*
* 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 <stdio.h>
# define __STRICT_ANSI__ 1
#else
# include <stdio.h>
#endif
#include "ide.h"
#include "atari.h"
#include "log.h"
#include "util.h"
#include "ide_internal.h"
#include <string.h>
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#include <stdlib.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#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 <file> 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;
}
}