From b4bc7ee1c685aaa318426b98b13a048c60f442ea Mon Sep 17 00:00:00 2001 From: Michael Zapf Date: Sun, 29 Jun 2014 14:58:02 +0000 Subject: (MESS) ti99: Work in progress on new HFDC / HDC9234. (nw) --- src/emu/machine/hdc9234.c | 237 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 189 insertions(+), 48 deletions(-) (limited to 'src/emu/machine/hdc9234.c') 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(); } /* @@ -256,37 +327,6 @@ void hdc9234_device::connect_floppy_drive(floppy_image_device* floppy) m_floppy = 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) @@ -350,6 +390,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. */ @@ -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; -- cgit v1.2.3-70-g09d2