Add support for 1801VP1-128 gate array (MFM codec) (#12089)

This commit is contained in:
shattered 2024-03-04 15:35:11 +00:00 committed by GitHub
parent 8b7c42655e
commit 36cf92be37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 1006 additions and 0 deletions

View File

@ -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

View 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);
}
}

View 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