trunk/src/emu/machine/hdc9234.c
r32114 | r32115 | |
| 1 | // license:BSD-3-Clause |
| 2 | // copyright-holders:Michael Zapf |
1 | 3 | /************************************************************************** |
2 | 4 | |
3 | 5 | HDC9234 Hard and Floppy Disk Controller |
r32114 | r32115 | |
190 | 192 | OUT2_HEADSEL = 0x0f |
191 | 193 | }; |
192 | 194 | |
| 195 | #define NODRIVE -1 |
| 196 | |
193 | 197 | enum |
194 | 198 | { |
195 | 199 | TYPE_AT = 0x00, |
r32114 | r32115 | |
229 | 233 | static const int step_flop5[] = { 436, 1000, 2000, 4000, 8000, 16000, 32000, 64000 }; |
230 | 234 | |
231 | 235 | /* |
| 236 | Head load timer increments in usec. Delay value is calculated from this value |
| 237 | multiplied by the factor in the DATA/DELAY register. For FM mode all |
| 238 | values are doubled. The values depend on the drive type. |
| 239 | */ |
| 240 | static const int head_load_timer_increment[] = { 200, 200, 2000, 4000 }; |
| 241 | |
| 242 | /* |
232 | 243 | ID fields association to registers |
233 | 244 | */ |
234 | 245 | static const int id_field[] = { CURRENT_CYLINDER, CURRENT_HEAD, CURRENT_SECTOR, CURRENT_SIZE, CURRENT_CRC1, CURRENT_CRC2 }; |
r32114 | r32115 | |
265 | 276 | RESTORE_CHECK1, |
266 | 277 | RESTORE_CHECK2, |
267 | 278 | SEEK_COMPLETE, |
| 279 | HEAD_DELAY, |
268 | 280 | |
269 | 281 | READ_ID = 0x40, |
270 | 282 | READ_ID1, |
r32114 | r32115 | |
312 | 324 | |
313 | 325 | const hdc9234_device::cmddef hdc9234_device::s_command[] = |
314 | 326 | { |
315 | | { 0x00, 0xff, &hdc9234_device::device_reset }, |
| 327 | { 0x00, 0xff, &hdc9234_device::reset_controller }, |
316 | 328 | { 0x01, 0xff, &hdc9234_device::drive_deselect }, |
317 | 329 | { 0x02, 0xfe, &hdc9234_device::restore_drive }, |
318 | 330 | { 0x04, 0xfc, &hdc9234_device::step_drive }, |
r32114 | r32115 | |
510 | 522 | // If an error occured (no IDAM found), terminate the command |
511 | 523 | if ((m_register_r[CHIP_STATUS] & CS_SYNCERR) != 0) |
512 | 524 | { |
513 | | logerror("%s: READ_ID failed to find an IDAM\n", tag()); |
| 525 | logerror("%s: READ_ID failed to find any IDAM\n", tag()); |
514 | 526 | cont = ERROR; |
515 | 527 | break; |
516 | 528 | } |
r32114 | r32115 | |
602 | 614 | // (This test is only relevant when we did not have a seek phase before) |
603 | 615 | if ((m_register_r[CHIP_STATUS] & CS_SYNCERR) != 0) |
604 | 616 | { |
605 | | logerror("%s: VERIFY failed to find an IDAM\n", tag()); |
| 617 | logerror("%s: VERIFY failed to find any IDAM\n", tag()); |
606 | 618 | cont = ERROR; |
607 | 619 | break; |
608 | 620 | } |
r32114 | r32115 | |
853 | 865 | // =========================================================================== |
854 | 866 | |
855 | 867 | /* |
| 868 | RESET |
| 869 | */ |
| 870 | void hdc9234_device::reset_controller() |
| 871 | { |
| 872 | if (TRACE_COMMAND) logerror("%s: RESET command\n", tag()); |
| 873 | device_reset(); |
| 874 | } |
| 875 | /* |
856 | 876 | DESELECT DRIVE |
857 | | done when no drive is in use |
858 | 877 | */ |
859 | 878 | void hdc9234_device::drive_deselect() |
860 | 879 | { |
861 | 880 | if (TRACE_COMMAND) logerror("%s: DESELECT command\n", tag()); |
862 | | set_bits(m_output1, OUT1_DRVSEL3|OUT1_DRVSEL2|OUT1_DRVSEL1|OUT1_DRVSEL0, false); |
| 881 | m_selected_drive_number = NODRIVE; |
863 | 882 | auxbus_out(); |
864 | 883 | set_command_done(TC_SUCCESS); |
865 | 884 | } |
866 | 885 | |
867 | 886 | /* |
868 | | Step on / off; used by RESTORE and STEP IN/OUT |
869 | | */ |
870 | | void hdc9234_device::step_on(bool towards00, int next) |
871 | | { |
872 | | if (TRACE_ACT) logerror("%s: substate STEP_ON\n", tag()); |
873 | | |
874 | | // STEPDIR = 0 -> towards TRK00 |
875 | | set_bits(m_output2, OUT2_STEPDIR, !towards00); |
876 | | |
877 | | // Raising edge (note that all signals must be inverted before leading them to the drive) |
878 | | set_bits(m_output2, OUT2_STEPPULSE, true); |
879 | | auxbus_out(); |
880 | | wait_time(m_timer, pulse_width(), next); |
881 | | } |
882 | | |
883 | | void hdc9234_device::step_off(int next) |
884 | | { |
885 | | if (TRACE_ACT) logerror("%s: substate STEP_OFF\n", tag()); |
886 | | set_bits(m_output2, OUT2_STEPPULSE, false); |
887 | | auxbus_out(); |
888 | | wait_time(m_timer, step_time(), next); |
889 | | } |
890 | | |
891 | | /* |
892 | 887 | // RESTORE DRIVE |
893 | 888 | // bit 0: |
894 | 889 | // 0 -> command ends after last seek pulse, |
r32114 | r32115 | |
1023 | 1018 | |
1024 | 1019 | void hdc9234_device::drive_select() |
1025 | 1020 | { |
1026 | | int driveparm = current_command() & 0x1f; |
| 1021 | int cont = CONTINUE; |
| 1022 | int head_load_delay = 0; |
1027 | 1023 | |
1028 | | m_output1 = (0x10 << (driveparm & 0x03)) | (m_register_w[RETRY_COUNT]&0x0f); |
| 1024 | if (m_substate == UNDEF) |
| 1025 | { |
| 1026 | int driveparm = current_command() & 0x1f; |
| 1027 | bool head_load_delay_enable = (driveparm & 0x10)!=0; |
1029 | 1028 | |
1030 | | // The drive type is used to configure DMA burst mode ([1], p.12) |
1031 | | // and to select the timing parameters |
1032 | | m_selected_drive_type = (driveparm>>2) & 0x03; |
1033 | | m_head_load_delay_enable = (driveparm>>4)&0x01; |
| 1029 | // The drive type is used to configure DMA burst mode ([1], p.12) |
| 1030 | // and to select the timing parameters |
| 1031 | m_selected_drive_type = (driveparm>>2) & 0x03; |
| 1032 | m_selected_drive_number = driveparm & 0x03; |
1034 | 1033 | |
1035 | | if (TRACE_COMMAND) logerror("%s: DRIVE SELECT command (%02x): head load delay=%d, type=%d, drive=%d, pout=%02x\n", tag(), current_command(), m_head_load_delay_enable, m_selected_drive_type, driveparm&3, m_register_w[RETRY_COUNT]&0x0f); |
| 1034 | // Calculate the head load delays |
| 1035 | head_load_delay = head_load_delay_enable? m_register_w[DATA] * head_load_timer_increment[m_selected_drive_type] : 0; |
| 1036 | if (fm_mode()) head_load_delay <<= 1; |
1036 | 1037 | |
1037 | | if (m_substate != UNDEF) |
| 1038 | if (TRACE_COMMAND) logerror("%s: DRIVE SELECT command (%02x): head load delay=%d, type=%d, drive=%d, pout=%02x\n", tag(), current_command(), head_load_delay, m_selected_drive_type, driveparm&3, m_register_w[RETRY_COUNT]&0x0f); |
| 1039 | |
| 1040 | // Copy the DMA registers to registers CURRENT_HEAD, CURRENT_CYLINDER, |
| 1041 | // and CURRENT_IDENT. This is required during formatting ([1], p. 14) |
| 1042 | // as the format command reuses the registers for formatting parameters. |
| 1043 | m_register_r[CURRENT_HEAD] = m_register_r[DMA7_0]; |
| 1044 | m_register_r[CURRENT_CYLINDER] = m_register_r[DMA15_8]; |
| 1045 | m_register_r[CURRENT_IDENT] = m_register_r[DMA23_16]; |
| 1046 | |
| 1047 | // Copy the selected drive number to the chip status register |
| 1048 | m_register_r[CHIP_STATUS] = (m_register_r[CHIP_STATUS] & 0xfc) | m_selected_drive_number; |
| 1049 | auxbus_out(); |
| 1050 | |
| 1051 | m_substate = (head_load_delay>0)? HEAD_DELAY : DONE; |
| 1052 | } |
| 1053 | |
| 1054 | // As for the head delay, the specs are not clear when it is applied. |
| 1055 | // There is no input line indicating whether the head is already loaded |
| 1056 | // (see WD17xx: HLT). Let's assume for now that the head is loaded with |
| 1057 | // this drive select operation, and that we have the delay here. |
| 1058 | switch (m_substate) |
1038 | 1059 | { |
1039 | | logerror("%s: substate = %d\n", tag(), m_substate); |
| 1060 | case HEAD_DELAY: |
| 1061 | wait_time(m_timer, head_load_delay, DONE); |
| 1062 | cont = WAIT; |
| 1063 | break; |
| 1064 | case DONE: |
| 1065 | cont = SUCCESS; |
| 1066 | break; |
1040 | 1067 | } |
1041 | 1068 | |
1042 | | // Copy the DMA registers to registers CURRENT_HEAD, CURRENT_CYLINDER, |
1043 | | // and CURRENT_IDENT. This is required during formatting ([1], p. 14) |
1044 | | // as the format command reuses the registers for formatting parameters. |
1045 | | m_register_r[CURRENT_HEAD] = m_register_r[DMA7_0]; |
1046 | | m_register_r[CURRENT_CYLINDER] = m_register_r[DMA15_8]; |
1047 | | m_register_r[CURRENT_IDENT] = m_register_r[DMA23_16]; |
1048 | | |
1049 | | // Copy the selected drive number to the chip status register |
1050 | | m_register_r[CHIP_STATUS] = (m_register_r[CHIP_STATUS] & 0xfc) | (driveparm & 0x03); |
1051 | | |
1052 | | auxbus_out(); |
1053 | | set_command_done(TC_SUCCESS); |
| 1069 | if (cont==SUCCESS) set_command_done(TC_SUCCESS); |
1054 | 1070 | } |
1055 | 1071 | |
1056 | 1072 | /* |
r32114 | r32115 | |
1197 | 1213 | // =========================================================================== |
1198 | 1214 | |
1199 | 1215 | /* |
| 1216 | Step on / off; used by RESTORE and STEP IN/OUT |
| 1217 | */ |
| 1218 | void hdc9234_device::step_on(bool towards00, int next) |
| 1219 | { |
| 1220 | if (TRACE_ACT) logerror("%s: substate STEP_ON\n", tag()); |
| 1221 | |
| 1222 | // STEPDIR = 0 -> towards TRK00 |
| 1223 | set_bits(m_output2, OUT2_STEPDIR, !towards00); |
| 1224 | |
| 1225 | // Raising edge (note that all signals must be inverted before leading them to the drive) |
| 1226 | set_bits(m_output2, OUT2_STEPPULSE, true); |
| 1227 | auxbus_out(); |
| 1228 | wait_time(m_timer, pulse_width(), next); |
| 1229 | } |
| 1230 | |
| 1231 | void hdc9234_device::step_off(int next) |
| 1232 | { |
| 1233 | if (TRACE_ACT) logerror("%s: substate STEP_OFF\n", tag()); |
| 1234 | set_bits(m_output2, OUT2_STEPPULSE, false); |
| 1235 | auxbus_out(); |
| 1236 | wait_time(m_timer, step_time(), next); |
| 1237 | } |
| 1238 | |
| 1239 | /* |
1200 | 1240 | Delivers the step time (in microseconds) minus the pulse width |
1201 | 1241 | */ |
1202 | 1242 | int hdc9234_device::step_time() |
r32114 | r32115 | |
1697 | 1737 | |
1698 | 1738 | if (m_transfer_enabled) |
1699 | 1739 | { |
| 1740 | m_register_r[DATA] = m_register_w[DATA] = m_live_state.data_reg; |
1700 | 1741 | m_out_dip(ASSERT_LINE); |
1701 | | m_out_dma(0, m_live_state.data_reg, 0xff); |
| 1742 | m_out_dma(0, m_register_r[DATA], 0xff); |
1702 | 1743 | m_out_dip(CLEAR_LINE); |
1703 | 1744 | |
1704 | 1745 | m_out_dmarq(CLEAR_LINE); |
r32114 | r32115 | |
1843 | 1884 | { |
1844 | 1885 | // Read byte via DMA |
1845 | 1886 | m_out_dip(ASSERT_LINE); |
1846 | | UINT8 data = m_in_dma(0, 0xff); |
1847 | | if (TRACE_WRITE) logerror("%s: [%s] Write %02x\n", tag(), tts(m_live_state.time).cstr(), data); |
1848 | | encode_byte(data); |
| 1887 | m_register_r[DATA] = m_register_w[DATA] = m_in_dma(0, 0xff); |
| 1888 | if (TRACE_WRITE) logerror("%s: [%s] Write %02x\n", tag(), tts(m_live_state.time).cstr(), m_register_r[DATA]); |
| 1889 | encode_byte(m_register_r[DATA]); |
1849 | 1890 | m_out_dip(CLEAR_LINE); |
1850 | 1891 | m_out_dmarq(CLEAR_LINE); |
1851 | 1892 | |
r32114 | r32115 | |
2148 | 2189 | */ |
2149 | 2190 | WRITE8_MEMBER( hdc9234_device::write ) |
2150 | 2191 | { |
2151 | | m_data = data & 0xff; |
2152 | | |
2153 | 2192 | if ((offset & 1) == 0) |
2154 | 2193 | { |
| 2194 | m_regvalue = data & 0xff; |
2155 | 2195 | wait_time(m_cmd_timer, attotime::from_nsec(REGISTER_COMMIT), REGISTER_ACCESS); |
2156 | 2196 | } |
2157 | 2197 | else |
2158 | 2198 | { |
2159 | 2199 | if (m_executing) |
2160 | 2200 | { |
2161 | | logerror("%s: [%s] Error - previous command %02x not completed; new command %02x ignored\n", tag(), ttsn().cstr(), current_command(), m_data); |
| 2201 | logerror("%s: [%s] Error - previous command %02x not completed; new command %02x ignored\n", tag(), ttsn().cstr(), current_command(), data); |
2162 | 2202 | } |
2163 | 2203 | else |
2164 | 2204 | { |
| 2205 | m_register_w[COMMAND] = data; |
2165 | 2206 | wait_time(m_cmd_timer, attotime::from_nsec(COMMAND_COMMIT), COMMAND_INIT); |
2166 | 2207 | } |
2167 | 2208 | } |
r32114 | r32115 | |
2179 | 2220 | if (TRACE_REG) |
2180 | 2221 | { |
2181 | 2222 | if (m_register_pointer == INT_COMM_TERM) |
2182 | | logerror("%s: Setting interrupt trigger DONE=%d READY=%d\n", tag(), (m_data & TC_INTDONE)? 1:0, (m_data & TC_INTRDCH)? 1:0); |
| 2223 | logerror("%s: Setting interrupt trigger DONE=%d READY=%d\n", tag(), (m_regvalue & TC_INTDONE)? 1:0, (m_regvalue & TC_INTRDCH)? 1:0); |
2183 | 2224 | else |
2184 | | logerror("%s: register[%d] <- %02x\n", tag(), m_register_pointer, m_data); |
| 2225 | logerror("%s: register[%d] <- %02x\n", tag(), m_register_pointer, m_regvalue); |
2185 | 2226 | } |
2186 | | m_register_w[m_register_pointer] = m_data; |
| 2227 | m_register_w[m_register_pointer] = m_regvalue; |
2187 | 2228 | |
2188 | 2229 | // Changes to these registers must be output via the auxbus |
2189 | 2230 | if (m_register_pointer == DESIRED_HEAD || m_register_pointer == RETRY_COUNT) |
r32114 | r32115 | |
2191 | 2232 | |
2192 | 2233 | // The DMA registers and the sector register for read and |
2193 | 2234 | // write are identical, so in that case we copy the contents |
2194 | | if (m_register_pointer < DESIRED_HEAD) m_register_r[m_register_pointer] = m_data; |
| 2235 | if (m_register_pointer < DESIRED_HEAD) m_register_r[m_register_pointer] = m_regvalue; |
2195 | 2236 | |
2196 | 2237 | // Autoincrement until DATA is reached. |
2197 | 2238 | if (m_register_pointer < DATA) m_register_pointer++; |
r32114 | r32115 | |
2207 | 2248 | // Clear Interrupt Pending and Ready Change |
2208 | 2249 | set_bits(m_register_r[INT_STATUS], ST_INTPEND | ST_RDYCHNG, false); |
2209 | 2250 | |
2210 | | // Store command |
2211 | | UINT8 command = m_data; |
2212 | | m_register_w[COMMAND] = command; |
2213 | | m_stop_after_index = false; |
2214 | | m_wait_for_index = false; |
2215 | | |
2216 | 2251 | int index = 0; |
2217 | 2252 | bool found = false; |
2218 | 2253 | |
2219 | 2254 | while (s_command[index].mask!=0 && !found) |
2220 | 2255 | { |
2221 | | if ((command & s_command[index].mask) == s_command[index].baseval) |
| 2256 | if ((m_register_w[COMMAND] & s_command[index].mask) == s_command[index].baseval) |
2222 | 2257 | { |
2223 | | // Invoke command |
| 2258 | found = true; |
| 2259 | |
| 2260 | m_stop_after_index = false; |
| 2261 | m_wait_for_index = false; |
2224 | 2262 | m_substate = UNDEF; |
2225 | | found = true; |
2226 | 2263 | m_executing = true; |
2227 | 2264 | m_command = s_command[index].command; |
| 2265 | // Invoke command |
2228 | 2266 | (this->*m_command)(); |
2229 | 2267 | } |
2230 | 2268 | else index++; |
2231 | 2269 | } |
2232 | 2270 | if (!found) |
2233 | 2271 | { |
2234 | | logerror("%s: Command %02x not defined\n", tag(), command); |
| 2272 | logerror("%s: Command %02x not defined\n", tag(), m_register_w[COMMAND]); |
2235 | 2273 | } |
2236 | 2274 | } |
2237 | 2275 | } |
r32114 | r32115 | |
2405 | 2443 | */ |
2406 | 2444 | void hdc9234_device::auxbus_out() |
2407 | 2445 | { |
| 2446 | m_output1 = (m_selected_drive_number != NODRIVE)? (0x10 << m_selected_drive_number) : 0; |
| 2447 | m_output1 |= (m_register_w[RETRY_COUNT]&0x0f); |
| 2448 | |
2408 | 2449 | if (TRACE_AUXBUS) logerror("%s: Setting OUTPUT1 to %02x\n", tag(), m_output1); |
2409 | 2450 | m_out_auxbus((offs_t)HDC_OUTPUT_1, m_output1); |
2410 | 2451 | |
r32114 | r32115 | |
2454 | 2495 | { |
2455 | 2496 | if (state==ASSERT_LINE) |
2456 | 2497 | { |
2457 | | if (TRACE_LINES) logerror("%s: [%s] DMA acknowledged\n", tag(), ttsn().cstr()); |
| 2498 | if (TRACE_LIVE) logerror("%s: [%s] DMA acknowledged\n", tag(), ttsn().cstr()); |
2458 | 2499 | set_bits(m_register_r[INT_STATUS], ST_OVRUN, false); |
2459 | 2500 | } |
2460 | 2501 | } |
r32114 | r32115 | |
2466 | 2507 | { |
2467 | 2508 | if (state == ASSERT_LINE) |
2468 | 2509 | { |
2469 | | if (TRACE_ACT) logerror("%s: Reset via RST line\n", tag()); |
| 2510 | if (TRACE_LINES) logerror("%s: Reset via RST line\n", tag()); |
2470 | 2511 | device_reset(); |
2471 | 2512 | } |
2472 | 2513 | } |
2473 | 2514 | |
2474 | 2515 | void hdc9234_device::device_start() |
2475 | 2516 | { |
2476 | | logerror("%s: Start\n", tag()); |
2477 | 2517 | m_out_intrq.resolve_safe(); |
2478 | 2518 | m_out_dip.resolve_safe(); |
2479 | 2519 | m_out_auxbus.resolve_safe(); |
r32114 | r32115 | |
2491 | 2531 | |
2492 | 2532 | void hdc9234_device::device_reset() |
2493 | 2533 | { |
2494 | | logerror("%s: Reset\n", tag()); |
2495 | | |
2496 | | m_selected_drive_type = 0; |
2497 | | m_head_load_delay_enable = false; |
2498 | | |
2499 | | m_register_pointer = 0; |
2500 | | |
| 2534 | m_deleted = false; |
| 2535 | m_executing = false; |
| 2536 | m_initialized = true; |
| 2537 | m_live_state.state = IDLE; |
| 2538 | m_live_state.time = attotime::never; |
| 2539 | m_multi_sector = false; |
2501 | 2540 | m_output1 = 0; |
2502 | 2541 | m_output2 = 0x80; |
2503 | | |
2504 | | set_interrupt(CLEAR_LINE); |
2505 | | m_out_dip(CLEAR_LINE); |
2506 | | m_out_dmarq(CLEAR_LINE); |
2507 | | |
2508 | | for (int i=0; i<=11; i++) |
2509 | | m_register_r[i] = m_register_w[i] = 0; |
2510 | | |
| 2542 | m_precompensation = 0; |
| 2543 | m_reduced_write_current = false; |
| 2544 | m_regvalue = 0; |
| 2545 | m_register_pointer = 0; |
| 2546 | m_retry_save = 0; |
| 2547 | m_seek_count = 0; |
| 2548 | m_selected_drive_number = NODRIVE; |
| 2549 | m_selected_drive_type = 0; |
2511 | 2550 | m_state_after_line = UNDEF; |
2512 | | m_seek_count = 0; |
2513 | | |
2514 | | m_live_state.time = attotime::never; |
2515 | | m_live_state.state = IDLE; |
2516 | | |
| 2551 | m_stop_after_index = false; |
| 2552 | m_substate = UNDEF; |
2517 | 2553 | m_track_delta = 0; |
2518 | | |
2519 | | m_multi_sector = false; |
2520 | | m_retry_save = 0; |
2521 | | |
2522 | | m_substate = UNDEF; |
2523 | | |
2524 | | m_executing = false; |
2525 | | |
2526 | | m_stop_after_index = false; |
| 2554 | m_transfer_enabled = true; |
2527 | 2555 | m_wait_for_index = false; |
2528 | | |
2529 | | m_transfer_enabled = true; |
2530 | 2556 | m_write = false; |
2531 | | m_deleted = false; |
2532 | 2557 | |
2533 | | m_data = 0; |
2534 | | m_precompensation = 0; |
2535 | | m_reduced_write_current = false; |
| 2558 | for (int i=0; i<=11; i++) |
| 2559 | m_register_r[i] = m_register_w[i] = 0; |
2536 | 2560 | |
2537 | | m_initialized = true; |
| 2561 | set_interrupt(CLEAR_LINE); |
| 2562 | m_out_dip(CLEAR_LINE); |
| 2563 | m_out_dmarq(CLEAR_LINE); |
2538 | 2564 | } |
2539 | 2565 | |
2540 | 2566 | const device_type HDC9234 = &device_creator<hdc9234_device>; |