bus/qbus: Added DVK MX floppy controller. (#11840)

This commit is contained in:
shattered 2024-02-08 18:42:43 +03:00 committed by GitHub
parent 7364fefcbe
commit b992130045
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 1017 additions and 53 deletions

View File

@ -4587,6 +4587,8 @@ if (BUSES["QBUS"]~=null) then
MAME_DIR .. "src/devices/bus/qbus/dsd4432.h",
MAME_DIR .. "src/devices/bus/qbus/dvk_kgd.cpp",
MAME_DIR .. "src/devices/bus/qbus/dvk_kgd.h",
MAME_DIR .. "src/devices/bus/qbus/dvk_mx.cpp",
MAME_DIR .. "src/devices/bus/qbus/dvk_mx.h",
MAME_DIR .. "src/devices/bus/qbus/pc11.cpp",
MAME_DIR .. "src/devices/bus/qbus/pc11.h",
MAME_DIR .. "src/devices/bus/qbus/qbus.cpp",

View File

@ -0,0 +1,742 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************
DVK "MX" floppy controller (decimal ID 3.057.122)
Supports four 5.25" drives (single- or double-sided, 40- or 80-track).
Uses FM encoding and software-defined track format;
usually 11 sectors of 256 bytes, with simple additive checksum.
RT-11 driver name: MX.SYS
References:
https://emuverse.ru/downloads/computers/DVK/docs/KMD/kngmd_MX_2.djvu
https://torlus.com/floppy/forum/viewtopic.php?t=1384
http://hobot.pdp-11.ru/ukdwk_archive/dwkwebcomplekt/DWKFiles/mx/README.mx1
***************************************************************************/
#include "emu.h"
#include "dvk_mx.h"
#include "formats/dvk_mx_dsk.h"
#define LOG_WARN (1U << 1) // Show warnings
#define LOG_SHIFT (1U << 2) // Shows shift register contents
#define LOG_REGS (1U << 3) // Digital input/output register and data rate select
#define LOG_STATE (1U << 4) // State machine
#define LOG_LIVE (1U << 5) // Live states
#define VERBOSE (LOG_GENERAL | LOG_REGS | LOG_LIVE | 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
//**************************************************************************
#define MXCSR_GETDRIVE(x) (((x) >> MXCSR_V_DRIVE) & MXCSR_M_DRIVE)
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(DVK_MX, dvk_mx_device, "dvk_mx", "DVK MX floppy controller")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// dvk_mx_device - constructor
//-------------------------------------------------
dvk_mx_device::dvk_mx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, DVK_MX, tag, owner, clock)
, device_qbus_card_interface(mconfig, *this)
, m_connectors(*this, "%u", 0U)
{
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 dvk_mx_device::device_start()
{
for (int i = 0; i != 4; i++)
{
flopi[i].tm = timer_alloc(FUNC(dvk_mx_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(&dvk_mx_device::index_callback, this));
}
else
flopi[i].dev = nullptr;
flopi[i].main_state = IDLE;
flopi[i].sub_state = IDLE;
flopi[i].live = false;
}
// save state
save_item(NAME(m_mxcs));
save_item(NAME(m_rbuf));
save_item(NAME(m_wbuf));
save_item(NAME(selected_drive));
m_timer_2khz = timer_alloc(FUNC(dvk_mx_device::twokhz_tick), this);
m_bus->install_device(0177130, 0177133, read16sm_delegate(*this, FUNC(dvk_mx_device::read)),
write16sm_delegate(*this, FUNC(dvk_mx_device::write)));
m_mxcs = 0;
selected_drive = -1;
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void dvk_mx_device::device_reset()
{
m_rbuf = m_wbuf = 0;
m_mxcs &= ~MXCSR_ERR;
m_timer_2khz->adjust(attotime::never, 0, attotime::never);
live_abort();
set_ds(-1);
}
void dvk_mx_device::floppy_formats(format_registration &fr)
{
fr.add_mfm_containers();
fr.add(FLOPPY_DVK_MX_FORMAT);
}
static void mx_floppies(device_slot_interface &device)
{
device.option_add("525qd", FLOPPY_525_QD);
device.option_add("525dd", FLOPPY_525_DD);
}
void dvk_mx_device::device_add_mconfig(machine_config &config)
{
FLOPPY_CONNECTOR(config, "0", mx_floppies, "525qd", dvk_mx_device::floppy_formats);
FLOPPY_CONNECTOR(config, "1", mx_floppies, "525qd", dvk_mx_device::floppy_formats);
FLOPPY_CONNECTOR(config, "2", mx_floppies, "525dd", dvk_mx_device::floppy_formats);
FLOPPY_CONNECTOR(config, "3", mx_floppies, "525dd", dvk_mx_device::floppy_formats);
}
uint16_t dvk_mx_device::read(offs_t offset)
{
uint16_t data = 0;
switch (offset)
{
case 0:
data = m_mxcs & MXCSR_RD;
if (selected_drive != -1)
{
floppy_info &fi = flopi[selected_drive];
if (!fi.dev->trk00_r()) data |= MXCSR_TRK0;
if (fi.dev->wpt_r()) data |= MXCSR_WP;
if (fi.index) data |= MXCSR_INDEX;
}
if (!machine().side_effects_disabled())
m_mxcs &= ~MXCSR_TIMER;
break;
case 1:
data = m_rbuf;
if (!machine().side_effects_disabled())
m_mxcs &= ~MXCSR_TR;
break;
}
return data;
}
void dvk_mx_device::write(offs_t offset, uint16_t data)
{
switch (offset)
{
case 0:
if (!BIT(data, 1))
set_ds(MXCSR_GETDRIVE(data));
else
set_ds(-1);
if (selected_drive != -1)
{
floppy_info &fi = flopi[selected_drive];
fi.dev->mon_w(!BIT(data, 6));
fi.dev->ss_w(BIT(data, 12));
if (BIT(data, 4))
{
LOG("COMMAND STEP %s\n", BIT(data, 5) ? "+1" : "-1");
execute_command(2);
}
if (data & MXCSR_GO)
{
LOG("COMMAND %s drive %d c:h %d:%d\n", BIT(data, 13)?"WRITE":"READ", selected_drive, fi.dev->get_cyl(), BIT(data, 12));
m_mxcs &= ~MXCSR_TR;
execute_command(BIT(data, 13));
}
}
if (BIT(data, 7))
{
m_timer_2khz->adjust(attotime::from_hz(2000), 0, attotime::from_hz(2000));
}
else
{
m_timer_2khz->adjust(attotime::never, 0, attotime::never);
}
UPDATE_16BIT(&m_mxcs, data, MXCSR_WR);
break;
case 1:
m_wbuf = data;
m_mxcs &= ~MXCSR_TR;
}
}
void dvk_mx_device::execute_command(int command)
{
m_mxcs &= ~MXCSR_ERR;
live_abort();
switch (command)
{
case 0:
read_data_start(flopi[selected_drive]);
break;
case 1:
write_data_start(flopi[selected_drive]);
break;
case 2:
seek_start(flopi[selected_drive]);
break;
}
}
TIMER_CALLBACK_MEMBER(dvk_mx_device::twokhz_tick)
{
m_mxcs |= MXCSR_TIMER;
}
std::string dvk_mx_device::ttsn() const
{
return machine().time().to_string();
}
//-------------------------------------------------
// update_tick - pump the device life cycle
//-------------------------------------------------
TIMER_CALLBACK_MEMBER(dvk_mx_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 dvk_mx_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.bit_counter = 0;
cur_live.data_separator_phase = false;
cur_live.data_reg = 0;
cur_live.pll.reset(cur_live.tm);
cur_live.pll.set_clock(attotime::from_hz(250000));
checkpoint_live = cur_live;
fi.live = true;
LOGLIVE("%s: Called live_start (%d)\n", cur_live.tm.to_string(), state);
live_run();
}
void dvk_mx_device::checkpoint()
{
if (cur_live.fi)
cur_live.pll.commit(cur_live.fi->dev, cur_live.tm);
checkpoint_live = cur_live;
}
void dvk_mx_device::rollback()
{
cur_live = checkpoint_live;
}
void dvk_mx_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 dvk_mx_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 dvk_mx_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 dvk_mx_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_ID:
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 == 0xaaaaffaf)
{
LOGLIVE("%s: Found SYNC\n", cur_live.tm.to_string());
cur_live.data_separator_phase = false;
cur_live.bit_counter = 0;
cur_live.state = READ_TRACK_DATA_BYTE;
cur_live.data_reg = 0363; // 0xf3
checkpoint();
}
break;
case READ_TRACK_DATA:
if (read_one_bit(limit))
return;
if (cur_live.bit_counter & 31)
break;
live_delay(READ_TRACK_DATA_BYTE);
return;
case READ_TRACK_DATA_BYTE:
m_rbuf = cur_live.data_reg;
m_mxcs |= MXCSR_TR;
cur_live.state = READ_TRACK_DATA;
checkpoint();
break;
case WRITE_TRACK_DATA:
live_write_fm(m_wbuf);
cur_live.state = WRITE_TRACK_DATA_BYTE;
cur_live.bit_counter = 32;
checkpoint();
break;
case WRITE_TRACK_DATA_BYTE:
if (write_one_bit(limit))
return;
if (cur_live.bit_counter == 0)
{
LOGLIVE("%s: Write next %s\n", cur_live.tm.to_string(), (m_mxcs & MXCSR_TR)?"TR":"");
if (m_mxcs & MXCSR_TR)
{
m_mxcs |= MXCSR_ERR;
cur_live.pll.stop_writing(cur_live.fi->dev, cur_live.tm);
cur_live.state = IDLE;
}
else
{
m_mxcs |= MXCSR_TR;
live_delay(WRITE_TRACK_DATA);
}
return;
}
break;
default:
LOGWARN("%s: Unknown live state %d\n", cur_live.tm.to_string(), cur_live.state);
return;
}
}
}
void dvk_mx_device::seek_start(floppy_info &fi)
{
fi.main_state = SEEK;
fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
fi.dir = !BIT(m_mxcs, 5);
seek_continue(fi);
}
void dvk_mx_device::seek_continue(floppy_info &fi)
{
for (;;)
{
switch (fi.sub_state)
{
case SEEK_MOVE:
LOGSTATE("sub SEEK_MOVE\n");
if (fi.dev)
{
fi.dev->dir_w(fi.dir);
fi.dev->stp_w(0);
fi.dev->stp_w(1);
}
fi.main_state = fi.sub_state = IDLE;
m_mxcs &= ~MXCSR_STEP;
return;
case SEEK_WAIT_STEP_SIGNAL_TIME:
LOGSTATE("sub SEEK_WAIT_STEP_SIGNAL_TIME\n");
fi.tm->adjust(attotime::from_msec(2), fi.id); // FIXME
return;
case SEEK_WAIT_STEP_SIGNAL_TIME_DONE:
LOGSTATE("sub SEEK_WAIT_STEP_SIGNAL_TIME_DONE\n");
fi.sub_state = SEEK_MOVE;
break;
}
}
}
void dvk_mx_device::read_data_start(floppy_info &fi)
{
fi.main_state = READ_DATA;
fi.sub_state = WAIT_INDEX;
read_data_continue(fi);
}
void dvk_mx_device::read_data_continue(floppy_info &fi)
{
for (;;)
{
switch (fi.sub_state)
{
case WAIT_INDEX:
LOGSTATE("sub WAIT_INDEX\n");
return;
case WAIT_INDEX_DONE:
LOGSTATE("sub WAIT_INDEX_DONE\n");
fi.counter = 0;
fi.sub_state = SCAN_ID;
LOGSTATE("live SEARCH_ID\n");
live_start(fi, SEARCH_ID);
return;
case SCAN_ID:
LOGSTATE("sub SCAN_ID\n");
fi.sub_state = TRACK_READ;
LOGSTATE("live READ_TRACK_DATA_BYTE\n");
live_start(fi, READ_TRACK_DATA_BYTE);
return;
case SCAN_ID_FAILED:
LOGSTATE("sub SCAN_ID_FAILED\n");
fi.sub_state = COMMAND_DONE;
break;
case TRACK_READ:
LOGSTATE("sub TRACK_READ\n");
fi.sub_state = COMMAND_DONE;
break;
case COMMAND_DONE:
LOGSTATE("sub COMMAND_DONE\n");
fi.main_state = fi.sub_state = IDLE;
return;
default:
LOGWARN("%s: read sector unknown sub-state %d\n", ttsn(), fi.sub_state);
return;
}
}
}
void dvk_mx_device::write_data_start(floppy_info &fi)
{
fi.main_state = WRITE_DATA;
fi.sub_state = WAIT_INDEX;
write_data_continue(fi);
}
void dvk_mx_device::write_data_continue(floppy_info &fi)
{
for (;;)
{
switch (fi.sub_state)
{
case WAIT_INDEX:
LOGSTATE("sub WAIT_INDEX\n");
return;
case WAIT_INDEX_DONE:
LOGSTATE("sub WAIT_INDEX_DONE\n");
fi.sub_state = TRACK_WRITTEN;
LOGSTATE("live WRITE_TRACK_DATA\n");
live_start(fi, WRITE_TRACK_DATA);
return;
case TRACK_WRITTEN:
LOGSTATE("sub TRACK_WRITTEN\n");
fi.sub_state = COMMAND_DONE;
break;
case COMMAND_DONE:
LOGSTATE("sub COMMAND_DONE\n");
fi.main_state = fi.sub_state = IDLE;
return;
default:
LOGWARN("%s: write sector unknown sub-state %d\n", ttsn(), fi.sub_state);
return;
}
}
}
void dvk_mx_device::index_callback(floppy_image_device *floppy, int state)
{
for (floppy_info &fi : flopi)
{
if (fi.dev != floppy)
continue;
fi.index = state;
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 SCAN_ID_FAILED:
break;
case TRACK_READ:
fi.sub_state = IDLE;
break;
case WAIT_INDEX:
fi.sub_state = WAIT_INDEX_DONE;
live_abort();
break;
case SCAN_ID:
fi.counter++;
if (fi.counter == 2)
{
fi.sub_state = SCAN_ID_FAILED;
live_abort();
}
break;
default:
LOGWARN("%s: Index pulse on unknown sub-state %d\n", ttsn(), fi.sub_state);
break;
}
general_continue(fi);
}
}
void dvk_mx_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 SEEK:
seek_continue(fi);
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 dvk_mx_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;
}
cur_live.data_separator_phase = !cur_live.data_separator_phase;
return false;
}
bool dvk_mx_device::write_one_bit(const attotime &limit)
{
bool bit = cur_live.shift_reg & 0x80000000;
if (cur_live.pll.write_next_bit(bit, cur_live.tm, cur_live.fi->dev, limit))
return true;
cur_live.shift_reg = cur_live.shift_reg << 1;
cur_live.bit_counter--;
return false;
}
void dvk_mx_device::live_write_fm(uint16_t fm)
{
uint32_t raw = 0xaaaaaaaa;
for (int i = 0; i < 16; i++)
if (fm & (0x8000 >> i))
raw |= 0x40000000 >> (2*i);
cur_live.data_reg = fm;
cur_live.shift_reg = raw;
LOGLIVE("%s: Write %04x (%06o) FM %08x\n", cur_live.tm.to_string(), fm, fm, raw);
}
//-------------------------------------------------
// set_floppy -
//-------------------------------------------------
void dvk_mx_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;
}

View File

@ -0,0 +1,172 @@
// license:BSD-3-Clause
// copyright-holders:Sergey Svishchev
/***************************************************************************
DVK "MX" floppy controller (decimal ID 3.057.122)
***************************************************************************/
#ifndef MAME_BUS_QBUS_DVK_MX_H
#define MAME_BUS_QBUS_DVK_MX_H
#pragma once
#include "qbus.h"
#include "imagedev/floppy.h"
#include "machine/fdc_pll.h"
#include "machine/pdp11.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> dvk_mx_device
class dvk_mx_device : public device_t, public device_qbus_card_interface
{
public:
dvk_mx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
uint16_t read(offs_t offset);
void write(offs_t offset, uint16_t data);
protected:
// device_t implementation
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
TIMER_CALLBACK_MEMBER(update_floppy);
TIMER_CALLBACK_MEMBER(twokhz_tick);
private:
enum {
IDLE,
// Main states
SEEK,
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,
WAIT_INDEX,
WAIT_INDEX_DONE,
SCAN_ID,
SCAN_ID_FAILED,
TRACK_READ,
TRACK_WRITTEN,
// Live states
SEARCH_ID,
READ_TRACK_DATA,
READ_TRACK_DATA_BYTE,
WRITE_TRACK_DATA,
WRITE_TRACK_DATA_BYTE,
};
enum {
MXCSR_DRVSE_L = 0000002,
MXCSR_STEP = 0000020,
MXCSR_DIR = 0000040,
MXCSR_MON = 0000100,
MXCSR_TIMER = 0000200,
MXCSR_ERR = 0000400,
MXCSR_INDEX = 0001000,
MXCSR_WP = 0002000,
MXCSR_TRK0 = 0004000,
MXCSR_TOPHEAD = 0010000,
MXCSR_WRITE = 0020000,
MXCSR_GO = 0040000,
MXCSR_TR = 0100000,
MXCSR_V_DRIVE = 2,
MXCSR_M_DRIVE = 3,
MXCSR_DRIVE = (MXCSR_M_DRIVE << MXCSR_V_DRIVE),
MXCSR_RD = (MXCSR_TR|MXCSR_GO|MXCSR_WRITE|MXCSR_TOPHEAD|MXCSR_TRK0|MXCSR_WP|MXCSR_INDEX|MXCSR_ERR|MXCSR_TIMER|MXCSR_MON|MXCSR_DIR|MXCSR_STEP|MXCSR_DRIVE|MXCSR_DRVSE_L),
MXCSR_WR = (MXCSR_WRITE|MXCSR_TOPHEAD|MXCSR_TIMER|MXCSR_MON|MXCSR_DIR|MXCSR_STEP|MXCSR_DRIVE|MXCSR_DRVSE_L)
};
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;
uint32_t shift_reg;
int bit_counter, byte_counter;
bool data_separator_phase;
uint16_t data_reg, cksum;
fdc_pll_t pll;
};
required_device_array<floppy_connector, 4> m_connectors;
uint16_t m_mxcs;
uint16_t m_rbuf;
uint16_t m_wbuf;
int selected_drive;
emu_timer *m_timer_2khz;
floppy_info flopi[4];
live_info cur_live, checkpoint_live;
static void floppy_formats(format_registration &fr);
std::string ttsn() const;
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_fm(uint16_t fm);
bool read_one_bit(const attotime &limit);
bool write_one_bit(const attotime &limit);
};
// device type declaration
DECLARE_DEVICE_TYPE(DVK_MX, dvk_mx_device)
#endif // MAME_BUS_QBUS_DVK_MX_H

View File

@ -15,6 +15,7 @@
#include "pc11.h"
#include "qtx.h"
#include "dvk_kgd.h"
#include "dvk_mx.h"
void qbus_cards(device_slot_interface &device)
@ -23,6 +24,7 @@ void qbus_cards(device_slot_interface &device)
device.option_add("qts1", TTI_QTS1);
device.option_add("dsd4432", DSD4432);
device.option_add("kgd", DVK_KGD);
device.option_add("mx", DVK_MX);
}

View File

@ -23,65 +23,45 @@
#include "ioprocs.h"
#include "multibyte.h"
constexpr uint32_t ID_PATTERN = 0xaaaaffaf;
const floppy_image_format_t::desc_e dvk_mx_format::dvk_mx_new_desc[] = {
/* 01 */ { FM, 0x00, 8*2 }, // eight 0x0000 words
/* 03 */ { FM, 0x00, 1 },
/* 02 */ { FM, 0xf3, 1 }, // word 0x00f3
/* 05 */ { FM, 0x00, 1 },
/* 04 */ { TRACK_ID_FM }, // track number word
/* 05 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors
/* 02 */ { FM, 0x00, 1 },
/* 03 */ { FM, 0xf3, 1 }, // word 0x00f3
/* 04 */ { FM, 0x00, 1 },
/* 05 */ { TRACK_ID_FM }, // track number word
/* 06 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors
/* 07 */ { SECTOR_DATA_MX, -1 },
/* 10 */ { SECTOR_LOOP_END },
/* 13 */ { FM, 0x83, 1 },
/* 08 */ { SECTOR_LOOP_END },
/* 09 */ { FM, 0x83, 1 },
/* 10 */ { OFFSET_ID_FM },
/* 11 */ { FM, 0x83, 1 },
/* 12 */ { OFFSET_ID_FM },
/* 15 */ { FM, 0x83, 1 },
/* 13 */ { FM, 0x83, 1 },
/* 14 */ { OFFSET_ID_FM },
/* 17 */ { FM, 0x83, 1 },
/* 16 */ { OFFSET_ID_FM },
/* 18 */ { END }
/* 15 */ { END }
};
const floppy_image_format_t::desc_e dvk_mx_format::dvk_mx_old_desc[] = {
/* 01 */ { FM, 0x00, 30*2 },
/* 03 */ { FM, 0x00, 1 },
/* 02 */ { FM, 0xf3, 1 }, // word 0x00f3
/* 05 */ { FM, 0x00, 1 },
/* 04 */ { TRACK_ID_FM }, // track number word
/* 02 */ { FM, 0x00, 1 },
/* 03 */ { FM, 0xf3, 1 }, // word 0x00f3
/* 04 */ { FM, 0x00, 1 },
/* 05 */ { TRACK_ID_FM }, // track number word
/* 06 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors
/* 07 */ { SECTOR_DATA_MX, -1 },
/* 10 */ { SECTOR_LOOP_END },
/* 13 */ { FM, 0x83, 1 },
/* 11 */ { FM, 0x01, 1 },
/* 15 */ { FM, 0x83, 1 },
/* 14 */ { FM, 0x01, 1 },
/* 16 */ { END }
/* 08 */ { SECTOR_LOOP_END },
/* 09 */ { FM, 0x00, 1 },
/* 10 */ { FM, 0116, 1 },
/* 11 */ { FM, 0x00, 4*2 }, // four 0x0000 words
/* 12 */ { END }
};
dvk_mx_format::dvk_mx_format()
{
}
const char *dvk_mx_format::name() const noexcept
{
return "mx";
}
const char *dvk_mx_format::description() const noexcept
{
return "DVK MX: floppy image";
}
const char *dvk_mx_format::extensions() const noexcept
{
return "mx";
}
bool dvk_mx_format::supports_save() const noexcept
{
return false;
}
void dvk_mx_format::find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
{
uint64_t size;
@ -95,17 +75,17 @@ void dvk_mx_format::find_size(util::random_read &io, uint8_t &track_count, uint8
{
case 112640:
track_count = 40;
sector_count = 11;
sector_count = MX_SECTORS;
head_count = 1;
break;
case 225280:
track_count = 40;
sector_count = 11;
sector_count = MX_SECTORS;
head_count = 2;
break;
case 450560:
track_count = 80;
sector_count = 11;
sector_count = MX_SECTORS;
head_count = 2;
break;
default:
@ -143,16 +123,16 @@ bool dvk_mx_format::load(util::random_read &io, uint32_t form_factor, const std:
find_size(io, track_count, head_count, sector_count);
if (track_count == 0) return false;
uint8_t sectdata[11 * 256];
desc_s sectors[11];
uint8_t sectdata[MX_SECTORS * MX_SECTOR_SIZE];
desc_s sectors[MX_SECTORS];
for (int i = 0; i < sector_count; i++)
{
sectors[i].data = sectdata + 256 * i;
sectors[i].size = 256;
sectors[i].data = sectdata + MX_SECTOR_SIZE * i;
sectors[i].size = MX_SECTOR_SIZE;
sectors[i].sector_id = i;
}
int track_size = sector_count * 256;
int track_size = sector_count * MX_SECTOR_SIZE;
for (int track = 0; track < track_count; track++)
{
for (int head = 0; head < head_count; head++)
@ -182,4 +162,65 @@ bool dvk_mx_format::load(util::random_read &io, uint32_t form_factor, const std:
return true;
}
bool dvk_mx_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int tracks;
int heads;
bool res = false;
image.get_actual_geometry(tracks, heads);
for (int cyl = 0; cyl < tracks; cyl++)
{
for (int head = 0; head < heads; head++)
{
auto bitstream = generate_bitstream_from_track(cyl, head, 4000, image, 0);
uint8_t sector_data[MX_SECTORS * MX_SECTOR_SIZE];
if (get_next_sector(bitstream, sector_data))
{
unsigned offset_in_image = (cyl * heads + head) * MX_SECTOR_SIZE * MX_SECTORS;
size_t actual;
res = true;
io.write_at(offset_in_image, sector_data, MX_SECTOR_SIZE * MX_SECTORS, actual);
}
}
}
return res;
}
bool dvk_mx_format::get_next_sector(const std::vector<bool> &bitstream, uint8_t *sector_data)
{
uint32_t sr = 0;
int pos = 0;
while (pos < bitstream.size() && sr != ID_PATTERN)
{
sr = (sr << 1) | bitstream[pos];
pos++;
}
if (pos == bitstream.size())
{
// End of track reached
return false;
}
unsigned to_dump = MX_SECTORS * (MX_SECTOR_SIZE + 2);
pos += 32; // skip track ID
for (unsigned i = 0, k = 0; i < to_dump && pos < bitstream.size(); i++)
{
uint8_t byte = 0;
unsigned j;
for (j = 0; j < 8 && pos < bitstream.size(); j++)
{
bool bit = bitstream[pos + 1];
pos += 2;
byte <<= 1;
byte |= bit;
}
if (j == 8 && (i % (MX_SECTOR_SIZE + 2)) < MX_SECTOR_SIZE)
{
sector_data[k ^ 1] = byte;
k++;
}
}
return true;
}
const dvk_mx_format FLOPPY_DVK_MX_FORMAT;

View File

@ -19,17 +19,22 @@ public:
virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
virtual const char *name() const noexcept override;
virtual const char *description() const noexcept override;
virtual const char *extensions() const noexcept override;
virtual bool supports_save() const noexcept override;
virtual const char *name() const noexcept override { return "mx"; }
virtual const char *description() const noexcept override { return "DVK MX disk image"; }
virtual const char *extensions() const noexcept override { return "mx"; }
virtual bool supports_save() const noexcept override { return true; }
static const desc_e dvk_mx_old_desc[];
static const desc_e dvk_mx_new_desc[];
private:
static constexpr unsigned MX_SECTORS = 11;
static constexpr unsigned MX_SECTOR_SIZE = 256;
static void find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
static bool get_next_sector(const std::vector<bool> &bitstream , uint8_t *sector_data);
};
extern const dvk_mx_format FLOPPY_DVK_MX_FORMAT;