Add support for 1801VP1-128 gate array (MFM codec) (#12089)
This commit is contained in:
parent
8b7c42655e
commit
36cf92be37
@ -1938,6 +1938,18 @@ if (MACHINES["K056230"]~=null) then
|
||||
}
|
||||
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
|
||||
|
785
src/devices/machine/1801vp128.cpp
Normal file
785
src/devices/machine/1801vp128.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
209
src/devices/machine/1801vp128.h
Normal file
209
src/devices/machine/1801vp128.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user