mirror of
https://github.com/holub/mame
synced 2025-05-30 09:33:05 +03:00
1617 lines
40 KiB
C
1617 lines
40 KiB
C
/***************************************************************************
|
|
|
|
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::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<ide_slot_device>("drive_0");
|
|
slot[1] = owner()->subdevice<ide_slot_device>("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>;
|
|
|
|
//-------------------------------------------------
|
|
// 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<ide_device_interface *>(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::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"));
|
|
}
|
|
}
|
|
}
|
|
}
|