// license:BSD-3-Clause
// copyright-holders:Michael Zapf
/**************************************************************************

    HDC9234 Hard and Floppy Disk Controller
    Standard Microsystems Corporation (SMC)

    This controller handles MFM and FM encoded floppy disks and hard disks.
    A variant, the HDC9224, is used in some DEC systems.

    The HDC9234 is used in the Myarc HFDC card for the TI99/4A.

    References:
    [1] SMC HDC9234 preliminary data book (1988)

    The HDC9234 controller is also referred to as the "Universal Disk Controller" (UDC)
    by the data book

    Michael Zapf, September 2014

***************************************************************************/

#include "emu.h"
#include "hdc9234.h"

// Per-command debugging
#define TRACE_SELECT 0
#define TRACE_STEP 0
#define TRACE_RESTORE 0
#define TRACE_SUBSTATES 0
#define TRACE_READ 0
#define TRACE_WRITE 0
#define TRACE_READREG 0
#define TRACE_SETREG 0
#define TRACE_SETPTR 0
#define TRACE_FORMAT 0
#define TRACE_READTRACK 0

// Common states
#define TRACE_READID 0
#define TRACE_VERIFY 0
#define TRACE_TRANSFER 0

// Live states debugging
#define TRACE_LIVE 0
#define TRACE_SHIFT 0
#define TRACE_SYNC 0

// Misc debugging
#define TRACE_DELAY 0
#define TRACE_INT 0
#define TRACE_LINES 0
#define TRACE_INDEX 0
#define TRACE_DMA 0
#define TRACE_DONE 0
#define TRACE_FAIL 0
#define TRACE_AUXBUS 0

#define TRACE_DETAIL 0

#define UNRELIABLE_MEDIA 0

/*
   === Not implemented ===
   ECC
   Write long (see MODE register; only useful with ECC)
   Tape operations
   Hard disk operation

   === Implemented but untested ===
   Burst mode
   Restore
   Poll drives
   Seek/Read ID
   Multi-sector read/write
   Usage of seek complete
   Read/write sectors physical
   Read track
*/

/*
    Some registers of the HDC.

            +------+------+------+------+------+------+------+------+
    DHEAD:  |   0  | Sector size |  0   |   Desired head  (OUTPUT2) |  AT mode
            +------+------+------+------+------+------+------+------+
            |   0  |  Desired cylinder  |   Desired head  (OUTPUT2) |  SMC mode
            +------+------+------+------+------+------+------+------+
            +------+------+------+------+------+------+------+------+
    RETRY:  |  Retry count (ones comp!) |   Progr. output (OUTPUT1) |
            +------+------+------+------+------+------+------+------+
            +------+------+------+------+------+------+------+------+
    MODE:   | HD   | use CRC/ECC |  FM  |  0   |      step rate     |
            +------+------+------+------+------+------+------+------+
            +------+------+------+------+------+------+------+------+
    INTCOMM:|   1  |  0   | Done | DelD | User | WrPrt| Ready|Wfault|
            +------+------+------+------+------+------+------+------+
            +------+------+------+------+------+------+------+------+
    DDELAY: |   0  |  0   | Sector size |  0   |  0   |    Zone     | AT mode
            +------+------+------+------+------+------+------+------+
            |          Data to be written on disk                   | writing
            +------+------+------+------+------+------+------+------+
            |          Head load timer count                        | drselect
            +------+------+------+------+------+------+------+------+

    Read registers
            +------+------+------+------+------+------+------+------+
    CHIP_ST:| Retry|  ECC |  CRC | DelD | Sync | Comp | Current Drv |
            +------+------+------+------+------+------+------+------+
            +------+------+------+------+------+------+------+------+
    INT_ST: | Pend | DMARQ| Done |  Termcode   | RdyCh| Ovrun| BdSec|
            +------+------+------+------+------+------+------+------+
            +------+------+------+------+------+------+------+------+
    DRV_ST: | ECC  | Index| SeekC| Trk00| User | WrPrt| Ready|Wfault|
            +------+------+------+------+------+------+------+------+

*/
enum
{
	// Write registers   |   Read registers
	//--------------------------------------
	DMA7_0=0,
	DMA15_8=1,
	DMA23_16=2,
	DESIRED_SECTOR=3,       CURRENT_SECTOR=3,
	DESIRED_HEAD=4,         CURRENT_HEAD=4,
	DESIRED_CYLINDER=5,     CURRENT_CYLINDER=5,
	SECTOR_COUNT=6,         CURRENT_IDENT=6,
	RETRY_COUNT=7,          TEMP_STORAGE2=7,
	MODE=8,                 CHIP_STATUS=8,
	INT_COMM_TERM=9,        DRIVE_STATUS=9,
	DATA_DELAY=10,          DATA=10,
	COMMAND=11,             INT_STATUS=11,

	//======================
	// Internal registers
	CURRENT_SIZE=12,
	CURRENT_CRC1=13,
	CURRENT_CRC2=14
};

/*
    Definition of bits in the status register [1] p.7
*/
enum
{
	ST_INTPEND = 0x80,       // interrupt pending
	ST_DMAREQ  = 0x40,       // DMA request
	ST_DONE    = 0x20,       // command done
	ST_TERMCOD = 0x18,       // termination code (see below)
		TC_SUCCESS = 0x00,   // Successful completion
		TC_RDIDERR = 0x08,   // Error in READ-ID sequence
		TC_VRFYERR = 0x10,   // Error in VERIFY sequence
		TC_DATAERR = 0x18,   // Error in DATA-TRANSFER seq.
	ST_RDYCHNG = 0x04,       // ready change
	ST_OVRUN   = 0x02,       // overrun/underrun
	ST_BADSECT = 0x01        // bad sector
};

/*
    Definition of bits in the Termination-Conditions register
*/
enum
{
	TC_CRCPRE  = 0x80,       // CRC register preset, must be 1
	TC_UNUSED  = 0x40,       // bit 6 is not used and must be 0
	TC_INTDONE = 0x20,       // interrupt on command completion
	TC_TDELDAT = 0x10,       // terminate on deleted data mark detection
	TC_TDUSER  = 0x08,       // user-defined condition
	TC_TWPROT  = 0x04,       // terminate on write protection
	TC_INTRDCH = 0x02,       // interrupt on ready change
	TC_TWRFLT  = 0x01        // interrupt on write fault
};

/*
    Definition of bits in the chip status register.
*/
enum
{
	CS_RETREQ  = 0x80,        // retry required
	CS_ECCATT  = 0x40,        // ECC correction attempted
	CS_CRCERR  = 0x20,        // ECC/CRC error
	CS_DELDATA = 0x10,        // deleted data mark
	CS_SYNCERR = 0x08,        // synchronization error
	CS_COMPERR = 0x04,        // compare error
	CS_PRESDRV = 0x03         // present drive selected
};

/*
    Bits in the internal output registers. The registers are output via the
    auxiliary bus (AB)

    OUTPUT1
    AB7     drive select 3
    AB6     drive select 2
    AB5     drive select 1
    AB4     drive select 0
    AB3     programmable outputs
    AB2     programmable outputs
    AB1     programmable outputs
    AB0     programmable outputs

    OUTPUT2
    AB7     drive select 3* (active low, used for tape operations)
    AB6     reduce write current
    AB5     step direction       (0=towards TRK00)
    AB4     step pulse           (1=active)
    AB3     desired head 3
    AB2     desired head 2
    AB1     desired head 1
    AB0     desired head 0
*/
enum
{
	OUT1_DRVSEL3    = 0x80,
	OUT1_DRVSEL2    = 0x40,
	OUT1_DRVSEL1    = 0x20,
	OUT1_DRVSEL0    = 0x10,
	OUT2_DRVSEL3I   = 0x80,
	OUT2_REDWRT     = 0x40,
	OUT2_STEPDIR    = 0x20,
	OUT2_STEPPULSE  = 0x10,
	OUT2_HEADSEL    = 0x0f
};

#define NODRIVE -1

enum
{
	TYPE_AT = 0x00,
	TYPE_HD = 0x01,
	TYPE_FLOPPY8 = 0x02,
	TYPE_FLOPPY5 = 0x03
};

/*
    Timers
*/
enum
{
	GEN_TIMER = 1,
	COM_TIMER,
	LIVE_TIMER
};

/*
    Definition of bits in the Mode register
*/
enum {
	MO_TYPE     = 0x80,        // Hard disk (1) or floppy (0)
	MO_CRCECC   = 0x60,        // Values for CRC/ECC handling
	MO_DENSITY  = 0x10,        // FM = 1; MFM = 0
	MO_UNUSED   = 0x08,        // Unused, 0
	MO_STEPRATE = 0x07         // Step rates
};

/*
    Step rates in microseconds for MFM. This is set in the mode register,
    bits 0-2. FM mode doubles all values.
*/
static const int step_hd[]      = { 22, 50, 100, 200, 400, 800, 1600, 3200 };
static const int step_flop8[]   = { 218, 500, 1000, 2000, 4000, 8000, 16000, 32000 };
static const int step_flop5[]   = { 436, 1000, 2000, 4000, 8000, 16000, 32000, 64000 };

/*
    Head load timer increments in usec. Delay value is calculated from this value
    multiplied by the factor in the DATA/DELAY register. For FM mode all
    values are doubled. The values depend on the drive type.
*/
static const int head_load_timer_increment[] = { 200, 200, 2000, 4000 };

/*
    ID fields association to registers
*/
static const int id_field[] = { CURRENT_CYLINDER, CURRENT_HEAD, CURRENT_SECTOR, CURRENT_SIZE, CURRENT_CRC1, CURRENT_CRC2 };

/*
    Pulse widths for stepping in usec
*/
enum
{
	pulse_hd = 11,
	pulse_flop8 = 112,
	pulse_flop5 = 224
};

/*
    Times for UDC's acceptance of command and register write accesses (ns).
*/
enum
{
	REGISTER_COMMIT = 1000,
	COMMAND_COMMIT = 1000
};

enum
{
	UNDEF = 0x00,
	IDLE,
	DONE,
	COMMAND_INIT,
	REGISTER_ACCESS,

	STEP_ON,
	STEP_OFF,
	RESTORE_CHECK,
	WAIT_SEEK_COMPLETE,
	SEEK_COMPLETE,
	HEAD_DELAY,
	WAITINDEX0,
	WAITINDEX1,
	TRACKSTART,
	TRACKDONE,
	POLL1,
	POLL2,
	POLL3,

	READ_ID = 0x40,
	READ_ID1,
	READ_ID_STEPON,
	READ_ID_STEPOFF,
	READ_ID_SEEK_COMPLETE,

	VERIFY = 0x50,
	VERIFY1,
	VERIFY2,
	VERIFY3,

	DATA_TRANSFER = 0x60,
	DATA_TRANSFER_READ,
	DATA_TRANSFER_WRITE,

	// Live states
	LIVE_STATES = 0x80,
	SEARCH_IDAM,
	SEARCH_IDAM_FAILED,
	READ_TWO_MORE_A1_IDAM,
	READ_ID_FIELDS_INTO_REGS,
	SEARCH_DAM,
	READ_TWO_MORE_A1_DAM,
	SEARCH_DAM_FAILED,
	READ_SECTOR_DATA,
	READ_SECTOR_DATA1,
	WRITE_DAM_AND_SECTOR,
	WRITE_SEC_SKIP_GAP2,
	WRITE_SEC_SKIP_GAP2_LOOP,
	WRITE_SEC_BYTE,
	WRITE_SEC_NEXT_BYTE,

	WRITE_TRACK_BYTE,
	WRITE_TRACK_NEXT_BYTE,

	READ_TRACK_BYTE,
	READ_TRACK_NEXT_BYTE,

	FORMAT_TRACK,
	WRITE_GAP0,
	WRITE_GAP1,
	WRITE_GAP2,
	WRITE_GAP3,
	WRITE_GAP4,
	WRITE_IXAM_SYNC,
	WRITE_IXAM,
	WRITE_FC,
	WRITE_IDAM_SYNC,
	WRITE_IDAM,
	WRITE_HEADER,
	WRITE_IDENT,

	WRITE_DAM_SYNC,
	WRITE_A1,
	WRITE_DATAMARK,
	WRITE_SECDATA,
	WRITE_DATA_CRC,
	WRITE_DONE,
	WRITE_HEADER_CRC,

	READ_TRACK,
	READ_TRACK_ID,
	READ_TRACK_ID_DONE,

	NO_DMA_ACK
};

/*
    Event lines
*/
enum
{
	INDEX_LINE = 1,
	READY_LINE,
	SEEKCOMP_LINE
};

/*
    State machine metastates.
*/
enum
{
	CONTINUE = 0,
	WAIT,
	NEXT,
	ERROR,
	SUCCESS
};

const hdc9234_device::cmddef hdc9234_device::s_command[] =
{
	{ 0x00, 0xff, &hdc9234_device::reset_controller },
	{ 0x01, 0xff, &hdc9234_device::drive_deselect },
	{ 0x02, 0xfe, &hdc9234_device::restore_drive },
	{ 0x04, 0xfc, &hdc9234_device::step_drive },
	{ 0x08, 0xf8, &hdc9234_device::tape_backup },
	{ 0x10, 0xf0, &hdc9234_device::poll_drives },
	{ 0x20, 0xe0, &hdc9234_device::drive_select },
	{ 0x40, 0xf0, &hdc9234_device::set_register_pointer },
	{ 0x50, 0xf8, &hdc9234_device::seek_read_id },
	{ 0x58, 0xfe, &hdc9234_device::read_sectors },
	{ 0x5a, 0xfe, &hdc9234_device::read_track },
	{ 0x5c, 0xfc, &hdc9234_device::read_sectors },
	{ 0x60, 0xe0, &hdc9234_device::format_track },
	{ 0x80, 0x80, &hdc9234_device::write_sectors },
	{ 0, 0, 0 }
};

/*
    Standard constructor.
*/
hdc9234_device::hdc9234_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
	: device_t(mconfig, HDC9234, "SMC HDC9234 Universal Disk Controller", tag, owner, clock, "hdc9234", __FILE__),
	m_out_intrq(*this),
	m_out_dmarq(*this),
	m_out_dip(*this),
	m_out_auxbus(*this),
	m_in_dma(*this),
	m_out_dma(*this),
	m_initialized(false)
{
}

/*
    Set or reset some bits.
*/
void hdc9234_device::set_bits(UINT8& byte, int mask, bool set)
{
	if (set) byte |= mask;
	else byte &= ~mask;
}

/*
    Tell whether the controller is in FM mode.
*/
bool hdc9234_device::fm_mode()
{
	return ((m_register_w[MODE]&MO_DENSITY)!=0);
}

/*
    Are we back on track 0?
*/
bool hdc9234_device::on_track00()
{
	return (m_register_r[DRIVE_STATUS] & HDC_DS_TRK00)!=0;
}

/*
    Accessor functions for specific parameters.
*/
int hdc9234_device::desired_head()
{
	return m_register_w[DESIRED_HEAD] & 0x0f;
}

int hdc9234_device::desired_cylinder()
{
	return (m_register_w[DESIRED_CYLINDER] & 0xff) | ((m_register_w[DESIRED_HEAD] & 0x70) << 4);
}

int hdc9234_device::desired_sector()
{
	return m_register_w[DESIRED_SECTOR] & 0xff;
}

int hdc9234_device::current_head()
{
	return m_register_r[CURRENT_HEAD] & 0x0f;
}

int hdc9234_device::current_cylinder()
{
	return (m_register_r[CURRENT_CYLINDER] & 0xff) | ((m_register_r[CURRENT_HEAD] & 0x70) << 4);
}

int hdc9234_device::current_sector()
{
	return m_register_r[CURRENT_SECTOR] & 0xff;
}

UINT8 hdc9234_device::current_command()
{
	return m_register_w[COMMAND];
}

bool hdc9234_device::using_floppy()
{
	return (m_selected_drive_type == TYPE_FLOPPY5 || m_selected_drive_type == TYPE_FLOPPY8);
}

/*
    Delivers the step time (in microseconds) minus the pulse width
*/
int hdc9234_device::step_time()
{
	int time = 0;
	int index = m_register_w[MODE] & MO_STEPRATE;
	// Get seek time.
	if (m_selected_drive_type == TYPE_FLOPPY8)
		time = step_flop8[index] - pulse_flop8;

	else if (m_selected_drive_type == TYPE_FLOPPY5)
		time = step_flop5[index] - pulse_flop5;
	else
		time = step_hd[index] - pulse_hd;

	if (fm_mode()) time = time * 2;
	return time;
}

/*
    Delivers the pulse width time (in microseconds)
*/
int hdc9234_device::pulse_width()
{
	int time = 0;
	// Get seek time.
	if (m_selected_drive_type == TYPE_FLOPPY8)
		time = pulse_flop8;

	else if (m_selected_drive_type == TYPE_FLOPPY5)
		time = pulse_flop5;
	else
		time = pulse_hd;

	if (fm_mode()) time = time * 2;
	return time;
}

bool hdc9234_device::rapid_steps()
{
	return (m_register_w[MODE] & MO_STEPRATE) == 0;
}

/*
    Delivers the sector size
*/
int hdc9234_device::calc_sector_size()
{
	return 128 << (m_register_r[CURRENT_SIZE] & 3);
}

// ===========================================================================
//    Wait handling
//    We can wait for a given time period or for a line to be set or cleared
// ===========================================================================

void hdc9234_device::wait_time(emu_timer *tm, int microsec, int next_substate)
{
	if (TRACE_DELAY) logerror("%s: Delay by %d microsec\n", tag(), microsec);
	tm->adjust(attotime::from_usec(microsec));
	m_substate = next_substate;
}

void hdc9234_device::wait_time(emu_timer *tm, attotime delay, int param)
{
	if (TRACE_DELAY) logerror("%s: [%s] Delaying by %4.2f microsecs\n", tag(), ttsn().cstr(), delay.as_double()*1000000);
	tm->adjust(delay);
	m_substate = param;
}

/*
    Set the hook for line level handling
*/
void hdc9234_device::wait_line(int line, line_state level, int substate, bool stopwrite)
{
	m_event_line = line;
	m_line_level = level;
	m_state_after_line = substate;
	m_stopwrite = stopwrite;
}

// ==================================================================
//     Common subroutines READ ID, VERIFY, DATA TRANSFER
//     called by all sector access commands
// ==================================================================
/*
    READ ID FIELD ([1] p. 9)
    The controller
    - scans for the next IDAM
    - reads the ID field values into the CURRENT_HEAD/CYLINDER/SECTOR registers
    - checks the CRC
    - calculates the number of steps and the direction towards DESIRED_CYLINDER
    (must have saved that value before!)
    - steps to that location during OUTPUT2 times
*/
void hdc9234_device::read_id(int& cont, bool implied_seek, bool wait_seek_complete)
{
	cont = CONTINUE;

	while (cont==CONTINUE)
	{
		switch (m_substate)
		{
		case READ_ID:
			// Implied seek: Enter the READ_ID subprogram.
			if (TRACE_READID && TRACE_SUBSTATES) logerror("%s: substate READ_ID\n", tag());

			// First step: Search the next IDAM, and if found, read the
			// ID values into the registers

			// Depending on the implied seek flag, continue with read_id,
			// else switch to verify.
			m_substate = implied_seek? READ_ID1 : VERIFY;

			m_live_state.bit_count_total = 0;
			live_start(SEARCH_IDAM);
			cont = WAIT;
			break;

		case READ_ID1:
			// If an error occured (no IDAM found), terminate the command
			if ((m_register_r[CHIP_STATUS] & CS_SYNCERR) != 0)
			{
				if (TRACE_FAIL) logerror("%s: READ_ID failed to find any IDAM\n", tag());
				cont = ERROR;
				break;
			}

			if (TRACE_READID && TRACE_SUBSTATES)
			{
				logerror("%s: substate READ_ID1\n", tag());
				logerror("%s: DESIRED_CYL = %d; CURRENT_CYL = %d\n", tag(), desired_cylinder(), current_cylinder());
			}

			// The CRC has been updated automatically with each read_one_bit during the live_run.
			// We just need to check whether it ended in 0000
			if (m_live_state.crc != 0)
			{
				if (TRACE_FAIL) logerror("%s: CRC error in sector header\n", tag());
				set_bits(m_register_r[CHIP_STATUS], CS_CRCERR, true);
				cont = ERROR;
				break;
			}

			// Calculate the direction and number of step pulses
			// positive -> towards inner cylinders
			// negative -> towards outer cylinders
			// zero -> we're already there
			m_track_delta = desired_cylinder() - current_cylinder();
			m_substate = READ_ID_STEPON;
			break;

		case READ_ID_STEPON:
			// Any more steps left?
			if (m_track_delta == 0)
			{
				if (wait_seek_complete)
				{
					// We have to wait for SEEK COMPLETE
					wait_line(SEEKCOMP_LINE, ASSERT_LINE, READ_ID_SEEK_COMPLETE, false);
					cont = WAIT;
				}
				else
				{
					// We do not wait for SEEK COMPLETE
					m_substate = VERIFY;
					cont = NEXT;
				}
				break;
			}

			if (TRACE_READID && TRACE_SUBSTATES) logerror("%s: substate STEP_ON\n", tag());
			// STEPDIR = 0 -> towards TRK00
			set_bits(m_output2, OUT2_STEPDIR, (m_track_delta>0));
			set_bits(m_output2, OUT2_STEPPULSE, true);
			wait_time(m_timer, pulse_width(), READ_ID_STEPOFF);
			cont = WAIT;
			break;

		case READ_ID_STEPOFF:
			if (TRACE_READID && TRACE_SUBSTATES) logerror("%s: substate STEP_OFF\n", tag());
			set_bits(m_output2, OUT2_STEPPULSE, false);
			m_track_delta += (m_track_delta<0)? 1 : -1;
			// Return to STEP_ON, check whether there are more steps
			wait_time(m_timer, step_time(), READ_ID_STEPON);
			cont = WAIT;
			break;

		case READ_ID_SEEK_COMPLETE:
			m_substate = VERIFY;
			cont = NEXT;
			break;

		default:
			logerror("%s: unknown substate %d in read_id\n", tag(), m_substate);
			cont = ERROR;
		}
	}

	//  When an error occurs, the COMMAND_TERMINATION bits are set to 01
	if (cont == ERROR)
	{
		live_abort();
		set_command_done(TC_RDIDERR);
	}
}

/*
    VERIFY ([1] p. 10)
    The controller
    - continues to read the next ID field until the current values match the
    contents of the DESIRED_HEAD/CYLINDER/SECTOR registers
    - checks the CRC
*/
void hdc9234_device::verify(int& cont, bool verify_all)
{
	cont = CONTINUE;

	while (cont==CONTINUE)
	{
		switch (m_substate)
		{
		case VERIFY:
			// After seeking (or immediately when implied seek has been disabled),
			// find the desired sector.

			if (TRACE_VERIFY && TRACE_SUBSTATES) logerror("%s: substate VERIFY\n", tag());

			// If an error occured (no IDAM found), terminate the command
			// (This test is only relevant when we did not have a seek phase before)
			if ((m_register_r[CHIP_STATUS] & CS_SYNCERR) != 0)
			{
				if (TRACE_FAIL) logerror("%s: VERIFY failed to find any IDAM\n", tag());
				cont = ERROR;
				break;
			}

			// Count from 0 again
			m_live_state.bit_count_total = 0;
			m_substate = VERIFY1;
			break;

		case VERIFY1:
			// Check whether we are already there
			if (desired_cylinder() == current_cylinder()
				&& desired_head() == current_head()
				&& desired_sector() == current_sector())
			{
				if (TRACE_VERIFY) logerror("%s: Found the desired sector CHS=(%d,%d,%d)\n", tag(),
					desired_cylinder(),
					desired_head(),
					desired_sector());
				m_substate = DATA_TRANSFER;
				cont = NEXT;
			}
			else
			{
				if (TRACE_VERIFY && TRACE_DETAIL) logerror("%s: Current CHS=(%d,%d,%d), desired CHS=(%d,%d,%d).\n", tag(),
					current_cylinder(),
					current_head(),
					current_sector(),
					desired_cylinder(),
					desired_head(),
					desired_sector());
				m_substate = VERIFY2;
			}
			break;

		case VERIFY2:
			// Search the next ID
			m_substate = VERIFY3;
			live_start(SEARCH_IDAM);
			cont = WAIT;
			break;

		case VERIFY3:
			if ((m_register_r[CHIP_STATUS] & CS_SYNCERR) != 0)
			{
				if (TRACE_FAIL) logerror("%s: VERIFY failed to find sector CHS=(%d,%d,%d)\n", tag(), desired_cylinder(), desired_head(), desired_sector());
				// live_run has set the sync error; clear it
				set_bits(m_register_r[CHIP_STATUS], CS_SYNCERR, false);
				// and set the compare error bit instead
				set_bits(m_register_r[CHIP_STATUS], CS_COMPERR, true);
				cont = ERROR;
				break;
			}

			// Continue with the loop
			if (verify_all)
			{
				// this is for the logical sector reading/writing
				m_substate = VERIFY1;
			}
			else
			{
				// this is for the physical sector reading/writing
				// do not verify the next ID field
				m_substate = DATA_TRANSFER;
				m_wait_for_index = true;
				cont = NEXT;
			}
			break;

		default:
			logerror("%s: unknown substate %d in verify\n", tag(), m_substate);
			cont = ERROR;
		}
	}

	// When an error occurs, the COMMAND_TERMINATION bits are set to 10
	if (cont == ERROR)
	{
		live_abort();
		set_command_done(TC_VRFYERR);
	}
}

/*
    DATA TRANSFER ([1], p. 10)
    only during READ/WRITE PHYSICAL/LOGICAL
    The controller
    - scans for the next DAM
    - initiates a DMA request and waits for ACK from the system processor
    - transfers the contents of the current sector into memory via DMA (read) or
      via DMA to the sector (write)
*/
void hdc9234_device::data_transfer(int& cont)
{
	cont = CONTINUE;

	while (cont==CONTINUE)
	{
		switch (m_substate)
		{
		case DATA_TRANSFER:
			if (TRACE_TRANSFER && TRACE_SUBSTATES) logerror("%s: substate DATA_TRANSFER\n", tag());

			// Count from 0 again
			m_live_state.bit_count_total = 0;

			if (m_transfer_enabled) dma_address_out(m_register_w[DMA23_16], m_register_w[DMA15_8], m_register_w[DMA7_0]);

			if (TRACE_TRANSFER && TRACE_DETAIL) logerror("%s: %s sector CHS=(%d,%d,%d)\n", tag(), m_write? "write" : "read",
					desired_cylinder(),
					desired_head(),
					desired_sector());

			if (m_write)
			{
				m_substate = DATA_TRANSFER_WRITE;
				live_start(WRITE_DAM_AND_SECTOR);
			}
			else
			{
				m_substate = DATA_TRANSFER_READ;
				live_start(SEARCH_DAM);
			}

			cont = WAIT;
			break;

		case DATA_TRANSFER_READ:
			// OK, sector has been read.
			// Check CRC
			if (m_live_state.crc != 0)
			{
				// Set Retry Required flag
				set_bits(m_register_r[CHIP_STATUS], CS_RETREQ, true);

				// Decrement the retry register (one's complemented value; 0000 = 15)
				int retry = 15-((m_register_w[RETRY_COUNT] >> 4)&0x0f);

				if (TRACE_FAIL) logerror("%s: DATA TRANSFER got CRC error in sector data, retries = %d\n", tag(), retry);
				m_register_w[RETRY_COUNT] = (m_register_w[RETRY_COUNT] & 0x0f) | ((15-(retry-1))<<4);

				if (retry == 0)
				{
					if (TRACE_FAIL) logerror("%s: CRC error; no retries left\n", tag());
					set_bits(m_register_r[CHIP_STATUS], CS_CRCERR, true);
					cont = ERROR;
				}
				else
				{
					// Go back to VERIFY and try again
					// Note that the specs recommend to set the retry to 0 (1111)
					// for physical reading; failing to do so will result in
					// unpredictable behavior.
					// We'll rely on the properly written software as well.
					m_live_state.bit_count_total = 0;
					m_substate = VERIFY2;
					cont = NEXT;
				}
			}
			else
			{
				if (TRACE_TRANSFER) logerror("%s: Sector successfully read\n", tag());

				// Update the DMA registers for multi-sector operations
				if (m_multi_sector)
				{
					int dma_address = (m_register_w[DMA23_16] & 0xff) << 16 |
						(m_register_w[DMA15_8] & 0xff) << 8 |
						(m_register_w[DMA7_0] & 0xff);

					dma_address = (dma_address + calc_sector_size()) & 0xffffff;

					m_register_w[DMA23_16] = m_register_r[DMA23_16] = (dma_address & 0xff0000) >> 16;
					m_register_w[DMA15_8] = m_register_r[DMA15_8] = (dma_address & 0x00ff00) >> 16;
					m_register_w[DMA7_0] = m_register_r[DMA7_0] = (dma_address & 0x0000ff) >> 16;
				}

				// Decrement the count
				m_register_w[SECTOR_COUNT] = (m_register_w[SECTOR_COUNT]-1) & 0xff;

				// Do we have more sectors to read?
				// Surprisingly, the manual does not say what happens when
				// the sector count is zero for the first access.
				// It explicitly states that the check is done after the access.
				// If we take it (and especially the state charts) seriously, zero means 256.
				// m_stop_after_index is important for physical reading
				if (m_register_w[SECTOR_COUNT] != 0 && !m_stop_after_index)
				{
					// Increment the sector number
					// What happens when we exceed the highest sector number
					// in the track? We have to assume that this is possible
					// and that in this case the VERIFY routine fails.
					m_register_w[DESIRED_SECTOR] = (desired_sector() + 1) & 0xff;
					m_substate = VERIFY1;
					cont = NEXT;
					m_live_state.bit_count_total = 0;
				}
				else
					cont = SUCCESS;
			}
			break;

		case DATA_TRANSFER_WRITE:
			if (TRACE_TRANSFER) logerror("%s: Sector successfully written\n", tag());

			// Update the DMA registers for multi-sector operations
			if (m_multi_sector)
			{
				int dma_address = (m_register_w[DMA23_16] & 0xff) << 16 |
				(m_register_w[DMA15_8] & 0xff) << 8 |
				(m_register_w[DMA7_0] & 0xff);

				dma_address = (dma_address + calc_sector_size()) & 0xffffff;

				m_register_w[DMA23_16] = m_register_r[DMA23_16] = (dma_address & 0xff0000) >> 16;
				m_register_w[DMA15_8] = m_register_r[DMA15_8] = (dma_address & 0x00ff00) >> 16;
				m_register_w[DMA7_0] = m_register_r[DMA7_0] = (dma_address & 0x0000ff) >> 16;
			}

			// Decrement the count
			m_register_w[SECTOR_COUNT] = (m_register_w[SECTOR_COUNT]-1) & 0xff;
			if (m_register_w[SECTOR_COUNT] != 0 && !m_stop_after_index)
			{
				m_register_w[DESIRED_SECTOR] = (desired_sector() + 1) & 0xff;
				m_substate = VERIFY1;
				cont = NEXT;
				m_live_state.bit_count_total = 0;
			}
			else
				cont = SUCCESS;

			break;

		default:
			logerror("%s: unknown substate %d in data_transfer\n", tag(), m_substate);
			cont = ERROR;
		}
	}

	if (cont==SUCCESS) set_command_done(TC_SUCCESS);

	//  When an error occurs, the COMMAND_TERMINATION bits are set to 11
	if (cont==ERROR)
	{
		live_abort();
		set_command_done(TC_DATAERR);
	}
}

// ===========================================================================
//     Commands
// ===========================================================================

/*
    RESET
    Reset the controller. This has the same effect as asserting the RST* input line.

    Command word

       7     6     5     4     3     2     1     0
    +-----+-----+-----+-----+-----+-----+-----+-----+
    |  0  |  0  |  0  |  0  |  0  |  0  |  0  |  0  |
    +-----+-----+-----+-----+-----+-----+-----+-----+

*/
void hdc9234_device::reset_controller()
{
	logerror("%s: RESET command\n", tag());
	device_reset();
}

/*
    DESELECT DRIVE
    Deselect all drives.

    Command word

       7     6     5     4     3     2     1     0
    +-----+-----+-----+-----+-----+-----+-----+-----+
    |  0  |  0  |  0  |  0  |  0  |  0  |  0  |  1  |
    +-----+-----+-----+-----+-----+-----+-----+-----+
*/
void hdc9234_device::drive_deselect()
{
	if (TRACE_SELECT) logerror("%s: DESELECT command\n", tag());
	m_selected_drive_number = NODRIVE;
	set_command_done(TC_SUCCESS);
}

/*
    RESTORE DRIVE
    Moves the heads to cylinder 0. If skcom is set, the command terminates
    after the SEEK COMPLETE line is set.

    Command word

       7     6     5     4     3     2     1     0
    +-----+-----+-----+-----+-----+-----+-----+-----+
    |  0  |  0  |  0  |  0  |  0  |  0  |  1  |skcom|
    +-----+-----+-----+-----+-----+-----+-----+-----+
*/
void hdc9234_device::restore_drive()
{
	int cont = CONTINUE;
	bool buffered_step = current_command() & 1;

	// The substate is set to UNDEF when the command is started;
	// when we reenter the command processing after a pause, the substate is set to some other value
	// In wd_fdc this is solved using two methods <command>_start and <command>_continue

	if (m_substate == UNDEF)
	{
		if (TRACE_RESTORE) logerror("%s: RESTORE command %02x\n", tag(), current_command());
		m_seek_count = 0;
		m_substate = RESTORE_CHECK;
	}

	while (cont==CONTINUE)
	{
		switch (m_substate)
		{
		case RESTORE_CHECK:
			// Track 0 has not been reached yet
			if ((m_register_r[DRIVE_STATUS] & HDC_DS_READY)==0)
			{
				if (TRACE_RESTORE) logerror("%s: restore command: drive not ready\n", tag());
				// Does not look like a success, but this takes into account
				// that if a drive is not connected we do not want an error message
				cont = SUCCESS;
				break;
			}

			// Are we done?
			if (m_seek_count>=4096 || on_track00())
			{
				if (buffered_step)
				{
					// When we have buffered steps, the seek limit will be reached
					// before TRK00 is asserted. In that case we have to wait for
					// SEEK_COMPLETE. We also wait as soon as TRK00 is asserted.
					wait_line(SEEKCOMP_LINE, ASSERT_LINE, SEEK_COMPLETE, false);
					cont = WAIT;
				}
				else
				{
					// No buffered seek. If the seek limit has been reached
					// and TRK00 is not true, we failed. This will be decided below.
					m_substate = SEEK_COMPLETE;
				}
			}
			else m_substate = STEP_ON;
			break;

		case STEP_ON:
			if (TRACE_RESTORE && TRACE_SUBSTATES) logerror("%s: substate STEP_ON\n", tag());

			// Increase step count
			m_seek_count++;

			// STEPDIR = 0 -> towards TRK00
			set_bits(m_output2, OUT2_STEPDIR, false);

			// Raising edge (note that all signals must be inverted before leading them to the drive)
			set_bits(m_output2, OUT2_STEPPULSE, true);
			wait_time(m_timer, pulse_width(), STEP_OFF);
			cont = WAIT;
			break;

		case STEP_OFF:
			if (TRACE_RESTORE && TRACE_SUBSTATES) logerror("%s: substate STEP_OFF\n", tag());
			set_bits(m_output2, OUT2_STEPPULSE, false);
			wait_time(m_timer, step_time(), RESTORE_CHECK);
			cont = WAIT;
			break;

		case SEEK_COMPLETE:
			// If TRK00 is not set, the drive failed to reach it.
			if (!on_track00())
			{
				if (TRACE_FAIL) logerror("%s: restore command: failed to reach track 00\n", tag());
				set_command_done(TC_VRFYERR);
				cont = ERROR;
			}
			else
				cont = SUCCESS;
			break;
		}
	}
	if (cont==SUCCESS) set_command_done(TC_SUCCESS);
}

/*
    STEP IN / OUT 1 CYLINDER
    Move the heads 1 step towards the center (in) or towards the outermost
    track (out).

    Command word

         7     6     5     4     3     2     1     0
      +-----+-----+-----+-----+-----+-----+-----+-----+
      |  0  |  0  |  0  |  0  |  0  |  1  | out |skcom|
      +-----+-----+-----+-----+-----+-----+-----+-----+

*/
void hdc9234_device::step_drive()
{
	int cont = CONTINUE;

	if (m_substate == UNDEF)
	{
		if (TRACE_STEP) logerror("%s: STEP IN/OUT command %02x\n", tag(), current_command());
		m_substate = STEP_ON;
	}

	while (cont==CONTINUE)
	{
		switch (m_substate)
		{
		case STEP_ON:
			if (TRACE_STEP && TRACE_SUBSTATES) logerror("%s: substate STEP_ON\n", tag());

			// STEPDIR = 0 -> towards TRK00
			set_bits(m_output2, OUT2_STEPDIR, (current_command() & 0x02)==0);

			// Raising edge (note that all signals must be inverted before leading them to the drive)
			set_bits(m_output2, OUT2_STEPPULSE, true);
			wait_time(m_timer, pulse_width(), STEP_OFF);
			cont = WAIT;
			break;

		case STEP_OFF:
			if (TRACE_STEP && TRACE_SUBSTATES) logerror("%s: substate STEP_OFF\n", tag());
			set_bits(m_output2, OUT2_STEPPULSE, false);
			wait_time(m_timer, step_time(), ((current_command() & 0x01)!=0)? WAIT_SEEK_COMPLETE : DONE);
			cont = WAIT;
			break;

		case WAIT_SEEK_COMPLETE:
			wait_line(SEEKCOMP_LINE, ASSERT_LINE, DONE, false);
			cont = WAIT;
			break;

		case DONE:
			cont = SUCCESS;
			break;
		}
	}
	if (cont==SUCCESS) set_command_done(TC_SUCCESS);
}

/*
    TAPE BACKUP
    Not implemented
*/
void hdc9234_device::tape_backup()
{
	logerror("%s: TAPE BACKUP command %02x not implemented\n", tag(), current_command());
	set_command_done(TC_SUCCESS);
}

/*
    POLL DRIVES
    Repeat
     - i = i+1 % 4
     - select drive if its bit is set in the command word
    until seek_complete is true.

    Command word

            7     6     5     4     3     2     1     0
         +-----+-----+-----+-----+-----+-----+-----+-----+
         |  0  |  0  |  0  |  1  | Drv3| Drv2| Drv1| Drv0|
         +-----+-----+-----+-----+-----+-----+-----+-----+

    This command only sets the select lines but does not process parameters
    like head load times or drive types.
*/
void hdc9234_device::poll_drives()
{
	UINT8 drivebit = 0;
	if (m_substate == UNDEF)
	{
		logerror("%s: POLL DRIVES command %02x\n", tag(), current_command());
		m_substate = POLL1;
		m_selected_drive_number = 0;
		// If there is no selection, do not enter the loop
		if ((current_command() & 0x0f)==0) m_substate = DONE;
	}

	int cont = CONTINUE;

	while (cont==CONTINUE)
	{
		switch (m_substate)
		{
		case POLL1:
			drivebit = (1 << m_selected_drive_number) & 0x0f;

			if ((current_command() & drivebit) != 0)
			{
				// Test this drive
				m_register_r[CHIP_STATUS] = (m_register_r[CHIP_STATUS] & 0xfc) | m_selected_drive_number;

				m_output1 = (drivebit << 4) | (m_register_w[RETRY_COUNT]&0x0f);
				if (TRACE_AUXBUS) logerror("%s: Setting OUTPUT1 to %02x\n", tag(), m_output1);
				wait_time(m_timer, 1, POLL2);   // Wait for 1 usec
				cont = WAIT;
			}
			else
				m_substate = POLL3;

			break;

		case POLL2:
			if ((m_register_r[DRIVE_STATUS] & HDC_DS_SKCOM)!=0)
			{
				// Seek complete has been set
				m_substate = DONE;
				// Selected drive is still found in the chip status register
			}
			else m_substate = POLL3;
			break;

		case POLL3:
			m_selected_drive_number = (m_selected_drive_number + 1) & 0x03;
			m_substate = POLL1;
			break;

		case DONE:
			cont = SUCCESS;
			break;
		}
	}

	if (cont==SUCCESS) set_command_done(TC_SUCCESS);
}

/*
    DRIVE SELECT
    Selects a drive. With this command, parameters for the drive are also
    defined, like the type of drive (Floppy 8" or 5", AT Hard disk, or generic
    Hard disk), the drive number, and the head load delay.

    On the next OUTPUT1 time, the number of the drive (one of four lines)
    is set on the higher four bits of the auxiliary bus. Also, the lower
    4 bits of the RETRY COUNT register are put on the lower 4 bits of the bus
    (user-programmable output, [1] p. 5).

    The HFDC controller board uses the user-programmable output to
    select one of four floppy disk drives with Drive set to 00.
    Drive codes 01, 10, and 11 remain for three hard disk drives.

     Command word

            7     6     5     4     3     2     1     0
         +-----+-----+-----+-----+-----+-----+-----+-----+
         |  0  |  0  |  1  |Delay|    Type   |   Drive   |
         +-----+-----+-----+-----+-----+-----+-----+-----+
*/

void hdc9234_device::drive_select()
{
	int cont = CONTINUE;
	int head_load_delay = 0;

	if (m_substate == UNDEF)
	{
		int driveparm = current_command() & 0x1f;
		bool head_load_delay_enable = (driveparm & 0x10)!=0;

		// The drive type is used to configure DMA burst mode ([1], p.12)
		// and to select the timing parameters
		m_selected_drive_type = (driveparm>>2) & 0x03;
		m_selected_drive_number = driveparm & 0x03;

		// Calculate the head load delays
		head_load_delay = head_load_delay_enable? m_register_w[DATA] * head_load_timer_increment[m_selected_drive_type] : 0;
		if (fm_mode()) head_load_delay <<= 1;

		if (TRACE_SELECT) logerror("%s: DRIVE SELECT command (%02x): head load delay=%d, type=%d, drive=%d, pout=%02x, step_rate=%d\n", tag(), current_command(), head_load_delay, m_selected_drive_type, driveparm&3, m_register_w[RETRY_COUNT]&0x0f, pulse_width() + step_time());

		// Copy the DMA registers to registers CURRENT_HEAD, CURRENT_CYLINDER,
		// and CURRENT_IDENT. This is required during formatting ([1], p. 14)
		// as the format command reuses the registers for formatting parameters.
		m_register_r[CURRENT_HEAD] = m_register_r[DMA7_0];
		m_register_r[CURRENT_CYLINDER] = m_register_r[DMA15_8];
		m_register_r[CURRENT_IDENT] = m_register_r[DMA23_16];

		// Copy the selected drive number to the chip status register
		m_register_r[CHIP_STATUS] = (m_register_r[CHIP_STATUS] & 0xfc) | m_selected_drive_number;

		m_output1 = (m_selected_drive_number != NODRIVE)? (0x10 << m_selected_drive_number) : 0;
		m_output1 |= (m_register_w[RETRY_COUNT]&0x0f);
		if (TRACE_AUXBUS) logerror("%s: Setting OUTPUT1 to %02x\n", tag(), m_output1);
		m_substate = (head_load_delay>0)? HEAD_DELAY : DONE;
	}

	// As for the head delay, the specs are not clear when it is applied.
	// There is no input line indicating whether the head is already loaded
	// (see WD17xx: HLT). Let's assume for now that the head is loaded with
	// this drive select operation, and that we have the delay here.
	switch (m_substate)
	{
	case HEAD_DELAY:
		wait_time(m_timer, head_load_delay, DONE);
		cont = WAIT;
		break;
	case DONE:
		cont = SUCCESS;
		break;
	}

	if (cont==SUCCESS) set_command_done(TC_SUCCESS);
}

/*
    SET REGISTER POINTER

    Sets the pointer to the read and write registers. On read or write accesses,
    the pointer is increased until it reaches the DATA register.
*/
void hdc9234_device::set_register_pointer()
{
	m_register_pointer = current_command() & 0xf;
	if (TRACE_SETPTR) logerror("%s: SET REGISTER POINTER command; start reg=%d\n", tag(), m_register_pointer);
	// The specification does not say anything about the effect of setting an
	// invalid value (only "care should be taken")
	if (m_register_pointer > 10)
	{
		logerror("%s: set register pointer: Invalid register number: %d. Setting to 10.\n", tag(), m_register_pointer);
		m_register_pointer = 10;
	}
	set_command_done(TC_SUCCESS);
}

/*
    SEEK / READ ID
    This command is used to move the head to the desired cylinder.
    Depending on the Verify setting, the target sector is sought on the
    track, else the command terminates after the step pulses have been issued.

    Command word

            7     6     5     4     3     2     1     0
         +-----+-----+-----+-----+-----+-----+-----+-----+
         |  0  |  1  |  0  |  1  |  0  | Step| Seek| Verf|
         +-----+-----+-----+-----+-----+-----+-----+-----+

    All combinations of flags are legal ([1], p.12).
*/
void hdc9234_device::seek_read_id()
{
	if (m_substate == UNDEF)
	{
		// Command init
		if (TRACE_READ) logerror("%s: SEEK / READ ID command %02x, CHS=(%d,%d,%d)\n", tag(), current_command(), desired_cylinder(), desired_head(), desired_sector());
		m_substate = READ_ID;
	}

	int cont = NEXT;
	bool step_enable = (current_command() & 0x04)==1;
	bool wait_seek_comp = ((current_command() & 0x02)==1) || rapid_steps();
	bool do_verify = (current_command() & 0x01)==1;

	while (cont == NEXT)
	{
		switch (m_substate & 0xf0)
		{
		case READ_ID:
			read_id(cont, step_enable, wait_seek_comp);
			break;
		case VERIFY:
			if (!do_verify)
				cont = SUCCESS;
			else
				verify(cont, true);
			break;
		case DATA_TRANSFER:
			// No data transfer here. Just exit.
			cont = SUCCESS;
			break;
		default:
			logerror("%s: unknown substate %d in seek_read_id\n", tag(), m_substate);
			cont = ERROR;
		}
	}

	if (cont==SUCCESS) set_command_done(TC_SUCCESS);
}

/*
    READ SECTORS PHYSICAL / LOGICAL
    Read the desired sectors, maximum count being specified in SECTOR_COUNT

    Physical:
    For multiple sectors, read the sectors in the order as they appear on the track.
    The command terminates with the next index pulse or when all sectors have been read before.
    Implied seek (locate the correct track) is always true (opcodes 5a and 5b
    are used for READ TRACK).

    Logical:
    For multiple sectors, read the sectors in ascending order of their sector field (sector n, n+1, n+2 ...).

    Command word

       7     6     5     4     3      2       1       0
    +-----+-----+-----+-----+-----+--------+------+------+
    |  0  |  1  |  0  |  1  |  1  | Logical|NoSeek| Trans|
    +-----+-----+-----+-----+-----+--------+------+------+

*/
void hdc9234_device::read_sectors()
{
	bool logical = (current_command() & 0x04)!=0;

	if (m_substate == UNDEF)
	{
		// Command init
		if (TRACE_READ) logerror("%s: READ SECTORS %s command %02x, CHS=(%d,%d,%d)\n", tag(), logical? "LOGICAL": "PHYSICAL", current_command(), desired_cylinder(), desired_head(), desired_sector());
		m_retry_save = m_register_w[RETRY_COUNT];
		m_multi_sector = (m_register_w[SECTOR_COUNT] != 1);
		m_write = false;
		m_substate = READ_ID;
	}

	int cont = NEXT;
	bool implied_seek = (current_command() & 0x02)==0;
	m_transfer_enabled = (current_command() & 0x01)!=0;

	while (cont == NEXT)
	{
		switch (m_substate & 0xf0)
		{
		case READ_ID:
			read_id(cont, implied_seek, rapid_steps());
			break;
		case VERIFY:
			verify(cont, logical);  // for physical, only verify the first sector
			break;
		case DATA_TRANSFER:
			data_transfer(cont);
			break;
		default:
			logerror("%s: unknown substate %d in read_sectors\n", tag(), m_substate);
			cont = ERROR;
		}
	}
}

/*
    READ TRACK
    Read all ID and data fields as they appear on the track. Command 5A only
    transmits the ID fields via DMA, which 5B transmits all ID and data fields.
    Note that the specifications do not mention any gaps to be transmitted as
    well.

    Command word

       7     6     5     4     3     2     1      0
    +-----+-----+-----+-----+-----+-----+-----+------+
    |  0  |  1  |  0  |  1  |  1  |  0  |  1  |  All |
    +-----+-----+-----+-----+-----+-----+-----+------+

*/
void hdc9234_device::read_track()
{
	if (m_substate == UNDEF)
	{
		if (TRACE_READTRACK) logerror("%s: READ TRACK command %02x, head = %d\n", tag(), current_command(), desired_head());
		dma_address_out(m_register_w[DMA23_16], m_register_w[DMA15_8], m_register_w[DMA7_0]);
		m_transfer_enabled = (current_command() & 1)!=0;
	}

	int cont = NEXT;
	while (cont == NEXT)
	{
		switch (m_substate)
		{
		case WAITINDEX0:
			// Do we happen to have an index hole right now?
			if ((m_register_r[DRIVE_STATUS] & HDC_DS_INDEX)==0)
			{
				m_substate = WAITINDEX1;
			}
			else
			{
				// Waiting for the index line going down
				wait_line(INDEX_LINE, ASSERT_LINE, WAITINDEX1, false);
				cont = WAIT;
			}
			break;
		case WAITINDEX1:
			// Waiting for the next rising edge
			wait_line(INDEX_LINE, ASSERT_LINE, TRACKSTART, false);
			cont = WAIT;
			break;
		case TRACKSTART:
			live_start(READ_TRACK);
			wait_line(INDEX_LINE, ASSERT_LINE, TRACKDONE, true);
			cont = WAIT;
			break;
		case TRACKDONE:
			if (TRACE_READTRACK && TRACE_SUBSTATES) logerror("%s: Track reading done\n", tag());
			cont = SUCCESS;
			break;
		}
	}

	if (cont==SUCCESS) set_command_done(TC_SUCCESS);
}

/*
    FORMAT TRACK
    Writes a track on the selected drive at the current cylinder. The write
    process starts with the falling edge of the index hole and stops with
    the rising edge of the next index hole.

    The formatting is done exclusively by the controller; user programs may
    set parameters for gaps and interleaving.

    1. Before starting the command, the user program must have set up a
    sector sequence table in the controller RAM (located on the PCB):
    (ident, cylinder, head, sector1, size)  (5 bytes)
    (ident, cylinder, head, sector2, size)
    (ident, cylinder, head, sector3, size)
    ...
    ident is not required for floppy FM operation. size is not required
    for IBM AT-compatible hard disks.

    2. The DMA registers must point to the beginning of the table

    3. DRIVE_SELECT must be executed (which moves DMA regs to CUR_HEAD ...)

    4. DESIRED_HEAD register must be loaded

    5. The following setup must be done:

    GAP 0 size            DMA7_0              (2s comp)
    GAP 1 size            DMA15_8             (2s comp)
    GAP 2 size            DMA23_16            (2s comp)
    GAP 3 size            DESIRED_SECTOR      (2s comp)
    Sync size             DESIRED_CYLINDER    (1s comp)
    Sector count          SECTOR_COUNT        (1s comp)
    Sector size multiple  RETRY_COUNT         (1s comp)

    GAP4 is variable and fills the rest of the track until the next
    index hole.

    6. The step rate and density must be loaded into the MODE register

    7. The drive must be stepped to the desired track.

    8. Now this command may be started.

    All data bytes of a sector are filled with 0xe5. The gaps will be filled
    with 0x4e (MFM) or 0xff (FM).

    To format another track, the sector id table must be updated, and steps
    7 and 8 must be repeated. If the DESIRED_HEAD register must be updated,
    the complete setup process must be done.

    Command word

       7     6     5     4      3     2     1      0
    +-----+-----+-----+------+-----+-----+-----+------+
    |  0  |  1  |  1  |Normal|RedWC|  Precompensation |
    +-----+-----+-----+------+-----+-----+-----+------+
*/
void hdc9234_device::format_track()
{
	if (m_substate == UNDEF)
	{
		if (TRACE_FORMAT) logerror("%s: FORMAT TRACK command %02x, head = %d\n", tag(), current_command(), desired_head());
		m_substate = WAITINDEX0;
		m_deleted = (current_command() & 0x10)==0;
		m_reduced_write_current = (current_command() & 0x08)!=0;
		m_precompensation = (current_command() & 0x07);
		m_write = true;

		m_gap0_size = -m_register_w[DMA7_0] & 0xff;
		m_gap1_size = -m_register_w[DMA15_8] & 0xff;
		m_gap2_size = -m_register_w[DMA23_16] & 0xff;
		m_gap3_size = -m_register_w[DESIRED_SECTOR] & 0xff;
		m_sync_size = ~m_register_w[DESIRED_CYLINDER] & 0xff;
		m_sector_count = ~m_register_w[SECTOR_COUNT] & 0xff;
		m_sector_size = (~m_register_w[RETRY_COUNT] & 0xff) * 128;

		if (TRACE_FORMAT && TRACE_DETAIL)
		{
			logerror("%s: GAP0 length  = %d\n", tag(), m_gap0_size);
			logerror("%s: GAP1 length  = %d\n", tag(), m_gap1_size);
			logerror("%s: GAP2 length  = %d\n", tag(), m_gap2_size);
			logerror("%s: GAP3 length  = %d\n", tag(), m_gap3_size);
			logerror("%s: Sync size    = %d\n", tag(), m_sync_size);
			logerror("%s: Sector count = %d\n", tag(), m_sector_count);
			logerror("%s: Sector size  = %d\n", tag(), m_sector_size);
		}

		dma_address_out(m_register_r[CURRENT_IDENT], m_register_r[CURRENT_CYLINDER], m_register_r[CURRENT_HEAD]);
	}

	int cont = NEXT;
	while (cont == NEXT)
	{
		switch (m_substate)
		{
		case WAITINDEX0:
			// Do we happen to have an index hole right now?
			if ((m_register_r[DRIVE_STATUS] & HDC_DS_INDEX)==0)
			{
				m_substate = WAITINDEX1;
				cont = NEXT;
			}
			else
			{
				// Waiting for the index line going down
				wait_line(INDEX_LINE, ASSERT_LINE, WAITINDEX1, false);
				cont = WAIT;
			}
			break;
		case WAITINDEX1:
			// Waiting for the next rising edge
			wait_line(INDEX_LINE, ASSERT_LINE, TRACKSTART, false);
			cont = WAIT;
			break;
		case TRACKSTART:
			live_start(FORMAT_TRACK);
			wait_line(INDEX_LINE, ASSERT_LINE, TRACKDONE, true);
			cont = WAIT;
			break;
		case TRACKDONE:
			if (FORMAT_TRACK && TRACE_SUBSTATES) logerror("%s: Track writing done\n", tag());
			cont = SUCCESS;
			break;
		}
	}

	if (cont==SUCCESS) set_command_done(TC_SUCCESS);
}

/*
    WRITE SECTORS PHYSICAL / LOGICAL

    Write the desired sectors, maximum count being specified in SECTOR_COUNT

    Physical:
    For multiple sectors, write sector contents into the data fields of
    the sectors as they are arranged on the track.
    The command terminates with the next index pulse or when all sectors have been written before.

    Logical:
    For multiple sectors, write the sectors in ascending order of their
    sector field (sector n, n+1, n+2 ...).

    Command word

       7      6      5      4      3     2     1      0
    +-----+------+-------+------+-----+-----+-----+------+
    |  1  |NoSeek|Logical|Normal|RedWC|  Precompensation |
    +-----+------+-------+------+-----+-----+-----+------+

*/
void hdc9234_device::write_sectors()
{
	bool logical = (current_command() & 0x20)!=0;

	if (m_substate == UNDEF)
	{
		if (TRACE_WRITE) logerror("%s: WRITE SECTORS %s command %02x, CHS=(%d,%d,%d)\n", tag(), logical? "LOGICAL" : "PHYSICAL", current_command(), desired_cylinder(), desired_head(), desired_sector());
		m_multi_sector = (m_register_w[SECTOR_COUNT] != 1);
		m_substate = READ_ID;

		m_deleted = (current_command() & 0x10)==0;
		m_reduced_write_current = (current_command() & 0x08)!=0;
		m_precompensation = (current_command() & 0x07);
		// Important for DATA TRANSFER
		m_transfer_enabled = true;
		m_sync_size = fm_mode()? 6 : 12;
		m_gap2_size = fm_mode()? 11 : 22;
		m_write = false; // until we're writing
	}

	int cont = NEXT;
	bool implied_seek = (current_command() & 0x40)==0;

	while (cont == NEXT)
	{
		// We're dispatching by substate value range
		switch (m_substate & 0xf0)
		{
		case READ_ID:
			read_id(cont, implied_seek, rapid_steps());
			break;
		case VERIFY:
			verify(cont, logical);
			break;
		case DATA_TRANSFER:
			m_write = true;
			data_transfer(cont);
			break;
		default:
			logerror("%s: unknown substate %d in write_sectors\n", tag(), m_substate);
			cont = ERROR;
		}
	}
}

/*
===========================================================================

    Live state machine

    We follow a very similar approach to track access like in wd_fdc. The live
    state machine attempts to find marks on the track, starting from the current
    position. When found, it waits for the machine to catch up. When an event
    happens in the meantime, the state machine is rolled back, and the actions
    are replayed until the position where the event occured.

    Lots of code is taken from wd_fdc, with some minor restructuring and renaming.
    Same ideas, though. More comments.

===========================================================================
*/

astring hdc9234_device::tts(const attotime &t)
{
	char buf[256];
	int nsec = t.attoseconds / ATTOSECONDS_PER_NANOSECOND;
	sprintf(buf, "%4d.%03d,%03d,%03d", int(t.seconds), nsec/1000000, (nsec/1000)%1000, nsec % 1000);
	return buf;
}

astring hdc9234_device::ttsn()
{
	return tts(machine().time());
}

/*
    The controller starts to read bits from the disk. This method takes an
    argument for the state machine called at the end.
*/
void hdc9234_device::live_start(int state)
{
	if (TRACE_LIVE) logerror("%s: [%s] Live start substate=%d\n", tag(), ttsn().cstr(), state);
	m_live_state.time = machine().time();
	m_live_state.state = state;
	m_live_state.next_state = -1;

	m_live_state.shift_reg = 0;
	m_live_state.crc = 0xffff;
	m_live_state.bit_counter = 0;
	m_live_state.byte_counter = 0;
	m_live_state.data_separator_phase = false;
	m_live_state.data_reg = 0;
	m_live_state.last_data_bit = false;

	pll_reset(m_live_state.time, m_write);
	m_checkpoint_state = m_live_state;

	// Save checkpoint
	m_checkpoint_pll = m_pll;

	live_run();
	m_last_live_state = UNDEF;
}

void hdc9234_device::live_run()
{
	live_run_until(attotime::never);
}

/*
    The main method of the live state machine. We stay in this method until
    the requested data are read.
    limit: if unlimited (attotime::never), run up to the end of the track and wait there
    otherwise, used to replay the read/write operation up to the point where the event happened
*/
void hdc9234_device::live_run_until(attotime limit)
{
	int slot = 0;

	if (m_live_state.state == IDLE || m_live_state.next_state != -1)
		return;

	if (TRACE_LIVE)
	{
		if (limit == attotime::never)
			logerror("%s: [%s] live_run, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).cstr(), m_live_state.state, fm_mode()? "FM":"MFM");
		else
			logerror("%s: [%s] live_run until %s, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).cstr(), tts(limit).cstr(), m_live_state.state, fm_mode()? "FM":"MFM");
	}

	if (limit == attotime::never)
	{
		// We did not specify an upper time bound, so we take the next index pulse
		if (m_floppy != NULL) limit = m_floppy->time_next_index();

		if (limit == attotime::never)
		{
			// We don't have an index pulse? (no disk?)
			// See wd_fdc: Force a sync from time to time in that case
			// so that the main cpu timeout isn't too painful.  Avoids
			// looping into infinity looking for data too.
			limit = machine().time() + attotime::from_msec(1);
			m_timer->adjust(attotime::from_msec(1));
		}
	}

	while (true)
	{
		switch (m_live_state.state)
		{
		case SEARCH_IDAM:

			// We're doing this complicated logerror check to avoid
			// repeated logging in the same state. This can be found for the
			// other live states as well. m_last_live_state is only used to
			// control this logging.

			if (TRACE_LIVE && m_last_live_state != SEARCH_IDAM)
			{
				logerror("%s: [%s] SEARCH_IDAM [limit %s]\n", tag(),tts(m_live_state.time).cstr(), tts(limit).cstr());
				m_last_live_state = m_live_state.state;
			}

			// This bit will be set when the IDAM cannot be found
			set_bits(m_register_r[CHIP_STATUS], CS_SYNCERR, false);

			if (read_one_bit(limit))
			{
				if (TRACE_LIVE) logerror("%s: [%s] SEARCH_IDAM limit reached\n", tag(), tts(m_live_state.time).cstr());
				return;
			}
			// logerror("%s: SEARCH_IDAM\n", tts(m_live_state.time).cstr());
			if (TRACE_SHIFT) logerror("%s: shift = %04x data=%02x c=%d\n", tts(m_live_state.time).cstr(), m_live_state.shift_reg,
				get_data_from_encoding(m_live_state.shift_reg), m_live_state.bit_counter);

			// [1] p. 9: The ID field sync mark must be found within 33,792 byte times
			if (m_live_state.bit_count_total > 33792*16)
			{
				wait_for_realtime(SEARCH_IDAM_FAILED);
				return;
			}

			if (!fm_mode())
			{
				// MFM case
				if (m_live_state.shift_reg == 0x4489)
				{
					if (TRACE_LIVE) logerror("%s: [%s] Found an A1 mark\n", tag(),tts(m_live_state.time).cstr());
					m_live_state.crc = 0x443b;
					m_live_state.data_separator_phase = false;
					m_live_state.bit_counter = 0;
					// Next task: find the next two A1 marks
					m_live_state.state = READ_TWO_MORE_A1_IDAM;
				}
			}
			else
			{
				// FM case
				if (m_live_state.shift_reg == 0xf57e)
				{
					if (TRACE_LIVE) logerror("%s: SEARCH_IDAM: IDAM found\n", tag());
					m_live_state.crc = 0xef21;
					m_live_state.data_separator_phase = false;
					m_live_state.bit_counter = 0;
					m_live_state.state = READ_ID_FIELDS_INTO_REGS;
				}
			}
			break;

		case SEARCH_IDAM_FAILED:
			set_bits(m_register_r[CHIP_STATUS], CS_SYNCERR, true);
			m_live_state.state = IDLE;
			return;

		case READ_TWO_MORE_A1_IDAM:     // This state only applies for MFM mode.

			if (TRACE_LIVE && m_last_live_state != READ_TWO_MORE_A1_IDAM)
			{
				logerror("%s: [%s] READ_TWO_MORE_A1\n", tag(),tts(m_live_state.time).cstr());
				m_last_live_state = m_live_state.state;
			}

			// Beyond time limit?
			if (read_one_bit(limit)) return;

			if (TRACE_SHIFT) logerror("%s: shift = %04x data=%02x c=%d\n", tts(m_live_state.time).cstr(), m_live_state.shift_reg,
					get_data_from_encoding(m_live_state.shift_reg), m_live_state.bit_counter);

			if (m_live_state.bit_count_total > 33792*16)
			{
				wait_for_realtime(SEARCH_IDAM_FAILED);
				return;
			}

			// Repeat until we have collected 16 bits
			if (m_live_state.bit_counter & 15) break;

			// So we now got 16 bits. Fill this value into the next slot. We expect two more A1 values.
			slot = m_live_state.bit_counter >> 4;
			if (slot < 3)
			{
				if (m_live_state.shift_reg != 0x4489)
				{
					// This ain't A1. Step back into the previous state (look for the next IDAM).
					m_live_state.state = SEARCH_IDAM;
				}
				else
					if (TRACE_LIVE) logerror("%s: [%s] Found an A1 mark\n", tag(),tts(m_live_state.time).cstr());
				// Continue
				break;
			}

			if (TRACE_LIVE) logerror("%s: [%s] Found data value %02X\n", tag(),tts(m_live_state.time).cstr(), m_live_state.data_reg);

			// Check for ident field (fe, ff, fd, fc)
			if ((m_live_state.data_reg & 0xfc) != 0xfc)
			{
				// This may happen when we accidentally locked onto the DAM. Look for the next IDAM.
				if (TRACE_LIVE) logerror("%s: Missing ident data after A1A1A1\n", tag());
				m_live_state.state = SEARCH_IDAM;
				break;
			}

			m_register_r[CURRENT_IDENT] = m_live_state.data_reg;

			// We're here after we got the three A1 and FE
			m_live_state.bit_counter = 0;
			m_live_state.state = READ_ID_FIELDS_INTO_REGS;
			break;

		case READ_ID_FIELDS_INTO_REGS:
			if (TRACE_LIVE && m_last_live_state != READ_ID_FIELDS_INTO_REGS)
			{
				logerror("%s: [%s] READ_ID_FIELDS_INTO_REGS\n", tag(),tts(m_live_state.time).cstr());
				m_last_live_state = m_live_state.state;
			}

			if (read_one_bit(limit))
			{
				return;
			}
			// Already got 16 bits?
			if (m_live_state.bit_counter & 15) break;

			slot = (m_live_state.bit_counter >> 4)-1;

			if (TRACE_LIVE) logerror("%s: slot %d = %02x, crc=%04x\n", tag(), slot, m_live_state.data_reg, m_live_state.crc);

			// The id_field is an array of indexes into the chip registers.
			// Thus we get the values properly assigned to the registers.
			m_register_r[id_field[slot]] = m_live_state.data_reg;

			if(slot > 4)
			{
				// We successfully read the ID fields; let's wait for the machine time to catch up.
				m_live_state.bit_count_total = 0;

				if ((current_command() & 0xfe) == 0x5a)
					// Continue if we're reading a complete track
					wait_for_realtime(READ_TRACK_ID_DONE);
				else
					// Live run is done here; it is the main state machine's turn again.
					wait_for_realtime(IDLE);
				return;
			}
			break;

		// ==================================================
		// Live states for sector read operations
		// ==================================================

		case SEARCH_DAM:
			if (TRACE_LIVE && m_last_live_state != SEARCH_DAM)
			{
				logerror("%s: [%s] SEARCH_DAM\n", tag(),tts(m_live_state.time).cstr());
				m_last_live_state = m_live_state.state;
			}

			set_bits(m_register_r[CHIP_STATUS], CS_DELDATA, false);

			if(read_one_bit(limit))
				return;

			if (TRACE_SHIFT) logerror("%s: shift = %04x data=%02x c=%d\n", tts(m_live_state.time).cstr(), m_live_state.shift_reg,
					get_data_from_encoding(m_live_state.shift_reg), m_live_state.bit_counter);

			if (!fm_mode())
			{   // MFM
				if(m_live_state.bit_counter > 43*16)
				{
					if (TRACE_FAIL) logerror("%s: SEARCH_DAM failed\n", tag());
					wait_for_realtime(SEARCH_DAM_FAILED);
					return;
				}

				if (m_live_state.bit_counter >= 28*16 && m_live_state.shift_reg == 0x4489)
				{
					if (TRACE_LIVE) logerror("%s: [%s] Found an A1 mark\n", tag(),tts(m_live_state.time).cstr());
					m_live_state.crc = 0x443b;
					m_live_state.data_separator_phase = false;
					m_live_state.bit_counter = 0;
					m_live_state.state = READ_TWO_MORE_A1_DAM;
				}
			}
			else
			{   // FM
				if (m_live_state.bit_counter > 23*16)
				{
					if (TRACE_FAIL) logerror("%s: SEARCH_DAM failed\n", tag());
					wait_for_realtime(SEARCH_DAM_FAILED);
					return;
				}

				if (m_live_state.bit_counter >= 11*16 && (m_live_state.shift_reg == 0xf56a || m_live_state.shift_reg == 0xf56b ||
														m_live_state.shift_reg == 0xf56e || m_live_state.shift_reg == 0xf56f)) {
					if (TRACE_LIVE) logerror("%s: SEARCH_DAM: found DAM = %04x\n", tag(), m_live_state.shift_reg);
					m_live_state.crc =
						m_live_state.shift_reg == 0xf56a ? 0x8fe7 :
						m_live_state.shift_reg == 0xf56b ? 0x9fc6 :
						m_live_state.shift_reg == 0xf56e ? 0xafa5 :
						0xbf84;
					m_live_state.data_separator_phase = false;
					m_live_state.bit_counter = 0;
					m_live_state.state = READ_SECTOR_DATA;
				}
			}
			break;

		case READ_TWO_MORE_A1_DAM: {
			if (TRACE_LIVE && m_last_live_state != READ_TWO_MORE_A1_DAM)
			{
				logerror("%s: [%s] READ_TWO_MORE_A1_DAM\n", tag(),tts(m_live_state.time).cstr());
				m_last_live_state = m_live_state.state;
			}

			if(read_one_bit(limit))
				return;

			if (TRACE_SHIFT) logerror("%s: shift = %04x data=%02x c=%d\n", tts(m_live_state.time).cstr(), m_live_state.shift_reg,
				get_data_from_encoding(m_live_state.shift_reg), m_live_state.bit_counter);

			// Repeat until we have collected 16 bits
			if (m_live_state.bit_counter & 15) break;

			// Fill this value into the next slot. We expect three A1 values.
			int slot = m_live_state.bit_counter >> 4;

			if (slot < 3)
			{
				if (m_live_state.shift_reg != 0x4489)
				{
					wait_for_realtime(SEARCH_DAM_FAILED);
					return;
				}
				else
					if (TRACE_LIVE) logerror("%s: [%s] Found an A1 mark\n", tag(),tts(m_live_state.time).cstr());
				// Continue
				break;
			}

			if (TRACE_LIVE) logerror("%s: [%s] Found data value %02X\n", tag(),tts(m_live_state.time).cstr(), m_live_state.data_reg);

			if ((m_live_state.data_reg & 0xff) == 0xf8)
			{
				if (TRACE_LIVE) logerror("%s: Found deleted data mark F8 after DAM sync\n", tag());
				set_bits(m_register_r[CHIP_STATUS], CS_DELDATA, true);
			}
			else
			{
				if ((m_live_state.data_reg & 0xff) != 0xfb)
				{
					if (TRACE_FAIL) logerror("%s: Missing FB/F8 data mark after DAM sync\n", tag());
					wait_for_realtime(SEARCH_DAM_FAILED);
					return;
				}
			}

			m_live_state.bit_counter = 0;
			m_live_state.state = READ_SECTOR_DATA;
			break;
		}
		case SEARCH_DAM_FAILED:
			if (TRACE_FAIL) logerror("%s: SEARCH_DAM failed\n", tag());
			m_live_state.state = IDLE;
			return;

		case READ_SECTOR_DATA:
		{
			if (TRACE_LIVE && m_last_live_state != READ_SECTOR_DATA)
			{
				logerror("%s: [%s] READ_SECTOR_DATA\n", tag(),tts(m_live_state.time).cstr());
				m_last_live_state = m_live_state.state;
			}

			if(read_one_bit(limit))
				return;

			// Request bus release at the first bit of each byte (floppy; [1], fig 5 and 6)
			if (m_transfer_enabled)
			{
				if ((m_live_state.bit_counter & 15)== 1)
				{
					// For floppies, request DMA for each byte. For hard disk, get it
					// only for the first byte and then keep the bus until the last byte.
					if (using_floppy() || m_live_state.bit_counter < 16)
					{
						set_bits(m_register_r[INT_STATUS], ST_OVRUN, true);
						m_out_dmarq(ASSERT_LINE);
					}
				}
			}

			// Repeat until we have collected 16 bits
			if (m_live_state.bit_counter & 15) break;

			if (TRACE_LIVE) logerror("%s: [%s] Found data value %02X, CRC=%04x\n", tag(),tts(m_live_state.time).cstr(), m_live_state.data_reg, m_live_state.crc);
			int slot = (m_live_state.bit_counter >> 4)-1;

			if (slot < calc_sector_size())
			{
				// Sector data
				wait_for_realtime(READ_SECTOR_DATA1);
				return;
			}
			else if (slot < calc_sector_size()+2)
			{
				// CRC
				if (slot == calc_sector_size()+1)
				{
					if ((current_command() & 0xfe)==0x5a)
					{
						// Reading a track? Continue with next ID.
						wait_for_realtime(READ_TRACK_ID);
					}
					else
					{
						if (TRACE_LIVE) logerror("%s: [%s] Sector read completed\n", tag(),tts(m_live_state.time).cstr());
						wait_for_realtime(IDLE);
					}
					return;
				}
			}
			break;
		}

		case READ_SECTOR_DATA1:
			if (TRACE_LIVE && m_last_live_state != READ_SECTOR_DATA1)
			{
				logerror("%s: [%s] READ_SECTOR_DATA1\n", tag(),tts(m_live_state.time).cstr());
				m_last_live_state = m_live_state.state;
			}

			// Did the system CPU send the DMA ACK in the meantime?
			if ((m_register_r[INT_STATUS] & ST_OVRUN)!=0)
			{
				if (TRACE_FAIL) logerror("%s: No DMA ACK - buffer overrun\n", tag());
				set_bits(m_register_r[INT_STATUS], TC_DATAERR, true);
				m_live_state.state = IDLE;
				return;
			}

			if (m_transfer_enabled)
			{
				m_register_r[DATA] = m_register_w[DATA] = m_live_state.data_reg;
				// See above: For floppy, do it for each byte; for hard disk, only for the first byte,
				if (using_floppy() || m_live_state.bit_counter == 16)
					m_out_dip(ASSERT_LINE);

				m_out_dma(0, m_register_r[DATA], 0xff);

				// And again, for floppies, clear line after writing each byte, for hard disk, only after the last byte
				if (using_floppy() || (m_live_state.bit_counter >> 4)==calc_sector_size()-1)
				{
					m_out_dip(CLEAR_LINE);
					m_out_dmarq(CLEAR_LINE);
				}
			}

			m_live_state.state = READ_SECTOR_DATA;
			checkpoint();
			break;

		// ==================================================
		// Live states for sector write operations
		// ==================================================

		case WRITE_DAM_AND_SECTOR:
			// 1. Wait for 22*16 cells (MFM) or 11*16 cells (FM)  [704 usec, Gap 2]
			// 2. Write 12 (MFM) or 6 (FM) zeros
			// 3. Write 3*A1 sync plus the ident byte (MFM) or FB (FM) or F8 (deleted)
			// 4. Write the sector content and calculate the CRC on the fly
			// 5. Write the CRC bytes

			if (TRACE_LIVE)
				logerror("%s: [%s] WRITE_DAM_AND_SECTOR\n", tag(), tts(m_live_state.time).cstr());

			skip_on_track(m_gap2_size, WRITE_DAM_SYNC);
			break;

		case WRITE_DAM_SYNC:
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Write sync zeros\n", tag());

			// Clear the overrun/underrun flag
			set_bits(m_register_r[INT_STATUS], ST_OVRUN, false);
			write_on_track(encode(0x00), m_sync_size, fm_mode()? WRITE_DATAMARK : WRITE_A1);
			break;

		case WRITE_A1:
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Write three A1\n", tag());
			write_on_track(0x4489, 3, WRITE_DATAMARK);
			break;

		case WRITE_DATAMARK:
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Write data mark and sector contents\n", tag());
			if (fm_mode())
			{
				// Init the CRC for the DAM and sector
				m_live_state.crc = 0xffff;

				// 1111 0101 0110 1010 = F8 deleted
				// 1111 0101 0110 1111 = FB normal
				write_on_track(m_deleted? 0xf56a : 0xf56f, 1, WRITE_SECDATA);
			}
			else
			{
				// Init the CRC for the ident byte and sector
				m_live_state.crc = 0xcdb4; // value for 3*A1
				write_on_track(encode(m_deleted? 0xf8 : 0xfb), 1, WRITE_SECDATA);
			}
			m_live_state.byte_counter = calc_sector_size();

			// Set the over/underrun flag and hope that it will be cleared before we start writing
			// (only for sector writing)
			if (m_substate == DATA_TRANSFER_WRITE)
			{
				set_bits(m_register_r[INT_STATUS], ST_OVRUN, true);
				m_out_dmarq(ASSERT_LINE);
			}
			break;

		case WRITE_SECDATA:
			if (m_substate == DATA_TRANSFER_WRITE)
			{
				// Check whether DMA has been acknowledged
				if ((m_register_r[INT_STATUS] & ST_OVRUN)!=0)
				{
					// No, then stop here
					m_live_state.state= NO_DMA_ACK;
				}
				else
				{
					// For floppies, set this for each byte; for hard disk, set it only at the beginning
					if (using_floppy() || m_live_state.byte_counter == calc_sector_size())
						m_out_dip(ASSERT_LINE);

					m_register_r[DATA] = m_register_w[DATA] = m_in_dma(0, 0xff);

					if (using_floppy() || m_live_state.byte_counter == 0)
					{
						m_out_dip(CLEAR_LINE);
						m_out_dmarq(CLEAR_LINE);
					}

					if (m_live_state.byte_counter > 0)
					{
						m_live_state.byte_counter--;
						write_on_track(encode(m_register_r[DATA]), 1, WRITE_SECDATA);
						if (using_floppy()) m_out_dmarq(ASSERT_LINE);
					}
					else
					{
						m_live_state.state = WRITE_DATA_CRC;
						m_live_state.byte_counter = 2;
					}
				}
			}
			else
			{
				// We are here in the context of track formatting. Write a
				// blank sector
				write_on_track(encode(0xe5), m_sector_size, WRITE_DATA_CRC);
				m_live_state.byte_counter = 2;
			}
			break;

		case WRITE_DATA_CRC:
			// N.B.: when we write the first CRC byte, the value of the CRC will
			// change to the previous second byte, so we can write the first
			// byte in two iterations to get both
			if (m_live_state.byte_counter > 0)
			{
				if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Write CRC\n", tag());
				m_live_state.byte_counter--;
				write_on_track(encode((m_live_state.crc >> 8) & 0xff), 1, WRITE_DATA_CRC);
			}
			else
				// Write a filler byte so that the last CRC bit is saved correctly
				write_on_track(encode(0xff), 1, WRITE_DONE);

			break;

		case WRITE_DONE:
			if (m_substate == DATA_TRANSFER_WRITE)
			{
				if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Write sector complete\n", tag());
				m_pll.stop_writing(m_floppy, m_live_state.time);
				m_live_state.state = IDLE;
				return;
			}
			else
			{
				// Continue for track writing: Write GAP3
				m_live_state.state = WRITE_GAP3;
			}
			break;

// --------------------------------------------------------

		// ==================================================
		// Live states for track formatting
			// Write GAP 0
			// Write Sync+IXAM
			// Write GAP 1
			// Per sector
			//    Write Sync+IDAM
			//    Write Sector header+CRC
			//    Write GAP2
			//    Write Sync+DAM
			//    Write Sector data
			//    Write CRC bytes
			//    Write GAP3
			// Write GAP4 until the next pulse
		// ==================================================

		case FORMAT_TRACK:
			if (TRACE_LIVE) logerror("%s: FORMAT_TRACK\n", tag());
			m_live_state.state = WRITE_GAP0;
			m_pll.start_writing(m_live_state.time);
			break;

		case WRITE_GAP0:
			// GAP0 length is in DMA7_0 (negated, 2s comp)
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Writing GAP0\n", tag());
			write_on_track(encode(fm_mode()? 0xff : 0x4e), m_gap0_size, WRITE_IXAM_SYNC);
			break;

		case WRITE_IXAM_SYNC:
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Writing IXAM sync\n", tag());
			write_on_track(encode(0x00), m_sync_size, WRITE_IXAM);
			break;

		case WRITE_IXAM:
			// FM: FC with clock D7 = 1111 -111 -111 1010
			// MFM: C2 = 11000010
			// 0101 0010 -010 0100
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Writing IXAM\n", tag());
			if (fm_mode())
				write_on_track(0xf77a, 1, WRITE_GAP1);
			else
				write_on_track(0x5224, 3, WRITE_FC);

			break;

		case WRITE_FC:
			// Only for MFM
			write_on_track(encode(0xfc), 1, WRITE_GAP1);
			break;

		case WRITE_GAP1:
			// GAP1 length is in DMA15_8
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Writing GAP1\n", tag());
			write_on_track(encode(fm_mode()? 0xff : 0x4e), m_gap1_size, WRITE_IDAM_SYNC);
			break;

			// When does the HDC actually fetch the per-sector data? All data
			// at the beginning? Only the bytes for the next sector?
			// We assume it reads the bytes and writes them directly on the disk

		case WRITE_IDAM_SYNC:
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Writing IDAM sync\n", tag());
			write_on_track(encode(0x00), m_sync_size, WRITE_IDAM);
			break;

		case WRITE_IDAM:
			// Set the over/underrun flag and hope that it will be cleared before we enter the next state (after writing)
			set_bits(m_register_r[INT_STATUS], ST_OVRUN, true);
			m_out_dmarq(ASSERT_LINE);

			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Writing IDAM and header\n", tag());
			if (fm_mode())
			{
				write_on_track(0xf57e, 1, WRITE_HEADER);
				m_live_state.byte_counter = 4;
			}
			else
			{
				write_on_track(0x4489, 3, WRITE_HEADER);
				m_live_state.byte_counter = 5;
			}
			m_live_state.crc = 0xffff;
			break;

		case WRITE_HEADER:
			if ((m_register_r[INT_STATUS] & ST_OVRUN)!=0)
				// No DMA (we do not get access to the ID table); exit
				m_live_state.state= NO_DMA_ACK;
			else
			{
				m_out_dip(ASSERT_LINE);
				m_live_state.byte_counter--;
				UINT8 headbyte = m_in_dma(0, 0xff);

				write_on_track(encode(headbyte), 1, (m_live_state.byte_counter>0)? WRITE_HEADER : WRITE_HEADER_CRC);

				if (using_floppy() || m_live_state.byte_counter==0)
				{
					m_out_dip(CLEAR_LINE);
					m_out_dmarq(CLEAR_LINE);
				}
				// Writing will occur after the break; set the DMARQ again
				if (m_live_state.byte_counter>0)
					m_out_dmarq(ASSERT_LINE);
				else
					// we will go to WRITE_HEADER_CRC state; set the byte counter for CRC
					m_live_state.byte_counter = 2;
			}
			break;

		case WRITE_HEADER_CRC:
			if (m_live_state.byte_counter > 0)
			{
				UINT8 crct = (m_live_state.crc >> 8) & 0xff;
				if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Write CRC byte %02x\n", tag(), crct);
				m_live_state.byte_counter--;
				write_on_track(encode(crct), 1, WRITE_HEADER_CRC);
			}
			else
				m_live_state.state = WRITE_GAP2;

			break;

		case WRITE_GAP2:
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Writing GAP2\n", tag());
			write_on_track(encode(fm_mode()? 0xff : 0x4e), m_gap2_size, WRITE_DAM_SYNC);
			break;

		case WRITE_GAP3:
			m_sector_count--;
			if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: Writing GAP3\n", tag());
			write_on_track(encode(fm_mode()? 0xff : 0x4e), m_gap3_size, (m_sector_count>0)? WRITE_IDAM_SYNC : WRITE_GAP4);
			break;

		case WRITE_GAP4:
			// Write bytes up to the end of the track
			if (TRACE_WRITE && TRACE_DETAIL && m_last_live_state != WRITE_GAP4)
			{
				logerror("%s: Writing GAP4\n", tag());
				m_last_live_state = WRITE_GAP4;
			}
			// Write a single byte; when the index hole shows up, the live run will be aborted
			write_on_track(encode(fm_mode()? 0xff : 0x4e), 1, WRITE_GAP4);
			break;
// --------------------------------------------------------

		// ==================================================
		// Live states for track reading
		// ==================================================

		// Quite simple. Read the next ID fields, then the sector contents.
		// Continue until the next index hole shows up (live_abort).
		case READ_TRACK:
			if (TRACE_LIVE) logerror("%s: READ_TRACK\n", tag());
			m_live_state.state = READ_TRACK_ID;
			break;

		case READ_TRACK_ID:
			m_live_state.state = SEARCH_IDAM;
			// Ask for access to bus
			set_bits(m_register_r[INT_STATUS], ST_OVRUN, true);
			m_out_dmarq(ASSERT_LINE);
			break;

		case READ_TRACK_ID_DONE:
			if ((m_register_r[INT_STATUS] & ST_OVRUN)!=0)
			{
				if (TRACE_FAIL) logerror("%s: No DMA ACK - buffer overrun\n", tag());
				set_bits(m_register_r[INT_STATUS], TC_DATAERR, true);
				m_live_state.state = IDLE;
				return;
			}
			if (TRACE_LIVE) logerror("%s: READ_TRACK1\n", tag());

			m_out_dip(ASSERT_LINE);

			// Write the header via DMA
			for (int slot = 0; slot < 6; slot++)
				m_out_dma(0, m_register_r[id_field[slot]], 0xff);

			m_out_dip(CLEAR_LINE);
			m_out_dmarq(CLEAR_LINE);

			// Continue with reading the sector data
			m_live_state.state = SEARCH_DAM;
			break;

//  =================================================================

		case READ_TRACK_BYTE:
			// The pause is implemented by doing dummy reads on the floppy
			if (read_one_bit(limit))
			{
				if (TRACE_LIVE) logerror("%s: [%s] return; limit=%s\n", tag(), tts(m_live_state.time).cstr(), tts(limit).cstr());
				return;
			}

			// Repeat until we have collected 16 bits
			if ((m_live_state.bit_counter & 15)==0)
			{
				if (TRACE_READ && TRACE_DETAIL) logerror("%s: [%s] Read byte %02x, repeat = %d\n", tag(), tts(m_live_state.time).cstr(), m_live_state.data_reg, m_live_state.repeat);
				wait_for_realtime(READ_TRACK_NEXT_BYTE);
				return;
			}
			break;

		case READ_TRACK_NEXT_BYTE:
			m_live_state.state = READ_TRACK_BYTE;
			m_live_state.repeat--;
			if (m_live_state.repeat == 0)
			{
				// All bytes read
				m_live_state.state = m_live_state.return_state;
				checkpoint();
			}
			break;

		case WRITE_TRACK_BYTE:
			if (write_one_bit(limit))
				return;

			if (m_live_state.bit_counter == 0)
			{
				// All bits written; get the next byte into the shift register
				wait_for_realtime(WRITE_TRACK_NEXT_BYTE);
				return;
			}
			break;

		case WRITE_TRACK_NEXT_BYTE:
			m_live_state.state = WRITE_TRACK_BYTE;
			m_live_state.repeat--;

			// Write all bytes
			if (m_live_state.repeat == 0)
			{
				// All bytes written
				m_live_state.state = m_live_state.return_state;
				checkpoint();
			}
			else
				encode_again();

			break;

		case NO_DMA_ACK:
			if (TRACE_FAIL) logerror("%s: No DMA ACK - buffer underrun\n", tag());
			set_bits(m_register_r[INT_STATUS], TC_DATAERR, true);
			m_pll.stop_writing(m_floppy, m_live_state.time);
			m_live_state.state = IDLE;
			return;

		default:
			logerror("%s: Unknown live state: %02x\n", tag(), m_live_state.state);
			m_last_live_state = m_live_state.state;
			return;
		}
	}
	m_last_live_state = UNDEF;
}

/*
    Synchronize the live position on the track with the real time.
    Results in a new checkpoint and a live position at machine time or behind.
    As a side effect, portions of the track may be re-read
*/
void hdc9234_device::live_sync()
{
	// Do we have some time set?
	if (!m_live_state.time.is_never())
	{
		// Are we ahead of the machine time?
		if(m_live_state.time > machine().time())
		{
			// If so, we must roll back to the last checkpoint
			if (TRACE_SYNC) logerror("%s: [%s] Rolling back and replaying (%s)\n", tag(), ttsn().cstr(), tts(m_live_state.time).cstr());
			rollback();
			// and replay until we reach the machine time
			live_run_until(machine().time());
			// Caught up, write on floppy image
			m_pll.commit(m_floppy, m_live_state.time);
		}
		else
		{
			// We are behind machine time, so we will never get back to that
			// time, thus we can commit that position
			if (TRACE_SYNC) logerror("%s: [%s] Committing (%s)\n", tag(), ttsn().cstr(), tts(m_live_state.time).cstr());
			// Write on floppy image
			m_pll.commit(m_floppy, m_live_state.time);

			if (m_live_state.next_state != -1)
			{
				m_live_state.state = m_live_state.next_state;
			}

			if (m_live_state.state == IDLE)
			{
				m_pll.stop_writing(m_floppy, m_live_state.time);
				m_live_state.time = attotime::never;
			}
		}

		m_live_state.next_state = -1;
		checkpoint();
	}
}

void hdc9234_device::live_abort()
{
	if (!m_live_state.time.is_never() && m_live_state.time > machine().time())
	{
		if (TRACE_LIVE) logerror("%s: Abort; rolling back and replaying (%s)\n", ttsn().cstr(), tts(m_live_state.time).cstr());
		rollback();
		live_run_until(machine().time());
	}

	m_pll.stop_writing(m_floppy, m_live_state.time);
	m_live_state.time = attotime::never;
	m_live_state.state = IDLE;
	m_live_state.next_state = -1;
}

/*
    Brings the live state machine into the WRITE substate part
    comprised by WRITE_TRACK_(NEXT_)BYTE
    Arguments: byte to be written, number, state on return
*/
void hdc9234_device::write_on_track(UINT16 encoded, int repeat, int next_state)
{
	m_live_state.repeat = repeat;
	m_live_state.state = WRITE_TRACK_BYTE;
	m_live_state.return_state = next_state;
	encode_raw(encoded);
}

/*
    Brings the live state machine into the READ substate part. This is
    only intended for skipping bytes.
    Arguments: number, state on return
*/
void hdc9234_device::skip_on_track(int repeat, int next_state)
{
	m_live_state.bit_counter = 0;
	m_live_state.repeat = repeat;
	m_live_state.state = READ_TRACK_BYTE;
	m_live_state.return_state = next_state;
}

UINT8 hdc9234_device::get_data_from_encoding(UINT16 raw)
{
	return (raw & 0x4000 ? 0x80 : 0x00) |
			(raw & 0x1000 ? 0x40 : 0x00) |
			(raw & 0x0400 ? 0x20 : 0x00) |
			(raw & 0x0100 ? 0x10 : 0x00) |
			(raw & 0x0040 ? 0x08 : 0x00) |
			(raw & 0x0010 ? 0x04 : 0x00) |
			(raw & 0x0004 ? 0x02 : 0x00) |
			(raw & 0x0001 ? 0x01 : 0x00);
}

void hdc9234_device::rollback()
{
	m_live_state = m_checkpoint_state;
	m_pll = m_checkpoint_pll;
}

/*
    Wait for real time to catch up. This way we pretend that the last
    operation actually needed the real time.
*/
void hdc9234_device::wait_for_realtime(int state)
{
	m_live_state.next_state = state;
	m_timer->adjust(m_live_state.time - machine().time());
	if (TRACE_LIVE) logerror("%s: [%s] Waiting for real time [%s] to catch up\n", tag(), tts(m_live_state.time).cstr(), tts(machine().time()).cstr());
}

/*
    Read the next bit from the disk.
    Return true: the time limit has been reached
    Return false: The next bit is read into the shift register as the
    rightmost bit; the shift register is a member of m_live_state. Also,
    the CRC is updated.
*/
bool hdc9234_device::read_one_bit(const attotime &limit)
{
	// Get the next bit from the phase-locked loop.
	int bit = m_pll.get_next_bit(m_live_state.time, m_floppy, limit);

	// We have reached the time limit
	if (bit < 0) return true;

	// For test purposes: Drop a bit at some occasions
	// value > 1000: rare occasions
	// value = 500: can cope with
	// value < 100: big trouble for controller, will fail
	if (UNRELIABLE_MEDIA)
	{
		if ((machine().time().attoseconds % 1009)==0) bit = 0;
	}

	// Push into shift register
	m_live_state.shift_reg = (m_live_state.shift_reg << 1) | bit;
	m_live_state.bit_counter++;

	// Used for timeout handling
	m_live_state.bit_count_total++;

	// Clock bit (false) or data bit (true)?
	if (m_live_state.data_separator_phase==true)
	{
		m_live_state.data_reg = (m_live_state.data_reg << 1) | bit;
		// Update CRC
		if ((m_live_state.crc ^ (bit ? 0x8000 : 0x0000)) & 0x8000)
			m_live_state.crc = (m_live_state.crc << 1) ^ 0x1021;
		else
			m_live_state.crc = m_live_state.crc << 1;
	}

	m_live_state.data_separator_phase = !m_live_state.data_separator_phase;
	return false;
}

bool hdc9234_device::write_one_bit(const attotime &limit)
{
	bool bit = (m_live_state.shift_reg & 0x8000)!=0;

	bool over_limit = m_pll.write_next_bit(bit, m_live_state.time, m_floppy, limit);
	if (over_limit) return true;

	// Calculate the CRC from the data bits on the odd positions
	if (m_live_state.bit_counter & 1)
	{
		if ((m_live_state.crc ^ (bit ? 0x8000 : 0x0000)) & 0x8000)
			m_live_state.crc = (m_live_state.crc << 1) ^ 0x1021;
		else
			m_live_state.crc = m_live_state.crc << 1;
	}
	m_live_state.shift_reg = m_live_state.shift_reg << 1;
	m_live_state.bit_counter--;
	return false;
}

UINT16 hdc9234_device::encode(UINT8 byte)
{
	UINT16 raw;
	UINT8 check_pos;
	bool last_bit_set;
	check_pos =  0x80;

	if (fm_mode())
	{
		// Set all clock bits
		raw = 0xaaaa;

		// FM: data bit = 1 -> encode as 11
		//     data bit = 0 -> encode as 10
		for (int i=0; i<8; i++)
		{
			if (byte & check_pos) raw |= 0x4000 >> (2*i);
			check_pos >>= 1;
		}
		last_bit_set = ((byte & 1)!=0);
	}
	else
	{
		last_bit_set = m_live_state.last_data_bit;
		raw = 0;
		for (int i=0; i<8; i++)
		{
			bool bit_set = ((byte & check_pos)!=0);

			// MFM: data bit = 1 -> encode as 01
			//      data bit = 0 -> encode as x0 (x = !last_bit)
			if (bit_set)
				raw |= 0x4000 >> (2*i);
			else
				raw |= (last_bit_set? 0x0000 : 0x8000) >> (2*i);

			last_bit_set = bit_set;
			check_pos >>= 1;
		}
	}
	m_live_state.data_reg = byte;
	return raw;
}

/*
    Encode a byte for FM or MFM recording. Result is returned in the
    shift register of m_live_state.
*/
void hdc9234_device::encode_byte(UINT8 byte)
{
	UINT16 raw = encode(byte);
	m_live_state.bit_counter = 16;
	m_live_state.last_data_bit = raw & 1;
	m_live_state.shift_reg = m_live_state.shift_reg_save = raw;
	if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: [%s] Write %02x (%04x)\n", tag(), tts(m_live_state.time).cstr(), byte, raw);
	checkpoint();
}

void hdc9234_device::encode_again()
{
	encode_raw(m_live_state.shift_reg_save);
}

void hdc9234_device::encode_raw(UINT16 raw)
{
	m_live_state.bit_counter = 16;
	m_live_state.shift_reg = m_live_state.shift_reg_save = raw;
	m_live_state.last_data_bit = raw & 1;
	if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: [%s] Write %02x (%04x)\n", tag(), tts(m_live_state.time).cstr(), get_data_from_encoding(raw), raw);
	checkpoint();
}

/*
    Reset the PLL. For reading, data must pass through a dedicated data
    separator. The clock rate is delivered from
    m_clock_divider with values 1-3, where 1 is FM (4000), 2 is MFM (2000),
    and 3 is MFM (1000).
    When writing, the controller generates the proper output bitstream, so we
    have to set it from its own state (fm/mfm and device type).
*/
void hdc9234_device::pll_reset(const attotime &when, bool output)
{
	m_pll.reset(when);

	if (output)
	{
		if (fm_mode())
			m_pll.set_clock(attotime::from_nsec(4000));
		else
			m_pll.set_clock(attotime::from_nsec((m_selected_drive_type==TYPE_FLOPPY5)? 2000 : 1000));
	}
	else
		m_pll.set_clock(attotime::from_nsec(8000 >> (~m_clock_divider & 0x03)));
}

void hdc9234_device::checkpoint()
{
	// Write on floppy image
	m_pll.commit(m_floppy, m_live_state.time);
	m_checkpoint_state = m_live_state;
	m_checkpoint_pll = m_pll;
}

// ===========================================================================

/*
    Read a byte of data from the controller
    The address (offset) encodes the C/D* line (command and /data)
*/
READ8_MEMBER( hdc9234_device::read )
{
	UINT8 reply = 0;

	if ((offset & 1) == 0)
	{
		// Data register
		reply = m_register_r[m_register_pointer];
		if (TRACE_READREG) logerror("%s: read register[%d] -> %02x\n", tag(), m_register_pointer, reply);

		// Autoincrement until DATA is reached.
		if (m_register_pointer < DATA)  m_register_pointer++;
	}
	else
	{
		// Status register
		reply = m_register_r[INT_STATUS];

		// "The interrupt pin is reset to its inactive state
		// when the UDC interrupt status register is read." [1] (p.3)
		if (TRACE_READREG) logerror("%s: read interrupt status register -> %02x\n", tag(), reply);
		set_interrupt(CLEAR_LINE);

		// Clear the bits due to interrupt status register read.
		m_register_r[INT_STATUS] &= ~(ST_INTPEND | ST_RDYCHNG);
	}
	return reply;
}

/*
    Write a byte to the controller
    The address (offset) encodes the C/D* line (command and /data), so there
    are only two addresses: 0 (register) and 1 (command).
    The operation terminates immediately, and the controller picks up the
    values stored in this phase at a later time.
*/
WRITE8_MEMBER( hdc9234_device::write )
{
	if ((offset & 1) == 0)
	{
		m_regvalue = data & 0xff;
		wait_time(m_cmd_timer, attotime::from_nsec(REGISTER_COMMIT), REGISTER_ACCESS);
	}
	else
	{
		if (m_executing)
		{
			logerror("%s: [%s] Error - previous command %02x not completed; new command %02x ignored\n", tag(), ttsn().cstr(), current_command(), data);
		}
		else
		{
			m_register_w[COMMAND] = data;
			wait_time(m_cmd_timer, attotime::from_nsec(COMMAND_COMMIT), COMMAND_INIT);
		}
	}
}

/*
    When the commit period has passed, process the command or register access
*/
void hdc9234_device::process_command()
{
	if (m_substate == REGISTER_ACCESS)
	{
		// Writing data to registers
		// Data register
		if (TRACE_SETREG)
		{
			if (m_register_pointer == INT_COMM_TERM)
				logerror("%s: Setting interrupt trigger DONE=%d READY=%d\n", tag(), (m_regvalue & TC_INTDONE)? 1:0, (m_regvalue & TC_INTRDCH)? 1:0);
			else
				logerror("%s: register[%d] <- %02x\n", tag(), m_register_pointer, m_regvalue);
		}
		m_register_w[m_register_pointer] = m_regvalue;

		// The DMA registers and the sector register for read and
		// write are identical, so in that case we copy the contents
		if (m_register_pointer < DESIRED_HEAD) m_register_r[m_register_pointer] = m_regvalue;

		// Autoincrement until DATA is reached.
		if (m_register_pointer < DATA)  m_register_pointer++;
	}
	else
	{
		// Reset DONE and BAD_SECTOR [1], p.7
		set_bits(m_register_r[INT_STATUS], ST_DONE | ST_BADSECT, false);

		// Reset interrupt line (not explicitly mentioned in spec, but seems reasonable
		set_interrupt(CLEAR_LINE);

		// Clear Interrupt Pending and Ready Change
		set_bits(m_register_r[INT_STATUS], ST_INTPEND | ST_RDYCHNG, false);

		int index = 0;
		bool found = false;

		while (s_command[index].mask!=0 && !found)
		{
			if ((m_register_w[COMMAND] & s_command[index].mask) == s_command[index].baseval)
			{
				found = true;

				m_stop_after_index = false;
				m_wait_for_index = false;
				m_substate = UNDEF;
				m_executing = true;
				m_command = s_command[index].command;
				// Invoke command
				(this->*m_command)();
			}
			else index++;
		}
		if (!found)
		{
			logerror("%s: Command %02x not defined\n", tag(), m_register_w[COMMAND]);
		}
	}
	auxbus_out();
}

void hdc9234_device::reenter_command_processing()
{
	// Do we have a live run on the track?
	if (m_live_state.state != IDLE)
	{
		// Continue with it
		live_run();
		if (m_live_state.state != IDLE)  return;
	}

	// We're here when there is no live_run anymore
	// Where were we last time?
	// Take care not to restart commands because of the index callback
	if (m_executing && m_substate != UNDEF) (this->*m_command)();
	auxbus_out();
}

/*
    Assert Command Done status bit, triggering interrupts as needed
*/
void hdc9234_device::set_command_done(int flags)
{
	// Do another output, then set the flag
	auxbus_out();

	set_bits(m_register_r[INT_STATUS], ST_DONE, true);

	if (flags != -1)
	{
		set_bits(m_register_r[INT_STATUS], ST_TERMCOD, false); // clear the previously set flags
		m_register_r[INT_STATUS] |= flags;
		if (TRACE_DONE) logerror("%s: command %02x done, flags=%02x\n", tag(), current_command(), flags);
	}
	else
	{
		if (TRACE_DONE) logerror("%s: command %02x done\n", tag(), current_command());
	}

	// [1], p. 6
	if (TRACE_INT) logerror("%s: Raise interrupt DONE\n", tag());
	set_interrupt(ASSERT_LINE);

	m_substate = UNDEF;
	m_executing = false;
}

/*
    Preserve previously set termination code
*/
void hdc9234_device::set_command_done()
{
	set_command_done(-1);
}

/*
    Auxiliary bus operation.

    The auxbus of the HDC9234 is used to poll the drive status of the cur-
    rently selected drive, to transmit DMA address bytes, to output the
    OUTPUT1 register, and to output the OUTPUT2 register.

    The specification is not really precise on the times when this bus is
    used, but at least we can rely on this information:

    - Whenever there is no output of data, the bus is sampled. ([1], p.8,
      Drive status register). Data is sampled at the rising edge of STB*.
      As the minimum STB* pulse is 800ns with min 100ns S0/S1 settling time
      and min 100ns hold time we can say that the bus is polled at a maximum
      rate of 1 MHz.

    - Data for the DMA address is output only when the address is initially
      set; also when the address must be set again on error ([1], p.5,
      DMA registers). The external memory system has to take care of the
      addressing for subsequent bytes. The address will be increased by the
      length of a sector during multiple sector read/write operations.

    We may assume that the OUTPUT1 and OUTPUT2 operations only occur on
    changes to the registers in the controller. The values showing up on the
    auxiliary bus must be latched anyway.

    For the sampling of drive status values, the emulation would have to
    invoke a callback to the hosting board at a rate of about 1 MHz. Since
    the devices like floppy or hard disks are pushing their status changes,
    it makes much more sense to allow for an incoming call to the controller
    instead of a polling. This also allows to raise interrupts as soon
    as the drive status changes. The difference to the real controller
    would be less than 3 microseconds (in the worst case when the auxbus is
    currently outputting data as the drive status change occurs).

    Drive status read
    S0 = 0, S1 = 0
    +------+------+------+------+------+------+------+------+
    | ECC  |Index | SeekC| Tr00 | User | WrPrt| Ready|Fault |
    +------+------+------+------+------+------+------+------+
*/

/*
    Read the drive status over the auxbus
    (as said, let the controller board push the values into the controller)
*/
void hdc9234_device::auxbus_in(UINT8 data)
{
	// Kill unwanted input via auxbus until we are initialized.
	if (!m_initialized)
		return;

	if (TRACE_AUXBUS) logerror("%s: Got value %02x via auxbus: ecc=%d index=%d seek_comp=%d tr00=%d user=%d writeprot=%d ready=%d fault=%d\n",
				tag(), data,
				(data&HDC_DS_ECCERR)? 1:0, (data&HDC_DS_INDEX)? 1:0,
				(data&HDC_DS_SKCOM)? 1:0, (data&HDC_DS_TRK00)? 1:0,
				(data&HDC_DS_UDEF)? 1:0, (data&HDC_DS_WRPROT)? 1:0,
				(data&HDC_DS_READY)? 1:0, (data&HDC_DS_WRFAULT)? 1:0);
	UINT8 prev = m_register_r[DRIVE_STATUS];
	m_register_r[DRIVE_STATUS] = data;

	if ((prev & HDC_DS_INDEX) != (data &  HDC_DS_INDEX))
	{
		// Check whether index value changed
		index_callback((data & HDC_DS_INDEX)? ASSERT_LINE : CLEAR_LINE);
	}

	if ((prev & HDC_DS_READY) != (data & HDC_DS_READY))
	{
		// Check whether ready value changed
		ready_callback((data & HDC_DS_READY)? ASSERT_LINE : CLEAR_LINE);
	}

	if ((prev & HDC_DS_SKCOM) != (data & HDC_DS_SKCOM))
	{
		// Check whether seek complete value changed
		seek_complete_callback((data & HDC_DS_SKCOM)? ASSERT_LINE : CLEAR_LINE);
	}
}

void hdc9234_device::index_callback(int level)
{
	if (TRACE_LINES) logerror("%s: [%s] Index callback level=%d\n", tag(), ttsn().cstr(), level);

	// Synchronize our position on the track
	live_sync();

	if (level==ASSERT_LINE)
	{
		if (TRACE_INDEX) logerror("%s: Index pulse\n", tag());
		if (m_wait_for_index) m_stop_after_index = true;
	}

	if (m_event_line == INDEX_LINE && level == m_line_level && m_state_after_line != UNDEF)
	{
		if (TRACE_LINES) logerror("%s: [%s] Index pulse level=%d triggers event\n", tag(), ttsn().cstr(), level);
		m_substate = m_state_after_line;
		m_state_after_line = UNDEF;
		if (m_stopwrite)
		{
			m_pll.stop_writing(m_floppy, m_live_state.time);
			m_live_state.state = IDLE;
		}
	}

	reenter_command_processing();
}

void hdc9234_device::ready_callback(int level)
{
	if (TRACE_LINES) logerror("%s: [%s] Ready callback level=%d\n", tag(), ttsn().cstr(), level);

	// Set the interrupt status flag
	set_bits(m_register_r[INT_STATUS], ST_RDYCHNG, true);

	// Synchronize our position on the track
	live_sync();

	// Raise an interrupt if desired
	if (m_register_w[INT_COMM_TERM] & TC_INTRDCH)
	{
		if (TRACE_INT) logerror("%s: Raise interrupt READY change\n", tag());
		set_interrupt(ASSERT_LINE);
	}

	if (m_event_line == READY_LINE && level == m_line_level && m_state_after_line != UNDEF)
	{
		m_substate = m_state_after_line;
		m_state_after_line = UNDEF;
		reenter_command_processing();
	}
}

void hdc9234_device::seek_complete_callback(int level)
{
	if (TRACE_LINES) logerror("%s: [%s] Seek complete callback level=%d\n", tag(), ttsn().cstr(), level);

	// Synchronize our position on the track
	live_sync();

	if (m_event_line == SEEKCOMP_LINE && level == m_line_level && m_state_after_line != UNDEF)
	{
		m_substate = m_state_after_line;
		m_state_after_line = UNDEF;
		reenter_command_processing();
	}
}

/*
    Push the output registers over the auxiliary bus. It is expected that
    the PCB contains latches to store the values.

    OUTPUT1 register contents
    S0 = 0, S1 = 1
    +------+------+------+------+------+------+------+------+
    | Drv3 | Drv2 | Drv1 | Drv0 |  PO3 |  PO2 |  PO1 |  PO0 |
    +------+------+------+------+------+------+------+------+

    DrvX = select Drive X (only one bit allowed)
    POX = Programmable output X (contents from low 4 bits of register RETRY_COUNT)


    OUTPUT2 register contents
    S0 = 1, S1 = 1
    +------+------+------+------+------+------+------+------+
    | Drv3*| WCur | Dir  | Step |           Head            |
    +------+------+------+------+------+------+------+------+

    Drv3* = inverted Drv3 signal of OUTPUT1
    WCur = Reduced write current
    Dir = Step direction (0 = towards track 0)
    Step = Step pulse
    Head = desired head
*/
void hdc9234_device::auxbus_out()
{
	// prepare output2
	set_bits(m_output2, OUT2_DRVSEL3I, (m_output1 & OUT1_DRVSEL3)==0);

	m_output2 = (m_output2 & 0xb0) | desired_head();
	if (m_reduced_write_current) m_output2 |= OUT2_REDWRT;

	if (TRACE_AUXBUS) logerror("%s: Setting OUTPUT2 to %02x\n", tag(), m_output2);

	if (m_output1 != m_output1_old || m_output2 != m_output2_old)
	{
		// Only propagate changes
		m_out_auxbus((offs_t)HDC_OUTPUT_1, m_output1);
		m_out_auxbus((offs_t)HDC_OUTPUT_2, m_output2);
		m_output1_old = m_output1;
		m_output2_old = m_output2;
	}
}

void hdc9234_device::dma_address_out(UINT8 addrub, UINT8 addrhb, UINT8 addrlb)
{
	if (TRACE_DMA) logerror("%s: Setting DMA address %06x\n", tag(), (addrub<<16 | addrhb<<8 | addrlb)&0xffffff);
	m_out_auxbus((offs_t)HDC_OUTPUT_DMA_ADDR, addrub);
	m_out_auxbus((offs_t)HDC_OUTPUT_DMA_ADDR, addrhb);
	m_out_auxbus((offs_t)HDC_OUTPUT_DMA_ADDR, addrlb);
}

/*
    Set/clear INT

    Interupts are generated in the following occasions:
    - when the DONE bit is set to 1 in the ISR and ST_DONE is set to 1
    - when the READY_CHANGE bit is set to 1 in the ISR and ST_RDYCHNG is set to 1
    (ready change: 1->0 or 0->1)
*/
void hdc9234_device::set_interrupt(line_state intr)
{
	if (intr == ASSERT_LINE)
	{
		// Only if there is not already a pending interrupt
		if ((m_register_r[INT_STATUS] & ST_INTPEND) == 0)
		{
			m_register_r[INT_STATUS] |= ST_INTPEND;
			m_out_intrq(intr);
		}
	}
	else
	{
		// if there is a pending interrupt
		if ((m_register_r[INT_STATUS] & ST_INTPEND) != 0)
			m_out_intrq(intr);
	}
}

/*
    DMA acknowledge line.
*/
WRITE_LINE_MEMBER( hdc9234_device::dmaack )
{
	if (state==ASSERT_LINE)
	{
		if (TRACE_DMA) logerror("%s: [%s] DMA acknowledged\n", tag(), ttsn().cstr());
		set_bits(m_register_r[INT_STATUS], ST_OVRUN, false);
	}
}

/*
    This is pretty simple here, compared to wd17xx, because index and ready
    callbacks have to be tied to the controller board outside the chip.
*/
void hdc9234_device::connect_floppy_drive(floppy_image_device* floppy)
{
	m_floppy = floppy;
}

/*
    Clock divider. This input line actually belongs to the data separator which
    is a separate circuit. Maybe we will take it out of this implementation
    at some time and make it a device of its own.
    line: CD0 (0) and CD1(1), value 0 or 1
*/
void hdc9234_device::set_clock_divider(int line, int value)
{
	set_bits(m_clock_divider, (line==0)? 1 : 2, value&1);
}

/*
    This is reached when a timer has expired
*/
void hdc9234_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	live_sync();

	switch (id)
	{
	case GEN_TIMER:
		reenter_command_processing();
		break;
	case COM_TIMER:
		process_command();
		break;
	case LIVE_TIMER:
		live_run();
		break;
	}
}

/*
    Reset the controller. Negative logic, but we use ASSERT_LINE.
*/
WRITE_LINE_MEMBER( hdc9234_device::reset )
{
	if (state == ASSERT_LINE)
	{
		if (TRACE_LINES) logerror("%s: Reset via RST line\n", tag());
		device_reset();
	}
}

void hdc9234_device::device_start()
{
	m_out_intrq.resolve_safe();
	m_out_dip.resolve_safe();
	m_out_auxbus.resolve_safe();
	m_out_dmarq.resolve_safe();
	m_out_dma.resolve_safe();
	m_in_dma.resolve_safe(0);

	// allocate timers
	m_timer = timer_alloc(GEN_TIMER);
	m_cmd_timer = timer_alloc(COM_TIMER);
	m_live_timer = timer_alloc(LIVE_TIMER);

	m_live_state.state = IDLE;
}

void hdc9234_device::device_reset()
{
	m_clock_divider = 0;
	m_deleted = false;
	m_executing = false;
	m_event_line = UNDEF;
	m_initialized = true;
	m_line_level = CLEAR_LINE;
	m_live_state.state = IDLE;
	m_live_state.time = attotime::never;
	m_multi_sector = false;
	m_output1 = 0;
	m_output2 = 0x80;
	m_output1_old = 1;      // force an initial output
	m_output2_old = 0x81;
	m_precompensation = 0;
	m_reduced_write_current = false;
	m_regvalue = 0;
	m_register_pointer = 0;
	m_retry_save = 0;
	m_seek_count = 0;
	m_selected_drive_number = NODRIVE;
	m_selected_drive_type = 0;
	m_state_after_line = UNDEF;
	m_stop_after_index = false;
	m_substate = UNDEF;
	m_track_delta = 0;
	m_transfer_enabled = true;
	m_wait_for_index = false;
	m_write = false;

	for (int i=0; i<=11; i++)
		m_register_r[i] = m_register_w[i] = 0;

	set_interrupt(CLEAR_LINE);
	m_out_dip(CLEAR_LINE);
	m_out_dmarq(CLEAR_LINE);
}

const device_type HDC9234 = &device_creator<hdc9234_device>;
