mirror of
https://github.com/Pecusx/libretro-atari800.git
synced 2026-05-21 21:33:56 +02:00
initial commit
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user