nkeynes@31: /** nkeynes@561: * $Id$ nkeynes@2: * nkeynes@31: * IDE interface implementation nkeynes@31: * nkeynes@152: * nkeynes@152: * Note: All references to read/write are from the host's point of view. nkeynes@152: * nkeynes@152: * See: INF-8020 nkeynes@31: * Copyright (c) 2005 Nathan Keynes. nkeynes@31: * nkeynes@31: * This program is free software; you can redistribute it and/or modify nkeynes@31: * it under the terms of the GNU General Public License as published by nkeynes@31: * the Free Software Foundation; either version 2 of the License, or nkeynes@31: * (at your option) any later version. nkeynes@31: * nkeynes@31: * This program is distributed in the hope that it will be useful, nkeynes@31: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@31: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@31: * GNU General Public License for more details. nkeynes@2: */ nkeynes@35: nkeynes@35: #define MODULE ide_module nkeynes@35: nkeynes@138: #include nkeynes@2: #include nkeynes@35: #include "dream.h" nkeynes@422: #include "mem.h" nkeynes@125: #include "asic.h" nkeynes@39: #include "gdrom/ide.h" nkeynes@125: #include "gdrom/gdrom.h" nkeynes@142: #include "gdrom/packet.h" nkeynes@2: nkeynes@138: #define MAX_WRITE_BUF 4096 nkeynes@138: #define MAX_SECTOR_SIZE 2352 /* Audio sector */ nkeynes@138: #define DEFAULT_DATA_SECTORS 8 nkeynes@2: nkeynes@138: static void ide_init( void ); nkeynes@138: static void ide_reset( void ); nkeynes@342: static uint32_t ide_run_slice( uint32_t nanosecs ); nkeynes@138: static void ide_save_state( FILE *f ); nkeynes@138: static int ide_load_state( FILE *f ); nkeynes@138: static void ide_raise_interrupt( void ); nkeynes@138: static void ide_clear_interrupt( void ); nkeynes@152: static void ide_packet_command( unsigned char *data ); nkeynes@342: static void ide_read_next_sector(void); nkeynes@15: nkeynes@342: struct dreamcast_module ide_module = { "IDE", ide_init, ide_reset, NULL, ide_run_slice, nkeynes@138: NULL, ide_save_state, ide_load_state }; nkeynes@15: nkeynes@2: struct ide_registers idereg; nkeynes@342: gdrom_disc_t gdrom_disc = NULL; nkeynes@342: nkeynes@342: unsigned char data_buffer[MAX_SECTOR_SIZE]; nkeynes@2: nkeynes@152: #define WRITE_BUFFER(x16) *((uint16_t *)(data_buffer + idereg.data_offset)) = x16 nkeynes@152: #define READ_BUFFER() *((uint16_t *)(data_buffer + idereg.data_offset)) nkeynes@152: nkeynes@493: /* 10 bytes followed by "SE REV 6.42990316" */ nkeynes@493: unsigned char default_gdrom_mode[GDROM_MODE_LENGTH] = nkeynes@493: { 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x19, 0x00, 0x00, 0x08, nkeynes@493: 0x53, 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, nkeynes@493: 0x52, 0x65, 0x76, 0x20, 0x36, 0x2e, 0x34, 0x32, 0x39, 0x39, 0x30, 0x33, 0x31, 0x36 }; nkeynes@493: nkeynes@158: unsigned char gdrom_71[] = { 0x3E, 0x0F, 0x90, 0xBE, 0x1D, 0xD9, 0x89, 0x04, 0x28, 0x3A, 0x8E, 0x26, 0x5C, 0x95, 0x10, 0x5A, nkeynes@158: 0x0A, 0x99, 0xEE, 0xFB, 0x69, 0xCE, 0xD9, 0x63, 0x00, 0xF5, 0x0A, 0xBC, 0x2C, 0x0D, 0xF8, 0xE2, nkeynes@158: 0x05, 0x02, 0x00, 0x7C, 0x03, 0x00, 0x3D, 0x08, 0xD8, 0x8D, 0x08, 0x7A, 0x6D, 0x00, 0x35, 0x06, nkeynes@158: 0xBA, 0x66, 0x10, 0x00, 0x91, 0x08, 0x10, 0x29, 0xD0, 0x45, 0xDA, 0x00, 0x2D, 0x05, 0x69, 0x09, nkeynes@158: 0x00, 0x5E, 0x0F, 0x70, 0x86, 0x12, 0x6C, 0x77, 0x5A, 0xFB, 0xCD, 0x56, 0xFB, 0xF7, 0xB7, 0x00, nkeynes@158: 0x5D, 0x07, 0x19, 0x99, 0xF2, 0xAF, 0x00, 0x63, 0x03, 0x00, 0xF0, 0x10, 0xBE, 0xD7, 0xA0, 0x63, nkeynes@158: 0xFA, 0x84, 0xA7, 0x74, 0x94, 0xEF, 0xAD, 0xC2, 0xAC, 0x00, 0x78, 0x07, 0x9F, 0x57, 0x0B, 0x62, nkeynes@158: 0x00, 0xFE, 0x08, 0x08, 0x5D, 0x5A, 0x6A, 0x54, 0x00, 0xE2, 0x09, 0x93, 0x7E, 0x62, 0x2A, 0x5E, nkeynes@158: 0xDA, 0x00, 0x7E, 0x0F, 0xF0, 0x07, 0x01, 0x6D, 0x50, 0x86, 0xDD, 0x4A, 0x15, 0x54, 0xC7, 0xEC, nkeynes@158: 0x00, 0xF2, 0x0B, 0x07, 0xF8, 0x1A, 0xB0, 0x99, 0x3B, 0xF1, 0x36, 0x00, 0x94, 0x07, 0x34, 0xE3, nkeynes@158: 0xBC, 0x6E, 0x00, 0x34, 0x0D, 0x6F, 0xDA, 0xBD, 0xEE, 0xF7, 0xCC, 0xCE, 0x39, 0x7E, 0xE3, 0x00, nkeynes@158: 0x14, 0x08, 0xDC, 0xD2, 0xB9, 0xF9, 0x31, 0x00, 0xB0, 0x0C, 0x10, 0xA3, 0x45, 0x12, 0xC7, 0xCD, nkeynes@158: 0xBF, 0x05, 0x37, 0x00, 0xC4, 0x0D, 0x5F, 0xE0, 0x59, 0xBB, 0x01, 0x59, 0x03, 0xD6, 0x29, 0x9C, nkeynes@158: 0x00, 0x01, 0x0A, 0x09, 0xAA, 0xA8, 0xA8, 0x24, 0x0B, 0x66, 0x00, 0x5C, 0x05, 0xA5, 0xCE, 0x00, nkeynes@158: 0xC1, 0x0B, 0xB7, 0xA0, 0x6F, 0xE9, 0x2B, 0xCC, 0xB5, 0xFC, 0x00, 0x8D, 0x05, 0xF4, 0xAC, 0x00, nkeynes@158: 0x57, 0x04, 0xB6, 0x00, 0xFC, 0x03, 0x00, 0xC3, 0x10, 0x43, 0x3B, 0xBE, 0xA2, 0x96, 0xC3, 0x65, nkeynes@158: 0x9F, 0x9A, 0x88, 0xD5, 0x49, 0x68, 0x00, 0xDC, 0x11, 0x56, 0x23, 0x2D, 0xF9, 0xFC, 0xF5, 0x8B, nkeynes@158: 0x1B, 0xB1, 0xB7, 0x10, 0x21, 0x1C, 0x12, 0x00, 0x0D, 0x0D, 0xEB, 0x86, 0xA2, 0x49, 0x8D, 0x8D, nkeynes@158: 0xBE, 0xA1, 0x6D, 0x53, 0x00, 0xE1, 0x0A, 0x8E, 0x67, 0xAA, 0x16, 0x79, 0x39, 0x59, 0x00, 0x36, nkeynes@158: 0x0B, 0x2A, 0x4E, 0xAE, 0x51, 0x4B, 0xD0, 0x66, 0x33, 0x00, 0x8A, 0x07, 0xCD, 0x6F, 0xBA, 0x92, nkeynes@158: 0x00, 0x1A, 0x0E, 0xDF, 0x4A, 0xB3, 0x77, 0x1F, 0xA5, 0x90, 0x19, 0xFA, 0x59, 0xD7, 0x00, 0x04, nkeynes@158: 0x0F, 0xAC, 0xCA, 0x9F, 0xA4, 0xFC, 0x6D, 0x90, 0x86, 0x9E, 0x1F, 0x44, 0x40, 0x00, 0x9F, 0x04, nkeynes@158: 0x56, 0x00, 0x22, 0x03, 0x00, 0xB8, 0x10, 0x2C, 0x7A, 0x53, 0xA4, 0xBF, 0xA3, 0x90, 0x90, 0x14, nkeynes@158: 0x9D, 0x46, 0x6C, 0x96, 0x00, 0xC6, 0x0B, 0x9B, 0xBB, 0xB0, 0xAE, 0x60, 0x92, 0x8E, 0x0C, 0x00, nkeynes@158: 0x14, 0x06, 0x4B, 0xAE, 0x7F, 0x00, 0x5C, 0x0B, 0x23, 0xFA, 0xE7, 0x51, 0xDA, 0x61, 0x49, 0x5E, nkeynes@158: 0x00, 0xD7, 0x0B, 0x01, 0xFC, 0x55, 0x31, 0x84, 0xC5, 0x0C, 0x98, 0x00, 0x97, 0x50, 0x6E, 0xF9, nkeynes@158: 0xEE, 0x75, 0x92, 0x53, 0xD3, 0x66, 0xA4, 0xAF, 0x3B, 0xFE, 0x7B, 0x27, 0x30, 0xBB, 0xB6, 0xF2, nkeynes@158: 0x76, 0x22, 0x45, 0x42, 0xCA, 0xF9, 0xF0, 0xDE, 0x9F, 0x45, 0x16, 0x68, 0x22, 0xB9, 0x84, 0x28, nkeynes@158: 0x8F, 0x2B, 0xB5, 0x5C, 0xD2, 0xF5, 0x45, 0x36, 0x3E, 0x76, 0xC6, 0xBF, 0x32, 0x5C, 0x41, 0xA6, nkeynes@158: 0x26, 0xC7, 0x82, 0x2F, 0x2E, 0xB5, 0x75, 0xC6, 0xE6, 0x67, 0x9E, 0x77, 0x94, 0xAF, 0x6A, 0x05, nkeynes@158: 0xC0, 0x05, 0x61, 0x71, 0x89, 0x5A, 0xB1, 0xD0, 0xFC, 0x7E, 0xC0, 0x9B, 0xCB, 0x3B, 0x69, 0xD9, nkeynes@158: 0x5F, 0xAF, 0xCA, 0xAB, 0x25, 0xD5, 0xBE, 0x8A, 0x6B, 0xB0, 0xFB, 0x61, 0x6C, 0xEB, 0x85, 0x6E, nkeynes@158: 0x7A, 0x48, 0xFF, 0x97, 0x91, 0x06, 0x3D, 0x4D, 0x68, 0xD3, 0x65, 0x83, 0x90, 0xA0, 0x08, 0x5C, nkeynes@158: 0xFC, 0xEE, 0x7C, 0x33, 0x43, 0x7F, 0x80, 0x52, 0x8B, 0x19, 0x72, 0xF2, 0xC9, 0xAB, 0x93, 0xAF, nkeynes@158: 0x16, 0xED, 0x36, 0x48, 0xAB, 0xC9, 0xD1, 0x03, 0xB3, 0xDC, 0x2F, 0xF2, 0x92, 0x3F, 0x0A, 0x19, nkeynes@158: 0x25, 0xE2, 0xEF, 0x7A, 0x22, 0xDA, 0xDB, 0xCB, 0x32, 0x12, 0x61, 0x49, 0x5B, 0x74, 0x7C, 0x65, nkeynes@158: 0x20, 0x89, 0x54, 0x9E, 0x0E, 0xC9, 0x52, 0xE3, 0xC9, 0x9A, 0x44, 0xC9, 0x5D, 0xA6, 0x77, 0xC0, nkeynes@158: 0xE7, 0x60, 0x91, 0x80, 0x50, 0x1F, 0x33, 0xB1, 0xCD, 0xAD, 0xF4, 0x0D, 0xBB, 0x08, 0xB1, 0xD0, nkeynes@158: 0x13, 0x95, 0xAE, 0xC9, 0xE2, 0x64, 0xA2, 0x65, 0xFB, 0x8F, 0xE9, 0xA2, 0x8A, 0xBC, 0x98, 0x81, nkeynes@158: 0x45, 0xB4, 0x55, 0x4E, 0xB9, 0x74, 0xB4, 0x50, 0x76, 0xBF, 0xF0, 0x45, 0xE7, 0xEE, 0x41, 0x64, nkeynes@158: 0x9F, 0xB5, 0xE0, 0xBB, 0x1C, 0xBB, 0x28, 0x66, 0x1B, 0xDD, 0x2B, 0x02, 0x66, 0xBF, 0xFD, 0x7D, nkeynes@158: 0x37, 0x35, 0x1D, 0x76, 0x21, 0xC3, 0x8F, 0xAF, 0xF6, 0xF9, 0xE9, 0x27, 0x48, 0xE7, 0x3D, 0x95, nkeynes@158: 0x74, 0x0C, 0x77, 0x88, 0x56, 0xD9, 0x84, 0xC8, 0x7D, 0x20, 0x31, 0x43, 0x53, 0xF1, 0xC1, 0xC7, nkeynes@158: 0xC9, 0xF7, 0x5C, 0xC0, 0xA6, 0x5A, 0x27, 0x0A, 0x41, 0xD4, 0x44, 0x94, 0x65, 0x4F, 0xE2, 0x53, nkeynes@158: 0x60, 0x0B, 0xD1, 0x23, 0x6C, 0x0C, 0xBC, 0x70, 0x6C, 0x26, 0x1A, 0x61, 0x1D, 0x35, 0x88, 0xEC, nkeynes@158: 0xB8, 0x15, 0xE3, 0xB4, 0x82, 0xEE, 0xB3, 0x21, 0xAC, 0x6C, 0xB7, 0x33, 0x6D, 0x78, 0x0C, 0x0D, nkeynes@158: 0xB4, 0x0B, 0x29, 0xF2, 0xD4, 0x8C, 0x3F, 0xDD, 0x3F, 0x47, 0xDD, 0xF2, 0xD8, 0x39, 0x57, 0x20, nkeynes@158: 0x28, 0xD8, 0xDD, 0x32, 0xE2, 0x6A, 0x47, 0x53, 0x57, 0xC6, 0xFA, 0x7A, 0x38, 0x30, 0x31, 0x8F, nkeynes@158: 0xE7, 0xD3, 0x84, 0x2B, 0x5D, 0x4F, 0x95, 0x98, 0xED, 0x0B, 0xD7, 0x50, 0x0C, 0x49, 0xDA, 0x59, nkeynes@158: 0x15, 0xF1, 0x39, 0xF3, 0x40, 0xDC, 0xDC, 0x25, 0x24, 0x56, 0x6E, 0xA9, 0x2F, 0xF0, 0x00, 0x00 }; nkeynes@158: nkeynes@2: nkeynes@250: char gdrom_status[] = { nkeynes@250: 0x00, 0x15, 0x00, 0x64, 0x00, 0x40, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, nkeynes@250: 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x40, nkeynes@250: 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, nkeynes@250: 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x40, 0x40, nkeynes@250: 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, nkeynes@250: 0x00, 0x40, 0x00, 0x00 }; nkeynes@250: nkeynes@250: nkeynes@138: static void ide_init( void ) nkeynes@138: { nkeynes@138: ide_reset(); nkeynes@138: } nkeynes@138: nkeynes@138: static void ide_reset( void ) nkeynes@138: { nkeynes@138: ide_clear_interrupt(); nkeynes@138: idereg.error = 0x01; nkeynes@138: idereg.count = 0x01; nkeynes@138: idereg.lba0 = /* 0x21; */ 0x81; nkeynes@138: idereg.lba1 = 0x14; nkeynes@138: idereg.lba2 = 0xeb; nkeynes@138: idereg.feature = 0; /* Indeterminate really */ nkeynes@254: idereg.status = 0x00; nkeynes@138: idereg.device = 0x00; nkeynes@152: idereg.state = IDE_STATE_IDLE; nkeynes@152: memset( idereg.gdrom_sense, '\0', 10 ); nkeynes@493: memcpy( idereg.gdrom_mode, default_gdrom_mode, GDROM_MODE_LENGTH ); nkeynes@152: idereg.data_offset = -1; nkeynes@152: idereg.data_length = -1; nkeynes@245: idereg.last_read_track = 1; nkeynes@493: idereg.current_lba = 150; nkeynes@493: idereg.current_mode = 0x28; nkeynes@342: idereg.sectors_left = 0; nkeynes@254: idereg.was_reset = TRUE; nkeynes@138: } nkeynes@138: nkeynes@342: static uint32_t ide_run_slice( uint32_t nanosecs ) nkeynes@342: { nkeynes@342: if( gdrom_disc != NULL && gdrom_disc->run_time_slice != NULL ) { nkeynes@342: gdrom_disc->run_time_slice(gdrom_disc, nanosecs); nkeynes@342: } nkeynes@342: return nanosecs; nkeynes@342: } nkeynes@342: nkeynes@138: static void ide_save_state( FILE *f ) nkeynes@138: { nkeynes@152: fwrite( &idereg, sizeof(idereg), 1, f ); nkeynes@342: fwrite( data_buffer, MAX_SECTOR_SIZE, 1, f ); nkeynes@138: } nkeynes@138: nkeynes@138: static int ide_load_state( FILE *f ) nkeynes@138: { nkeynes@152: fread( &idereg, sizeof(idereg), 1, f ); nkeynes@342: fread( data_buffer, MAX_SECTOR_SIZE, 1, f ); nkeynes@138: return 0; nkeynes@138: } nkeynes@125: nkeynes@152: /************************ State transitions *************************/ nkeynes@152: nkeynes@158: void ide_set_packet_result( uint16_t result ) nkeynes@158: { nkeynes@158: idereg.gdrom_sense[0] = 0xf0; nkeynes@158: idereg.gdrom_sense[2] = result & 0xFF; nkeynes@158: idereg.gdrom_sense[8] = (result >> 8) & 0xFF; nkeynes@158: idereg.error = (result & 0x0F) << 4; nkeynes@254: idereg.count = 3; nkeynes@158: if( result != 0 ) { nkeynes@158: idereg.status = 0x51; nkeynes@158: ide_raise_interrupt(); nkeynes@158: } else { nkeynes@158: idereg.status = idereg.status & ~(IDE_STATUS_BSY|IDE_STATUS_CHK); nkeynes@158: } nkeynes@158: } nkeynes@158: nkeynes@152: /** nkeynes@152: * Begin command packet write to the device. This is always 12 bytes of PIO data nkeynes@152: */ nkeynes@152: static void ide_start_command_packet_write( ) nkeynes@2: { nkeynes@152: idereg.state = IDE_STATE_CMD_WRITE; nkeynes@254: idereg.status = 0x58; nkeynes@152: idereg.error = idereg.feature & 0x03; /* Copy values of OVL/DMA */ nkeynes@152: idereg.count = IDE_COUNT_CD; nkeynes@152: idereg.data_offset = 0; nkeynes@152: idereg.data_length = 12; nkeynes@2: } nkeynes@2: nkeynes@152: /** nkeynes@493: * Begin PIO/DMA read from the device. The data is assumed to already be nkeynes@152: * in the buffer at this point. nkeynes@152: */ nkeynes@342: static void ide_start_read( int length, gboolean dma ) nkeynes@2: { nkeynes@152: idereg.count = IDE_COUNT_IO; nkeynes@152: idereg.data_length = length; nkeynes@152: idereg.data_offset = 0; nkeynes@152: if( dma ) { nkeynes@152: idereg.state = IDE_STATE_DMA_READ; nkeynes@158: idereg.status = 0xD0; nkeynes@152: } else { nkeynes@152: idereg.state = IDE_STATE_PIO_READ; nkeynes@254: idereg.status = 0x58; nkeynes@342: idereg.lba1 = length & 0xFF; nkeynes@342: idereg.lba2 = length >> 8; nkeynes@158: ide_raise_interrupt( ); nkeynes@152: } nkeynes@152: } nkeynes@152: nkeynes@493: static void ide_start_write( int length, gboolean dma ) nkeynes@493: { nkeynes@493: idereg.count = 0; nkeynes@493: idereg.data_length = length; nkeynes@493: idereg.data_offset = 0; nkeynes@493: if( dma ) { nkeynes@493: idereg.state = IDE_STATE_DMA_WRITE; nkeynes@493: idereg.status = 0xD0; nkeynes@493: } else { nkeynes@493: idereg.state = IDE_STATE_PIO_WRITE; nkeynes@493: idereg.status = 0x58; nkeynes@493: idereg.lba1 = length & 0xFF; nkeynes@493: idereg.lba2 = length >> 8; nkeynes@493: ide_raise_interrupt( ); nkeynes@493: } nkeynes@493: } nkeynes@493: nkeynes@342: static void ide_start_packet_read( int length, int sector_count ) nkeynes@152: { nkeynes@342: idereg.sectors_left = sector_count; nkeynes@158: ide_set_packet_result( PKT_ERR_OK ); nkeynes@342: ide_start_read( length, (idereg.feature & IDE_FEAT_DMA) ? TRUE : FALSE ); nkeynes@2: } nkeynes@2: nkeynes@493: static void ide_start_packet_write( int length, int sector_count ) nkeynes@493: { nkeynes@493: idereg.sectors_left = sector_count; nkeynes@493: ide_set_packet_result( PKT_ERR_OK ); nkeynes@493: ide_start_write( length, (idereg.feature & IDE_FEAT_DMA) ? TRUE : FALSE ); nkeynes@493: } nkeynes@493: nkeynes@125: static void ide_raise_interrupt( void ) nkeynes@2: { nkeynes@125: if( idereg.intrq_pending == 0 ) { nkeynes@125: idereg.intrq_pending = 1; nkeynes@125: if( IS_IDE_IRQ_ENABLED() ) nkeynes@125: asic_event( EVENT_IDE ); nkeynes@125: } nkeynes@125: } nkeynes@125: nkeynes@125: static void ide_clear_interrupt( void ) nkeynes@125: { nkeynes@125: if( idereg.intrq_pending != 0 ) { nkeynes@125: idereg.intrq_pending = 0; nkeynes@125: if( IS_IDE_IRQ_ENABLED() ) nkeynes@125: asic_clear_event( EVENT_IDE ); nkeynes@125: } nkeynes@125: } nkeynes@125: nkeynes@125: static void ide_set_error( int error_code ) nkeynes@125: { nkeynes@125: idereg.status = 0x51; nkeynes@125: idereg.error = error_code; nkeynes@2: } nkeynes@2: nkeynes@125: uint8_t ide_read_status( void ) nkeynes@125: { nkeynes@152: if( (idereg.status & IDE_STATUS_BSY) == 0 ) nkeynes@125: ide_clear_interrupt(); nkeynes@125: return idereg.status; nkeynes@2: } nkeynes@2: nkeynes@2: uint16_t ide_read_data_pio( void ) { nkeynes@152: if( idereg.state == IDE_STATE_PIO_READ ) { nkeynes@152: uint16_t rv = READ_BUFFER(); nkeynes@152: idereg.data_offset += 2; nkeynes@152: if( idereg.data_offset >= idereg.data_length ) { nkeynes@152: idereg.state = IDE_STATE_IDLE; nkeynes@152: idereg.status &= ~IDE_STATUS_DRQ; nkeynes@152: idereg.data_offset = -1; nkeynes@254: idereg.count = 3; /* complete */ nkeynes@152: ide_raise_interrupt(); nkeynes@342: if( idereg.sectors_left > 0 ) { nkeynes@342: ide_read_next_sector(); nkeynes@342: } nkeynes@152: } nkeynes@152: return rv; nkeynes@152: } else { nkeynes@2: return 0xFFFF; nkeynes@2: } nkeynes@2: } nkeynes@2: nkeynes@152: nkeynes@152: /** nkeynes@152: * DMA read request nkeynes@152: * nkeynes@152: * This method is called from the ASIC side when a DMA read request is nkeynes@152: * initiated. If there is a pending DMA transfer already, we copy the nkeynes@152: * data immediately, otherwise we record the DMA buffer for use when we nkeynes@152: * get to actually doing the transfer. nkeynes@152: * @return number of bytes transfered nkeynes@152: */ nkeynes@152: uint32_t ide_read_data_dma( uint32_t addr, uint32_t length ) nkeynes@152: { nkeynes@342: uint32_t xfercount = 0; nkeynes@152: if( idereg.state == IDE_STATE_DMA_READ ) { nkeynes@342: while( xfercount < length ) { nkeynes@342: int xferlen = length - xfercount; nkeynes@342: int remaining = idereg.data_length - idereg.data_offset; nkeynes@342: if( xferlen > remaining ) { nkeynes@342: xferlen = remaining; nkeynes@342: } nkeynes@430: mem_copy_to_sh4( addr, (data_buffer + idereg.data_offset), xferlen ); nkeynes@342: xfercount += xferlen; nkeynes@342: addr += xferlen; nkeynes@342: idereg.data_offset += xferlen; nkeynes@342: if( idereg.data_offset >= idereg.data_length ) { nkeynes@342: if( idereg.sectors_left > 0 ) { nkeynes@342: ide_read_next_sector(); nkeynes@342: } else { nkeynes@342: idereg.data_offset = -1; nkeynes@342: idereg.state = IDE_STATE_IDLE; nkeynes@342: idereg.status = 0x50; nkeynes@342: idereg.count = 0x03; nkeynes@342: ide_raise_interrupt(); nkeynes@342: asic_event( EVENT_IDE_DMA ); nkeynes@342: break; nkeynes@342: } nkeynes@342: } nkeynes@152: } nkeynes@152: } nkeynes@342: return xfercount; nkeynes@152: } nkeynes@152: nkeynes@2: void ide_write_data_pio( uint16_t val ) { nkeynes@152: if( idereg.state == IDE_STATE_CMD_WRITE ) { nkeynes@152: WRITE_BUFFER(val); nkeynes@152: idereg.data_offset+=2; nkeynes@152: if( idereg.data_offset >= idereg.data_length ) { nkeynes@152: idereg.state = IDE_STATE_BUSY; nkeynes@152: idereg.status = (idereg.status & ~IDE_STATUS_DRQ) | IDE_STATUS_BSY; nkeynes@152: idereg.data_offset = -1; nkeynes@152: ide_packet_command(data_buffer); nkeynes@152: } nkeynes@152: } else if( idereg.state == IDE_STATE_PIO_WRITE ) { nkeynes@152: WRITE_BUFFER(val); nkeynes@493: idereg.data_offset +=2; nkeynes@152: if( idereg.data_offset >= idereg.data_length ) { nkeynes@493: idereg.state = IDE_STATE_IDLE; nkeynes@493: idereg.status &= ~IDE_STATUS_DRQ; nkeynes@152: idereg.data_offset = -1; nkeynes@493: idereg.count = 3; /* complete */ nkeynes@493: ide_raise_interrupt(); nkeynes@493: ide_write_buffer( data_buffer, idereg.data_length ); nkeynes@152: } nkeynes@2: } nkeynes@2: } nkeynes@2: nkeynes@2: void ide_write_control( uint8_t val ) { nkeynes@125: if( IS_IDE_IRQ_ENABLED() ) { nkeynes@125: if( (val & 0x02) != 0 && idereg.intrq_pending != 0 ) nkeynes@125: asic_clear_event( EVENT_IDE ); nkeynes@125: } else { nkeynes@125: if( (val & 0x02) == 0 && idereg.intrq_pending != 0 ) nkeynes@125: asic_event( EVENT_IDE ); nkeynes@125: } nkeynes@125: idereg.control = val; nkeynes@2: } nkeynes@2: nkeynes@2: void ide_write_command( uint8_t val ) { nkeynes@125: ide_clear_interrupt(); nkeynes@2: idereg.command = val; nkeynes@2: switch( val ) { nkeynes@240: case IDE_CMD_NOP: /* Effectively an "abort" */ nkeynes@240: idereg.state = IDE_STATE_IDLE; nkeynes@240: idereg.status = 0x51; nkeynes@240: idereg.error = 0x04; nkeynes@240: idereg.data_offset = -1; nkeynes@240: ide_raise_interrupt(); nkeynes@240: return; nkeynes@39: case IDE_CMD_RESET_DEVICE: nkeynes@39: ide_reset(); nkeynes@39: break; nkeynes@39: case IDE_CMD_PACKET: nkeynes@152: ide_start_command_packet_write(); nkeynes@39: break; nkeynes@39: case IDE_CMD_SET_FEATURE: nkeynes@39: switch( idereg.feature ) { nkeynes@47: case IDE_FEAT_SET_TRANSFER_MODE: nkeynes@47: switch( idereg.count & 0xF8 ) { nkeynes@47: case IDE_XFER_PIO: nkeynes@47: INFO( "Set PIO default mode: %d", idereg.count&0x07 ); nkeynes@47: break; nkeynes@47: case IDE_XFER_PIO_FLOW: nkeynes@47: INFO( "Set PIO Flow-control mode: %d", idereg.count&0x07 ); nkeynes@47: break; nkeynes@47: case IDE_XFER_MULTI_DMA: nkeynes@47: INFO( "Set Multiword DMA mode: %d", idereg.count&0x07 ); nkeynes@47: break; nkeynes@47: case IDE_XFER_ULTRA_DMA: nkeynes@47: INFO( "Set Ultra DMA mode: %d", idereg.count&0x07 ); nkeynes@47: break; nkeynes@47: default: nkeynes@47: INFO( "Setting unknown transfer mode: %02X", idereg.count ); nkeynes@47: break; nkeynes@47: } nkeynes@47: break; nkeynes@39: default: nkeynes@39: WARN( "IDE: unimplemented feature: %02X", idereg.feature ); nkeynes@39: } nkeynes@254: idereg.status = 0x50; nkeynes@254: idereg.error = 0x00; nkeynes@254: idereg.lba1 = 0x00; nkeynes@254: idereg.lba2 = 0x00; nkeynes@256: ide_raise_interrupt(); nkeynes@39: break; nkeynes@39: default: nkeynes@39: WARN( "IDE: Unimplemented command: %02X", val ); nkeynes@2: } nkeynes@2: } nkeynes@2: nkeynes@342: uint8_t ide_get_drive_status( void ) nkeynes@342: { nkeynes@342: if( gdrom_disc == NULL ) { nkeynes@342: return IDE_DISC_NONE; nkeynes@342: } else { nkeynes@342: return gdrom_disc->drive_status(gdrom_disc); nkeynes@342: } nkeynes@342: } nkeynes@342: nkeynes@342: #define REQUIRE_DISC() if( gdrom_disc == NULL ) { ide_set_packet_result( PKT_ERR_NODISC ); return; } nkeynes@342: nkeynes@342: /** nkeynes@342: * Read the next sector from the active read, if any nkeynes@342: */ nkeynes@342: static void ide_read_next_sector( void ) nkeynes@342: { nkeynes@422: uint32_t sector_size; nkeynes@342: REQUIRE_DISC(); nkeynes@342: gdrom_error_t status = nkeynes@493: gdrom_disc->read_sector( gdrom_disc, idereg.current_lba, idereg.current_mode, nkeynes@342: data_buffer, §or_size ); nkeynes@342: if( status != PKT_ERR_OK ) { nkeynes@342: ide_set_packet_result( status ); nkeynes@493: idereg.gdrom_sense[5] = (idereg.current_lba >> 16) & 0xFF; nkeynes@493: idereg.gdrom_sense[6] = (idereg.current_lba >> 8) & 0xFF; nkeynes@493: idereg.gdrom_sense[7] = idereg.current_lba & 0xFF; nkeynes@342: WARN( " => Read CD returned sense key %02X, %02X", status & 0xFF, status >> 8 ); nkeynes@342: } else { nkeynes@493: idereg.current_lba++; nkeynes@342: idereg.sectors_left--; nkeynes@342: ide_start_read( sector_size, (idereg.feature & IDE_FEAT_DMA) ? TRUE : FALSE ); nkeynes@342: } nkeynes@342: } nkeynes@342: nkeynes@142: /** nkeynes@142: * Execute a packet command. This particular method is responsible for parsing nkeynes@142: * the command buffers (12 bytes), and generating the appropriate responses, nkeynes@142: * although the actual implementation is mostly delegated to gdrom.c nkeynes@142: */ nkeynes@125: void ide_packet_command( unsigned char *cmd ) nkeynes@125: { nkeynes@422: uint32_t length; nkeynes@143: uint32_t lba, status; nkeynes@125: nkeynes@125: /* Okay we have the packet in the command buffer */ nkeynes@166: INFO( "ATAPI packet: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", nkeynes@166: cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], nkeynes@166: cmd[8], cmd[9], cmd[10], cmd[11] ); nkeynes@254: nkeynes@254: if( cmd[0] != PKT_CMD_SENSE && idereg.was_reset ) { nkeynes@254: ide_set_packet_result( PKT_ERR_RESET ); nkeynes@254: idereg.was_reset = FALSE; nkeynes@254: return; nkeynes@254: } nkeynes@254: nkeynes@125: switch( cmd[0] ) { nkeynes@142: case PKT_CMD_TEST_READY: nkeynes@342: REQUIRE_DISC(); nkeynes@342: ide_set_packet_result( 0 ); nkeynes@342: ide_raise_interrupt(); nkeynes@342: idereg.status = 0x50; nkeynes@142: break; nkeynes@493: case PKT_CMD_MODE_SENSE: nkeynes@140: lba = cmd[2]; nkeynes@493: if( lba >= GDROM_MODE_LENGTH ) { nkeynes@142: ide_set_error(PKT_ERR_BADFIELD); nkeynes@158: } else { nkeynes@158: length = cmd[4]; nkeynes@493: if( lba+length > GDROM_MODE_LENGTH ) nkeynes@493: length = GDROM_MODE_LENGTH - lba; nkeynes@493: memcpy( data_buffer, idereg.gdrom_mode + lba, length ); nkeynes@342: ide_start_packet_read( length, 0 ); nkeynes@140: } nkeynes@125: break; nkeynes@493: case PKT_CMD_MODE_SELECT: nkeynes@493: lba = cmd[2]; nkeynes@493: if( lba >= GDROM_MODE_LENGTH ) { nkeynes@493: ide_set_error(PKT_ERR_BADFIELD); nkeynes@493: } else { nkeynes@493: length = cmd[4]; nkeynes@493: if( lba+length > GDROM_MODE_LENGTH ) nkeynes@493: length = GDROM_MODE_LENGTH - lba; nkeynes@493: idereg.current_lba = lba; nkeynes@493: ide_start_packet_write( length, 0 ); nkeynes@493: } nkeynes@493: break; nkeynes@142: case PKT_CMD_SENSE: nkeynes@142: length = cmd[4]; nkeynes@142: if( length > 10 ) nkeynes@142: length = 10; nkeynes@152: memcpy( data_buffer, idereg.gdrom_sense, length ); nkeynes@342: ide_start_packet_read( length, 0 ); nkeynes@142: break; nkeynes@125: case PKT_CMD_READ_TOC: nkeynes@342: REQUIRE_DISC(); nkeynes@142: length = (cmd[3]<<8) | cmd[4]; nkeynes@142: if( length > sizeof(struct gdrom_toc) ) nkeynes@142: length = sizeof(struct gdrom_toc); nkeynes@152: nkeynes@342: status = gdrom_disc->read_toc( gdrom_disc, data_buffer ); nkeynes@152: if( status != PKT_ERR_OK ) { nkeynes@152: ide_set_packet_result( status ); nkeynes@158: } else { nkeynes@342: ide_start_packet_read( length, 0 ); nkeynes@152: } nkeynes@125: break; nkeynes@158: case PKT_CMD_SESSION_INFO: nkeynes@342: REQUIRE_DISC(); nkeynes@149: length = cmd[4]; nkeynes@149: if( length > 6 ) nkeynes@149: length = 6; nkeynes@342: status = gdrom_disc->read_session( gdrom_disc, cmd[2], data_buffer ); nkeynes@152: if( status != PKT_ERR_OK ) { nkeynes@152: ide_set_packet_result( status ); nkeynes@158: } else { nkeynes@342: ide_start_packet_read( length, 0 ); nkeynes@152: } nkeynes@149: break; nkeynes@342: case PKT_CMD_PLAY_AUDIO: nkeynes@342: REQUIRE_DISC(); nkeynes@250: ide_set_packet_result( 0 ); nkeynes@250: ide_raise_interrupt(); nkeynes@250: idereg.status = 0x50; nkeynes@250: break; nkeynes@125: case PKT_CMD_READ_SECTOR: nkeynes@342: REQUIRE_DISC(); nkeynes@493: idereg.current_lba = cmd[2] << 16 | cmd[3] << 8 | cmd[4]; nkeynes@342: idereg.sectors_left = cmd[8] << 16 | cmd[9] << 8 | cmd[10]; /* blocks */ nkeynes@493: idereg.current_mode = cmd[1]; nkeynes@342: ide_read_next_sector(); nkeynes@125: break; nkeynes@149: case PKT_CMD_SPIN_UP: nkeynes@342: REQUIRE_DISC(); nkeynes@149: /* do nothing? */ nkeynes@158: ide_set_packet_result( PKT_ERR_OK ); nkeynes@158: ide_raise_interrupt(); nkeynes@158: break; nkeynes@245: case PKT_CMD_STATUS: nkeynes@342: REQUIRE_DISC(); nkeynes@245: length = cmd[4]; nkeynes@342: switch( cmd[1] ) { nkeynes@342: case 0: nkeynes@342: if( length > sizeof(gdrom_status) ) { nkeynes@342: length = sizeof(gdrom_status); nkeynes@245: } nkeynes@342: memcpy( data_buffer, gdrom_status, length ); nkeynes@342: ide_start_packet_read( length, 0 ); nkeynes@342: break; nkeynes@342: case 1: nkeynes@342: if( length > 14 ) { nkeynes@342: length = 14; nkeynes@342: } nkeynes@493: gdrom_disc->read_position( gdrom_disc, idereg.current_lba, data_buffer ); nkeynes@342: data_buffer[0] = 0x00; nkeynes@342: data_buffer[1] = 0x15; /* audio status ? */ nkeynes@342: data_buffer[2] = 0x00; nkeynes@342: data_buffer[3] = 0x0E; nkeynes@342: ide_start_packet_read( length, 0 ); nkeynes@342: break; nkeynes@245: } nkeynes@245: break; nkeynes@158: case PKT_CMD_71: nkeynes@158: /* This is a weird one. As far as I can tell it returns random garbage nkeynes@158: * (and not even the same length each time, never mind the same data). nkeynes@158: * For sake of something to do, it returns the results from a test dump nkeynes@158: */ nkeynes@342: REQUIRE_DISC(); nkeynes@158: memcpy( data_buffer, gdrom_71, sizeof(gdrom_71) ); nkeynes@342: ide_start_packet_read( sizeof(gdrom_71), 0 ); nkeynes@149: break; nkeynes@142: default: nkeynes@152: ide_set_packet_result( PKT_ERR_BADCMD ); /* Invalid command */ nkeynes@142: return; nkeynes@125: } nkeynes@493: idereg.last_packet_command = cmd[0]; nkeynes@125: } nkeynes@493: nkeynes@493: void ide_write_buffer( unsigned char *data, uint32_t length ) nkeynes@493: { nkeynes@493: switch( idereg.last_packet_command ) { nkeynes@493: case PKT_CMD_MODE_SELECT: nkeynes@493: if( idereg.current_lba < 10 ) { nkeynes@493: if( idereg.current_lba + length > 10 ) { nkeynes@493: length = 10 - idereg.current_lba; nkeynes@493: } nkeynes@493: memcpy( idereg.gdrom_mode + idereg.current_lba, data, length ); nkeynes@493: } nkeynes@493: break; nkeynes@493: default: nkeynes@493: WARN( "Don't know what to do with received data buffer for command %02X", idereg.last_packet_command ); nkeynes@493: } nkeynes@493: }