/*RPCemu v0.6 by Tom Walker IDE emulation*/ //#define RPCEMU_IDE #define CDROM_ISO 200 #define IDE_TIME (5 * 100 * (1 << TIMER_SHIFT)) #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #define _GNU_SOURCE #include #include #include #include #include #ifdef RPCEMU_IDE #include "rpcemu.h" #include "iomd.h" #include "arm.h" #else #include "ibm.h" #include "io.h" #include "pic.h" #include "timer.h" #endif #include "ide.h" /* Bits of 'atastat' */ #define ERR_STAT 0x01 #define DRQ_STAT 0x08 /* Data request */ #define DSC_STAT 0x10 #define SERVICE_STAT 0x10 #define READY_STAT 0x40 #define BUSY_STAT 0x80 /* Bits of 'error' */ #define ABRT_ERR 0x04 /* Command aborted */ #define MCR_ERR 0x08 /* Media change request */ /* ATA Commands */ #define WIN_SRST 0x08 /* ATAPI Device Reset */ #define WIN_RECAL 0x10 #define WIN_RESTORE WIN_RECAL #define WIN_READ 0x20 /* 28-Bit Read */ #define WIN_READ_NORETRY 0x21 /* 28-Bit Read - no retry*/ #define WIN_WRITE 0x30 /* 28-Bit Write */ #define WIN_WRITE_NORETRY 0x31 /* 28-Bit Write */ #define WIN_VERIFY 0x40 /* 28-Bit Verify */ #define WIN_VERIFY_ONCE 0x41 /* Added by OBattler - deprected older ATA command, according to the specification I found, it is identical to 0x40 */ #define WIN_FORMAT 0x50 #define WIN_SEEK 0x70 #define WIN_DRIVE_DIAGNOSTICS 0x90 /* Execute Drive Diagnostics */ #define WIN_SPECIFY 0x91 /* Initialize Drive Parameters */ #define WIN_PACKETCMD 0xA0 /* Send a packet command. */ #define WIN_PIDENTIFY 0xA1 /* Identify ATAPI device */ #define WIN_READ_MULTIPLE 0xC4 #define WIN_WRITE_MULTIPLE 0xC5 #define WIN_SET_MULTIPLE_MODE 0xC6 #define WIN_READ_DMA 0xC8 #define WIN_WRITE_DMA 0xCA #define WIN_SETIDLE1 0xE3 #define WIN_IDENTIFY 0xEC /* Ask drive to identify itself */ /* ATAPI Commands */ #define GPCMD_TEST_UNIT_READY 0x00 #define GPCMD_REQUEST_SENSE 0x03 #define GPCMD_READ_6 0x08 #define GPCMD_INQUIRY 0x12 #define GPCMD_MODE_SELECT_6 0x15 #define GPCMD_MODE_SENSE_6 0x1a #define GPCMD_START_STOP_UNIT 0x1b #define GPCMD_PREVENT_REMOVAL 0x1e #define GPCMD_READ_CDROM_CAPACITY 0x25 #define GPCMD_READ_10 0x28 #define GPCMD_SEEK 0x2b #define GPCMD_READ_SUBCHANNEL 0x42 #define GPCMD_READ_TOC_PMA_ATIP 0x43 #define GPCMD_READ_HEADER 0x44 #define GPCMD_PLAY_AUDIO_10 0x45 #define GPCMD_GET_CONFIGURATION 0x46 #define GPCMD_PLAY_AUDIO_MSF 0x47 #define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a #define GPCMD_PAUSE_RESUME 0x4b #define GPCMD_STOP_PLAY_SCAN 0x4e #define GPCMD_READ_DISC_INFORMATION 0x51 #define GPCMD_MODE_SELECT_10 0x55 #define GPCMD_MODE_SENSE_10 0x5a #define GPCMD_PLAY_AUDIO_12 0xa5 #define GPCMD_READ_12 0xa8 #define GPCMD_READ_DVD_STRUCTURE 0xad /* For reading. */ #define GPCMD_SET_SPEED 0xbb #define GPCMD_MECHANISM_STATUS 0xbd #define GPCMD_READ_CD 0xbe #define GPCMD_SEND_DVD_STRUCTURE 0xbf /* This is for writing only, irrelevant to PCem. */ /* Mode page codes for mode sense/set */ #define GPMODE_R_W_ERROR_PAGE 0x01 #define GPMODE_CDROM_PAGE 0x0d #define GPMODE_CDROM_AUDIO_PAGE 0x0e #define GPMODE_CAPABILITIES_PAGE 0x2a #define GPMODE_ALL_PAGES 0x3f /* ATAPI Sense Keys */ #define SENSE_NONE 0 #define SENSE_NOT_READY 2 #define SENSE_ILLEGAL_REQUEST 5 #define SENSE_UNIT_ATTENTION 6 /* ATAPI Additional Sense Codes */ #define ASC_AUDIO_PLAY_OPERATION 0x00 #define ASC_ILLEGAL_OPCODE 0x20 #define ASC_INV_FIELD_IN_CMD_PACKET 0x24 #define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28 #define ASC_INCOMPATIBLE_FORMAT 0x30 #define ASC_MEDIUM_NOT_PRESENT 0x3a #define ASC_DATA_PHASE_ERROR 0x4b #define ASC_ILLEGAL_MODE_FOR_THIS_TRACK 0x64 #define ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS 0x11 #define ASCQ_AUDIO_PLAY_OPERATION_PAUSED 0x12 #define ASCQ_AUDIO_PLAY_OPERATION_COMPLETED 0x13 /* Tell RISC OS that we have a 4x CD-ROM drive (600kb/sec data, 706kb/sec raw). Not that it means anything */ #define CDROM_SPEED 706 /** Evaluate to non-zero if the currently selected drive is an ATAPI device */ #define IDE_DRIVE_IS_CDROM(ide) (ide->type == IDE_CDROM) /* \ (!ide.drive)*/ /* Some generally useful CD-ROM information */ #define CD_MINS 75 /* max. minutes per CD */ #define CD_SECS 60 /* seconds per minute */ #define CD_FRAMES 75 /* frames per second */ #define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */ #define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE) #define CD_MAX_SECTORS (CD_MAX_BYTES / 512) /* Event notification classes for GET EVENT STATUS NOTIFICATION */ #define GESN_NO_EVENTS 0 #define GESN_OPERATIONAL_CHANGE 1 #define GESN_POWER_MANAGEMENT 2 #define GESN_EXTERNAL_REQUEST 3 #define GESN_MEDIA 4 #define GESN_MULTIPLE_HOSTS 5 #define GESN_DEVICE_BUSY 6 /* Event codes for MEDIA event status notification */ #define MEC_NO_CHANGE 0 #define MEC_EJECT_REQUESTED 1 #define MEC_NEW_MEDIA 2 #define MEC_MEDIA_REMOVAL 3 /* only for media changers */ #define MEC_MEDIA_CHANGED 4 /* only for media changers */ #define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */ #define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */ #define MS_TRAY_OPEN 1 #define MS_MEDIA_PRESENT 2 /* * The MMC values are not IDE specific and might need to be moved * to a common header if they are also needed for the SCSI emulation */ /* Profile list from MMC-6 revision 1 table 91 */ #define MMC_PROFILE_NONE 0x0000 #define MMC_PROFILE_CD_ROM 0x0008 #define MMC_PROFILE_CD_R 0x0009 #define MMC_PROFILE_CD_RW 0x000A #define MMC_PROFILE_DVD_ROM 0x0010 #define MMC_PROFILE_DVD_R_SR 0x0011 #define MMC_PROFILE_DVD_RAM 0x0012 #define MMC_PROFILE_DVD_RW_RO 0x0013 #define MMC_PROFILE_DVD_RW_SR 0x0014 #define MMC_PROFILE_DVD_R_DL_SR 0x0015 #define MMC_PROFILE_DVD_R_DL_JR 0x0016 #define MMC_PROFILE_DVD_RW_DL 0x0017 #define MMC_PROFILE_DVD_DDR 0x0018 #define MMC_PROFILE_DVD_PLUS_RW 0x001A #define MMC_PROFILE_DVD_PLUS_R 0x001B #define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A #define MMC_PROFILE_DVD_PLUS_R_DL 0x002B #define MMC_PROFILE_BD_ROM 0x0040 #define MMC_PROFILE_BD_R_SRM 0x0041 #define MMC_PROFILE_BD_R_RRM 0x0042 #define MMC_PROFILE_BD_RE 0x0043 #define MMC_PROFILE_HDDVD_ROM 0x0050 #define MMC_PROFILE_HDDVD_R 0x0051 #define MMC_PROFILE_HDDVD_RAM 0x0052 #define MMC_PROFILE_HDDVD_RW 0x0053 #define MMC_PROFILE_HDDVD_R_DL 0x0058 #define MMC_PROFILE_HDDVD_RW_DL 0x005A #define MMC_PROFILE_INVALID 0xFFFF #define NONDATA 4 #define CHECK_READY 2 #define ALLOW_UA 1 /* Table of all ATAPI commands and their flags, needed for the new disc change / not ready handler. */ uint8_t atapi_cmd_table[0x100] = { [GPCMD_TEST_UNIT_READY] = CHECK_READY | NONDATA, [GPCMD_REQUEST_SENSE] = ALLOW_UA, [GPCMD_READ_6] = CHECK_READY, [GPCMD_INQUIRY] = ALLOW_UA, [GPCMD_MODE_SELECT_6] = 0, [GPCMD_MODE_SENSE_6] = 0, [GPCMD_START_STOP_UNIT] = 0, [GPCMD_PREVENT_REMOVAL] = CHECK_READY, [GPCMD_READ_CDROM_CAPACITY] = CHECK_READY, [GPCMD_READ_10] = CHECK_READY, [GPCMD_SEEK] = CHECK_READY | NONDATA, [GPCMD_READ_SUBCHANNEL] = CHECK_READY, [GPCMD_READ_TOC_PMA_ATIP] = CHECK_READY | ALLOW_UA, /* Read TOC - can get through UNIT_ATTENTION, per VIDE-CDD.SYS */ [GPCMD_READ_HEADER] = CHECK_READY, [GPCMD_PLAY_AUDIO_10] = CHECK_READY, #if 0 [GPCMD_GET_CONFIGURATION] = ALLOW_UA, #endif [GPCMD_PLAY_AUDIO_MSF] = CHECK_READY, [GPCMD_GET_EVENT_STATUS_NOTIFICATION] = ALLOW_UA, [GPCMD_PAUSE_RESUME] = CHECK_READY, [GPCMD_STOP_PLAY_SCAN] = CHECK_READY, [GPCMD_READ_DISC_INFORMATION] = CHECK_READY, [GPCMD_MODE_SELECT_10] = 0, [GPCMD_MODE_SENSE_10] = 0, [GPCMD_PLAY_AUDIO_12] = CHECK_READY, [GPCMD_READ_12] = CHECK_READY, [GPCMD_SEND_DVD_STRUCTURE] = CHECK_READY, /* Read DVD structure (NOT IMPLEMENTED YET) */ [GPCMD_SET_SPEED] = 0, [GPCMD_MECHANISM_STATUS] = 0, [GPCMD_READ_CD] = CHECK_READY, [0xBF] = CHECK_READY /* Send DVD structure (NOT IMPLEMENTED YET) */ }; #define IMPLEMENTED 1 uint8_t mode_sense_pages[0x40] = { [GPMODE_R_W_ERROR_PAGE] = IMPLEMENTED, [GPMODE_CDROM_PAGE] = IMPLEMENTED, [GPMODE_CDROM_AUDIO_PAGE] = IMPLEMENTED, [GPMODE_CAPABILITIES_PAGE] = IMPLEMENTED, [GPMODE_ALL_PAGES] = IMPLEMENTED }; ATAPI *atapi; int atapi_command = 0; int readcdmode = 0; int cdrom_channel = 2; /* Mode sense/select stuff. */ uint8_t mode_pages_in[256][256]; #define PAGE_CHANGEABLE 1 #define PAGE_CHANGED 2 uint8_t page_flags[256] = { [GPMODE_R_W_ERROR_PAGE] = 0, [GPMODE_CDROM_PAGE] = 0, [GPMODE_CDROM_AUDIO_PAGE] = PAGE_CHANGEABLE, [GPMODE_CAPABILITIES_PAGE] = 0, }; uint8_t prefix_len; uint8_t page_current; #define ATAPI_STATUS_IDLE 0 #define ATAPI_STATUS_COMMAND 1 #define ATAPI_STATUS_COMPLETE 2 #define ATAPI_STATUS_DATA 3 #define ATAPI_STATUS_PACKET_REQ 4 #define ATAPI_STATUS_PACKET_RECEIVED 5 #define ATAPI_STATUS_READCD 6 #define ATAPI_STATUS_REQ_SENSE 7 #define ATAPI_STATUS_ERROR 0x80 #define ATAPI_STATUS_ERROR_2 0x81 enum { IDE_NONE = 0, IDE_HDD, IDE_CDROM }; typedef struct IDE { int type; int board; uint8_t atastat; uint8_t error; int secount,sector,cylinder,head,drive,cylprecomp; uint8_t command; uint8_t fdisk; int pos; int packlen; int spt,hpc; int tracks; int packetstatus; int cdpos,cdlen; uint8_t asc; int reset; FILE *hdfile; uint16_t buffer[65536]; int irqstat; int service; int lba; uint32_t lba_addr; int skip512; int blocksize, blockcount; } IDE; IDE ide_drives[6]; IDE *ext_ide; char ide_fn[4][512]; int (*ide_bus_master_read_sector)(int channel, uint8_t *data); int (*ide_bus_master_write_sector)(int channel, uint8_t *data); void (*ide_bus_master_set_irq)(int channel); static void callnonreadcd(IDE *ide); static void callreadcd(IDE *ide); static void atapicommand(int ide_board); int idecallback[3] = {0, 0, 0}; int cur_ide[3]; uint8_t getstat(IDE *ide) { return ide->atastat; } static inline void ide_irq_raise(IDE *ide) { // pclog("IDE_IRQ_RAISE\n"); if (!(ide->fdisk&2)) { #ifdef RPCEMU_IDE iomd.irqb.status |= IOMD_IRQB_IDE; updateirqs(); #else // if (ide->board && !ide->irqstat) pclog("IDE_IRQ_RAISE\n"); #ifdef MAINLINE picint((ide->board)?(1<<15):(1<<14)); #else switch(ide->board) { case 0: picint(1 << 14); break; case 1: picint(1 << 15); break; case 2: picint(1 << 10); break; } #endif if (ide->board < 2) { if (ide_bus_master_set_irq) ide_bus_master_set_irq(ide->board); } #endif } ide->irqstat=1; ide->service=1; // pclog("raising interrupt %i\n", 14 + ide->board); } static inline void ide_irq_lower(IDE *ide) { // pclog("IDE_IRQ_LOWER\n"); // if (ide.board == 0) { #ifdef RPCEMU_IDE iomd.irqb.status &= ~IOMD_IRQB_IDE; updateirqs(); #else #ifdef MAINLINE picintc((ide->board)?(1<<15):(1<<14)); #else switch(ide->board) { case 0: picintc(1 << 14); break; case 1: picintc(1 << 15); break; case 2: picintc(1 << 10); break; } #endif #endif // } ide->irqstat=0; } int get_irq(uint8_t board) { if (board == 0) return 1 << 14; else if (board == 1) return 1 << 15; else if (board == 2) return 1 << 10; } void ide_irq_update(IDE *ide) { #ifdef RPCEMU_IDE if (ide->irqstat && !(iomd.irqb.status & IOMD_IRQB_IDE) && !(ide->fdisk & 2)) { iomd.irqb.status |= IOMD_IRQB_IDE; updateirqs(); } else if (iomd.irqb.status & IOMD_IRQB_IDE) { iomd.irqb.status &= ~IOMD_IRQB_IDE; updateirqs(); } #else #ifdef MAINLINE if (ide->irqstat && !((pic2.pend|pic2.ins)&0x40) && !(ide->fdisk & 2)) picint((ide->board)?(1<<15):(1<<14)); else if ((pic2.pend|pic2.ins)&0x40) picintc((ide->board)?(1<<15):(1<<14)); #else if (ide->irqstat && !((pic2.pend|pic2.ins)&0x40) && !(ide->fdisk & 2)) picint(get_irq(ide->board)); else if ((pic2.pend|pic2.ins)&0x40) picintc(get_irq(ide->board)); #endif #endif } /** * Copy a string into a buffer, padding with spaces, and placing characters as * if they were packed into 16-bit values, stored little-endian. * * @param str Destination buffer * @param src Source string * @param len Length of destination buffer to fill in. Strings shorter than * this length will be padded with spaces. */ static void ide_padstr(char *str, const char *src, int len) { int i, v; for (i = 0; i < len; i++) { if (*src != '\0') { v = *src++; } else { v = ' '; } str[i ^ 1] = v; } } /** * Copy a string into a buffer, padding with spaces. Does not add string * terminator. * * @param buf Destination buffer * @param buf_size Size of destination buffer to fill in. Strings shorter than * this length will be padded with spaces. * @param src Source string */ static void ide_padstr8(uint8_t *buf, int buf_size, const char *src) { int i; for (i = 0; i < buf_size; i++) { if (*src != '\0') { buf[i] = *src++; } else { buf[i] = ' '; } } } /** * Fill in ide->buffer with the output of the "IDENTIFY DEVICE" command */ static void ide_identify(IDE *ide) { memset(ide->buffer, 0, 512); //ide->buffer[1] = 101; /* Cylinders */ #ifdef RPCEMU_IDE ide->buffer[1] = 65535; /* Cylinders */ ide->buffer[3] = 16; /* Heads */ ide->buffer[6] = 63; /* Sectors */ #else ide->buffer[1] = hdc[cur_ide[ide->board]].tracks; /* Cylinders */ ide->buffer[3] = hdc[cur_ide[ide->board]].hpc; /* Heads */ ide->buffer[6] = hdc[cur_ide[ide->board]].spt; /* Sectors */ #endif ide_padstr((char *) (ide->buffer + 10), "", 20); /* Serial Number */ ide_padstr((char *) (ide->buffer + 23), "v1.0", 8); /* Firmware */ #ifdef RPCEMU_IDE ide_padstr((char *) (ide->buffer + 27), "RPCemuHD", 40); /* Model */ #else ide_padstr((char *) (ide->buffer + 27), "PCemHD", 40); /* Model */ #endif ide->buffer[20] = 3; /*Buffer type*/ ide->buffer[21] = 512; /*Buffer size*/ ide->buffer[47] = 16; /*Max sectors on multiple transfer command*/ ide->buffer[48] = 1; /*Dword transfers supported*/ ide->buffer[49] = (1 << 9) | (1 << 8); /* LBA and DMA supported */ ide->buffer[50] = 0x4000; /* Capabilities */ ide->buffer[51] = 2 << 8; /*PIO timing mode*/ ide->buffer[52] = 2 << 8; /*DMA timing mode*/ ide->buffer[59] = ide->blocksize ? (ide->blocksize | 0x100) : 0; #ifdef RPCEMU_IDE ide->buffer[60] = (65535 * 16 * 63) & 0xFFFF; /* Total addressable sectors (LBA) */ ide->buffer[61] = (65535 * 16 * 63) >> 16; #else ide->buffer[60] = (hdc[cur_ide[ide->board]].tracks * hdc[cur_ide[ide->board]].hpc * hdc[cur_ide[ide->board]].spt) & 0xFFFF; /* Total addressable sectors (LBA) */ ide->buffer[61] = (hdc[cur_ide[ide->board]].tracks * hdc[cur_ide[ide->board]].hpc * hdc[cur_ide[ide->board]].spt) >> 16; #endif ide->buffer[63] = 7; /*Multiword DMA*/ ide->buffer[80] = 0xe; /*ATA-1 to ATA-3 supported*/ } /** * Fill in ide->buffer with the output of the "IDENTIFY PACKET DEVICE" command */ static void ide_atapi_identify(IDE *ide) { memset(ide->buffer, 0, 512); ide->buffer[0] = 0x8000 | (5<<8) | 0x80 | (2<<5); /* ATAPI device, CD-ROM drive, removable media, accelerated DRQ */ ide_padstr((char *) (ide->buffer + 10), "", 20); /* Serial Number */ ide_padstr((char *) (ide->buffer + 23), "v1.0", 8); /* Firmware */ #ifdef RPCEMU_IDE ide_padstr((char *) (ide->buffer + 27), "RPCemuCD", 40); /* Model */ #else ide_padstr((char *) (ide->buffer + 27), "PCemCD", 40); /* Model */ #endif ide->buffer[49] = 0x200; /* LBA supported */ } /** * Fill in ide->buffer with the output of the ATAPI "MODE SENSE" command * * @param pos Offset within the buffer to start filling in data * * @return Offset within the buffer after the end of the data */ static uint32_t ide_atapi_mode_sense(IDE *ide, uint32_t pos, uint8_t type) { uint8_t *buf = (uint8_t *) ide->buffer; // pclog("ide_atapi_mode_sense %02X\n",type); if (type==GPMODE_ALL_PAGES || type==GPMODE_R_W_ERROR_PAGE) { /* &01 - Read error recovery */ buf[pos++] = GPMODE_R_W_ERROR_PAGE; buf[pos++] = 6; /* Page length */ buf[pos++] = 0; /* Error recovery parameters */ buf[pos++] = 5; /* Read retry count */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Reserved */ } if (type==GPMODE_ALL_PAGES || type==GPMODE_CDROM_PAGE) { /* &0D - CD-ROM Parameters */ buf[pos++] = GPMODE_CDROM_PAGE; buf[pos++] = 6; /* Page length */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 1; /* Inactivity time multiplier *NEEDED BY RISCOS* value is a guess */ buf[pos++] = 0; buf[pos++] = 60; /* MSF settings */ buf[pos++] = 0; buf[pos++] = 75; /* MSF settings */ } if (type==GPMODE_ALL_PAGES || type==GPMODE_CDROM_AUDIO_PAGE) { /* &0e - CD-ROM Audio Control Parameters */ buf[pos++] = GPMODE_CDROM_AUDIO_PAGE; buf[pos++] = 0xE; /* Page length */ if (page_flags[GPMODE_CDROM_AUDIO_PAGE] & PAGE_CHANGED) { int i; for (i = 0; i < 14; i++) { buf[pos++] = mode_pages_in[GPMODE_CDROM_AUDIO_PAGE][i]; } } else { buf[pos++] = 4; /* Reserved */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; buf[pos++] = 75; /* Logical audio block per second */ buf[pos++] = 1; /* CDDA Output Port 0 Channel Selection */ buf[pos++] = 0xFF; /* CDDA Output Port 0 Volume */ buf[pos++] = 2; /* CDDA Output Port 1 Channel Selection */ buf[pos++] = 0xFF; /* CDDA Output Port 1 Volume */ buf[pos++] = 0; /* CDDA Output Port 2 Channel Selection */ buf[pos++] = 0; /* CDDA Output Port 2 Volume */ buf[pos++] = 0; /* CDDA Output Port 3 Channel Selection */ buf[pos++] = 0; /* CDDA Output Port 3 Volume */ } } if (type==GPMODE_ALL_PAGES || type==GPMODE_CAPABILITIES_PAGE) { // pclog("Capabilities page\n"); /* &2A - CD-ROM capabilities and mechanical status */ buf[pos++] = GPMODE_CAPABILITIES_PAGE; buf[pos++] = 0x12; /* Page length */ buf[pos++] = 0; buf[pos++] = 0; /* CD-R methods */ buf[pos++] = 1; /* Supports audio play, not multisession */ buf[pos++] = 0; /* Some other stuff not supported */ buf[pos++] = 0; /* Some other stuff not supported (lock state + eject) */ buf[pos++] = 0; /* Some other stuff not supported */ buf[pos++] = (uint8_t) (CDROM_SPEED >> 8); buf[pos++] = (uint8_t) CDROM_SPEED; /* Maximum speed */ buf[pos++] = 0; buf[pos++] = 2; /* Number of audio levels - on and off only */ buf[pos++] = 0; buf[pos++] = 0; /* Buffer size - none */ buf[pos++] = (uint8_t) (CDROM_SPEED >> 8); buf[pos++] = (uint8_t) CDROM_SPEED; /* Current speed */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Drive digital format */ buf[pos++] = 0; /* Reserved */ buf[pos++] = 0; /* Reserved */ } return pos; } uint32_t atapi_get_cd_channel(int channel) { return (page_flags[GPMODE_CDROM_AUDIO_PAGE] & PAGE_CHANGED) ? mode_pages_in[GPMODE_CDROM_AUDIO_PAGE][channel ? 8 : 6] : (channel + 1); } uint32_t atapi_get_cd_volume(int channel) { // return ((page_flags[GPMODE_CDROM_AUDIO_PAGE] & PAGE_CHANGED) && (mode_pages_in[GPMODE_CDROM_AUDIO_PAGE][channel ? 8 : 6] != 0)) ? mode_pages_in[GPMODE_CDROM_AUDIO_PAGE][channel ? 9 : 7] : 0xFF; return (page_flags[GPMODE_CDROM_AUDIO_PAGE] & PAGE_CHANGED) ? mode_pages_in[GPMODE_CDROM_AUDIO_PAGE][channel ? 9 : 7] : 0xFF; } /* * Return the sector offset for the current register values */ static off64_t ide_get_sector(IDE *ide) { if (ide->lba) { return (off64_t)ide->lba_addr + ide->skip512; } else { int heads = ide->hpc; int sectors = ide->spt; return ((((off64_t) ide->cylinder * heads) + ide->head) * sectors) + (ide->sector - 1) + ide->skip512; } } /** * Move to the next sector using CHS addressing */ static void ide_next_sector(IDE *ide) { if (ide->lba) { ide->lba_addr++; } else { ide->sector++; if (ide->sector == (ide->spt + 1)) { ide->sector = 1; ide->head++; if (ide->head == ide->hpc) { ide->head = 0; ide->cylinder++; } } } } #ifdef RPCEMU_IDE static void loadhd(IDE *ide, int d, const char *fn) { char pathname[512]; append_filename(pathname, rpcemu_get_datadir(), fn, 512); rpclog("Loading %s\n",pathname); if (ide->hdfile == NULL) { /* Try to open existing hard disk image */ ide->hdfile = fopen64(pathname, "rb+"); if (ide->hdfile == NULL) { /* Failed to open existing hard disk image */ if (errno == ENOENT) { /* Failed because it does not exist, so try to create new file */ ide->hdfile = fopen64(pathname, "wb+"); if (ide->hdfile == NULL) { fatal("Cannot create file '%s': %s", pathname, strerror(errno)); } } else { /* Failed for another reason */ fatal("Cannot open file '%s': %s", pathname, strerror(errno)); } } } fseek(ide->hdfile, 0xfc1, SEEK_SET); ide->spt = getc(ide->hdfile); ide->hpc = getc(ide->hdfile); ide->skip512 = 1; // rpclog("First check - spt %i hpc %i\n",ide.spt[0],ide.hpc[0]); if (!ide->spt || !ide->hpc) { fseek(ide->hdfile, 0xdc1, SEEK_SET); ide->spt = getc(ide->hdfile); ide->hpc = getc(ide->hdfile); // rpclog("Second check - spt %i hpc %i\n",ide.spt[0],ide.hpc[0]); ide->skip512 = 0; if (!ide->spt || !ide->hpc) { ide->spt=63; ide->hpc=16; ide->skip512 = 1; // rpclog("Final check - spt %i hpc %i\n",ide.spt[0],ide.hpc[0]); } } ide->type = IDE_HDD; rpclog("%i %i %i\n",ide->spt,ide->hpc,ide->skip512); } #else static void loadhd(IDE *ide, int d, const char *fn) { if (ide->hdfile == NULL) { /* Try to open existing hard disk image */ ide->hdfile = fopen64(fn, "rb+"); if (ide->hdfile == NULL) { /* Failed to open existing hard disk image */ if (errno == ENOENT) { /* Failed because it does not exist, so try to create new file */ ide->hdfile = fopen64(fn, "wb+"); if (ide->hdfile == NULL) { ide->type = IDE_NONE; /* fatal("Cannot create file '%s': %s", fn, strerror(errno));*/ return; } } else { /* Failed for another reason */ ide->type = IDE_NONE; /* fatal("Cannot open file '%s': %s", fn, strerror(errno));*/ return; } } } ide->spt = hdc[d].spt; ide->hpc = hdc[d].hpc; ide->tracks = hdc[d].tracks; ide->type = IDE_HDD; } #endif void ide_set_signature(IDE *ide) { ide->secount=1; ide->sector=1; ide->head=0; ide->cylinder=(IDE_DRIVE_IS_CDROM(ide) ? 0xEB14 : ((ide->type == IDE_HDD) ? 0 : 0xFFFF)); if (ide->type == IDE_HDD) ide->drive = 0; } void resetide(void) { int d; /* Close hard disk image files (if previously open) */ for (d = 0; d < 4; d++) { ide_drives[d].type = IDE_NONE; if (ide_drives[d].hdfile != NULL) { fclose(ide_drives[d].hdfile); ide_drives[d].hdfile = NULL; } ide_drives[d].atastat = READY_STAT | DSC_STAT; ide_drives[d].service = 0; ide_drives[d].board = (d & 2) ? 1 : 0; } for (d = 4; d < 6; d++) { ide_drives[d].type = IDE_NONE; ide_drives[d].atastat = READY_STAT | DSC_STAT; ide_drives[d].service = 0; ide_drives[d].board = 2; } page_flags[GPMODE_CDROM_AUDIO_PAGE] &= 0xFD; /* Clear changed flag for CDROM AUDIO mode page. */ memset(mode_pages_in[GPMODE_CDROM_AUDIO_PAGE], 0, 256); /* Clear the page itself. */ idecallback[0]=idecallback[1]=0; #ifdef RPCEMU_IDE loadhd(&ide_drives[0], 0, "hd4.hdf"); if (!config.cdromenabled) { loadhd(&ide_drives[1], 1, "hd5.hdf"); } else ide_drives[1].type = IDE_CDROM; #else for (d = 0; d < 4; d++) { ide_drives[d].packetstatus = 0xFF; if ((cdrom_channel == d) && cdrom_enabled) { ide_drives[d].type = IDE_CDROM; } else { loadhd(&ide_drives[d], d, ide_fn[d]); } ide_set_signature(&ide_drives[d]); } /* REMOVE WHEN SUBMITTING TO MAINLINE - START */ for (d = 4; d < 6; d++) { ide_drives[d].packetstatus = 0xFF; if ((cdrom_channel == d) && cdrom_enabled) { ide_drives[d].type = IDE_CDROM; } else { ide_drives[d].type = IDE_NONE; } ide_set_signature(&ide_drives[d]); } /* REMOVE WHEN SUBMITTING TO MAINLINE - END */ #endif cur_ide[0] = 0; cur_ide[1] = 2; cur_ide[2] = 4; // ide_drives[1].type = IDE_CDROM; page_flags[GPMODE_CDROM_AUDIO_PAGE] &= ~PAGE_CHANGED; } int idetimes=0; void writeidew(int ide_board, uint16_t val) { IDE *ide = &ide_drives[cur_ide[ide_board]]; /*Some software issue excess writes after the 12 bytes required by the command, this will have all of them ignored*/ if (ide->packetstatus && (ide->packetstatus != ATAPI_STATUS_PACKET_REQ)) return; #ifndef RPCEMU_IDE /* if (ide_board && (cr0&1) && !(eflags&VM_FLAG)) { // pclog("Failed write IDE %04X:%08X\n",CS,pc); return; }*/ #endif #ifdef _RPCEMU_BIG_ENDIAN val=(val>>8)|(val<<8); #endif // pclog("Write IDEw %04X\n",val); ide->buffer[ide->pos >> 1] = val; ide->pos+=2; if (ide->packetstatus == ATAPI_STATUS_PACKET_REQ) { if ((ide->pos>=prefix_len+4) && (page_flags[page_current] & PAGE_CHANGEABLE)) { mode_pages_in[page_current][ide->pos - prefix_len - 4] = ((uint8_t *) ide->buffer)[ide->pos - 2]; mode_pages_in[page_current][ide->pos - prefix_len - 3] = ((uint8_t *) ide->buffer)[ide->pos - 1]; } if (ide->pos>=(ide->packlen+2)) { ide->packetstatus = ATAPI_STATUS_PACKET_RECEIVED; timer_process(); idecallback[ide_board]=6*IDE_TIME; timer_update_outstanding(); // pclog("Packet over!\n"); ide_irq_lower(ide); } return; } else if (ide->packetstatus == ATAPI_STATUS_PACKET_RECEIVED) return; else if (ide->command == WIN_PACKETCMD && ide->pos>=0xC) { ide->pos=0; ide->atastat = BUSY_STAT; ide->packetstatus = ATAPI_STATUS_COMMAND; /* idecallback[ide_board]=6*IDE_TIME;*/ timer_process(); callbackide(ide_board); timer_update_outstanding(); // idecallback[ide_board]=60*IDE_TIME; // if ((ide->buffer[0]&0xFF)==0x43) idecallback[ide_board]=1*IDE_TIME; // pclog("Packet now waiting!\n"); /* if (ide->buffer[0]==0x243) { idetimes++; output=3; }*/ } else if (ide->pos>=512) { ide->pos=0; ide->atastat = BUSY_STAT; timer_process(); if (ide->command == WIN_WRITE_MULTIPLE) callbackide(ide_board); else idecallback[ide_board]=6*IDE_TIME; timer_update_outstanding(); } } void writeidel(int ide_board, uint32_t val) { // pclog("WriteIDEl %08X\n", val); writeidew(ide_board, val); writeidew(ide_board, val >> 16); } void writeide(int ide_board, uint16_t addr, uint8_t val) { IDE *ide = &ide_drives[cur_ide[ide_board]]; IDE *ide_other = &ide_drives[cur_ide[ide_board] ^ 1]; #ifndef RPCEMU_IDE /* if (ide_board && (cr0&1) && !(eflags&VM_FLAG)) { // pclog("Failed write IDE %04X:%08X\n",CS,pc); return; }*/ #endif // if ((cr0&1) && !(eflags&VM_FLAG)) // pclog("WriteIDE %04X %02X from %04X(%08X):%08X %i\n", addr, val, CS, cs, pc, ins); // return; addr|=0x80; /* ONLY FOR EXPERIMENTAL */ addr|=0x10; /* 1F0 | 10 = 1F0, 1E8 | 10 = 1F8 */ addr&=0xFFF7; /* 1F0 & FFF7 = 1F0, 1F8 | FFF7 = 1F0 */ // if (ide_board) pclog("Write IDEb %04X %02X %04X(%08X):%04X %i %02X %02X\n",addr,val,CS,cs,pc,ins,ide->atastat,ide_drives[0].atastat); /*if (idedebug) */ // pclog("Write IDE %08X %02X %04X:%08X\n",addr,val,CS,pc); // int c; // rpclog("Write IDE %08X %02X %08X %08X\n",addr,val,PC,armregs[12]); if (ide->type == IDE_NONE && (addr == 0x1f0 || addr == 0x1f7)) return; switch (addr) { case 0x1F0: /* Data */ writeidew(ide_board, val | (val << 8)); return; case 0x1F1: /* Features */ ide->cylprecomp = val; ide_other->cylprecomp = val; return; case 0x1F2: /* Sector count */ ide->secount = val; ide_other->secount = val; return; case 0x1F3: /* Sector */ ide->sector = val; ide->lba_addr = (ide->lba_addr & 0xFFFFF00) | val; ide_other->sector = val; ide_other->lba_addr = (ide_other->lba_addr & 0xFFFFF00) | val; return; case 0x1F4: /* Cylinder low */ ide->cylinder = (ide->cylinder & 0xFF00) | val; ide->lba_addr = (ide->lba_addr & 0xFFF00FF) | (val << 8); ide_other->cylinder = (ide_other->cylinder&0xFF00) | val; ide_other->lba_addr = (ide_other->lba_addr&0xFFF00FF) | (val << 8); // pclog("Write cylinder low %02X\n",val); return; case 0x1F5: /* Cylinder high */ ide->cylinder = (ide->cylinder & 0xFF) | (val << 8); ide->lba_addr = (ide->lba_addr & 0xF00FFFF) | (val << 16); ide_other->cylinder = (ide_other->cylinder & 0xFF) | (val << 8); ide_other->lba_addr = (ide_other->lba_addr & 0xF00FFFF) | (val << 16); return; case 0x1F6: /* Drive/Head */ /* if (val==0xB0) { dumpregs(); exit(-1); }*/ if (cur_ide[ide_board] != ((val>>4)&1)+(ide_board<<1)) { cur_ide[ide_board]=((val>>4)&1)+(ide_board<<1); if (ide->reset || ide_other->reset) { ide->atastat = ide_other->atastat = READY_STAT | DSC_STAT; ide->error = ide_other->error = 1; ide->secount = ide_other->secount = 1; ide->sector = ide_other->sector = 1; ide->head = ide_other->head = 0; ide->cylinder = ide_other->cylinder = 0; ide->reset = ide_other->reset = 0; // ide->blocksize = ide_other->blocksize = 0; if (IDE_DRIVE_IS_CDROM(ide)) ide->cylinder=0xEB14; if (IDE_DRIVE_IS_CDROM(ide_other)) ide_other->cylinder=0xEB14; idecallback[ide_board] = 0; timer_update_outstanding(); return; } ide = &ide_drives[cur_ide[ide_board]]; } ide->head = val & 0xF; ide->lba = val & 0x40; ide_other->head = val & 0xF; ide_other->lba = val & 0x40; ide->lba_addr = (ide->lba_addr & 0x0FFFFFF) | ((val & 0xF) << 24); ide_other->lba_addr = (ide_other->lba_addr & 0x0FFFFFF)|((val & 0xF) << 24); ide_irq_update(ide); return; case 0x1F7: /* Command register */ if (ide->type == IDE_NONE) return; // pclog("IDE command %02X drive %i\n",val,ide.drive); ide_irq_lower(ide); ide->command=val; // pclog("New IDE command - %02X %i %i\n",ide->command,cur_ide[ide_board],ide_board); ide->error=0; switch (val) { case WIN_SRST: /* ATAPI Device Reset */ if (IDE_DRIVE_IS_CDROM(ide)) ide->atastat = BUSY_STAT; else ide->atastat = READY_STAT; timer_process(); idecallback[ide_board]=100*IDE_TIME; timer_update_outstanding(); return; case WIN_RESTORE: case WIN_SEEK: // pclog("WIN_RESTORE start\n"); ide->atastat = READY_STAT; timer_process(); idecallback[ide_board]=100*IDE_TIME; timer_update_outstanding(); return; case WIN_READ_MULTIPLE: if (!ide->blocksize && (ide->type != IDE_CDROM)) fatal("READ_MULTIPLE - blocksize = 0\n"); #if 0 if (ide->lba) pclog("Read Multiple %i sectors from LBA addr %07X\n",ide->secount,ide->lba_addr); else pclog("Read Multiple %i sectors from sector %i cylinder %i head %i %i\n",ide->secount,ide->sector,ide->cylinder,ide->head,ins); #endif ide->blockcount = 0; case WIN_READ: case WIN_READ_NORETRY: case WIN_READ_DMA: /* if (ide.secount>1) { fatal("Read %i sectors from sector %i cylinder %i head %i\n",ide.secount,ide.sector,ide.cylinder,ide.head); }*/ #if 0 if (ide->lba) pclog("Read %i sectors from LBA addr %07X\n",ide->secount,ide->lba_addr); else pclog("Read %i sectors from sector %i cylinder %i head %i %i\n",ide->secount,ide->sector,ide->cylinder,ide->head,ins); #endif ide->atastat = BUSY_STAT; timer_process(); idecallback[ide_board]=200*IDE_TIME; timer_update_outstanding(); return; case WIN_WRITE_MULTIPLE: if (!ide->blocksize && (ide->type != IDE_CDROM)) fatal("Write_MULTIPLE - blocksize = 0\n"); #if 0 if (ide->lba) pclog("Write Multiple %i sectors from LBA addr %07X\n",ide->secount,ide->lba_addr); else pclog("Write Multiple %i sectors to sector %i cylinder %i head %i\n",ide->secount,ide->sector,ide->cylinder,ide->head); #endif ide->blockcount = 0; case WIN_WRITE: case WIN_WRITE_NORETRY: /* if (ide.secount>1) { fatal("Write %i sectors to sector %i cylinder %i head %i\n",ide.secount,ide.sector,ide.cylinder,ide.head); }*/ #if 0 if (ide->lba) pclog("Write %i sectors from LBA addr %07X\n",ide->secount,ide->lba_addr); else pclog("Write %i sectors to sector %i cylinder %i head %i\n",ide->secount,ide->sector,ide->cylinder,ide->head); #endif ide->atastat = DRQ_STAT | DSC_STAT | READY_STAT; ide->pos=0; return; case WIN_WRITE_DMA: #if 0 if (ide->lba) pclog("Write %i sectors from LBA addr %07X\n",ide->secount,ide->lba_addr); else pclog("Write %i sectors to sector %i cylinder %i head %i\n",ide->secount,ide->sector,ide->cylinder,ide->head); #endif ide->atastat = BUSY_STAT; timer_process(); idecallback[ide_board]=200*IDE_TIME; timer_update_outstanding(); return; case WIN_VERIFY: case WIN_VERIFY_ONCE: #if 0 if (ide->lba) pclog("Read verify %i sectors from LBA addr %07X\n",ide->secount,ide->lba_addr); else pclog("Read verify %i sectors from sector %i cylinder %i head %i\n",ide->secount,ide->sector,ide->cylinder,ide->head); #endif ide->atastat = BUSY_STAT; timer_process(); idecallback[ide_board]=200*IDE_TIME; timer_update_outstanding(); return; case WIN_FORMAT: // pclog("Format track %i head %i\n",ide.cylinder,ide.head); ide->atastat = DRQ_STAT; // idecallback[ide_board]=200; ide->pos=0; return; case WIN_SPECIFY: /* Initialize Drive Parameters */ ide->atastat = BUSY_STAT; timer_process(); idecallback[ide_board]=30*IDE_TIME; timer_update_outstanding(); // pclog("SPECIFY\n"); // output=1; return; case WIN_DRIVE_DIAGNOSTICS: /* Execute Drive Diagnostics */ case WIN_PIDENTIFY: /* Identify Packet Device */ case WIN_SET_MULTIPLE_MODE: /*Set Multiple Mode*/ // output=1; case WIN_SETIDLE1: /* Idle */ ide->atastat = BUSY_STAT; timer_process(); callbackide(ide_board); // idecallback[ide_board]=200*IDE_TIME; timer_update_outstanding(); return; case WIN_IDENTIFY: /* Identify Device */ case 0xEF: // output=3; // timetolive=500; ide->atastat = BUSY_STAT; timer_process(); idecallback[ide_board]=200*IDE_TIME; timer_update_outstanding(); return; case WIN_PACKETCMD: /* ATAPI Packet */ ide->packetstatus = ATAPI_STATUS_IDLE; ide->atastat = BUSY_STAT; timer_process(); idecallback[ide_board]=1;//30*IDE_TIME; timer_update_outstanding(); ide->pos=0; return; case 0xF0: default: ide->atastat = READY_STAT | ERR_STAT | DSC_STAT; ide->error = ABRT_ERR; ide_irq_raise(ide); /* fatal("Bad IDE command %02X\n", val);*/ return; } return; case 0x3F6: /* Device control */ if ((ide->fdisk&4) && !(val&4) && (ide->type != IDE_NONE || ide_other->type != IDE_NONE)) { timer_process(); idecallback[ide_board]=500*IDE_TIME; timer_update_outstanding(); ide->reset = ide_other->reset = 1; ide->atastat = ide_other->atastat = BUSY_STAT; // pclog("IDE Reset %i\n", ide_board); } ide->fdisk = ide_other->fdisk = val; ide_irq_update(ide); return; } // fatal("Bad IDE write %04X %02X\n", addr, val); } uint8_t readide(int ide_board, uint16_t addr) { IDE *ide = &ide_drives[cur_ide[ide_board]]; uint8_t temp; uint16_t tempw; addr|=0x80; /* ONLY FOR EXPERIMENTAL */ addr|=0x10; /* 1F0 | 10 = 1F0, 1E8 | 10 = 1F8 */ addr&=0xFFF7; /* 1F0 & FFF7 = 1F0, 1F8 | FFF7 = 1F0 */ #ifndef RPCEMU_IDE /* if (ide_board && (cr0&1) && !(eflags&VM_FLAG)) { // pclog("Failed read IDE %04X:%08X\n",CS,pc); return 0xFF; }*/ #endif // if ((cr0&1) && !(eflags&VM_FLAG)) // pclog("ReadIDE %04X from %04X(%08X):%08X\n", addr, CS, cs, pc); // return 0xFF; if (ide->type == IDE_NONE && (addr == 0x1f0 || addr == 0x1f7)) return 0; // /*if (addr!=0x1F7 && addr!=0x3F6) */pclog("Read IDEb %04X %02X %02X %i %04X:%04X %i %04X\n",addr,ide->atastat,(ide->atastat & ~DSC_STAT) | (ide->service ? SERVICE_STAT : 0),cur_ide[ide_board],CS,pc,ide_board, BX); //rpclog("Read IDE %08X %08X %02X\n",addr,PC,iomd.irqb.mask); switch (addr) { case 0x1F0: /* Data */ tempw = readidew(ide_board); // pclog("Read IDEW %04X\n", tempw); temp = tempw & 0xff; break; case 0x1F1: /* Error */ // pclog("Read error %02X\n",ide.error); temp = ide->error; break; case 0x1F2: /* Sector count */ // pclog("Read sector count %02X\n",ide->secount); temp = (uint8_t)ide->secount; break; case 0x1F3: /* Sector */ temp = (uint8_t)ide->sector; break; case 0x1F4: /* Cylinder low */ // pclog("Read cyl low %02X\n",ide.cylinder&0xFF); temp = (uint8_t)(ide->cylinder&0xFF); break; case 0x1F5: /* Cylinder high */ // pclog("Read cyl low %02X\n",ide.cylinder>>8); temp = (uint8_t)(ide->cylinder>>8); break; case 0x1F6: /* Drive/Head */ temp = (uint8_t)(ide->head | ((cur_ide[ide_board] & 1) ? 0x10 : 0) | (ide->lba ? 0x40 : 0) | 0xa0); break; case 0x1F7: /* Status */ if (ide->type == IDE_NONE) { // pclog("Return status 00\n"); temp = 0; break; } ide_irq_lower(ide); if (ide->type == IDE_CDROM) { // pclog("Read CDROM status %02X\n",(ide->atastat & ~DSC_STAT) | (ide->service ? SERVICE_STAT : 0)); temp = (ide->atastat & ~DSC_STAT) | (ide->service ? SERVICE_STAT : 0); } else { // && ide->service) return ide.atastat[ide.board]|SERVICE_STAT; // pclog("Return status %02X %04X:%04X %02X %02X\n",ide->atastat, CS ,pc, AH, BH); temp = ide->atastat; } break; case 0x3F6: /* Alternate Status */ // pclog("3F6 read %02X\n",ide.atastat[ide.board]); // if (output) output=0; if (ide->type == IDE_NONE) { // pclog("Return status 00\n"); temp = 0; break; } if (ide->type == IDE_CDROM) { // pclog("Read CDROM status %02X\n",(ide->atastat & ~DSC_STAT) | (ide->service ? SERVICE_STAT : 0)); temp = (ide->atastat & ~DSC_STAT) | (ide->service ? SERVICE_STAT : 0); } else { // && ide->service) return ide.atastat[ide.board]|SERVICE_STAT; // pclog("Return status %02X\n",ide->atastat); temp = ide->atastat; } break; } // if (ide_board) pclog("Read IDEb %04X %02X %02X %02X %i %04X:%04X %i\n", addr, temp, ide->atastat,(ide->atastat & ~DSC_STAT) | (ide->service ? SERVICE_STAT : 0),cur_ide[ide_board],CS,pc,ide_board); return temp; // fatal("Bad IDE read %04X\n", addr); } uint16_t readidew(int ide_board) { IDE *ide = &ide_drives[cur_ide[ide_board]]; uint16_t temp; #ifndef RPCEMU_IDE /* if (ide_board && (cr0&1) && !(eflags&VM_FLAG)) { // pclog("Failed read IDEw %04X:%08X\n",CS,pc); return 0xFFFF; }*/ #endif // return 0xFFFF; // pclog("Read IDEw %04X %04X:%04X %02X %i %i\n",ide->buffer[ide->pos >> 1],CS,pc,opcode,ins, ide->pos); //if (idedebug) pclog("Read IDEW %08X\n",PC); temp = ide->buffer[ide->pos >> 1]; #ifdef _RPCEMU_BIG_ENDIAN temp=(temp>>8)|(temp<<8); #endif ide->pos+=2; if ((ide->command == WIN_PACKETCMD) && ((ide->packetstatus == ATAPI_STATUS_REQ_SENSE) || (ide->packetstatus==8))) { callnonreadcd(ide); return temp; } if ((ide->pos>=512 && ide->command != WIN_PACKETCMD) || (ide->command == WIN_PACKETCMD && ide->pos>=ide->packlen)) { // pclog("Over! packlen %i %i\n",ide->packlen,ide->pos); ide->pos=0; if (ide->command == WIN_PACKETCMD)// && ide.packetstatus==6) { // pclog("Call readCD\n"); callreadcd(ide); } else { ide->atastat = READY_STAT | DSC_STAT; ide->packetstatus = ATAPI_STATUS_IDLE; if (ide->command == WIN_READ || ide->command == WIN_READ_NORETRY || ide->command == WIN_READ_MULTIPLE) { ide->secount = (ide->secount - 1) & 0xff; if (ide->secount) { ide_next_sector(ide); ide->atastat = BUSY_STAT; timer_process(); if (ide->command == WIN_READ_MULTIPLE) callbackide(ide_board); else idecallback[ide_board]=6*IDE_TIME; timer_update_outstanding(); // pclog("set idecallback\n"); // callbackide(ide_board); } // else // pclog("readidew done %02X\n", ide->atastat); } } } // pclog("Read IDEw %04X\n",temp); return temp; } uint32_t readidel(int ide_board) { uint16_t temp; // pclog("Read IDEl %i\n", ide_board); temp = readidew(ide_board); return temp | (readidew(ide_board) << 16); } int times30=0; void callbackide(int ide_board) { IDE *ide = &ide_drives[cur_ide[ide_board]]; IDE *ide_other = &ide_drives[cur_ide[ide_board] ^ 1]; off64_t addr; int c; ext_ide = ide; // return; if (ide->command==0x30) times30++; // if (times30==2240) output=1; //if (times30==2471 && ide->command==0xA0) output=1; ///*if (ide_board) */pclog("CALLBACK %02X %i %i %i\n",ide->command,times30,ide->reset,cur_ide[ide_board]); // if (times30==1294) // output=1; if (ide->reset) { ide->atastat = ide_other->atastat = READY_STAT | DSC_STAT; ide->error = ide_other->error = 1; ide->secount = ide_other->secount = 1; ide->sector = ide_other->sector = 1; ide->head = ide_other->head = 0; ide->cylinder = ide_other->cylinder = 0; ide->reset = ide_other->reset = 0; if (IDE_DRIVE_IS_CDROM(ide)) { ide->cylinder=0xEB14; atapi->stop(); } if (ide->type == IDE_NONE) { ide->cylinder=0xFFFF; atapi->stop(); } if (IDE_DRIVE_IS_CDROM(ide_other)) { ide_other->cylinder=0xEB14; atapi->stop(); } if (ide_other->type == IDE_NONE) { ide_other->cylinder=0xFFFF; atapi->stop(); } // pclog("Reset callback\n"); return; } switch (ide->command) { //Initialize the Task File Registers as follows: Status = 00h, Error = 01h, Sector Count = 01h, Sector Number = 01h, Cylinder Low = 14h, Cylinder High =EBh and Drive/Head = 00h. case WIN_SRST: /*ATAPI Device Reset */ ide->atastat = READY_STAT | DSC_STAT; ide->error=1; /*Device passed*/ ide->secount = ide->sector = 1; ide_set_signature(ide); if (IDE_DRIVE_IS_CDROM(ide)) ide->atastat = 0; ide_irq_raise(ide); if (IDE_DRIVE_IS_CDROM(ide)) ide->service = 0; return; case WIN_RESTORE: case WIN_SEEK: if (IDE_DRIVE_IS_CDROM(ide)) { pclog("WIN_RESTORE callback on CD-ROM\n"); goto abort_cmd; } // pclog("WIN_RESTORE callback\n"); ide->atastat = READY_STAT | DSC_STAT; ide_irq_raise(ide); return; case WIN_READ: case WIN_READ_NORETRY: if (IDE_DRIVE_IS_CDROM(ide)) { ide_set_signature(ide); goto abort_cmd; } addr = ide_get_sector(ide) * 512; // pclog("Read %i %i %i %08X\n",ide.cylinder,ide.head,ide.sector,addr); /* if (ide.cylinder || ide.head) { fatal("Read from other cylinder/head"); }*/ fseeko64(ide->hdfile, addr, SEEK_SET); fread(ide->buffer, 512, 1, ide->hdfile); ide->pos=0; ide->atastat = DRQ_STAT | READY_STAT | DSC_STAT; // pclog("Read sector callback %i %i %i offset %08X %i left %i %02X\n",ide.sector,ide.cylinder,ide.head,addr,ide.secount,ide.spt,ide.atastat[ide.board]); // if (addr) output=3; ide_irq_raise(ide); #ifndef RPCEMU_IDE readflash=1; #endif return; case WIN_READ_DMA: if (IDE_DRIVE_IS_CDROM(ide)) { goto abort_cmd; } addr = ide_get_sector(ide) * 512; fseeko64(ide->hdfile, addr, SEEK_SET); fread(ide->buffer, 512, 1, ide->hdfile); ide->pos=0; if (ide_bus_master_read_sector) { if (ide_bus_master_read_sector(ide_board, (uint8_t *)ide->buffer)) idecallback[ide_board]=6*IDE_TIME; /*DMA not performed, try again later*/ else { /*DMA successful*/ ide->atastat = DRQ_STAT | READY_STAT | DSC_STAT; ide->secount = (ide->secount - 1) & 0xff; if (ide->secount) { ide_next_sector(ide); ide->atastat = BUSY_STAT; idecallback[ide_board]=6*IDE_TIME; } else { ide_irq_raise(ide); } } } #ifndef RPCEMU_IDE readflash=1; #endif return; case WIN_READ_MULTIPLE: if (IDE_DRIVE_IS_CDROM(ide)) { goto abort_cmd; } addr = ide_get_sector(ide) * 512; // pclog("Read multiple from %08X %i (%i) %i\n", addr, ide->blockcount, ide->blocksize, ide->secount); fseeko64(ide->hdfile, addr, SEEK_SET); fread(ide->buffer, 512, 1, ide->hdfile); ide->pos=0; ide->atastat = DRQ_STAT | READY_STAT | DSC_STAT; if (!ide->blockcount)// || ide->secount == 1) { // pclog("Read multiple int\n"); ide_irq_raise(ide); } ide->blockcount++; if (ide->blockcount >= ide->blocksize) ide->blockcount = 0; #ifndef RPCEMU_IDE readflash=1; #endif return; case WIN_WRITE: case WIN_WRITE_NORETRY: if (IDE_DRIVE_IS_CDROM(ide)) { goto abort_cmd; } addr = ide_get_sector(ide) * 512; // pclog("Write sector callback %i %i %i offset %08X %i left %i\n",ide.sector,ide.cylinder,ide.head,addr,ide.secount,ide.spt); fseeko64(ide->hdfile, addr, SEEK_SET); fwrite(ide->buffer, 512, 1, ide->hdfile); ide_irq_raise(ide); ide->secount = (ide->secount - 1) & 0xff; if (ide->secount) { ide->atastat = DRQ_STAT | READY_STAT | DSC_STAT; ide->pos=0; ide_next_sector(ide); } else ide->atastat = READY_STAT | DSC_STAT; #ifndef RPCEMU_IDE readflash=1; #endif return; case WIN_WRITE_DMA: if (IDE_DRIVE_IS_CDROM(ide)) { goto abort_cmd; } if (ide_bus_master_write_sector) { if (ide_bus_master_write_sector(ide_board, (uint8_t *)ide->buffer)) idecallback[ide_board]=6*IDE_TIME; /*DMA not performed, try again later*/ else { /*DMA successful*/ addr = ide_get_sector(ide) * 512; fseeko64(ide->hdfile, addr, SEEK_SET); fwrite(ide->buffer, 512, 1, ide->hdfile); ide->atastat = DRQ_STAT | READY_STAT | DSC_STAT; ide->secount = (ide->secount - 1) & 0xff; if (ide->secount) { ide_next_sector(ide); ide->atastat = BUSY_STAT; idecallback[ide_board]=6*IDE_TIME; } else { ide_irq_raise(ide); } } } #ifndef RPCEMU_IDE readflash=1; #endif return; case WIN_WRITE_MULTIPLE: if (IDE_DRIVE_IS_CDROM(ide)) { goto abort_cmd; } addr = ide_get_sector(ide) * 512; // pclog("Write sector callback %i %i %i offset %08X %i left %i\n",ide.sector,ide.cylinder,ide.head,addr,ide.secount,ide.spt); fseeko64(ide->hdfile, addr, SEEK_SET); fwrite(ide->buffer, 512, 1, ide->hdfile); ide->blockcount++; if (ide->blockcount >= ide->blocksize || ide->secount == 1) { ide->blockcount = 0; ide_irq_raise(ide); } ide->secount = (ide->secount - 1) & 0xff; if (ide->secount) { ide->atastat = DRQ_STAT | READY_STAT | DSC_STAT; ide->pos=0; ide_next_sector(ide); } else ide->atastat = READY_STAT | DSC_STAT; #ifndef RPCEMU_IDE readflash=1; #endif return; case WIN_VERIFY: case WIN_VERIFY_ONCE: if (IDE_DRIVE_IS_CDROM(ide)) { goto abort_cmd; } ide->pos=0; ide->atastat = READY_STAT | DSC_STAT; // pclog("Read verify callback %i %i %i offset %08X %i left\n",ide.sector,ide.cylinder,ide.head,addr,ide.secount); ide_irq_raise(ide); #ifndef RPCEMU_IDE readflash=1; #endif return; case WIN_FORMAT: if (IDE_DRIVE_IS_CDROM(ide)) { goto abort_cmd; } addr = ide_get_sector(ide) * 512; // pclog("Format cyl %i head %i offset %08X %08X %08X secount %i\n",ide.cylinder,ide.head,addr,addr>>32,addr,ide.secount); fseeko64(ide->hdfile, addr, SEEK_SET); memset(ide->buffer, 0, 512); for (c=0;csecount;c++) { fwrite(ide->buffer, 512, 1, ide->hdfile); } ide->atastat = READY_STAT | DSC_STAT; ide_irq_raise(ide); #ifndef RPCEMU_IDE readflash=1; #endif return; case WIN_DRIVE_DIAGNOSTICS: ide_set_signature(ide); ide->error=1; /*No error detected*/ if (IDE_DRIVE_IS_CDROM(ide)) { ide->atastat = 0; } else { ide->atastat = READY_STAT | DSC_STAT; ide_irq_raise(ide); } return; case WIN_SPECIFY: /* Initialize Drive Parameters */ if (IDE_DRIVE_IS_CDROM(ide)) { #ifndef RPCEMU_IDE pclog("IS CDROM - ABORT\n"); #endif goto abort_cmd; } ide->spt=ide->secount; ide->hpc=ide->head+1; ide->atastat = READY_STAT | DSC_STAT; #ifndef RPCEMU_IDE // pclog("SPECIFY - %i sectors per track, %i heads per cylinder %i %i\n",ide->spt,ide->hpc,cur_ide[ide_board],ide_board); #endif ide_irq_raise(ide); return; case WIN_PIDENTIFY: /* Identify Packet Device */ if (IDE_DRIVE_IS_CDROM(ide)) { // pclog("ATAPI identify\n"); ide_atapi_identify(ide); ide->pos=0; ide->error=0; ide->atastat = DRQ_STAT | READY_STAT | DSC_STAT; ide_irq_raise(ide); return; } // pclog("Not ATAPI\n"); goto abort_cmd; case WIN_SET_MULTIPLE_MODE: if (IDE_DRIVE_IS_CDROM(ide)) { #ifndef RPCEMU_IDE pclog("IS CDROM - ABORT\n"); #endif goto abort_cmd; } ide->blocksize = ide->secount; ide->atastat = READY_STAT | DSC_STAT; #ifndef RPCEMU_IDE pclog("Set multiple mode - %i\n", ide->blocksize); #endif ide_irq_raise(ide); return; case WIN_SETIDLE1: /* Idle */ case 0xEF: goto abort_cmd; case WIN_IDENTIFY: /* Identify Device */ if (ide->type == IDE_NONE) { goto abort_cmd; } if (IDE_DRIVE_IS_CDROM(ide)) { ide_set_signature(ide); goto abort_cmd; } else { ide_identify(ide); ide->pos=0; ide->atastat = DRQ_STAT | READY_STAT | DSC_STAT; // pclog("ID callback\n"); ide_irq_raise(ide); } return; case WIN_PACKETCMD: /* ATAPI Packet */ if (!IDE_DRIVE_IS_CDROM(ide)) goto abort_cmd; // pclog("Packet callback! %i %08X\n",ide->packetstatus,ide); if (ide->packetstatus == ATAPI_STATUS_IDLE) { readcdmode=0; ide->pos=0; ide->secount = (uint8_t)((ide->secount&0xF8)|1); ide->atastat = READY_STAT | DRQ_STAT |(ide->atastat&ERR_STAT); //ide_irq_raise(ide); // pclog("1 Preparing to recieve packet max DRQ count %04X\n",ide->cylinder); } else if (ide->packetstatus == ATAPI_STATUS_COMMAND) { ide->atastat = BUSY_STAT|(ide->atastat&ERR_STAT); // pclog("Running ATAPI command 2\n"); atapicommand(ide_board); // exit(-1); } else if (ide->packetstatus == ATAPI_STATUS_COMPLETE) { // pclog("packetstatus==2\n"); ide->atastat = READY_STAT; ide->secount=3; ide_irq_raise(ide); // if (iomd.irqb.mask&2) output=1; } else if (ide->packetstatus == ATAPI_STATUS_DATA) { ide->atastat = READY_STAT|DRQ_STAT|(ide->atastat&ERR_STAT); // rpclog("Recieve data packet 3! %02X\n",ide->atastat); ide_irq_raise(ide); ide->packetstatus=0xFF; } else if (ide->packetstatus == ATAPI_STATUS_PACKET_REQ) { ide->atastat = 0x58 | (ide->atastat & ERR_STAT); // pclog("Send data packet 4!\n"); ide_irq_raise(ide); // ide.packetstatus=5; ide->pos=2; } else if (ide->packetstatus == ATAPI_STATUS_PACKET_RECEIVED) { // pclog("Packetstatus 5 !\n"); atapicommand(ide_board); } else if (ide->packetstatus == ATAPI_STATUS_READCD) /*READ CD callback*/ { ide->atastat = DRQ_STAT|(ide->atastat&ERR_STAT); // pclog("Recieve data packet 6!\n"); ide_irq_raise(ide); // ide.packetstatus=0xFF; } else if (ide->packetstatus == ATAPI_STATUS_REQ_SENSE) /*REQUEST SENSE callback #1*/ { pclog("REQUEST SENSE callback #1: setting status to 0x5A\n"); ide->atastat = 0x58 | (ide->atastat & ERR_STAT); ide_irq_raise(ide); } else if (ide->packetstatus == ATAPI_STATUS_ERROR) /*Error callback*/ { // pclog("Packet error\n"); ide->atastat = READY_STAT | ERR_STAT; ide_irq_raise(ide); } else if (ide->packetstatus == ATAPI_STATUS_ERROR_2) /*Error callback with atastat already set - needed for the disc change stuff.*/ { //pclog("Packet check status\n"); ide->atastat = ERR_STAT; ide_irq_raise(ide); } return; } abort_cmd: ide->command = 0; ide->atastat = READY_STAT | ERR_STAT | DSC_STAT; ide->error = ABRT_ERR; ide->pos = 0; ide_irq_raise(ide); } void ide_callback_pri() { idecallback[0] = 0; callbackide(0); } void ide_callback_sec() { idecallback[1] = 0; callbackide(1); } void ide_callback_ter() { idecallback[2] = 0; callbackide(2); } /*ATAPI CD-ROM emulation*/ struct { int sensekey,asc,ascq; } atapi_sense; static uint8_t atapi_set_profile(uint8_t *buf, uint8_t *index, uint16_t profile) { uint8_t *buf_profile = buf + 12; /* start of profiles */ buf_profile += ((*index) * 4); /* start of indexed profile */ buf_profile[0] = (profile >> 8) & 0xff; buf_profile[1] = profile & 0xff; buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7])); /* each profile adds 4 bytes to the response */ (*index)++; buf[11] += 4; /* Additional Length */ return 4; } static int atapi_read_structure(IDE *ide, int format, const uint8_t *packet, uint8_t *buf) { switch (format) { case 0x0: /* Physical format information */ { int layer = packet[6]; uint64_t total_sectors; if (layer != 0) return -ASC_INV_FIELD_IN_CMD_PACKET; total_sectors >>= 2; if (total_sectors == 0) return -ASC_MEDIUM_NOT_PRESENT; buf[4] = 1; /* DVD-ROM, part version 1 */ buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ buf[7] = 0; /* default densities */ /* FIXME: 0x30000 per spec? */ buf[8] = buf[9] = buf[10] = buf[11] = 0; /* start sector */ buf[12] = (total_sectors >> 24) & 0xff; /* end sector */ buf[13] = (total_sectors >> 16) & 0xff; buf[14] = (total_sectors >> 8) & 0xff; buf[15] = total_sectors & 0xff; buf[16] = (total_sectors >> 24) & 0xff; /* l0 end sector */ buf[17] = (total_sectors >> 16) & 0xff; buf[18] = (total_sectors >> 8) & 0xff; buf[19] = total_sectors & 0xff; /* Size of buffer, not including 2 byte size field */ buf[0] = ((2048+2)>>8)&0xff; buf[1] = (2048+2)&0xff; /* 2k data + 4 byte header */ return (2048 + 4); } case 0x01: /* DVD copyright information */ buf[4] = 0; /* no copyright data */ buf[5] = 0; /* no region restrictions */ /* Size of buffer, not including 2 byte size field */ buf[0] = ((4+2)>>8)&0xff; buf[1] = (4+2)&0xff; /* 4 byte header + 4 byte data */ return (4 + 4); case 0x03: /* BCA information - invalid field for no BCA info */ return -ASC_INV_FIELD_IN_CMD_PACKET; case 0x04: /* DVD disc manufacturing information */ /* Size of buffer, not including 2 byte size field */ buf[0] = ((2048+2)>>8)&0xff; buf[1] = (2048+2)&0xff; /* 2k data + 4 byte header */ return (2048 + 4); case 0xff: /* * This lists all the command capabilities above. Add new ones * in order and update the length and buffer return values. */ buf[4] = 0x00; /* Physical format */ buf[5] = 0x40; /* Not writable, is readable */ buf[6] = ((2048+4)>>8)&0xff; buf[7] = (2048+4)&0xff; buf[8] = 0x01; /* Copyright info */ buf[9] = 0x40; /* Not writable, is readable */ buf[10] = ((4+4)>>8)&0xff; buf[11] = (4+4)&0xff; buf[12] = 0x03; /* BCA info */ buf[13] = 0x40; /* Not writable, is readable */ buf[14] = ((188+4)>>8)&0xff; buf[15] = (188+4)&0xff; buf[16] = 0x04; /* Manufacturing info */ buf[17] = 0x40; /* Not writable, is readable */ buf[18] = ((2048+4)>>8)&0xff; buf[19] = (2048+4)&0xff; /* Size of buffer, not including 2 byte size field */ buf[6] = ((16+2)>>8)&0xff; buf[7] = (16+2)&0xff; /* data written + 4 byte header */ return (16 + 4); default: /* TODO: formats beyond DVD-ROM requires */ return -ASC_INV_FIELD_IN_CMD_PACKET; } } static uint32_t atapi_event_status(IDE *ide, uint8_t *buffer) { uint8_t event_code, media_status = 0; if (buffer[5]) { media_status = MS_TRAY_OPEN; atapi->stop(); } else { media_status = MS_MEDIA_PRESENT; } event_code = MEC_NO_CHANGE; if (media_status != MS_TRAY_OPEN) { if (!buffer[4]) { event_code = MEC_NEW_MEDIA; atapi->load(); } else if (buffer[4]==2) { event_code = MEC_EJECT_REQUESTED; atapi->eject(); } } buffer[4] = event_code; buffer[5] = media_status; buffer[6] = 0; buffer[7] = 0; return 8; } static int changed_status = 0; void atapi_cmd_error(IDE *ide, uint8_t sensekey, uint8_t asc) { ide->error = (sensekey << 4); ide->atastat = READY_STAT | ERR_STAT; ide->secount = (ide->secount & ~7) | 3; ide->packetstatus = 0x80; idecallback[ide->board]=50*IDE_TIME; } uint8_t atapi_prev; int toctimes=0; void atapi_insert_cdrom() { atapi_sense.sensekey=SENSE_UNIT_ATTENTION; atapi_sense.asc=ASC_MEDIUM_MAY_HAVE_CHANGED; atapi_sense.ascq=0; } void atapi_command_send_init(IDE *ide, uint8_t command, int req_length, int alloc_length) { if (ide->cylinder == 0xffff) ide->cylinder = 0xfffe; if ((ide->cylinder & 1) && !(alloc_length <= ide->cylinder)) { pclog("Odd byte count (0x%04x) to ATAPI command 0x%02x, using 0x%04x\n", ide->cylinder, command, ide->cylinder - 1); ide->cylinder--; } if (alloc_length < 0) fatal("Allocation length < 0\n"); if (alloc_length == 0) alloc_length = ide->cylinder; // Status: 0x80 (busy), 0x08 (drq), 0x01 (err) // Interrupt: 0x02 (i_o), 0x01 (c_d) /* No atastat setting: PCem actually emulates the callback cycle. */ ide->secount = 2; // no bytes transferred yet ide->pos = 0; if ((ide->cylinder > req_length) || (ide->cylinder == 0)) ide->cylinder = req_length; if (ide->cylinder > alloc_length) ide->cylinder = alloc_length; //pclog("atapi_command_send_init(ide, %02X, %04X, %04X)\n", command, req_length, alloc_length); //pclog("IDE settings: Pos=%08X, Secount=%08X, Cylinder=%08X\n", ide->pos, ide->secount, ide->cylinder); } static void atapi_command_ready(int ide_board, int packlen) { IDE *ide = &ide_drives[cur_ide[ide_board]]; ide->packetstatus = ATAPI_STATUS_REQ_SENSE; idecallback[ide_board]=60*IDE_TIME; ide->packlen=packlen; } static void atapi_sense_clear(int command, int ignore_ua) { if ((atapi_sense.sensekey == SENSE_UNIT_ATTENTION) || ignore_ua) { atapi_prev=command; atapi_sense.sensekey=0; atapi_sense.asc=0; atapi_sense.ascq=0; } } int cd_status = CD_STATUS_EMPTY; int prev_status; static void atapicommand(int ide_board) { IDE *ide = &ide_drives[cur_ide[ide_board]]; uint8_t *idebufferb = (uint8_t *) ide->buffer; uint8_t rcdmode = 0; int c; int len; int msf; int pos=0; unsigned char temp; uint32_t size; int is_error; uint8_t page_code; int max_len; unsigned idx = 0; unsigned size_idx; unsigned preamble_len; int toc_format; int temp_command; int alloc_length; int completed; #ifndef RPCEMU_IDE pclog("New ATAPI command %02X %i\n",idebufferb[0],ins); #endif // readflash=1; msf=idebufferb[1]&2; ide->cdlen=0; is_error = 0; if (atapi->medium_changed()) { atapi_insert_cdrom(); } /*If UNIT_ATTENTION is set, error out with NOT_READY. VIDE-CDD.SYS will then issue a READ_TOC, which can pass through UNIT_ATTENTION and will clear sense. NT 3.1 / AZTIDECD.SYS will then issue a REQUEST_SENSE, which can also pass through UNIT_ATTENTION but will clear sense AFTER sending it back. In any case, if the command cannot pass through, set our state to errored.*/ if (!(atapi_cmd_table[idebufferb[0]] & ALLOW_UA) && atapi_sense.sensekey == SENSE_UNIT_ATTENTION) { atapi_cmd_error(ide, atapi_sense.sensekey, atapi_sense.asc); is_error = 1; } /*Unless the command issued was a REQUEST_SENSE or TEST_UNIT_READY, clear sense. This is important because both VIDE-CDD.SYS and NT 3.1 / AZTIDECD.SYS rely on this behaving VERY specifically. VIDE-CDD.SYS will clear sense through READ_TOC, while NT 3.1 / AZTIDECD.SYS will issue a REQUEST_SENSE.*/ if ((idebufferb[0]!=GPCMD_REQUEST_SENSE) && (idebufferb[0]!=GPCMD_TEST_UNIT_READY)) { /* GPCMD_TEST_UNIT_READY is NOT supposed to clear sense! */ atapi_sense_clear(idebufferb[0], 1); } /*If our state has been set to errored, clear it, and return.*/ if (is_error) { is_error = 0; return; } if ((atapi_cmd_table[idebufferb[0]] & CHECK_READY) && !atapi->ready()) { atapi_cmd_error(ide, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); atapi_sense.sensekey = SENSE_NOT_READY; atapi_sense.asc = ASC_MEDIUM_NOT_PRESENT; atapi_sense.ascq = 0; return; } prev_status = cd_status; cd_status = atapi->status(); if (((prev_status == CD_STATUS_PLAYING) || (prev_status == CD_STATUS_PAUSED)) && ((cd_status != CD_STATUS_PLAYING) && (cd_status != CD_STATUS_PAUSED))) { completed = 1; } else { completed = 0; } switch (idebufferb[0]) { case GPCMD_TEST_UNIT_READY: ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; case GPCMD_REQUEST_SENSE: /* Used by ROS 4+ */ alloc_length = idebufferb[4]; temp_command = idebufferb[0]; atapi_command_send_init(ide, temp_command, 18, alloc_length); /*Will return 18 bytes of 0*/ memset(idebufferb,0,512); idebufferb[0]=0x80|0x70; if ((atapi_sense.sensekey > 0) || (cd_status < CD_STATUS_PLAYING)) { if (completed) { idebufferb[2]=SENSE_ILLEGAL_REQUEST; idebufferb[12]=ASC_AUDIO_PLAY_OPERATION; idebufferb[13]=ASCQ_AUDIO_PLAY_OPERATION_COMPLETED; } else { idebufferb[2]=atapi_sense.sensekey; idebufferb[12]=atapi_sense.asc; idebufferb[13]=atapi_sense.ascq; } } else { idebufferb[2]=SENSE_ILLEGAL_REQUEST; idebufferb[12]=ASC_AUDIO_PLAY_OPERATION; idebufferb[13]=(cd_status == CD_STATUS_PLAYING) ? ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS : ASCQ_AUDIO_PLAY_OPERATION_PAUSED; } idebufferb[7]=10; // pclog("REQUEST SENSE start\n"); atapi_command_ready(ide_board, 18); /* Clear the sense stuff as per the spec. */ atapi_sense_clear(temp_command, 0); break; case GPCMD_SET_SPEED: ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; case GPCMD_MECHANISM_STATUS: /*0xbd*/ len=(idebufferb[7]<<16)|(idebufferb[8]<<8)|idebufferb[9]; if (len == 0) fatal("Zero allocation length to MECHANISM STATUS not impl.\n"); atapi_command_send_init(ide, idebufferb[0], 8, alloc_length); idebufferb[0] = 0; idebufferb[1] = 0; idebufferb[2] = 0; idebufferb[3] = 0; idebufferb[4] = 0; idebufferb[5] = 1; idebufferb[6] = 0; idebufferb[7] = 0; // len = 8; atapi_command_ready(ide_board, 8); break; case GPCMD_READ_TOC_PMA_ATIP: // pclog("Read TOC ready? %08X\n",ide); toctimes++; // if (toctimes==2) output=3; // pclog("Read TOC %02X\n",idebufferb[9]); toc_format = idebufferb[2] & 0xf; if (toc_format == 0) toc_format = (idebufferb[9]>>6) & 3; switch (toc_format) { case 0: /*Normal*/ // pclog("ATAPI: READ TOC type requested: Normal\n"); len=idebufferb[8]+(idebufferb[7]<<8); len=atapi->readtoc(idebufferb,idebufferb[6],msf,len,0); break; case 1: /*Multi session*/ // pclog("ATAPI: READ TOC type requested: Multi-session\n"); len=idebufferb[8]+(idebufferb[7]<<8); len=atapi->readtoc_session(idebufferb,msf,len); idebufferb[0]=0; idebufferb[1]=0xA; break; case 2: /*Raw*/ // pclog("ATAPI: READ TOC type requested: Raw TOC\n"); len=idebufferb[8]+(idebufferb[7]<<8); len=atapi->readtoc_raw(idebufferb,len); break; default: // pclog("ATAPI: Unknown READ TOC type requested: %i\n", (idebufferb[9]>>6)); ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; return; /* pclog("Bad read TOC format\n"); pclog("Packet data :\n"); for (c=0;c<12;c++) pclog("%02X ",idebufferb[c]); pclog("\n"); exit(-1);*/ } // pclog("ATAPI buffer len %i\n",len); ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=len; ide->secount=2; ide->pos=0; idecallback[ide_board]=60*IDE_TIME; ide->packlen=len; return; case GPCMD_READ_CD: // pclog("Read CD : start LBA %02X%02X%02X%02X Length %02X%02X%02X Flags %02X\n",idebufferb[2],idebufferb[3],idebufferb[4],idebufferb[5],idebufferb[6],idebufferb[7],idebufferb[8],idebufferb[9]); rcdmode = idebufferb[9] & 0xF8; if ((rcdmode != 0x10) && (rcdmode != 0xF8)) { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; atapi_sense.asc = ASC_ILLEGAL_OPCODE; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; // pclog("Bad flags bits %02X\n",idebufferb[9]); // exit(-1); } /* if (idebufferb[6] || idebufferb[7] || (idebufferb[8]!=1)) { pclog("More than 1 sector!\n"); exit(-1); }*/ ide->cdlen=(idebufferb[6]<<16)|(idebufferb[7]<<8)|idebufferb[8]; ide->cdpos=(idebufferb[2]<<24)|(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; // pclog("Read at %08X %08X\n",ide.cdpos,ide.cdpos*2048); if (rcdmode == 0x10) atapi->readsector(idebufferb,ide->cdpos); else atapi->readsector_raw(idebufferb,ide->cdpos); #ifndef RPCEMU_IDE readflash=1; #endif readcdmode = (rcdmode == 0xF8); ide->cdpos++; ide->cdlen--; if (ide->cdlen >= 0) ide->packetstatus = ATAPI_STATUS_READCD; else ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=(idebufferb[9] == 0x10) ? 2048 : 2352; ide->secount=2; ide->pos=0; idecallback[ide_board]=60*IDE_TIME; ide->packlen=(idebufferb[9] == 0x10) ? 2048 : 2352; return; case GPCMD_READ_6: case GPCMD_READ_10: case GPCMD_READ_12: // pclog("Read 10 : start LBA %02X%02X%02X%02X Length %02X%02X%02X Flags %02X\n",idebufferb[2],idebufferb[3],idebufferb[4],idebufferb[5],idebufferb[6],idebufferb[7],idebufferb[8],idebufferb[9]); readcdmode = 0; if (idebufferb[0] == GPCMD_READ_6) { ide->cdlen=idebufferb[4]; ide->cdpos=((((uint32_t) idebufferb[1]) & 0x1f)<<16)|(((uint32_t) idebufferb[2])<<8)|((uint32_t) idebufferb[3]); } else if (idebufferb[0] == GPCMD_READ_10) { ide->cdlen=(idebufferb[7]<<8)|idebufferb[8]; ide->cdpos=(idebufferb[2]<<24)|(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; } else { ide->cdlen=(((uint32_t) idebufferb[6])<<24)|(((uint32_t) idebufferb[7])<<16)|(((uint32_t) idebufferb[8])<<8)|((uint32_t) idebufferb[9]); ide->cdpos=(((uint32_t) idebufferb[2])<<24)|(((uint32_t) idebufferb[3])<<16)|(((uint32_t) idebufferb[4])<<8)|((uint32_t) idebufferb[5]); } if (!ide->cdlen) { // pclog("All done - callback set\n"); ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=20*IDE_TIME; break; } atapi->readsector(idebufferb,ide->cdpos); #ifndef RPCEMU_IDE readflash=1; #endif ide->cdpos++; ide->cdlen--; if (ide->cdlen >= 0) ide->packetstatus = ATAPI_STATUS_READCD; else ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=2048; ide->secount=2; ide->pos=0; idecallback[ide_board]=60*IDE_TIME; ide->packlen=2048; return; case GPCMD_READ_HEADER: if (msf) { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) { ide->error |= MCR_ERR; } atapi_sense.asc = ASC_ILLEGAL_OPCODE; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; // pclog("Read Header MSF!\n"); // exit(-1); } for (c=0;c<4;c++) idebufferb[c+4]=idebufferb[c+2]; idebufferb[0]=1; /*2048 bytes user data*/ idebufferb[1]=idebufferb[2]=idebufferb[3]=0; ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=8; ide->secount=2; ide->pos=0; idecallback[ide_board]=60*IDE_TIME; ide->packlen=8; return; case GPCMD_MODE_SENSE_6: case GPCMD_MODE_SENSE_10: temp_command = idebufferb[0]; if (temp_command == GPCMD_MODE_SENSE_6) len=idebufferb[4]; else len=(idebufferb[8]|(idebufferb[7]<<8)); temp=idebufferb[2] & 0x3F; memset(idebufferb, 0, len); alloc_length = len; // for (c=0;catastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; idecallback[ide_board]=50*IDE_TIME; atapi_cmd_error(ide, atapi_sense.sensekey, atapi_sense.asc); ide->atastat = 0x53; ide->packetstatus = ATAPI_STATUS_ERROR; atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; atapi_sense.ascq = 0; return; } if (temp_command == GPCMD_MODE_SENSE_6) { len = ide_atapi_mode_sense(ide,4,temp); idebufferb[0] = len - 1; idebufferb[1]=3; /*120mm data CD-ROM*/ } else { len = ide_atapi_mode_sense(ide,8,temp); idebufferb[0]=(len - 2)>>8; idebufferb[1]=(len - 2)&255; idebufferb[2]=3; /*120mm data CD-ROM*/ } atapi_command_send_init(ide, temp_command, len, alloc_length); atapi_command_ready(ide_board, len); return; case GPCMD_MODE_SELECT_6: case GPCMD_MODE_SELECT_10: if (ide->packetstatus == ATAPI_STATUS_PACKET_RECEIVED) { ide->atastat = READY_STAT; ide->secount=3; // pclog("Recieve data packet!\n"); ide_irq_raise(ide); ide->packetstatus=0xFF; ide->pos=0; // pclog("Length - %02X%02X\n",idebufferb[0],idebufferb[1]); // pclog("Page %02X length %02X\n",idebufferb[8],idebufferb[9]); } else { if (idebufferb[0] == GPCMD_MODE_SELECT_6) { len=idebufferb[4]; prefix_len = 6; } else { len=(idebufferb[7]<<8)|idebufferb[8]; prefix_len = 10; } page_current = idebufferb[2]; if (page_flags[page_current] & PAGE_CHANGEABLE) page_flags[GPMODE_CDROM_AUDIO_PAGE] |= PAGE_CHANGED; ide->packetstatus = ATAPI_STATUS_PACKET_REQ; ide->cylinder=len; ide->secount=0; ide->pos=0; idecallback[ide_board]=60*IDE_TIME; ide->packlen=len; /* pclog("Waiting for ARM to send packet %i\n",len); pclog("Packet data :\n"); for (c=0;c<12;c++) pclog("%02X ",idebufferb[c]); pclog("\n");*/ } return; case GPCMD_GET_CONFIGURATION: { temp_command = idebufferb[0]; /* XXX: could result in alignment problems in some architectures */ len = (idebufferb[7]<<8)|idebufferb[8]; alloc_length = len; uint8_t index = 0; /* only feature 0 is supported */ if (idebufferb[2] != 0 || idebufferb[3] != 0) { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; return; } /* * XXX: avoid overflow for io_buffer if len is bigger than * the size of that buffer (dimensioned to max number of * sectors to transfer at once) * * Only a problem if the feature/profiles grow. */ if (alloc_length > 512) /* XXX: assume 1 sector */ alloc_length = 512; memset(idebufferb, 0, alloc_length); /* * the number of sectors from the media tells us which profile * to use as current. 0 means there is no media */ if (len > CD_MAX_SECTORS ) { idebufferb[6] = (MMC_PROFILE_DVD_ROM >> 8) & 0xff; idebufferb[7] = MMC_PROFILE_DVD_ROM & 0xff; } else if (len <= CD_MAX_SECTORS) { idebufferb[6] = (MMC_PROFILE_CD_ROM >> 8) & 0xff; idebufferb[7] = MMC_PROFILE_CD_ROM & 0xff; } idebufferb[10] = 0x02 | 0x01; /* persistent and current */ alloc_length = 12; /* headers: 8 + 4 */ alloc_length += atapi_set_profile(idebufferb, &index, MMC_PROFILE_DVD_ROM); alloc_length += atapi_set_profile(idebufferb, &index, MMC_PROFILE_CD_ROM); idebufferb[0] = ((alloc_length-4) >> 24) & 0xff; idebufferb[1] = ((alloc_length-4) >> 16) & 0xff; idebufferb[2] = ((alloc_length-4) >> 8) & 0xff; idebufferb[3] = (alloc_length-4) & 0xff; atapi_command_send_init(ide, temp_command, len, alloc_length); atapi_command_ready(ide_board, len); } break; case GPCMD_GET_EVENT_STATUS_NOTIFICATION: /*0x4a*/ temp_command = idebufferb[0]; alloc_length = len; { struct { uint8_t opcode; uint8_t polled; uint8_t reserved2[2]; uint8_t class; uint8_t reserved3[2]; uint16_t len; uint8_t control; } *gesn_cdb; struct { uint16_t len; uint8_t notification_class; uint8_t supported_events; } *gesn_event_header; unsigned int used_len; gesn_cdb = (void *)idebufferb; gesn_event_header = (void *)idebufferb; /* It is fine by the MMC spec to not support async mode operations */ if (!(gesn_cdb->polled & 0x01)) { /* asynchronous mode */ /* Only pollign is supported, asynchronous mode is not. */ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; idecallback[ide_board]=50*IDE_TIME; atapi_cmd_error(ide, atapi_sense.sensekey, atapi_sense.asc); ide->atastat = 0x53; ide->packetstatus=0x80; atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; atapi_sense.ascq = 0; return; } /* polling mode operation */ /* * These are the supported events. * * We currently only support requests of the 'media' type. * Notification class requests and supported event classes are bitmasks, * but they are built from the same values as the "notification class" * field. */ gesn_event_header->supported_events = 1 << GESN_MEDIA; /* * We use |= below to set the class field; other bits in this byte * are reserved now but this is useful to do if we have to use the * reserved fields later. */ gesn_event_header->notification_class = 0; /* * Responses to requests are to be based on request priority. The * notification_class_request_type enum above specifies the * priority: upper elements are higher prio than lower ones. */ if (gesn_cdb->class & (1 << GESN_MEDIA)) { gesn_event_header->notification_class |= GESN_MEDIA; used_len = atapi_event_status(ide, idebufferb); } else { gesn_event_header->notification_class = 0x80; /* No event available */ used_len = sizeof(*gesn_event_header); } gesn_event_header->len = used_len - sizeof(*gesn_event_header); } atapi_command_send_init(ide, temp_command, len, alloc_length); atapi_command_ready(ide_board, len); break; case GPCMD_READ_DISC_INFORMATION: idebufferb[1] = 32; idebufferb[2] = 0xe; /* last session complete, disc finalized */ idebufferb[3] = 1; /* first track on disc */ idebufferb[4] = 1; /* # of sessions */ idebufferb[5] = 1; /* first track of last session */ idebufferb[6] = 1; /* last track of last session */ idebufferb[7] = 0x20; /* unrestricted use */ idebufferb[8] = 0x00; /* CD-ROM */ len=34; ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=len; ide->secount=2; ide->pos=0; idecallback[ide_board]=60*IDE_TIME; ide->packlen=len; break; case GPCMD_PLAY_AUDIO_10: case GPCMD_PLAY_AUDIO_12: case GPCMD_PLAY_AUDIO_MSF: /*This is apparently deprecated in the ATAPI spec, and apparently has been since 1995 (!). Hence I'm having to guess most of it*/ if (idebufferb[0] == GPCMD_PLAY_AUDIO_10) { pos=(idebufferb[2]<<24)|(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; len=(idebufferb[7]<<8)|idebufferb[8]; } else if (idebufferb[0] == GPCMD_PLAY_AUDIO_MSF) { pos=(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; len=(idebufferb[6]<<16)|(idebufferb[7]<<8)|idebufferb[8]; } else { pos=(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; len=(idebufferb[7]<<16)|(idebufferb[8]<<8)|idebufferb[9]; } if ((cdrom_drive < 1) || (cdrom_drive == CDROM_ISO) || (cd_status <= CD_STATUS_DATA_ONLY) || !atapi->is_track_audio(pos, (idebufferb[0] == GPCMD_PLAY_AUDIO_MSF) ? 1 : 0)) { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; atapi_sense.asc = ASC_ILLEGAL_MODE_FOR_THIS_TRACK; atapi_sense.ascq = 0; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; atapi_cmd_error(ide, atapi_sense.sensekey, atapi_sense.asc); break; } atapi->playaudio(pos, len, (idebufferb[0] == GPCMD_PLAY_AUDIO_MSF) ? 1 : 0); ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; case GPCMD_READ_SUBCHANNEL: temp=idebufferb[2]&0x40; if (idebufferb[3]!=1) { // pclog("Read subchannel check condition %02X\n",idebufferb[3]); ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; // ide->discchanged=1; /* Fixes some bugs with NT 3.1. */ atapi_sense.asc = ASC_ILLEGAL_OPCODE; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; /* pclog("Bad read subchannel!\n"); pclog("Packet data :\n"); for (c=0;c<12;c++) pclog("%02X\n",idebufferb[c]); dumpregs(); exit(-1);*/ } pos=0; idebufferb[pos++]=0; idebufferb[pos++]=0; /*Audio status*/ idebufferb[pos++]=0; idebufferb[pos++]=0; /*Subchannel length*/ idebufferb[pos++]=1; /*Format code*/ idebufferb[1]=atapi->getcurrentsubchannel(&idebufferb[5],msf); // pclog("Read subchannel complete - audio status %02X\n",idebufferb[1]); len=11+5; if (!temp) len=4; ide->packetstatus = ATAPI_STATUS_DATA; ide->cylinder=len; ide->secount=2; ide->pos=0; idecallback[ide_board]=1000*IDE_TIME; ide->packlen=len; break; case GPCMD_READ_DVD_STRUCTURE: temp_command = idebufferb[0]; int media = idebufferb[1]; int format = idebufferb[7]; int ret; len = (((uint32_t) idebufferb[6])<<24)|(((uint32_t) idebufferb[7])<<16)|(((uint32_t) idebufferb[8])<<8)|((uint32_t) idebufferb[9]); alloc_length = len; if (format < 0xff) { if (len <= CD_MAX_SECTORS) { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; atapi_sense.asc = ASC_INCOMPATIBLE_FORMAT; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; } else { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; return; } } memset(idebufferb, 0, alloc_length > 256 * 512 + 4 ? 256 * 512 + 4 : alloc_length); switch (format) { case 0x00 ... 0x7f: case 0xff: if (media == 0) { ret = atapi_read_structure(ide, format, idebufferb, idebufferb); if (ret < 0) atapi_cmd_error(ide, SENSE_ILLEGAL_REQUEST, -ret); else { atapi_command_send_init(ide, temp_command, len, alloc_length); atapi_command_ready(ide_board, len); } break; } /* TODO: BD support, fall through for now */ /* Generic disk structures */ case 0x80: /* TODO: AACS volume identifier */ case 0x81: /* TODO: AACS media serial number */ case 0x82: /* TODO: AACS media identifier */ case 0x83: /* TODO: AACS media key block */ case 0x90: /* TODO: List of recognized format layers */ case 0xc0: /* TODO: Write protection status */ default: ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; return; } break; case GPCMD_START_STOP_UNIT: if (idebufferb[4]!=2 && idebufferb[4]!=3 && idebufferb[4]) { ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; atapi_sense.asc = ASC_ILLEGAL_OPCODE; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; /* pclog("Bad start/stop unit command\n"); pclog("Packet data :\n"); for (c=0;c<12;c++) pclog("%02X\n",idebufferb[c]); exit(-1);*/ } if (!idebufferb[4]) atapi->stop(); else if (idebufferb[4]==2) atapi->eject(); else atapi->load(); ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; case GPCMD_INQUIRY: page_code = idebufferb[2]; max_len = idebufferb[4]; alloc_length = max_len; temp_command = idebufferb[0]; if (idebufferb[1] & 1) { preamble_len = 4; size_idx = 3; idebufferb[idx++] = 05; idebufferb[idx++] = page_code; idebufferb[idx++] = 0; idx++; switch (page_code) { case 0x00: idebufferb[idx++] = 0x00; idebufferb[idx++] = 0x83; break; case 0x83: if (idx + 24 > max_len) { atapi_cmd_error(ide, SENSE_ILLEGAL_REQUEST, ASC_DATA_PHASE_ERROR); atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; atapi_sense.asc = ASC_DATA_PHASE_ERROR; return; } idebufferb[idx++] = 0x02; idebufferb[idx++] = 0x00; idebufferb[idx++] = 0x00; idebufferb[idx++] = 20; ide_padstr8(idebufferb + idx, 20, "53R141"); /* Serial */ idx += 20; if (idx + 72 > max_len) { goto atapi_out; } idebufferb[idx++] = 0x02; idebufferb[idx++] = 0x01; idebufferb[idx++] = 0x00; idebufferb[idx++] = 68; ide_padstr8(idebufferb + idx, 8, "PCem"); /* Vendor */ idx += 8; ide_padstr8(idebufferb + idx, 40, "PCemCD v1.0"); /* Product */ idx += 40; ide_padstr8(idebufferb + idx, 20, "53R141"); /* Product */ idx += 20; break; default: atapi_cmd_error(ide, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET); atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; atapi_sense.asc = ASC_INV_FIELD_IN_CMD_PACKET; return; } } else { preamble_len = 5; size_idx = 4; idebufferb[0] = 5; /*CD-ROM*/ idebufferb[1] = 0x80; /*Removable*/ idebufferb[2] = 0; idebufferb[3] = 0x21; idebufferb[4] = 31; idebufferb[5] = 0; idebufferb[6] = 0; idebufferb[7] = 0; #ifdef RPCEMU_IDE ide_padstr8(idebufferb + 8, 8, "RPCemu"); /* Vendor */ ide_padstr8(idebufferb + 16, 16, "RPCemuCD"); /* Product */ #else ide_padstr8(idebufferb + 8, 8, "PCem"); /* Vendor */ ide_padstr8(idebufferb + 16, 16, "PCemCD"); /* Product */ #endif ide_padstr8(idebufferb + 32, 4, "1.0"); /* Revision */ idx = 36; } atapi_out: idebufferb[size_idx] = idx - preamble_len; len=idx; atapi_command_send_init(ide, temp_command, len, alloc_length); atapi_command_ready(ide_board, len); break; case GPCMD_PREVENT_REMOVAL: ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; case GPCMD_PAUSE_RESUME: if (idebufferb[8]&1) atapi->resume(); else atapi->pause(); ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; case GPCMD_SEEK: pos=(idebufferb[3]<<16)|(idebufferb[4]<<8)|idebufferb[5]; atapi->seek(pos); ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; case GPCMD_READ_CDROM_CAPACITY: atapi_command_send_init(ide, temp_command, 8, 8); size = atapi->size(); idebufferb[0] = (size >> 24) & 0xff; idebufferb[1] = (size >> 16) & 0xff; idebufferb[2] = (size >> 8) & 0xff; idebufferb[3] = size & 0xff; idebufferb[4] = (2048 >> 24) & 0xff; idebufferb[5] = (2048 >> 16) & 0xff; idebufferb[6] = (2048 >> 8) & 0xff; idebufferb[7] = 2048 & 0xff; len=8; atapi_command_ready(ide_board, len); break; case GPCMD_SEND_DVD_STRUCTURE: default: bad_atapi_command: ide->atastat = READY_STAT | ERR_STAT; /*CHECK CONDITION*/ ide->error = (SENSE_ILLEGAL_REQUEST << 4) | ABRT_ERR; if (atapi_sense.sensekey == SENSE_UNIT_ATTENTION) ide->error |= MCR_ERR; atapi_sense.sensekey = SENSE_ILLEGAL_REQUEST; atapi_sense.asc = ASC_ILLEGAL_OPCODE; ide->packetstatus = ATAPI_STATUS_ERROR; idecallback[ide_board]=50*IDE_TIME; break; case GPCMD_STOP_PLAY_SCAN: atapi->stop(); ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide_board]=50*IDE_TIME; break; /* default: pclog("Bad ATAPI command %02X\n",idebufferb[0]); pclog("Packet data :\n"); for (c=0;c<12;c++) pclog("%02X\n",idebufferb[c]); exit(-1);*/ } } static void callnonreadcd(IDE *ide) /* Callabck for non-Read CD commands */ { ide_irq_lower(ide); if (ide->pos >= ide->packlen) { // pclog("Command finished, setting callback\n"); ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide->board]=20*IDE_TIME; } else { // pclog("Command not finished, keep sending data\n"); ide->atastat = BUSY_STAT; ide->packetstatus = ATAPI_STATUS_REQ_SENSE; ide->cylinder=2; ide->secount=2; idecallback[ide->board]=60*IDE_TIME; } } static void callreadcd(IDE *ide) { ide_irq_lower(ide); if (ide->cdlen<=0) { // pclog("All done - callback set\n"); ide->packetstatus = ATAPI_STATUS_COMPLETE; idecallback[ide->board]=20*IDE_TIME; return; } // pclog("Continue readcd! %i blocks left\n",ide->cdlen); ide->atastat = BUSY_STAT; if (readcdmode) atapi->readsector_raw((uint8_t *) ide->buffer, ide->cdpos); else atapi->readsector((uint8_t *) ide->buffer, ide->cdpos); #ifndef RPCEMU_IDE readflash=1; #endif ide->cdpos++; ide->cdlen--; ide->packetstatus = ATAPI_STATUS_READCD; ide->cylinder=readcdmode ? 2352 : 2048; ide->secount=2; ide->pos=0; idecallback[ide->board]=60*IDE_TIME; ide->packlen=readcdmode ? 2352 : 2048; } void ide_write_pri(uint16_t addr, uint8_t val, void *priv) { writeide(0, addr, val); } void ide_write_pri_w(uint16_t addr, uint16_t val, void *priv) { writeidew(0, val); } void ide_write_pri_l(uint16_t addr, uint32_t val, void *priv) { writeidel(0, val); } uint8_t ide_read_pri(uint16_t addr, void *priv) { return readide(0, addr); } uint16_t ide_read_pri_w(uint16_t addr, void *priv) { return readidew(0); } uint32_t ide_read_pri_l(uint16_t addr, void *priv) { return readidel(0); } void ide_write_sec(uint16_t addr, uint8_t val, void *priv) { writeide(1, addr, val); } void ide_write_sec_w(uint16_t addr, uint16_t val, void *priv) { writeidew(1, val); } void ide_write_sec_l(uint16_t addr, uint32_t val, void *priv) { writeidel(1, val); } uint8_t ide_read_sec(uint16_t addr, void *priv) { return readide(1, addr); } uint16_t ide_read_sec_w(uint16_t addr, void *priv) { return readidew(1); } uint32_t ide_read_sec_l(uint16_t addr, void *priv) { return readidel(1); } /* *** REMOVE FROM CODE SUBMITTED TO MAINLINE - START *** */ void ide_write_ter(uint16_t addr, uint8_t val, void *priv) { writeide(2, addr, val); } void ide_write_ter_w(uint16_t addr, uint16_t val, void *priv) { writeidew(2, val); } void ide_write_ter_l(uint16_t addr, uint32_t val, void *priv) { writeidel(2, val); } uint8_t ide_read_ter(uint16_t addr, void *priv) { return readide(2, addr); } uint16_t ide_read_ter_w(uint16_t addr, void *priv) { return readidew(2); } uint32_t ide_read_ter_l(uint16_t addr, void *priv) { return readidel(2); } /* *** REMOVE FROM CODE SUBMITTED TO MAINLINE - END *** */ void ide_pri_enable() { io_sethandler(0x01f0, 0x0008, ide_read_pri, ide_read_pri_w, ide_read_pri_l, ide_write_pri, ide_write_pri_w, ide_write_pri_l, NULL); io_sethandler(0x03f6, 0x0001, ide_read_pri, NULL, NULL, ide_write_pri, NULL, NULL , NULL); } void ide_pri_disable() { io_removehandler(0x01f0, 0x0008, ide_read_pri, ide_read_pri_w, ide_read_pri_l, ide_write_pri, ide_write_pri_w, ide_write_pri_l, NULL); io_removehandler(0x03f6, 0x0001, ide_read_pri, NULL, NULL, ide_write_pri, NULL, NULL , NULL); } void ide_sec_enable() { io_sethandler(0x0170, 0x0008, ide_read_sec, ide_read_sec_w, ide_read_sec_l, ide_write_sec, ide_write_sec_w, ide_write_sec_l, NULL); io_sethandler(0x0376, 0x0001, ide_read_sec, NULL, NULL, ide_write_sec, NULL, NULL , NULL); } void ide_sec_disable() { io_removehandler(0x0170, 0x0008, ide_read_sec, ide_read_sec_w, ide_read_sec_l, ide_write_sec, ide_write_sec_w, ide_write_sec_l, NULL); io_removehandler(0x0376, 0x0001, ide_read_sec, NULL, NULL, ide_write_sec, NULL, NULL , NULL); } /* *** REMOVE FROM CODE SUBMITTED TO MAINLINE - START *** */ void ide_ter_enable() { io_sethandler(0x0168, 0x0008, ide_read_ter, ide_read_ter_w, ide_read_ter_l, ide_write_ter, ide_write_ter_w, ide_write_ter_l, NULL); io_sethandler(0x036e, 0x0001, ide_read_ter, NULL, NULL, ide_write_ter, NULL, NULL , NULL); } void ide_ter_disable() { io_removehandler(0x0168, 0x0008, ide_read_ter, ide_read_ter_w, ide_read_ter_l, ide_write_ter, ide_write_ter_w, ide_write_ter_l, NULL); io_removehandler(0x036e, 0x0001, ide_read_ter, NULL, NULL, ide_write_ter, NULL, NULL , NULL); } void ide_ter_init() { ide_ter_enable(); timer_add(ide_callback_ter, &idecallback[2], &idecallback[2], NULL); } /* *** REMOVE FROM CODE SUBMITTED TO MAINLINE - END *** */ void ide_init() { ide_pri_enable(); ide_sec_enable(); ide_bus_master_read_sector = ide_bus_master_write_sector = NULL; timer_add(ide_callback_pri, &idecallback[0], &idecallback[0], NULL); timer_add(ide_callback_sec, &idecallback[1], &idecallback[1], NULL); } void ide_set_bus_master(int (*read_sector)(int channel, uint8_t *data), int (*write_sector)(int channel, uint8_t *data), void (*set_irq)(int channel)) { ide_bus_master_read_sector = read_sector; ide_bus_master_write_sector = write_sector; ide_bus_master_set_irq = set_irq; }