summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/hdc9234.c
diff options
context:
space:
mode:
author Michael Zapf <michael.zapf@mizapf.de>2014-06-29 14:58:02 +0000
committer Michael Zapf <michael.zapf@mizapf.de>2014-06-29 14:58:02 +0000
commitb4bc7ee1c685aaa318426b98b13a048c60f442ea (patch)
tree307357ba39a3e2f88bc2746d587f147901335181 /src/emu/machine/hdc9234.c
parentea9fd2f6c2fdb34777ca0f3110d76453b17fa92b (diff)
(MESS) ti99: Work in progress on new HFDC / HDC9234. (nw)
Diffstat (limited to 'src/emu/machine/hdc9234.c')
-rw-r--r--src/emu/machine/hdc9234.c237
1 files changed, 189 insertions, 48 deletions
diff --git a/src/emu/machine/hdc9234.c b/src/emu/machine/hdc9234.c
index 6db995826da..5811b698a77 100644
--- a/src/emu/machine/hdc9234.c
+++ b/src/emu/machine/hdc9234.c
@@ -27,6 +27,28 @@
/*
Register names of the HDC. The left part is the set of write registers,
while the right part are the read registers.
+
+ +------+------+------+------+------+------+------+------+
+ DHEAD: | 0 | Sector size | 0 | Desired head (OUTPUT2) | AT mode
+ +------+------+------+------+------+------+------+------+
+ | 0 | Desired cylinder | Desired head (OUTPUT2) | SMC mode
+ +------+------+------+------+------+------+------+------+
+ +------+------+------+------+------+------+------+------+
+ RETRY: | Retry count | 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
+ +------+------+------+------+------+------+------+------+
*/
enum
{
@@ -51,17 +73,32 @@ enum
*/
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_SEEKERR = 0x10 , // Error in SEEK sequence
- TC_DATAERR = 0x18 , // Error in DATA-TRANSFER seq.
- ST_RDYCHNG = 0x04 , // ready change
- ST_OVRUN = 0x02 , // overrun/underrun
- ST_BADSECT = 0x01 // bad sector
+ 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_SEEKERR = 0x10, // Error in SEEK 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
};
/*
@@ -117,7 +154,6 @@ hdc9234_device::hdc9234_device(const machine_config &mconfig, const char *tag, d
m_out_intrq(*this),
m_out_dip(*this),
m_out_auxbus(*this),
- m_in_auxbus(*this),
m_in_dma(*this),
m_out_dma(*this)
{
@@ -189,19 +225,18 @@ void hdc9234_device::process_command(UINT8 opcode)
// done when no drive is in use
if (TRACE_ACT) logerror("%s: drdeselect command\n", tag());
set_bits(m_output1, OUT1_DRVSEL3|OUT1_DRVSEL2|OUT1_DRVSEL1|OUT1_DRVSEL0, false);
- set_bits(m_output2, OUT2_DRVSEL3I, true); // inverted level
+ sync_latches_out();
}
else if (opcode >= 0x20 && opcode <= 0x3f)
{
// DRIVE SELECT
- if (TRACE_ACT) logerror("%s: drselect command %02x\n", tag(), opcode);
drive_select(opcode&0x1f);
}
else if (opcode >= 0x40 && opcode <= 0x4f)
{
// SETREGPTR
- if (TRACE_ACT) logerror("%s: setregptr command %02x\n", tag(), opcode);
m_register_pointer = opcode & 0xf;
+ if (TRACE_ACT) logerror("%s: setregptr command; start reg=%d\n", tag(), m_register_pointer);
// Spec does not say anything about the effect of setting an
// invalid value (only "care should be taken")
if (m_register_pointer > 10)
@@ -210,6 +245,39 @@ void hdc9234_device::process_command(UINT8 opcode)
m_register_pointer = 10;
}
}
+
+ set_command_done();
+}
+
+/*
+ Assert Command Done status bit, triggering interrupts as needed
+*/
+void hdc9234_device::set_command_done(int flags)
+{
+ 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_ACT) logerror("%s: command %02x done, flags=%02x\n", tag(), m_command, flags);
+ }
+ else
+ {
+ if (TRACE_ACT) logerror("%s: command %02x done\n", tag(), m_command);
+ }
+
+ // [1], p. 6
+ if (m_register_w[INT_COMM_TERM] & TC_INTDONE)
+ set_interrupt(ASSERT_LINE);
+}
+
+/*
+ Preserve previously set termination code
+*/
+void hdc9234_device::set_command_done()
+{
+ set_command_done(-1);
}
void hdc9234_device::drive_select(int driveparm)
@@ -222,13 +290,17 @@ void hdc9234_device::drive_select(int driveparm)
// +-----+-----+-----+-----+-----+-----+-----+-----+
//
// [1] p.5: lower 4 bits of retry count register is put on OUTPUT1
+
m_output1 = (0x10 << (driveparm & 0x03)) | (m_register_w[RETRY_COUNT]&0x0f);
+ // Bit 7 of OUTPUT2 is complement of Bit 7 of OUTPUT1
// 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_head_load_delay_enable = (driveparm>>4)&0x01;
+ if (TRACE_ACT) logerror("%s: drive select command: head load delay=%d, type=%d, drive=%d\n", tag(), m_head_load_delay_enable, m_selected_drive_type, driveparm&3);
+
/*
// We need to store the type of the drive for the poll_drives command
// to be able to correctly select the device (floppy or hard disk).
@@ -244,7 +316,6 @@ void hdc9234_device::drive_select(int driveparm)
m_register_r[CHIP_STATUS] = (m_register_r[CHIP_STATUS] & 0xfc) | (driveparm & 0x03);
sync_latches_out();
- sync_status_in();
}
/*
@@ -257,37 +328,6 @@ void hdc9234_device::connect_floppy_drive(floppy_image_device* floppy)
}
/*
- Get the state from outside and latch it in the register.
- There should be a bus driver on the PCB which provides the signals from
- both the hard and floppy drives during S0=S1=0 and STB*=0 times via the
- auxiliary bus.
-*/
-void hdc9234_device::sync_status_in()
-{
- UINT8 prev = m_register_r[DRIVE_STATUS];
- m_register_r[DRIVE_STATUS] = m_in_auxbus(0);
-
- // Raise interrupt if ready changes.
- if (((m_register_r[DRIVE_STATUS] & HDC_DS_READY) != (prev & HDC_DS_READY))
- & (m_register_r[INT_STATUS] & ST_RDYCHNG))
- {
- set_interrupt(ASSERT_LINE);
- }
-}
-
-/*
- Push the output registers over the auxiliary bus. It is expected that
- the PCB contains latches to store the values.
- TODO: Timing (the spec is not clear at that point)
-*/
-void hdc9234_device::sync_latches_out()
-{
- m_output1 = (m_output1 & 0xf0) | (m_register_w[RETRY_COUNT]&0x0f);
- m_out_auxbus((offs_t)HDC_OUTPUT_1, m_output1);
- m_out_auxbus((offs_t)HDC_OUTPUT_2, m_output2);
-}
-
-/*
Read a byte of data from the controller
The address (offset) encodes the C/D* line (command and /data)
*/
@@ -351,6 +391,100 @@ WRITE8_MEMBER( hdc9234_device::write )
}
/*
+ 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 |
+ +------+------+------+------+------+------+------+------+
+
+
+ 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
+*/
+
+/*
+ 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)
+{
+ if (TRACE_ACT) logerror("%s: Got value %02x via auxbus\n", tag(), data);
+ UINT8 prev = m_register_r[DRIVE_STATUS];
+ m_register_r[DRIVE_STATUS] = data;
+
+ // Raise interrupt if ready changes.
+ if (((m_register_r[DRIVE_STATUS] & HDC_DS_READY) != (prev & HDC_DS_READY))
+ & (m_register_r[INT_STATUS] & ST_RDYCHNG))
+ {
+ set_interrupt(ASSERT_LINE);
+ }
+}
+
+/*
+ Push the output registers over the auxiliary bus. It is expected that
+ the PCB contains latches to store the values.
+*/
+void hdc9234_device::sync_latches_out()
+{
+ m_out_auxbus((offs_t)HDC_OUTPUT_1, m_output1);
+ set_bits(m_output2, OUT2_DRVSEL3I, (m_output1 & 0x80)==0);
+ m_out_auxbus((offs_t)HDC_OUTPUT_2, m_output2);
+}
+
+/*
Reset the controller. Negative logic, but we use ASSERT_LINE.
*/
WRITE_LINE_MEMBER( hdc9234_device::reset )
@@ -368,7 +502,6 @@ void hdc9234_device::device_start()
m_out_intrq.resolve_safe();
m_out_dip.resolve_safe();
m_out_auxbus.resolve_safe();
- m_in_auxbus.resolve_safe(0);
m_out_dma.resolve_safe();
m_in_dma.resolve_safe(0);
@@ -381,6 +514,14 @@ void hdc9234_device::device_reset()
m_selected_drive_type = 0;
m_head_load_delay_enable = false;
m_register_pointer = 0;
+ m_output1 = 0;
+ m_output2 = 0x80;
+
+ set_interrupt(CLEAR_LINE);
+ m_out_dip(CLEAR_LINE);
+
+ for (int i=0; i<=11; i++)
+ m_register_r[i] = m_register_w[i] = 0;
}
const device_type HDC9234 = &device_creator<hdc9234_device>;