/*************************************************************************** Generic (PC-style) IDE controller implementation ***************************************************************************/ #include "emu.h" #include "idectrl.h" #include "debugger.h" /*************************************************************************** DEBUGGING ***************************************************************************/ #define VERBOSE 0 #define PRINTF_IDE_COMMANDS 0 #define PRINTF_IDE_PASSWORD 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) #define LOGPRINT(x) do { if (VERBOSE) logerror x; if (PRINTF_IDE_COMMANDS) mame_printf_debug x; } while (0) /*************************************************************************** CONSTANTS ***************************************************************************/ #define MINIMUM_COMMAND_TIME (attotime::from_usec(10)) #define TIME_PER_SECTOR (attotime::from_usec(100)) #define TIME_PER_ROTATION (attotime::from_hz(5400/60)) #define TIME_SECURITY_ERROR (attotime::from_msec(1000)) #define TIME_SEEK_MULTISECTOR (attotime::from_msec(13)) #define TIME_NO_SEEK_MULTISECTOR (attotime::from_nsec(16300)) #define IDE_STATUS_ERROR 0x01 #define IDE_STATUS_HIT_INDEX 0x02 #define IDE_STATUS_BUFFER_READY 0x08 #define IDE_STATUS_SEEK_COMPLETE 0x10 #define IDE_STATUS_DRIVE_READY 0x40 #define IDE_STATUS_BUSY 0x80 #define IDE_BANK0_DATA 0 #define IDE_BANK0_ERROR 1 #define IDE_BANK0_SECTOR_COUNT 2 #define IDE_BANK0_SECTOR_NUMBER 3 #define IDE_BANK0_CYLINDER_LSB 4 #define IDE_BANK0_CYLINDER_MSB 5 #define IDE_BANK0_HEAD_NUMBER 6 #define IDE_BANK0_STATUS_COMMAND 7 #define IDE_BANK1_STATUS_CONTROL 6 #define IDE_BANK2_CONFIG_UNK 4 #define IDE_BANK2_CONFIG_REGISTER 8 #define IDE_BANK2_CONFIG_DATA 0xc #define IDE_COMMAND_READ_SECTORS 0x20 #define IDE_COMMAND_READ_SECTORS_NORETRY 0x21 #define IDE_COMMAND_WRITE_SECTORS 0x30 #define IDE_COMMAND_WRITE_SECTORS_NORETRY 0x31 #define IDE_COMMAND_DIAGNOSTIC 0x90 #define IDE_COMMAND_SET_CONFIG 0x91 #define IDE_COMMAND_READ_MULTIPLE 0xc4 #define IDE_COMMAND_WRITE_MULTIPLE 0xc5 #define IDE_COMMAND_SET_BLOCK_COUNT 0xc6 #define IDE_COMMAND_READ_DMA 0xc8 #define IDE_COMMAND_WRITE_DMA 0xca #define IDE_COMMAND_GET_INFO 0xec #define IDE_COMMAND_SET_FEATURES 0xef #define IDE_COMMAND_SECURITY_UNLOCK 0xf2 #define IDE_COMMAND_UNKNOWN_F9 0xf9 #define IDE_COMMAND_VERIFY_SECTORS 0x40 #define IDE_COMMAND_VERIFY_SECTORS_NORETRY 0x41 #define IDE_COMMAND_ATAPI_IDENTIFY 0xa1 #define IDE_COMMAND_RECALIBRATE 0x10 #define IDE_COMMAND_SEEK 0x70 #define IDE_COMMAND_IDLE_IMMEDIATE 0xe1 #define IDE_COMMAND_IDLE 0xe3 #define IDE_COMMAND_TAITO_GNET_UNLOCK_1 0xfe #define IDE_COMMAND_TAITO_GNET_UNLOCK_2 0xfc #define IDE_COMMAND_TAITO_GNET_UNLOCK_3 0x0f #define IDE_ERROR_NONE 0x00 #define IDE_ERROR_DEFAULT 0x01 #define IDE_ERROR_TRACK0_NOT_FOUND 0x02 #define IDE_ERROR_UNKNOWN_COMMAND 0x04 #define IDE_ERROR_BAD_LOCATION 0x10 #define IDE_ERROR_BAD_SECTOR 0x80 void ide_controller_device::set_irq(int state) { if (state == ASSERT_LINE) LOG(("IDE interrupt assert\n")); else LOG(("IDE interrupt clear\n")); /* signal an interrupt */ m_irq_handler(state); interrupt_pending = state; } void ide_controller_device::set_dmarq(int state) { } enum { TID_NULL, TID_DELAYED_INTERRUPT, TID_DELAYED_INTERRUPT_BUFFER_READY, TID_RESET_CALLBACK, TID_SECURITY_ERROR_DONE, TID_READ_SECTOR_DONE_CALLBACK, TID_WRITE_SECTOR_DONE_CALLBACK }; void ide_controller_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch(id) { case TID_DELAYED_INTERRUPT: status &= ~IDE_STATUS_BUSY; set_irq(ASSERT_LINE); break; case TID_DELAYED_INTERRUPT_BUFFER_READY: status &= ~IDE_STATUS_BUSY; status |= IDE_STATUS_BUFFER_READY; set_irq(ASSERT_LINE); break; case TID_RESET_CALLBACK: reset(); break; case TID_SECURITY_ERROR_DONE: /* clear error state */ status &= ~IDE_STATUS_ERROR; status |= IDE_STATUS_DRIVE_READY; break; case TID_READ_SECTOR_DONE_CALLBACK: read_sector_done(); break; case TID_WRITE_SECTOR_DONE_CALLBACK: write_sector_done(); break; } } void ide_controller_device::signal_delayed_interrupt(attotime time, int buffer_ready) { /* clear buffer ready and set the busy flag */ status &= ~IDE_STATUS_BUFFER_READY; status |= IDE_STATUS_BUSY; /* set a timer */ if (buffer_ready) timer_set(time, TID_DELAYED_INTERRUPT_BUFFER_READY); else timer_set(time, TID_DELAYED_INTERRUPT); } /*************************************************************************** INITIALIZATION AND RESET ***************************************************************************/ UINT8 *ide_controller_device::ide_get_features(int _drive) { return slot[_drive]->dev()->get_features(); } void ide_controller_device::ide_set_gnet_readlock(int _drive, const UINT8 onoff) { slot[_drive]->dev()->gnetreadlock = onoff; } void ide_controller_device::ide_set_master_password(int _drive, const UINT8 *password) { slot[_drive]->dev()->master_password = password; slot[_drive]->dev()->master_password_enable = (password != NULL); } void ide_controller_device::ide_set_user_password(int _drive, const UINT8 *password) { slot[_drive]->dev()->user_password = password; slot[_drive]->dev()->user_password_enable = (password != NULL); } /************************************* * * Advance to the next sector * *************************************/ void ide_controller_device::next_sector() { /* LBA direct? */ ide_device_interface *dev = slot[cur_drive]->dev(); if (dev->cur_head_reg & 0x40) { dev->cur_sector++; if (dev->cur_sector == 0) { dev->cur_cylinder++; if (dev->cur_cylinder == 0) dev->cur_head++; } } /* standard CHS */ else { /* sectors are 1-based */ dev->cur_sector++; if (dev->cur_sector > dev->get_sectors()) { /* heads are 0 based */ dev->cur_sector = 1; dev->cur_head++; if (dev->cur_head >= dev->get_heads()) { dev->cur_head = 0; dev->cur_cylinder++; } } } dev->cur_lba = dev->lba_address(); } /************************************* * * security error handling * *************************************/ void ide_controller_device::security_error() { /* set error state */ status |= IDE_STATUS_ERROR; status &= ~IDE_STATUS_DRIVE_READY; /* just set a timer and mark ourselves error */ timer_set(TIME_SECURITY_ERROR, TID_SECURITY_ERROR_DONE); } /************************************* * * Sector reading * *************************************/ void ide_controller_device::read_buffer_empty() { ide_device_interface *dev = slot[cur_drive]->dev(); /* reset the totals */ dev->buffer_offset = 0; /* clear the buffer ready and busy flag */ status &= ~IDE_STATUS_BUFFER_READY; status &= ~IDE_STATUS_BUSY; error = IDE_ERROR_DEFAULT; set_dmarq(0); if (dev->master_password_enable || dev->user_password_enable) { security_error(); dev->sector_count = 0; return; } /* if there is more data to read, keep going */ if (dev->sector_count > 0) dev->sector_count--; if (dev->sector_count > 0) read_next_sector(); } void ide_controller_device::read_sector_done() { ide_device_interface *dev = slot[cur_drive]->dev(); int lba = dev->lba_address(), count = 0; /* GNET readlock check */ if (dev->gnetreadlock) { status &= ~IDE_STATUS_ERROR; status &= ~IDE_STATUS_BUSY; return; } /* now do the read */ count = dev->read_sector(lba, dev->buffer); /* by default, mark the buffer ready and the seek complete */ if (!verify_only) status |= IDE_STATUS_BUFFER_READY; status |= IDE_STATUS_SEEK_COMPLETE; /* and clear the busy and error flags */ status &= ~IDE_STATUS_ERROR; status &= ~IDE_STATUS_BUSY; /* if we succeeded, advance to the next sector and set the nice bits */ if (count == 1) { /* advance the pointers, unless this is the last sector */ /* Gauntlet: Dark Legacy checks to make sure we stop on the last sector */ if (dev->sector_count != 1) next_sector(); /* clear the error value */ error = IDE_ERROR_NONE; /* signal an interrupt */ if (!verify_only) sectors_until_int--; if (sectors_until_int == 0 || dev->sector_count == 1) { sectors_until_int = ((command == IDE_COMMAND_READ_MULTIPLE) ? dev->block_count : 1); set_irq(ASSERT_LINE); } /* handle DMA */ if (dma_active) set_dmarq(1); /* if we're just verifying we can read the next sector */ if (verify_only) read_buffer_empty(); } /* if we got an error, we need to report it */ else { /* set the error flag and the error */ status |= IDE_STATUS_ERROR; error = IDE_ERROR_BAD_SECTOR; /* signal an interrupt */ set_irq(ASSERT_LINE); } } void ide_controller_device::read_first_sector() { ide_device_interface *dev = slot[cur_drive]->dev(); /* mark ourselves busy */ status |= IDE_STATUS_BUSY; /* just set a timer */ if (command == IDE_COMMAND_READ_MULTIPLE) { int new_lba = dev->lba_address(); attotime seek_time; if (new_lba == dev->cur_lba || new_lba == dev->cur_lba + 1) seek_time = TIME_NO_SEEK_MULTISECTOR; else seek_time = TIME_SEEK_MULTISECTOR; dev->cur_lba = new_lba; timer_set(seek_time, TID_READ_SECTOR_DONE_CALLBACK); } else timer_set(TIME_PER_SECTOR, TID_READ_SECTOR_DONE_CALLBACK); } void ide_controller_device::read_next_sector() { /* mark ourselves busy */ status |= IDE_STATUS_BUSY; if (command == IDE_COMMAND_READ_MULTIPLE) { if (sectors_until_int != 1) /* make ready now */ read_sector_done(); else /* just set a timer */ timer_set(attotime::from_usec(1), TID_READ_SECTOR_DONE_CALLBACK); } else /* just set a timer */ timer_set(TIME_PER_SECTOR, TID_READ_SECTOR_DONE_CALLBACK); } /************************************* * * Sector writing * *************************************/ void ide_controller_device::continue_write() { ide_device_interface *dev = slot[cur_drive]->dev(); /* reset the totals */ dev->buffer_offset = 0; /* clear the buffer ready flag */ status &= ~IDE_STATUS_BUFFER_READY; status |= IDE_STATUS_BUSY; if (command == IDE_COMMAND_WRITE_MULTIPLE) { if (sectors_until_int != 1) { /* ready to write now */ write_sector_done(); } else { /* set a timer to do the write */ timer_set(TIME_PER_SECTOR, TID_WRITE_SECTOR_DONE_CALLBACK); } } else { /* set a timer to do the write */ timer_set(TIME_PER_SECTOR, TID_WRITE_SECTOR_DONE_CALLBACK); } } void ide_controller_device::write_buffer_full() { ide_device_interface *dev = slot[cur_drive]->dev(); set_dmarq(0); if (command == IDE_COMMAND_SECURITY_UNLOCK) { if (dev->user_password_enable && memcmp(dev->buffer, dev->user_password, 2 + 32) == 0) { LOGPRINT(("IDE Unlocked user password\n")); dev->user_password_enable = 0; } if (dev->master_password_enable && memcmp(dev->buffer, dev->master_password, 2 + 32) == 0) { LOGPRINT(("IDE Unlocked master password\n")); dev->master_password_enable = 0; } if (PRINTF_IDE_PASSWORD) { int i; for (i = 0; i < 34; i += 2) { if (i % 8 == 2) mame_printf_debug("\n"); mame_printf_debug("0x%02x, 0x%02x, ", dev->buffer[i], dev->buffer[i + 1]); //mame_printf_debug("0x%02x%02x, ", dev->buffer[i], dev->buffer[i + 1]); } mame_printf_debug("\n"); } /* clear the busy and error flags */ status &= ~IDE_STATUS_ERROR; status &= ~IDE_STATUS_BUSY; status &= ~IDE_STATUS_BUFFER_READY; if (dev->master_password_enable || dev->user_password_enable) security_error(); else status |= IDE_STATUS_DRIVE_READY; } else if (command == IDE_COMMAND_TAITO_GNET_UNLOCK_2) { UINT8 key[5] = { 0 }; int i, bad = 0; dev->read_key(key); for (i=0; !bad && i<512; i++) bad = ((i < 2 || i >= 7) && dev->buffer[i]) || ((i >= 2 && i < 7) && dev->buffer[i] != key[i-2]); status &= ~IDE_STATUS_BUSY; status &= ~IDE_STATUS_BUFFER_READY; if (bad) status |= IDE_STATUS_ERROR; else { status &= ~IDE_STATUS_ERROR; dev->gnetreadlock= 0; } } else { continue_write(); } } void ide_controller_device::write_sector_done() { ide_device_interface *dev = slot[cur_drive]->dev(); int lba = dev->lba_address(), count = 0; /* now do the write */ count = dev->write_sector(lba, dev->buffer); /* by default, mark the buffer ready and the seek complete */ status |= IDE_STATUS_BUFFER_READY; status |= IDE_STATUS_SEEK_COMPLETE; /* and clear the busy adn error flags */ status &= ~IDE_STATUS_ERROR; status &= ~IDE_STATUS_BUSY; /* if we succeeded, advance to the next sector and set the nice bits */ if (count == 1) { /* advance the pointers, unless this is the last sector */ /* Gauntlet: Dark Legacy checks to make sure we stop on the last sector */ if (dev->sector_count != 1) next_sector(); /* clear the error value */ error = IDE_ERROR_NONE; /* signal an interrupt */ if (--sectors_until_int == 0 || dev->sector_count == 1) { sectors_until_int = ((command == IDE_COMMAND_WRITE_MULTIPLE) ? dev->block_count : 1); set_irq(ASSERT_LINE); } /* signal an interrupt if there's more data needed */ if (dev->sector_count > 0) dev->sector_count--; if (dev->sector_count == 0) status &= ~IDE_STATUS_BUFFER_READY; /* keep going for DMA */ if (dma_active && dev->sector_count != 0) { set_dmarq(1); } } /* if we got an error, we need to report it */ else { /* set the error flag and the error */ status |= IDE_STATUS_ERROR; error = IDE_ERROR_BAD_SECTOR; /* signal an interrupt */ set_irq(ASSERT_LINE); } } /************************************* * * Handle IDE commands * *************************************/ void ide_controller_device::handle_command(UINT8 _command) { UINT8 key[5]; ide_device_interface *dev = slot[cur_drive]->dev(); /* implicitly clear interrupts & dmarq here */ set_irq(CLEAR_LINE); set_dmarq(0); command = _command; switch (command) { case IDE_COMMAND_READ_SECTORS: case IDE_COMMAND_READ_SECTORS_NORETRY: LOGPRINT(("IDE Read multiple: C=%d H=%d S=%d LBA=%d count=%d\n", dev->cur_cylinder, dev->cur_head, dev->cur_sector, dev->lba_address(), dev->sector_count)); /* reset the buffer */ dev->buffer_offset = 0; sectors_until_int = 1; dma_active = 0; verify_only = 0; /* start the read going */ read_first_sector(); break; case IDE_COMMAND_READ_MULTIPLE: LOGPRINT(("IDE Read multiple block: C=%d H=%d S=%d LBA=%d count=%d\n", dev->cur_cylinder, dev->cur_head, dev->cur_sector, dev->lba_address(), dev->sector_count)); /* reset the buffer */ dev->buffer_offset = 0; sectors_until_int = 1; dma_active = 0; verify_only = 0; /* start the read going */ read_first_sector(); break; case IDE_COMMAND_VERIFY_SECTORS: case IDE_COMMAND_VERIFY_SECTORS_NORETRY: LOGPRINT(("IDE Read verify multiple with/without retries: C=%d H=%d S=%d LBA=%d count=%d\n", dev->cur_cylinder, dev->cur_head, dev->cur_sector, dev->lba_address(), dev->sector_count)); /* reset the buffer */ dev->buffer_offset = 0; sectors_until_int = 1; dma_active = 0; verify_only = 1; /* start the read going */ read_first_sector(); break; case IDE_COMMAND_READ_DMA: LOGPRINT(("IDE Read multiple DMA: C=%d H=%d S=%d LBA=%d count=%d\n", dev->cur_cylinder, dev->cur_head, dev->cur_sector, dev->lba_address(), dev->sector_count)); /* reset the buffer */ dev->buffer_offset = 0; sectors_until_int = dev->sector_count; dma_active = 1; verify_only = 0; /* start the read going */ read_first_sector(); break; case IDE_COMMAND_WRITE_SECTORS: case IDE_COMMAND_WRITE_SECTORS_NORETRY: LOGPRINT(("IDE Write multiple: C=%d H=%d S=%d LBA=%d count=%d\n", dev->cur_cylinder, dev->cur_head, dev->cur_sector, dev->lba_address(), dev->sector_count)); /* reset the buffer */ dev->buffer_offset = 0; sectors_until_int = 1; dma_active = 0; /* mark the buffer ready */ status |= IDE_STATUS_BUFFER_READY; break; case IDE_COMMAND_WRITE_MULTIPLE: LOGPRINT(("IDE Write multiple block: C=%d H=%d S=%d LBA=%d count=%d\n", dev->cur_cylinder, dev->cur_head, dev->cur_sector, dev->lba_address(), dev->sector_count)); /* reset the buffer */ dev->buffer_offset = 0; sectors_until_int = 1; dma_active = 0; /* mark the buffer ready */ status |= IDE_STATUS_BUFFER_READY; break; case IDE_COMMAND_WRITE_DMA: LOGPRINT(("IDE Write multiple DMA: C=%d H=%d S=%d LBA=%d count=%d\n", dev->cur_cylinder, dev->cur_head, dev->cur_sector, dev->lba_address(), dev->sector_count)); /* reset the buffer */ dev->buffer_offset = 0; sectors_until_int = dev->sector_count; dma_active = 1; /* start the read going */ set_dmarq(1); break; case IDE_COMMAND_SECURITY_UNLOCK: LOGPRINT(("IDE Security Unlock\n")); /* reset the buffer */ dev->buffer_offset = 0; sectors_until_int = 0; dma_active = 0; /* mark the buffer ready */ status |= IDE_STATUS_BUFFER_READY; set_irq(ASSERT_LINE); break; case IDE_COMMAND_GET_INFO: LOGPRINT(("IDE Read features\n")); /* reset the buffer */ dev->buffer_offset = 0; dev->sector_count = 1; /* build the features page */ memcpy(dev->buffer, slot[cur_drive]->dev()->get_features(), sizeof(dev->buffer)); /* indicate everything is ready */ status |= IDE_STATUS_BUFFER_READY; status |= IDE_STATUS_SEEK_COMPLETE; status |= IDE_STATUS_DRIVE_READY; /* and clear the busy adn error flags */ status &= ~IDE_STATUS_ERROR; status &= ~IDE_STATUS_BUSY; /* clear the error too */ error = IDE_ERROR_NONE; /* signal an interrupt */ signal_delayed_interrupt(MINIMUM_COMMAND_TIME, 1); break; case IDE_COMMAND_DIAGNOSTIC: error = IDE_ERROR_DEFAULT; /* signal an interrupt */ signal_delayed_interrupt(MINIMUM_COMMAND_TIME, 0); break; case IDE_COMMAND_RECALIBRATE: /* clear the error too */ error = IDE_ERROR_NONE; /* signal an interrupt */ signal_delayed_interrupt(MINIMUM_COMMAND_TIME, 0); break; case IDE_COMMAND_IDLE: /* clear the error too */ error = IDE_ERROR_NONE; /* for timeout disabled value is 0 */ dev->sector_count = 0; /* signal an interrupt */ set_irq(ASSERT_LINE); break; case IDE_COMMAND_SET_CONFIG: LOGPRINT(("IDE Set configuration (%d heads, %d sectors)\n", dev->cur_head + 1, dev->sector_count)); status &= ~IDE_STATUS_ERROR; error = IDE_ERROR_NONE; dev->set_geometry(dev->sector_count,dev->cur_head + 1); /* signal an interrupt */ signal_delayed_interrupt(MINIMUM_COMMAND_TIME, 0); break; case IDE_COMMAND_UNKNOWN_F9: /* only used by Killer Instinct AFAICT */ LOGPRINT(("IDE unknown command (F9)\n")); /* signal an interrupt */ set_irq(ASSERT_LINE); break; case IDE_COMMAND_SET_FEATURES: LOGPRINT(("IDE Set features (%02X %02X %02X %02X %02X)\n", dev->precomp_offset, dev->sector_count & 0xff, dev->cur_sector, dev->cur_cylinder & 0xff, dev->cur_cylinder >> 8)); /* signal an interrupt */ signal_delayed_interrupt(MINIMUM_COMMAND_TIME, 0); break; case IDE_COMMAND_SET_BLOCK_COUNT: LOGPRINT(("IDE Set block count (%02X)\n", dev->sector_count)); dev->block_count = dev->sector_count; // judge dredd wants 'drive ready' on this command status |= IDE_STATUS_DRIVE_READY; /* signal an interrupt */ set_irq(ASSERT_LINE); break; case IDE_COMMAND_TAITO_GNET_UNLOCK_1: LOGPRINT(("IDE GNET Unlock 1\n")); dev->sector_count = 1; status |= IDE_STATUS_DRIVE_READY; status &= ~IDE_STATUS_ERROR; set_irq(ASSERT_LINE); break; case IDE_COMMAND_TAITO_GNET_UNLOCK_2: LOGPRINT(("IDE GNET Unlock 2\n")); /* reset the buffer */ dev->buffer_offset = 0; sectors_until_int = 0; dma_active = 0; /* mark the buffer ready */ status |= IDE_STATUS_BUFFER_READY; set_irq(ASSERT_LINE); break; case IDE_COMMAND_TAITO_GNET_UNLOCK_3: LOGPRINT(("IDE GNET Unlock 3\n")); /* key check */ dev->read_key(key); if ((dev->precomp_offset == key[0]) && (dev->sector_count == key[1]) && (dev->cur_sector == key[2]) && (dev->cur_cylinder == (((UINT16)key[4]<<8)|key[3]))) { dev->gnetreadlock= 0; } /* update flags */ status |= IDE_STATUS_DRIVE_READY; status &= ~IDE_STATUS_ERROR; set_irq(ASSERT_LINE); break; case IDE_COMMAND_SEEK: /* cur_cylinder, cur_sector and cur_head are all already set in this case so no need so that implements actual seek */ /* clear the error too */ error = IDE_ERROR_NONE; /* for timeout disabled value is 0 */ dev->sector_count = 0; /* signal an interrupt */ set_irq(ASSERT_LINE); break; default: LOGPRINT(("IDE unknown command (%02X)\n", command)); status |= IDE_STATUS_ERROR; error = IDE_ERROR_UNKNOWN_COMMAND; set_irq(ASSERT_LINE); //debugger_break(device->machine()); break; } } /************************************* * * IDE controller read * *************************************/ READ8_MEMBER( ide_controller_device::read_via_config ) { UINT16 result = 0; /* logit */ LOG(("%s:IDE via config read at %X, mem_mask=%d\n", machine().describe_context(), offset, mem_mask)); switch(offset) { /* unknown config register */ case IDE_BANK2_CONFIG_UNK: result = config_unknown; break; /* active config register */ case IDE_BANK2_CONFIG_REGISTER: result = config_register_num; break; /* data from active config register */ case IDE_BANK2_CONFIG_DATA: if (config_register_num < IDE_CONFIG_REGISTERS) result = config_register[config_register_num]; break; default: logerror("%s:unknown IDE via config read at %03X, mem_mask=%d\n", machine().describe_context(), offset, mem_mask); break; } // printf( "read via config %04x %04x %04x\n", offset, result, mem_mask ); return result; } READ16_MEMBER( ide_controller_device::read_cs0_pc ) { if (mem_mask == 0xffff && offset == 1 ) offset = 0; // hack for 32 bit read of data register if (mem_mask == 0xff00) { return read_cs0(space, (offset * 2) + 1, 0xff) << 8; } else { return read_cs0(space, offset * 2, mem_mask); } } UINT16 ide_controller_device::read_dma() { ide_device_interface *dev = slot[cur_drive]->dev(); if (dev == NULL) return 0; UINT16 result = dev->buffer[dev->buffer_offset++]; result |= dev->buffer[dev->buffer_offset++] << 8; if (dev->buffer_offset >= IDE_DISK_SECTOR_SIZE) { LOG(("%s:IDE completed DMA read\n", machine().describe_context())); read_buffer_empty(); } return result; } READ16_MEMBER( ide_controller_device::read_cs0 ) { UINT16 result = 0; ide_device_interface *dev = slot[cur_drive]->dev(); /* logit */ // if (offset != IDE_BANK0_DATA && offset != IDE_BANK0_STATUS_COMMAND) LOG(("%s:IDE cs0 read at %X, mem_mask=%d\n", machine().describe_context(), offset, mem_mask)); if (dev != NULL) { if (dev->is_ready()) { status |= IDE_STATUS_DRIVE_READY; } else { status &= ~IDE_STATUS_DRIVE_READY; } } else { return 0; } switch (offset) { /* read data if there's data to be read */ case IDE_BANK0_DATA: if (status & IDE_STATUS_BUFFER_READY) { /* fetch the correct amount of data */ result = dev->buffer[dev->buffer_offset++]; if (mem_mask == 0xffff) result |= dev->buffer[dev->buffer_offset++] << 8; /* if we're at the end of the buffer, handle it */ if (dev->buffer_offset >= IDE_DISK_SECTOR_SIZE) { LOG(("%s:IDE completed PIO read\n", machine().describe_context())); read_buffer_empty(); } } break; /* return the current error */ case IDE_BANK0_ERROR: result = error; break; /* return the current sector count */ case IDE_BANK0_SECTOR_COUNT: result = dev->sector_count; break; /* return the current sector */ case IDE_BANK0_SECTOR_NUMBER: result = dev->cur_sector; break; /* return the current cylinder LSB */ case IDE_BANK0_CYLINDER_LSB: result = dev->cur_cylinder & 0xff; break; /* return the current cylinder MSB */ case IDE_BANK0_CYLINDER_MSB: result = dev->cur_cylinder >> 8; break; /* return the current head */ case IDE_BANK0_HEAD_NUMBER: result = dev->cur_head_reg; break; /* return the current status and clear any pending interrupts */ case IDE_BANK0_STATUS_COMMAND: result = status; if (last_status_timer->elapsed() > TIME_PER_ROTATION) { result |= IDE_STATUS_HIT_INDEX; last_status_timer->adjust(attotime::never); } if (interrupt_pending == ASSERT_LINE) set_irq(CLEAR_LINE); break; /* log anything else */ default: logerror("%s:unknown IDE cs0 read at %03X, mem_mask=%d\n", machine().describe_context(), offset, mem_mask); break; } // printf( "read cs0 %04x %04x %04x\n", offset, result, mem_mask ); /* return the result */ return result; } READ16_MEMBER( ide_controller_device::read_cs1_pc ) { if (mem_mask == 0xff00) { return read_cs1(space, (offset * 2) + 1, 0xff) << 8; } else { return read_cs1(space, offset * 2, mem_mask); } } READ16_MEMBER( ide_controller_device::read_cs1 ) { UINT16 result = 0; ide_device_interface *dev = slot[cur_drive]->dev(); if (dev != NULL) { if (dev->is_ready()) { status |= IDE_STATUS_DRIVE_READY; } else { status &= ~IDE_STATUS_DRIVE_READY; } } else { return 0; } /* logit */ // if (offset != IDE_BANK1_STATUS_CONTROL) LOG(("%s:IDE cs1 read at %X, mem_mask=%d\n", machine().describe_context(), offset, mem_mask)); /* return the current status but don't clear interrupts */ switch (offset) { case IDE_BANK1_STATUS_CONTROL: result = status; if (last_status_timer->elapsed() > TIME_PER_ROTATION) { result |= IDE_STATUS_HIT_INDEX; last_status_timer->adjust(attotime::never); } break; /* log anything else */ default: logerror("%s:unknown IDE cs1 read at %03X, mem_mask=%d\n", machine().describe_context(), offset, mem_mask); break; } // printf( "read cs1 %04x %04x %04x\n", offset, result, mem_mask ); /* return the result */ return result; } /************************************* * * IDE controller write * *************************************/ WRITE8_MEMBER( ide_controller_device::write_via_config ) { // printf( "write via config %04x %04x %04x\n", offset, data, mem_mask ); /* logit */ LOG(("%s:IDE via config write to %X = %08X, mem_mask=%d\n", machine().describe_context(), offset, data, mem_mask)); switch (offset) { /* unknown config register */ case IDE_BANK2_CONFIG_UNK: config_unknown = data; break; /* active config register */ case IDE_BANK2_CONFIG_REGISTER: config_register_num = data; break; /* data from active config register */ case IDE_BANK2_CONFIG_DATA: if (config_register_num < IDE_CONFIG_REGISTERS) config_register[config_register_num] = data; break; } } WRITE16_MEMBER( ide_controller_device::write_cs0_pc ) { if (mem_mask == 0xffff && offset == 1 ) offset = 0; // hack for 32 bit write to data register if (mem_mask == 0xff00) { return write_cs0(space, (offset * 2) + 1, data >> 8, 0xff); } else { return write_cs0(space, offset * 2, data, mem_mask); } } void ide_controller_device::write_dma( UINT16 data ) { ide_device_interface *dev = slot[cur_drive]->dev(); dev->buffer[dev->buffer_offset++] = data; dev->buffer[dev->buffer_offset++] = data >> 8; /* if we're at the end of the buffer, handle it */ if (dev->buffer_offset >= IDE_DISK_SECTOR_SIZE) { LOG(("%s:IDE completed DMA write\n", machine().describe_context())); write_buffer_full(); } } WRITE16_MEMBER( ide_controller_device::write_cs0 ) { // printf( "write cs0 %04x %04x %04x\n", offset, data, mem_mask ); switch (offset) { case IDE_BANK0_HEAD_NUMBER: cur_drive = (data & 0x10) >> 4; break; } ide_device_interface *dev = slot[cur_drive]->dev(); if (dev == NULL) return; /* logit */ if (offset != IDE_BANK0_DATA) LOG(("%s:IDE cs0 write to %X = %08X, mem_mask=%d\n", machine().describe_context(), offset, data, mem_mask)); // fprintf(stderr, "ide write %03x %02x mem_mask=%d\n", offset, data, size); switch (offset) { /* write data */ case IDE_BANK0_DATA: if (status & IDE_STATUS_BUFFER_READY) { /* store the correct amount of data */ dev->buffer[dev->buffer_offset++] = data; if (mem_mask == 0xffff) dev->buffer[dev->buffer_offset++] = data >> 8; /* if we're at the end of the buffer, handle it */ if (dev->buffer_offset >= IDE_DISK_SECTOR_SIZE) { LOG(("%s:IDE completed PIO write\n", machine().describe_context())); write_buffer_full(); } } break; /* precompensation offset?? */ case IDE_BANK0_ERROR: dev->precomp_offset = data; break; /* sector count */ case IDE_BANK0_SECTOR_COUNT: dev->sector_count = data ? data : 256; break; /* current sector */ case IDE_BANK0_SECTOR_NUMBER: dev->cur_sector = data; break; /* current cylinder LSB */ case IDE_BANK0_CYLINDER_LSB: dev->cur_cylinder = (dev->cur_cylinder & 0xff00) | (data & 0xff); break; /* current cylinder MSB */ case IDE_BANK0_CYLINDER_MSB: dev->cur_cylinder = (dev->cur_cylinder & 0x00ff) | ((data & 0xff) << 8); break; /* current head */ case IDE_BANK0_HEAD_NUMBER: dev->cur_head = data & 0x0f; dev->cur_head_reg = data; // LBA mode = data & 0x40 break; /* command */ case IDE_BANK0_STATUS_COMMAND: handle_command(data); break; } } WRITE16_MEMBER( ide_controller_device::write_cs1_pc ) { if (mem_mask == 0xff00) { return write_cs1(space, (offset * 2) + 1, data >> 8, 0xff); } else { return write_cs1(space, offset * 2, data, mem_mask); } } WRITE16_MEMBER( ide_controller_device::write_cs1 ) { ide_device_interface *dev = slot[cur_drive]->dev(); if (dev == NULL) return; // printf( "write cs1 %04x %04x %04x\n", offset, data, mem_mask ); /* logit */ LOG(("%s:IDE cs1 write to %X = %08X, mem_mask=%d\n", machine().describe_context(), offset, data, mem_mask)); switch (offset) { /* adapter control */ case IDE_BANK1_STATUS_CONTROL: dev->adapter_control = data; /* handle controller reset */ //if (data == 0x04) if (data & 0x04) { status |= IDE_STATUS_BUSY; status &= ~IDE_STATUS_DRIVE_READY; reset_timer->adjust(attotime::from_msec(5)); } break; } } SLOT_INTERFACE_START(ide_devices) SLOT_INTERFACE("hdd", IDE_HARDDISK) SLOT_INTERFACE_END ide_controller_device::ide_controller_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, type, name, tag, owner, clock), status(0), error(0), command(0), interrupt_pending(0), sectors_until_int(0), verify_only(0), config_unknown(0), config_register_num(0), cur_drive(0), m_irq_handler(*this) { } const device_type IDE_CONTROLLER = &device_creator; ide_controller_device::ide_controller_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, IDE_CONTROLLER, "IDE Controller", tag, owner, clock), error(0), command(0), interrupt_pending(0), sectors_until_int(0), verify_only(0), config_unknown(0), config_register_num(0), cur_drive(0), m_irq_handler(*this) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void ide_controller_device::device_start() { m_irq_handler.resolve_safe(); /* set MAME harddisk handle */ slot[0] = owner()->subdevice("drive_0"); slot[1] = owner()->subdevice("drive_1"); /* create a timer for timing status */ last_status_timer = timer_alloc(TID_NULL); reset_timer = timer_alloc(TID_RESET_CALLBACK); /* register ide states */ save_item(NAME(status)); save_item(NAME(error)); save_item(NAME(command)); save_item(NAME(interrupt_pending)); save_item(NAME(sectors_until_int)); save_item(NAME(dma_active)); save_item(NAME(config_unknown)); save_item(NAME(config_register)); save_item(NAME(config_register_num)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void ide_controller_device::device_reset() { ide_device_interface *dev = slot[cur_drive]->dev(); LOG(("IDE controller reset performed\n")); /* reset the drive state */ cur_drive = 0; status = IDE_STATUS_DRIVE_READY | IDE_STATUS_SEEK_COMPLETE; error = IDE_ERROR_DEFAULT; dev->buffer_offset = 0; dev->gnetreadlock = 0; dev->master_password_enable = (dev->master_password != NULL); dev->user_password_enable = (dev->user_password != NULL); set_irq(CLEAR_LINE); set_dmarq(0); } //************************************************************************** // IDE SLOT DEVICE //************************************************************************** // device type definition const device_type IDE_SLOT = &device_creator; //------------------------------------------------- // ide_slot_device - constructor //------------------------------------------------- ide_slot_device::ide_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, IDE_SLOT, "IDE Connector", tag, owner, clock), device_slot_interface(mconfig, *this), m_dev(NULL) { } //------------------------------------------------- // device_config_complete - perform any // operations now that the configuration is // complete //------------------------------------------------- void ide_slot_device::device_config_complete() { m_dev = dynamic_cast(get_card_device()); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void ide_slot_device::device_start() { } #define IDE_BUSMASTER_STATUS_ACTIVE 0x01 #define IDE_BUSMASTER_STATUS_ERROR 0x02 #define IDE_BUSMASTER_STATUS_IRQ 0x04 const device_type BUS_MASTER_IDE_CONTROLLER = &device_creator; bus_master_ide_controller_device::bus_master_ide_controller_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : ide_controller_device(mconfig, BUS_MASTER_IDE_CONTROLLER, "Bus Master IDE Controller", tag, owner, clock), dma_address(0), dma_bytes_left(0), dma_descriptor(0), dma_last_buffer(0), bus_master_command(0), bus_master_status(0), bus_master_descriptor(0) { } void bus_master_ide_controller_device::device_start() { ide_controller_device::device_start(); /* find the bus master space */ if (bmcpu != NULL) { device_t *bmtarget = machine().device(bmcpu); if (bmtarget == NULL) throw emu_fatalerror("IDE controller '%s' bus master target '%s' doesn't exist!", tag(), bmcpu); device_memory_interface *memory; if (!bmtarget->interface(memory)) throw emu_fatalerror("IDE controller '%s' bus master target '%s' has no memory!", tag(), bmcpu); dma_space = &memory->space(bmspace); dma_address_xor = (dma_space->endianness() == ENDIANNESS_LITTLE) ? 0 : 3; } save_item(NAME(dma_address)); save_item(NAME(dma_bytes_left)); save_item(NAME(dma_descriptor)); save_item(NAME(dma_last_buffer)); save_item(NAME(bus_master_command)); save_item(NAME(bus_master_status)); save_item(NAME(bus_master_descriptor)); } void bus_master_ide_controller_device::set_irq(int state) { ide_controller_device::set_irq(state); if (m_irq != state) { m_irq = state; if( m_irq ) bus_master_status |= IDE_BUSMASTER_STATUS_IRQ; } } void bus_master_ide_controller_device::set_dmarq(int state) { if (m_dmarq != state) { m_dmarq = state; execute_dma(); } } /************************************* * * Bus master read * *************************************/ READ32_MEMBER( bus_master_ide_controller_device::ide_bus_master32_r ) { LOG(("%s:ide_bus_master32_r(%d, %08x)\n", machine().describe_context(), offset, mem_mask)); switch( offset ) { case 0: /* command register/status register */ return bus_master_command | (bus_master_status << 16); case 1: /* descriptor table register */ return bus_master_descriptor; } return 0xffffffff; } /************************************* * * Bus master write * *************************************/ WRITE32_MEMBER( bus_master_ide_controller_device::ide_bus_master32_w ) { LOG(("%s:ide_bus_master32_w(%d, %08x, %08X)\n", machine().describe_context(), offset, mem_mask, data)); switch( offset ) { case 0: if( ACCESSING_BITS_0_7 ) { /* command register */ UINT8 old = bus_master_command; UINT8 val = data & 0xff; /* save the read/write bit and the start/stop bit */ bus_master_command = (old & 0xf6) | (val & 0x09); if ((old ^ bus_master_command) & 1) { if (bus_master_command & 1) { /* handle starting a transfer */ bus_master_status |= IDE_BUSMASTER_STATUS_ACTIVE; /* reset all the DMA data */ dma_bytes_left = 0; dma_descriptor = bus_master_descriptor; /* if we're going live, start the pending read/write */ execute_dma(); } else if (bus_master_status & IDE_BUSMASTER_STATUS_ACTIVE) { bus_master_status &= ~IDE_BUSMASTER_STATUS_ACTIVE; LOG(("DMA Aborted!\n")); } } } if( ACCESSING_BITS_16_23 ) { /* status register */ UINT8 old = bus_master_status; UINT8 val = data >> 16; /* save the DMA capable bits */ bus_master_status = (old & 0x9f) | (val & 0x60); /* clear interrupt and error bits */ if (val & IDE_BUSMASTER_STATUS_IRQ) bus_master_status &= ~IDE_BUSMASTER_STATUS_IRQ; if (val & IDE_BUSMASTER_STATUS_ERROR) bus_master_status &= ~IDE_BUSMASTER_STATUS_ERROR; } break; case 1: /* descriptor table register */ bus_master_descriptor = data & 0xfffffffc; break; } } void bus_master_ide_controller_device::execute_dma() { while (m_dmarq && (bus_master_status & IDE_BUSMASTER_STATUS_ACTIVE)) { /* if we're out of space, grab the next descriptor */ if (dma_bytes_left == 0) { /* fetch the address */ dma_address = dma_space->read_byte(dma_descriptor++ ^ dma_address_xor); dma_address |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 8; dma_address |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 16; dma_address |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 24; dma_address &= 0xfffffffe; /* fetch the length */ dma_bytes_left = dma_space->read_byte(dma_descriptor++ ^ dma_address_xor); dma_bytes_left |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 8; dma_bytes_left |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 16; dma_bytes_left |= dma_space->read_byte(dma_descriptor++ ^ dma_address_xor) << 24; dma_last_buffer = (dma_bytes_left >> 31) & 1; dma_bytes_left &= 0xfffe; if (dma_bytes_left == 0) dma_bytes_left = 0x10000; // LOG(("New DMA descriptor: address = %08X bytes = %04X last = %d\n", dma_address, dma_bytes_left, dma_last_buffer)); } if (bus_master_command & 8) { // read from ata bus UINT16 data = read_dma(); // write to memory dma_space->write_byte(dma_address++, data & 0xff); dma_space->write_byte(dma_address++, data >> 8); } else { // read from memory; UINT16 data = dma_space->read_byte(dma_address++); data |= dma_space->read_byte(dma_address++) << 8; // write to ata bus write_dma(data); } dma_bytes_left -= 2; if (dma_bytes_left == 0 && dma_last_buffer) { bus_master_status &= ~IDE_BUSMASTER_STATUS_ACTIVE; if (m_dmarq) { LOG(("DMA Out of buffer space!\n")); } } } }