diff options
-rw-r--r-- | scripts/src/machine.lua | 12 | ||||
-rw-r--r-- | src/devices/machine/1801vp128.cpp | 785 | ||||
-rw-r--r-- | src/devices/machine/1801vp128.h | 209 |
3 files changed, 1006 insertions, 0 deletions
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 01d9c14ed3d..30425b67a80 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1940,6 +1940,18 @@ end --------------------------------------------------- -- +--@src/devices/machine/1801vp128.h,MACHINES["K1801VP128"] = true +--------------------------------------------------- + +if (MACHINES["K1801VP128"]~=null) then + files { + MAME_DIR .. "src/devices/machine/1801vp128.cpp", + MAME_DIR .. "src/devices/machine/1801vp128.h", + } +end + +--------------------------------------------------- +-- --@src/devices/machine/kb3600.h,MACHINES["KB3600"] = true --------------------------------------------------- diff --git a/src/devices/machine/1801vp128.cpp b/src/devices/machine/1801vp128.cpp new file mode 100644 index 00000000000..58d399dbd2d --- /dev/null +++ b/src/devices/machine/1801vp128.cpp @@ -0,0 +1,785 @@ +// license:BSD-3-Clause +// copyright-holders:Sergey Svishchev +/********************************************************************** + + 1801VP1-128 gate array (MFM codec for floppy controllers) + + https://github.com/1801BM1/k1801/tree/master/128 + https://felixl.com/UKNC_FDD_1801vp1-128 + https://zx-pk.ru/threads/20406-emulyatsiya-1801vp1-128-v-plis.html + + To do: + - DRQ status in read and write modes is tracked separately (TR bit) + - missing MFM clock is added by MFM encoder for every 00 sequence + - deep internals of CRC, GDR bits; read/write mode switching + - optional external timer for PY device + +**********************************************************************/ + +#include "emu.h" +#include "1801vp128.h" + +#define LOG_WARN (1U << 1) // Show warnings +#define LOG_SHIFT (1U << 2) // Shows shift register contents +#define LOG_REGS (1U << 6) // Register I/O +#define LOG_STATE (1U << 11) // State machine +#define LOG_LIVE (1U << 12) // Live states + +//#define VERBOSE (LOG_GENERAL | LOG_REGS | LOG_STATE) +#include "logmacro.h" + +#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__) +#define LOGSHIFT(...) LOGMASKED(LOG_SHIFT, __VA_ARGS__) +#define LOGREGS(...) LOGMASKED(LOG_REGS, __VA_ARGS__) +#define LOGLIVE(...) LOGMASKED(LOG_LIVE, __VA_ARGS__) +#define LOGSTATE(...) LOGMASKED(LOG_STATE, __VA_ARGS__) + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +DEFINE_DEVICE_TYPE(K1801VP128, k1801vp128_device, "1801vp1-128", "1801VP1-128") + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// k1801vp128_device - constructor +//------------------------------------------------- + +k1801vp128_device::k1801vp128_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, K1801VP128, tag, owner, clock) + , m_connectors(*this, "%u", 0U) + , m_read_ds(*this, -1) +{ + memset(&cur_live, 0x00, sizeof(cur_live)); + cur_live.tm = attotime::never; + cur_live.state = IDLE; + cur_live.next_state = -1; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void k1801vp128_device::device_start() +{ + for (int i = 0; i != 4; i++) + { + flopi[i].tm = timer_alloc(FUNC(k1801vp128_device::update_floppy), this); + flopi[i].id = i; + if (m_connectors[i]) + { + flopi[i].dev = m_connectors[i]->get_device(); + if (flopi[i].dev != nullptr) + flopi[i].dev->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&k1801vp128_device::index_callback, this)); + } + else + flopi[i].dev = nullptr; + + flopi[i].main_state = IDLE; + flopi[i].sub_state = IDLE; + flopi[i].live = false; + } + + m_wbuf = m_rbuf = 0; + + // register for state saving + save_item(NAME(selected_drive)); + save_item(NAME(m_cr)); + save_item(NAME(m_sr)); + save_item(NAME(m_rbuf)); + save_item(NAME(m_wbuf)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void k1801vp128_device::device_reset() +{ + for (int i = 0; i < 4; i++) + { + flopi[i].main_state = IDLE; + flopi[i].sub_state = IDLE; + flopi[i].live = false; + } + live_abort(); + m_cr = m_sr = 0; + set_ds(-1); +} + +//------------------------------------------------- +// read - register read +//------------------------------------------------- + +uint16_t k1801vp128_device::read(offs_t offset) +{ + uint16_t data = 0; + + switch (offset & 1) + { + case 0: + data = m_sr; + if (selected_drive != -1) + { + floppy_info &fi = flopi[selected_drive]; + data = (fi.dev->trk00_r() ^ 1) | (fi.dev->ready_r() << 1) | (fi.dev->wpt_r() << 2) | (fi.dev->idx_r() << 15) | m_sr; + } + break; + + case 1: + data = m_rbuf; + if (!machine().side_effects_disabled()) + { + m_sr &= ~CSR_R_TR; + if (selected_drive != -1) + { + floppy_info &fi = flopi[selected_drive]; + if (fi.main_state == WRITE_DATA) + { + // semi-read mode + live_abort(); + fi.main_state = READ_DATA; + fi.sub_state = SCAN_ID; + read_data_continue(fi); + } + } + } + break; + } + + return data; +} + + +//------------------------------------------------- +// write - register write +//------------------------------------------------- + +void k1801vp128_device::write(offs_t offset, uint16_t data) +{ + LOGREGS("%s W %06o <- %06o\n", machine().describe_context(), 0177130 + (offset << 1), data); + + switch (offset & 1) + { + case 0: + set_ds((int16_t)m_read_ds(data & (CSR_W_DS | CSR_W_REZ))); + if (selected_drive != -1) + { + floppy_info &fi = flopi[selected_drive]; + fi.dev->mon_w(!BIT(data, 4)); + fi.dev->ss_w(BIT(data, 5)); + if (BIT(data, 7)) + { + LOG("COMMAND STEP %d %s\n", fi.id, BIT(data, 6) ? "+1" : "-1"); + execute_command(CMD_SEEK); + } + if (BIT(m_cr ^ data, 8) && !BIT(data, 8)) + { + LOG("COMMAND READ drive %d c:h %d:%d\n", selected_drive, fi.dev->get_cyl(), BIT(data, 5)); + m_sr &= ~CSR_R_TR; + execute_command(CMD_READ); + } + } + m_cr = data; + break; + + case 1: + m_wbuf = data; + m_sr &= ~CSR_R_TR; + if (selected_drive != -1) + { + floppy_info &fi = flopi[selected_drive]; + if (fi.main_state != WRITE_DATA) + { + LOG("COMMAND WRITE drive %d c:h %d:%d\n", selected_drive, fi.dev->get_cyl(), BIT(m_cr, 5)); + execute_command(CMD_WRITE); + } + } + break; + } +} + +void k1801vp128_device::execute_command(int command) +{ + live_abort(); + + switch (command) + { + case CMD_READ: + read_data_start(flopi[selected_drive]); + break; + + case CMD_WRITE: + write_data_start(flopi[selected_drive]); + break; + + case CMD_SEEK: + seek_start(flopi[selected_drive]); + break; + } +} + + +//------------------------------------------------- +// update_tick - pump the device life cycle +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(k1801vp128_device::update_floppy) +{ + live_sync(); + + floppy_info &fi = flopi[param]; + switch (fi.sub_state) + { + case SEEK_WAIT_STEP_SIGNAL_TIME: + fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME_DONE; + break; + case SEEK_WAIT_STEP_TIME: + fi.sub_state = SEEK_WAIT_STEP_TIME_DONE; + break; + } + + general_continue(fi); +} + +void k1801vp128_device::live_start(floppy_info &fi, int state) +{ + cur_live.tm = machine().time(); + cur_live.state = state; + cur_live.next_state = -1; + cur_live.fi = &fi; + cur_live.shift_reg = 0; + cur_live.crc = 0xffff; + cur_live.crc_init = false; + cur_live.bit_counter = 0; + cur_live.data_separator_phase = false; + cur_live.data_reg = 0; + cur_live.data_bit_context = false; + cur_live.pll.reset(cur_live.tm); + cur_live.pll.set_clock(attotime::from_hz(500000)); + checkpoint_live = cur_live; + fi.live = true; + + live_run(); +} + +void k1801vp128_device::checkpoint() +{ + if (cur_live.fi) + cur_live.pll.commit(cur_live.fi->dev, cur_live.tm); + checkpoint_live = cur_live; +} + +void k1801vp128_device::rollback() +{ + cur_live = checkpoint_live; +} + +void k1801vp128_device::live_delay(int state) +{ + cur_live.next_state = state; + if (cur_live.tm != machine().time()) + cur_live.fi->tm->adjust(cur_live.tm - machine().time(), cur_live.fi->id); + else + live_sync(); +} + +void k1801vp128_device::live_sync() +{ + if (!cur_live.tm.is_never()) + { + if (cur_live.tm > machine().time()) + { + rollback(); + live_run(machine().time()); + cur_live.pll.commit(cur_live.fi->dev, cur_live.tm); + } + else + { + cur_live.pll.commit(cur_live.fi->dev, cur_live.tm); + if (cur_live.next_state != -1) + { + cur_live.state = cur_live.next_state; + cur_live.next_state = -1; + } + if (cur_live.state == IDLE) + { + cur_live.pll.stop_writing(cur_live.fi->dev, cur_live.tm); + cur_live.tm = attotime::never; + cur_live.fi->live = false; + cur_live.fi = nullptr; + } + } + cur_live.next_state = -1; + checkpoint(); + } +} + +void k1801vp128_device::live_abort() +{ + if (!cur_live.tm.is_never() && cur_live.tm > machine().time()) + { + rollback(); + live_run(machine().time()); + } + + if (cur_live.fi) + { + cur_live.pll.stop_writing(cur_live.fi->dev, cur_live.tm); + cur_live.fi->live = false; + cur_live.fi = nullptr; + } + + cur_live.tm = attotime::never; + cur_live.state = IDLE; + cur_live.next_state = -1; +} + +void k1801vp128_device::live_run(attotime limit) +{ + if (cur_live.state == IDLE || cur_live.next_state != -1) + return; + + if (limit == attotime::never) + { + if (cur_live.fi->dev) + limit = cur_live.fi->dev->time_next_index(); + if (limit == attotime::never) + { + // Happens when there's no disk or if the fdc is not + // connected to a drive, hence no index pulse. 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); + cur_live.fi->tm->adjust(attotime::from_msec(1), cur_live.fi->id); + } + } + + for (;;) + { + switch (cur_live.state) + { + case SEARCH_ADDRESS_MARK_HEADER: + if (read_one_bit(limit)) + return; + + if (!(cur_live.bit_counter & 255)) + { + LOGSHIFT("%s (%s): shift = %04x data=%02x c=%d\n", cur_live.tm.to_string(), limit.to_string(), cur_live.shift_reg, + bitswap<8>(cur_live.shift_reg, 14, 12, 10, 8, 6, 4, 2, 0), cur_live.bit_counter); + } + + if (cur_live.shift_reg == 0x4489) + { + LOGLIVE("%s: Found A1\n", cur_live.tm.to_string()); + cur_live.crc = 0x443b; + cur_live.data_separator_phase = false; + cur_live.bit_counter = 0; + cur_live.state = READ_DATA_LOW; + cur_live.data_reg = 0xa1; + m_sr &= ~CSR_R_CRC; + checkpoint(); + } + break; + + case READ_DATA_HIGH: + if (read_one_bit(limit)) + return; + if (cur_live.bit_counter & 15) + break; + live_delay(READ_DATA_HIGH_BYTE); + return; + + case READ_DATA_HIGH_BYTE: + cur_live.state = READ_DATA_LOW; + checkpoint(); + break; + + case READ_DATA_LOW: + if (read_one_bit(limit)) + return; + if (cur_live.bit_counter & 15) + break; + live_delay(READ_DATA_LOW_BYTE); + return; + + case READ_DATA_LOW_BYTE: + m_rbuf = cur_live.data_reg; + if (cur_live.crc == 0) + { + m_sr |= CSR_R_CRC; + } + m_sr |= CSR_R_TR; + LOGLIVE("%s: Read %04x (CRC %04x)\n", cur_live.tm.to_string(), cur_live.data_reg, cur_live.crc); + cur_live.state = READ_DATA_HIGH; + checkpoint(); + break; + + case WRITE_MFM_DATA_LOW: + if ((m_wbuf & 255) == 0xa1 && cur_live.crc_init == false && !(m_sr & CSR_R_CRC)) + { + cur_live.crc_init = true; + cur_live.crc = 0xffff; + } + // if DRQ has not been serviced, write CRC + // if DRQ has not been serviced AND CRC has been written, write zeros + if (m_sr & CSR_R_TR) + { + if (cur_live.crc_init) + { + LOGLIVE("%s: Write CRC %04x\n", cur_live.tm.to_string(), cur_live.crc); + m_sr |= CSR_R_CRC; + m_sr &= ~CSR_R_TR; + cur_live.crc_init = false; + } + else + { + LOGLIVE("%s: Write after CRC\n", cur_live.tm.to_string()); + m_wbuf = 0; + } + } + live_write_mfm((m_sr & CSR_R_CRC) ? (cur_live.crc >> 8) : m_wbuf, BIT(m_cr, 9)); + cur_live.state++; + cur_live.bit_counter = 16; + checkpoint(); + break; + + case WRITE_MFM_DATA_HIGH: + live_write_mfm((m_sr & CSR_R_CRC) ? cur_live.crc : (m_wbuf >> 8), BIT(m_cr, 9)); + LOGLIVE("%s: Write next %s\n", cur_live.tm.to_string(), (m_sr & CSR_R_TR)?"TR":""); + if (!(m_sr & CSR_R_CRC)) + m_sr |= CSR_R_TR; + cur_live.state++; + cur_live.bit_counter = 16; + checkpoint(); + break; + + case WRITE_MFM_DATA_LOW_BYTE: + if (write_one_bit(limit)) + return; + if (cur_live.bit_counter == 0) + { + live_delay(WRITE_MFM_DATA_HIGH); + return; + } + break; + + case WRITE_MFM_DATA_HIGH_BYTE: + if (write_one_bit(limit)) + return; + if (cur_live.bit_counter == 0) + { + live_delay(WRITE_MFM_DATA_LOW); + m_sr &= ~CSR_R_CRC; + return; + } + break; + + default: + LOGWARN("%s: Unknown live state %d\n", cur_live.tm.to_string(), cur_live.state); + return; + } + } +} + + +void k1801vp128_device::seek_start(floppy_info &fi) +{ + fi.sub_state = SEEK_MOVE; + fi.dir = !BIT(m_cr, 6); + general_continue(fi); +} + + +void k1801vp128_device::read_data_start(floppy_info &fi) +{ + fi.main_state = READ_DATA; + fi.sub_state = WAIT_INDEX_DONE; + m_sr &= ~CSR_R_CRC; + read_data_continue(fi); +} + +void k1801vp128_device::read_data_continue(floppy_info &fi) +{ + for (;;) + { + switch (fi.sub_state) + { + case SEEK_MOVE: + LOGSTATE("sub %d SEEK_MOVE\n", fi.id); + fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME; + fi.tm->adjust(attotime::from_msec(2), fi.id); + return; + + case SEEK_WAIT_STEP_SIGNAL_TIME: + LOGSTATE("sub %d SEEK_WAIT_STEP_SIGNAL_TIME\n", fi.id); + return; + + case SEEK_WAIT_STEP_SIGNAL_TIME_DONE: + LOGSTATE("sub %d SEEK_WAIT_STEP_SIGNAL_TIME_DONE\n", fi.id); + if (fi.dev) + { + fi.dev->dir_w(fi.dir); + fi.dev->stp_w(0); + fi.dev->stp_w(1); + fi.sub_state = WAIT_INDEX_DONE; + } + else + fi.main_state = fi.sub_state = IDLE; + break; + + case WAIT_INDEX: + LOGSTATE("sub %d WAIT_INDEX\n", fi.id); + return; + + case WAIT_INDEX_DONE: + LOGSTATE("sub %d WAIT_INDEX_DONE\n", fi.id); + fi.counter = 0; + fi.sub_state = SCAN_ID; + LOGSTATE("live %d SEARCH_ADDRESS_MARK_HEADER\n", fi.id); + live_start(fi, SEARCH_ADDRESS_MARK_HEADER); + return; + + case SCAN_ID: + LOGSTATE("sub %d SCAN_ID\n", fi.id); + fi.sub_state = TRACK_READ; + LOGSTATE("live %d READ_DATA_HIGH\n", fi.id); + live_start(fi, READ_DATA_HIGH); + return; + + case SCAN_ID_FAILED: + LOGSTATE("sub %d SCAN_ID_FAILED\n", fi.id); + fi.sub_state = COMMAND_DONE; + break; + + case TRACK_READ: + LOGSTATE("sub %d TRACK_READ\n", fi.id); + fi.sub_state = COMMAND_DONE; + break; + + case COMMAND_DONE: + LOGSTATE("sub %d COMMAND_DONE\n", fi.id); + fi.main_state = fi.sub_state = IDLE; + return; + + default: + LOGWARN("%s: read sector unknown sub-state %d\n", ttsn(), fi.sub_state); + return; + } + } +} + +void k1801vp128_device::write_data_start(floppy_info &fi) +{ + fi.main_state = WRITE_DATA; + fi.sub_state = WAIT_INDEX_DONE; + m_sr &= ~CSR_R_CRC; + write_data_continue(fi); +} + +void k1801vp128_device::write_data_continue(floppy_info &fi) +{ + for (;;) + { + switch (fi.sub_state) + { + case WAIT_INDEX: + LOGSTATE("sub %d WAIT_INDEX\n", fi.id); + return; + + case WAIT_INDEX_DONE: + LOGSTATE("sub %d WAIT_INDEX_DONE\n", fi.id); + fi.sub_state = TRACK_WRITTEN; + LOGSTATE("live %d WRITE_MFM_DATA_LOW\n", fi.id); + live_start(fi, WRITE_MFM_DATA_LOW); + return; + + case TRACK_WRITTEN: + LOGSTATE("sub %d TRACK_WRITTEN\n", fi.id); + fi.sub_state = COMMAND_DONE; + break; + + case COMMAND_DONE: + LOGSTATE("sub %d COMMAND_DONE\n", fi.id); + fi.main_state = fi.sub_state = IDLE; + return; + + default: + LOGWARN("%s: write sector unknown sub-state %d\n", ttsn(), fi.sub_state); + return; + } + } +} + + +void k1801vp128_device::index_callback(floppy_image_device *floppy, int state) +{ + LOGLIVE("%s: Pulse %d\n", machine().time().to_string(), state); + for (floppy_info &fi : flopi) + { + if (fi.dev != floppy) + continue; + + if (!state) + { + general_continue(fi); + continue; + } + + switch (fi.sub_state) + { + case IDLE: + case SEEK_MOVE: + case SEEK_WAIT_STEP_SIGNAL_TIME: + case SEEK_WAIT_STEP_SIGNAL_TIME_DONE: + case SEEK_WAIT_STEP_TIME: + case SEEK_WAIT_STEP_TIME_DONE: + case HEAD_LOAD: + case HEAD_LOAD_DONE: + case SCAN_ID: + case SCAN_ID_FAILED: + break; + + case TRACK_READ: + fi.sub_state = IDLE; + break; + + case WAIT_INDEX: + fi.sub_state = WAIT_INDEX_DONE; + live_abort(); + break; + + default: + LOGWARN("%s: Index pulse on unknown sub-state %d\n", ttsn(), fi.sub_state); + break; + } + + general_continue(fi); + } +} + + +void k1801vp128_device::general_continue(floppy_info &fi) +{ + if (fi.live && cur_live.state != IDLE) + { + live_run(); + if (cur_live.state != IDLE) + return; + } + + switch (fi.main_state) + { + case IDLE: + break; + + case READ_DATA: + read_data_continue(fi); + break; + + case WRITE_DATA: + write_data_continue(fi); + break; + + default: + LOGWARN("%s: general_continue on unknown main-state %d\n", ttsn(), fi.main_state); + break; + } +} + + +bool k1801vp128_device::read_one_bit(const attotime &limit) +{ + int bit = cur_live.pll.get_next_bit(cur_live.tm, cur_live.fi->dev, limit); + if (bit < 0) + return true; + cur_live.shift_reg = (cur_live.shift_reg << 1) | bit; + cur_live.bit_counter++; + if (cur_live.data_separator_phase) + { + cur_live.data_reg = (cur_live.data_reg << 1) | bit; + if ((cur_live.crc ^ (bit ? 0x8000 : 0x0000)) & 0x8000) + cur_live.crc = (cur_live.crc << 1) ^ 0x1021; + else + cur_live.crc = cur_live.crc << 1; + } + cur_live.data_separator_phase = !cur_live.data_separator_phase; + return false; +} + +bool k1801vp128_device::write_one_bit(const attotime &limit) +{ + bool bit = cur_live.shift_reg & 0x8000; + if (cur_live.pll.write_next_bit(bit, cur_live.tm, cur_live.fi->dev, limit)) + return true; + if ((cur_live.bit_counter & 1) && cur_live.crc_init) + { + if ((cur_live.crc ^ (bit ? 0x8000 : 0x0000)) & 0x8000) + cur_live.crc = (cur_live.crc << 1) ^ 0x1021; + else + cur_live.crc = cur_live.crc << 1; + } + cur_live.shift_reg = cur_live.shift_reg << 1; + cur_live.bit_counter--; + return false; +} + +void k1801vp128_device::live_write_mfm(uint8_t mfm, bool marker) +{ + bool context = cur_live.data_bit_context; + uint16_t raw = 0; + for (int i = 0; i < 8; i++) + { + bool bit = mfm & (0x80 >> i); + if (!(bit || context)) + raw |= 0x8000 >> (2*i); + if (bit) + raw |= 0x4000 >> (2*i); + context = bit; + } + if (marker && (mfm & 0xc) == 0) raw &= 0xffdf; // A1 and C2 sync sequences + cur_live.data_reg = mfm; + cur_live.shift_reg = raw; + cur_live.data_bit_context = context; + LOGLIVE("%s: write %02x %04x %04x\n", cur_live.tm.to_string(), mfm, cur_live.crc, raw); +} + + +void k1801vp128_device::set_ds(int fid) +{ + if (selected_drive == fid) + return; + + live_abort(); + + // pass drive select to connected drives + for (floppy_info &fi : flopi) + if (fi.dev) + fi.dev->ds_w(fid); + + // record selected drive + selected_drive = fid; + + if (fid != -1) + { + LOG("COMMAND ~READ drive %d c:h %d:%d\n", selected_drive, flopi[fid].dev->get_cyl(), BIT(m_cr, 5)); + m_sr &= ~CSR_R_TR; + execute_command(CMD_READ); + } +} diff --git a/src/devices/machine/1801vp128.h b/src/devices/machine/1801vp128.h new file mode 100644 index 00000000000..339ddcbd423 --- /dev/null +++ b/src/devices/machine/1801vp128.h @@ -0,0 +1,209 @@ +// license:BSD-3-Clause +// copyright-holders:Sergey Svishchev +/********************************************************************** + + 1801VP1-128 gate array (MFM codec for floppy controllers) + +********************************************************************** + _____ _____ + _AD0 1 |* \_/ | 42 +5V + _AD1 2 | | 41 _DS0 + _AD2 3 | | 30 _DS1 + _AD3 4 | | 39 _DS2 + _AD4 5 | | 38 _DS3 + _AD5 6 | | 37 _MSW + _AD6 7 | | 36 HS + _AD7 8 | | 35 DIR + _AD8 9 | | 34 _ST + _AD9 10 | | 33 TR0 + _AD10 11 | 1801VP1-128 | 32 RDY + _AD11 12 | | 31 WPR + _AD12 13 | | 30 _REZ + _AD13 14 | | 29 DI + _AD14 15 | | 28 _WRE + _AD15 16 | | 27 _D01 + _SYNC 17 | | 26 _D02 + _DIN 18 | | 25 _D03 + _DOUT 19 | | 24 IND + _INIT 20 | | 23 _RPLY + GND 21 |_____________| 22 CLC + +**********************************************************************/ + +#ifndef MAME_MACHINE_1801VP128_H +#define MAME_MACHINE_1801VP128_H + +#pragma once + +#include "imagedev/floppy.h" +#include "machine/pdp11.h" + +#include "fdc_pll.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> k1801vp128_device + +class k1801vp128_device : public device_t +{ +public: + // construction/destruction + k1801vp128_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + auto ds_in_callback() { return m_read_ds.bind(); } + + uint16_t read(offs_t offset); + void write(offs_t offset, uint16_t data); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + + TIMER_CALLBACK_MEMBER(update_floppy); + +private: + enum { + CMD_READ, + CMD_WRITE, + CMD_SEEK + }; + + enum { + IDLE, + + // Main states + READ_DATA, + WRITE_DATA, + + // Sub-states + COMMAND_DONE, + + SEEK_MOVE, + SEEK_WAIT_STEP_SIGNAL_TIME, + SEEK_WAIT_STEP_SIGNAL_TIME_DONE, + SEEK_WAIT_STEP_TIME, + SEEK_WAIT_STEP_TIME_DONE, + SEEK_WAIT_DONE, + SEEK_DONE, + + HEAD_LOAD, + HEAD_LOAD_DONE, + + WAIT_INDEX, + WAIT_INDEX_DONE, + + SCAN_ID, + SCAN_ID_FAILED, + + TRACK_READ, + TRACK_WRITTEN, + + // Live states + SEARCH_ADDRESS_MARK_HEADER, + READ_DATA_HIGH, + READ_DATA_HIGH_BYTE, + READ_DATA_LOW, + READ_DATA_LOW_BYTE, + + WRITE_MFM_DATA_HIGH, + WRITE_MFM_DATA_HIGH_BYTE, + WRITE_MFM_DATA_LOW, + WRITE_MFM_DATA_LOW_BYTE, + }; + + enum { + CSR_R_TR0 = 0000001, + CSR_R_RDY = 0000002, + CSR_R_WPR = 0000004, + CSR_R_TR = CSR_DONE, + CSR_R_CRC = 0040000, + CSR_R_IND = 0100000, + + CSR_W_DS0 = 0000001, + CSR_W_DS1 = 0000002, + CSR_W_DS2 = 0000004, + CSR_W_DS3 = 0000010, + CSR_W_MSW = 0000020, // motor + CSR_W_HS = 0000040, // head select + CSR_W_DIR = 0000100, // step direction + CSR_W_ST = 0000200, // step pulse + CSR_W_GDR = 0000400, + CSR_W_WM = 0001000, + CSR_W_REZ = 0002000, + CSR_W_DS = 0000017, + }; + + struct floppy_info + { + emu_timer *tm; + floppy_image_device *dev; + int id; + int main_state, sub_state; + int dir, counter; + bool live, index; + }; + + struct live_info + { + attotime tm; + int state, next_state; + floppy_info *fi; + uint16_t shift_reg, crc; + int bit_counter; + bool data_separator_phase, data_bit_context, crc_init; + uint16_t data_reg; + fdc_pll_t pll; + }; + + required_device_array<floppy_connector, 4> m_connectors; + devcb_read16 m_read_ds; + + std::string ttsn() const; + + floppy_info flopi[4]; + int selected_drive; + + uint16_t m_cr; + uint16_t m_sr; + uint16_t m_rbuf; + uint16_t m_wbuf; + + live_info cur_live, checkpoint_live; + + void execute_command(int command); + void set_ds(int fid); + + void seek_start(floppy_info &fi); + void seek_continue(floppy_info &fi); + + void read_data_start(floppy_info &fi); + void read_data_continue(floppy_info &fi); + + void write_data_start(floppy_info &fi); + void write_data_continue(floppy_info &fi); + + void general_continue(floppy_info &fi); + void index_callback(floppy_image_device *floppy, int state); + + void live_start(floppy_info &fi, int live_state); + void live_abort(); + void checkpoint(); + void rollback(); + void live_delay(int state); + void live_sync(); + void live_run(attotime limit = attotime::never); + void live_write_mfm(uint8_t mfm, bool marker); + + bool read_one_bit(const attotime &limit); + bool write_one_bit(const attotime &limit); +}; + + +// device type definition +DECLARE_DEVICE_TYPE(K1801VP128, k1801vp128_device) + +#endif // MAME_MACHINE_1801VP128_H |