mirror of
https://github.com/holub/mame
synced 2025-05-07 06:44:51 +03:00
2992 lines
73 KiB
C++
2992 lines
73 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Olivier Galibert
|
|
|
|
#include "emu.h"
|
|
#include "upd765.h"
|
|
#include "debugger.h"
|
|
|
|
#define LOG_WARN (1U << 1) // Show warnings
|
|
#define LOG_SHIFT (1U << 2) // Shows shift register contents
|
|
#define LOG_HEADER (1U << 3) // Shows ID fields
|
|
#define LOG_FORMAT (1U << 4) // Sectors being formatted
|
|
#define LOG_TCIRQ (1U << 5) // Termination code line / interrupt
|
|
#define LOG_REGS (1U << 6) // Digital input/output register and data rate select
|
|
#define LOG_FIFO (1U << 7) // FIFO operations
|
|
#define LOG_COMMAND (1U << 8) // Commands
|
|
#define LOG_RW (1U << 9) // Read/write sector or track
|
|
#define LOG_MATCH (1U << 10) // Sector matching
|
|
#define LOG_STATE (1U << 11) // State machine
|
|
#define LOG_LIVE (1U << 12) // Live states
|
|
|
|
#define VERBOSE (LOG_GENERAL | LOG_WARN )
|
|
|
|
#include "logmacro.h"
|
|
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
|
|
#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__)
|
|
#define LOGSHIFT(...) LOGMASKED(LOG_SHIFT, __VA_ARGS__)
|
|
#define LOGHEADER(...) LOGMASKED(LOG_HEADER, __VA_ARGS__)
|
|
#define LOGFORMAT(...) LOGMASKED(LOG_FORMAT, __VA_ARGS__)
|
|
#define LOGTCIRQ(...) LOGMASKED(LOG_TCIRQ, __VA_ARGS__)
|
|
#define LOGREGS(...) LOGMASKED(LOG_REGS, __VA_ARGS__)
|
|
#define LOGFIFO(...) LOGMASKED(LOG_FIFO, __VA_ARGS__)
|
|
#define LOGCOMMAND(...) LOGMASKED(LOG_COMMAND, __VA_ARGS__)
|
|
#define LOGRW(...) LOGMASKED(LOG_RW, __VA_ARGS__)
|
|
#define LOGMATCH(...) LOGMASKED(LOG_MATCH, __VA_ARGS__)
|
|
#define LOGSTATE(...) LOGMASKED(LOG_STATE, __VA_ARGS__)
|
|
#define LOGLIVE(...) LOGMASKED(LOG_LIVE, __VA_ARGS__)
|
|
|
|
DEFINE_DEVICE_TYPE(UPD765A, upd765a_device, "upd765a", "NEC uPD765A FDC")
|
|
DEFINE_DEVICE_TYPE(UPD765B, upd765b_device, "upd765b", "NEC uPD765B FDC")
|
|
DEFINE_DEVICE_TYPE(I8272A, i8272a_device, "i8272a", "Intel 8272A FDC")
|
|
DEFINE_DEVICE_TYPE(UPD72065, upd72065_device, "upd72065", "NEC uPD72065 FDC")
|
|
DEFINE_DEVICE_TYPE(I82072, i82072_device, "i82072", "Intel 82072 FDC")
|
|
DEFINE_DEVICE_TYPE(SMC37C78, smc37c78_device, "smc37c78", "SMC FDC73C78 FDC")
|
|
DEFINE_DEVICE_TYPE(N82077AA, n82077aa_device, "n82077aa", "Intel N82077AA FDC")
|
|
DEFINE_DEVICE_TYPE(PC_FDC_SUPERIO, pc_fdc_superio_device, "pc_fdc_superio", "PC FDC SUPERIO")
|
|
DEFINE_DEVICE_TYPE(DP8473, dp8473_device, "dp8473", "National Semiconductor DP8473 FDC")
|
|
DEFINE_DEVICE_TYPE(PC8477A, pc8477a_device, "pc8477a", "National Semiconductor PC8477A FDC")
|
|
DEFINE_DEVICE_TYPE(WD37C65C, wd37c65c_device, "wd37c65c", "Western Digital WD37C65C FDC")
|
|
DEFINE_DEVICE_TYPE(MCS3201, mcs3201_device, "mcs3201", "Motorola MCS3201 FDC")
|
|
DEFINE_DEVICE_TYPE(TC8566AF, tc8566af_device, "tc8566af", "Toshiba TC8566AF FDC")
|
|
|
|
void upd765a_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(upd765a_device::msr_r));
|
|
map(0x1, 0x1).rw(FUNC(upd765a_device::fifo_r), FUNC(upd765a_device::fifo_w));
|
|
}
|
|
|
|
void upd765b_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(upd765b_device::msr_r));
|
|
map(0x1, 0x1).rw(FUNC(upd765b_device::fifo_r), FUNC(upd765b_device::fifo_w));
|
|
}
|
|
|
|
void i8272a_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(i8272a_device::msr_r));
|
|
map(0x1, 0x1).rw(FUNC(i8272a_device::fifo_r), FUNC(i8272a_device::fifo_w));
|
|
}
|
|
|
|
void upd72065_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(upd72065_device::msr_r));
|
|
map(0x1, 0x1).rw(FUNC(upd72065_device::fifo_r), FUNC(upd72065_device::fifo_w));
|
|
}
|
|
|
|
void i82072_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).rw(FUNC(i82072_device::msr_r), FUNC(i82072_device::dsr_w));
|
|
map(0x1, 0x1).rw(FUNC(i82072_device::fifo_r), FUNC(i82072_device::fifo_w));
|
|
}
|
|
|
|
void smc37c78_device::map(address_map &map)
|
|
{
|
|
map(0x2, 0x2).rw(FUNC(smc37c78_device::dor_r), FUNC(smc37c78_device::dor_w));
|
|
map(0x3, 0x3).rw(FUNC(smc37c78_device::tdr_r), FUNC(smc37c78_device::tdr_w));
|
|
map(0x4, 0x4).rw(FUNC(smc37c78_device::msr_r), FUNC(smc37c78_device::dsr_w));
|
|
map(0x5, 0x5).rw(FUNC(smc37c78_device::fifo_r), FUNC(smc37c78_device::fifo_w));
|
|
map(0x7, 0x7).rw(FUNC(smc37c78_device::dir_r), FUNC(smc37c78_device::ccr_w));
|
|
}
|
|
|
|
void n82077aa_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(n82077aa_device::sra_r));
|
|
map(0x1, 0x1).r(FUNC(n82077aa_device::srb_r));
|
|
map(0x2, 0x2).rw(FUNC(n82077aa_device::dor_r), FUNC(n82077aa_device::dor_w));
|
|
map(0x3, 0x3).rw(FUNC(n82077aa_device::tdr_r), FUNC(n82077aa_device::tdr_w));
|
|
map(0x4, 0x4).rw(FUNC(n82077aa_device::msr_r), FUNC(n82077aa_device::dsr_w));
|
|
map(0x5, 0x5).rw(FUNC(n82077aa_device::fifo_r), FUNC(n82077aa_device::fifo_w));
|
|
map(0x7, 0x7).rw(FUNC(n82077aa_device::dir_r), FUNC(n82077aa_device::ccr_w));
|
|
}
|
|
|
|
void pc_fdc_superio_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(pc_fdc_superio_device::sra_r));
|
|
map(0x1, 0x1).r(FUNC(pc_fdc_superio_device::srb_r));
|
|
map(0x2, 0x2).rw(FUNC(pc_fdc_superio_device::dor_r), FUNC(pc_fdc_superio_device::dor_w));
|
|
map(0x3, 0x3).rw(FUNC(pc_fdc_superio_device::tdr_r), FUNC(pc_fdc_superio_device::tdr_w));
|
|
map(0x4, 0x4).rw(FUNC(pc_fdc_superio_device::msr_r), FUNC(pc_fdc_superio_device::dsr_w));
|
|
map(0x5, 0x5).rw(FUNC(pc_fdc_superio_device::fifo_r), FUNC(pc_fdc_superio_device::fifo_w));
|
|
map(0x7, 0x7).rw(FUNC(pc_fdc_superio_device::dir_r), FUNC(pc_fdc_superio_device::ccr_w));
|
|
}
|
|
|
|
void dp8473_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(dp8473_device::sra_r));
|
|
map(0x1, 0x1).r(FUNC(dp8473_device::srb_r));
|
|
map(0x2, 0x2).rw(FUNC(dp8473_device::dor_r), FUNC(dp8473_device::dor_w));
|
|
map(0x3, 0x3).rw(FUNC(dp8473_device::tdr_r), FUNC(dp8473_device::tdr_w));
|
|
map(0x4, 0x4).rw(FUNC(dp8473_device::msr_r), FUNC(dp8473_device::dsr_w));
|
|
map(0x5, 0x5).rw(FUNC(dp8473_device::fifo_r), FUNC(dp8473_device::fifo_w));
|
|
map(0x7, 0x7).rw(FUNC(dp8473_device::dir_r), FUNC(dp8473_device::ccr_w));
|
|
}
|
|
|
|
void pc8477a_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(pc8477a_device::sra_r));
|
|
map(0x1, 0x1).r(FUNC(pc8477a_device::srb_r));
|
|
map(0x2, 0x2).rw(FUNC(pc8477a_device::dor_r), FUNC(pc8477a_device::dor_w));
|
|
map(0x3, 0x3).rw(FUNC(pc8477a_device::tdr_r), FUNC(pc8477a_device::tdr_w));
|
|
map(0x4, 0x4).rw(FUNC(pc8477a_device::msr_r), FUNC(pc8477a_device::dsr_w));
|
|
map(0x5, 0x5).rw(FUNC(pc8477a_device::fifo_r), FUNC(pc8477a_device::fifo_w));
|
|
map(0x7, 0x7).rw(FUNC(pc8477a_device::dir_r), FUNC(pc8477a_device::ccr_w));
|
|
}
|
|
|
|
void wd37c65c_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(wd37c65c_device::msr_r));
|
|
map(0x1, 0x1).rw(FUNC(wd37c65c_device::fifo_r), FUNC(wd37c65c_device::fifo_w));
|
|
}
|
|
|
|
void mcs3201_device::map(address_map &map)
|
|
{
|
|
map(0x0, 0x0).r(FUNC(mcs3201_device::input_r));
|
|
map(0x2, 0x2).w(FUNC(mcs3201_device::dor_w));
|
|
map(0x4, 0x4).r(FUNC(mcs3201_device::msr_r));
|
|
map(0x5, 0x5).rw(FUNC(mcs3201_device::fifo_r), FUNC(mcs3201_device::fifo_w));
|
|
map(0x7, 0x7).rw(FUNC(mcs3201_device::dir_r), FUNC(mcs3201_device::ccr_w));
|
|
}
|
|
|
|
void tc8566af_device::map(address_map &map)
|
|
{
|
|
map(0x2, 0x2).w(FUNC(tc8566af_device::dor_w));
|
|
map(0x3, 0x3).w(FUNC(tc8566af_device::cr1_w));
|
|
map(0x4, 0x4).r(FUNC(tc8566af_device::msr_r));
|
|
map(0x5, 0x5).rw(FUNC(tc8566af_device::fifo_r), FUNC(tc8566af_device::fifo_w));
|
|
}
|
|
|
|
|
|
constexpr int upd765_family_device::rates[4];
|
|
|
|
upd765_family_device::upd765_family_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
|
|
pc_fdc_interface(mconfig, type, tag, owner, clock),
|
|
intrq_cb(*this),
|
|
drq_cb(*this),
|
|
hdl_cb(*this)
|
|
{
|
|
ready_polled = true;
|
|
ready_connected = true;
|
|
select_connected = true;
|
|
external_ready = false;
|
|
dor_reset = 0x00;
|
|
mode = MODE_AT;
|
|
}
|
|
|
|
void upd765_family_device::set_ready_line_connected(bool _ready)
|
|
{
|
|
ready_connected = _ready;
|
|
}
|
|
|
|
void upd765_family_device::set_select_lines_connected(bool _select)
|
|
{
|
|
select_connected = _select;
|
|
}
|
|
|
|
void upd765_family_device::set_mode(int _mode)
|
|
{
|
|
mode = _mode;
|
|
}
|
|
|
|
void upd765_family_device::device_start()
|
|
{
|
|
save_item(NAME(motorcfg));
|
|
save_item(NAME(selected_drive));
|
|
|
|
intrq_cb.resolve_safe();
|
|
drq_cb.resolve_safe();
|
|
hdl_cb.resolve_safe();
|
|
|
|
for(int i=0; i != 4; i++) {
|
|
char name[2];
|
|
flopi[i].tm = timer_alloc(i);
|
|
flopi[i].id = i;
|
|
if(select_connected) {
|
|
name[0] = '0'+i;
|
|
name[1] = 0;
|
|
floppy_connector *con = subdevice<floppy_connector>(name);
|
|
if(con) {
|
|
flopi[i].dev = con->get_device();
|
|
if(flopi[i].dev != nullptr)
|
|
flopi[i].dev->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&upd765_family_device::index_callback, this));
|
|
} else
|
|
flopi[i].dev = nullptr;
|
|
} else
|
|
flopi[i].dev = nullptr;
|
|
|
|
flopi[i].main_state = IDLE;
|
|
flopi[i].sub_state = IDLE;
|
|
flopi[i].dir = 0;
|
|
flopi[i].counter = 0;
|
|
flopi[i].pcn = 0;
|
|
flopi[i].st0 = 0;
|
|
flopi[i].st0_filled = false;
|
|
flopi[i].live = false;
|
|
flopi[i].index = false;
|
|
flopi[i].ready = false;
|
|
}
|
|
cur_rate = 250000;
|
|
tc = false;
|
|
selected_drive = -1;
|
|
|
|
// reset at upper levels may cause a write to tc ending up with
|
|
// live_sync, which will crash if the live structure isn't
|
|
// initialized enough
|
|
|
|
cur_live.tm = attotime::never;
|
|
cur_live.state = IDLE;
|
|
cur_live.next_state = -1;
|
|
cur_live.fi = nullptr;
|
|
|
|
if(ready_polled) {
|
|
poll_timer = timer_alloc(TIMER_DRIVE_READY_POLLING);
|
|
poll_timer->adjust(attotime::from_usec(100), 0, attotime::from_usec(1024));
|
|
} else
|
|
poll_timer = nullptr;
|
|
|
|
cur_irq = false;
|
|
locked = false;
|
|
}
|
|
|
|
void upd765_family_device::device_reset()
|
|
{
|
|
dor = dor_reset;
|
|
locked = false;
|
|
soft_reset();
|
|
}
|
|
|
|
void upd765_family_device::soft_reset()
|
|
{
|
|
main_phase = PHASE_CMD;
|
|
for(int i=0; i<4; i++) {
|
|
flopi[i].main_state = IDLE;
|
|
flopi[i].sub_state = IDLE;
|
|
flopi[i].live = false;
|
|
flopi[i].ready = !ready_polled;
|
|
flopi[i].st0 = i;
|
|
flopi[i].st0_filled = false;
|
|
}
|
|
clr_drive_busy();
|
|
data_irq = false;
|
|
other_irq = false;
|
|
internal_drq = false;
|
|
fifo_pos = 0;
|
|
command_pos = 0;
|
|
result_pos = 0;
|
|
if(!locked)
|
|
fifocfg = FIF_DIS;
|
|
cur_live.fi = nullptr;
|
|
drq = false;
|
|
cur_live.tm = attotime::never;
|
|
cur_live.state = IDLE;
|
|
cur_live.next_state = -1;
|
|
cur_live.fi = nullptr;
|
|
tc_done = false;
|
|
st1 = st2 = st3 = 0x00;
|
|
selected_drive = -1;
|
|
|
|
check_irq();
|
|
if(ready_polled)
|
|
poll_timer->adjust(attotime::from_usec(100), 0, attotime::from_usec(1024));
|
|
}
|
|
|
|
void upd765_family_device::tc_w(bool _tc)
|
|
{
|
|
LOGTCIRQ("tc=%d\n", _tc);
|
|
if(tc != _tc && _tc) {
|
|
live_sync();
|
|
tc_done = true;
|
|
tc = _tc;
|
|
if(cur_live.fi)
|
|
general_continue(*cur_live.fi);
|
|
} else
|
|
tc = _tc;
|
|
}
|
|
|
|
void upd765_family_device::ready_w(bool _ready)
|
|
{
|
|
external_ready = _ready;
|
|
}
|
|
|
|
bool upd765_family_device::get_ready(int fid)
|
|
{
|
|
if(ready_connected)
|
|
return flopi[fid].dev ? !flopi[fid].dev->ready_r() : false;
|
|
return !external_ready;
|
|
}
|
|
|
|
void upd765_family_device::set_ds(int fid)
|
|
{
|
|
if(selected_drive == fid)
|
|
return;
|
|
|
|
// 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;
|
|
}
|
|
|
|
void upd765_family_device::set_floppy(floppy_image_device *flop)
|
|
{
|
|
for(floppy_info & elem : flopi) {
|
|
if(elem.dev)
|
|
elem.dev->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
|
|
elem.dev = flop;
|
|
}
|
|
if(flop)
|
|
flop->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&upd765_family_device::index_callback, this));
|
|
}
|
|
|
|
READ8_MEMBER(upd765_family_device::sra_r)
|
|
{
|
|
uint8_t sra = 0;
|
|
int fid = dor & 3;
|
|
floppy_info &fi = flopi[fid];
|
|
if(fi.dir)
|
|
sra |= 0x01;
|
|
if(fi.index)
|
|
sra |= 0x04;
|
|
if(cur_rate >= 500000)
|
|
sra |= 0x08;
|
|
if(fi.dev && fi.dev->trk00_r())
|
|
sra |= 0x10;
|
|
if(fi.main_state == SEEK_WAIT_STEP_SIGNAL_TIME)
|
|
sra |= 0x20;
|
|
sra |= 0x40;
|
|
if(cur_irq)
|
|
sra |= 0x80;
|
|
if(mode == MODE_M30)
|
|
sra ^= 0x1f;
|
|
return sra;
|
|
}
|
|
|
|
READ8_MEMBER(upd765_family_device::srb_r)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
READ8_MEMBER(upd765_family_device::dor_r)
|
|
{
|
|
return dor;
|
|
}
|
|
|
|
WRITE8_MEMBER(upd765_family_device::dor_w)
|
|
{
|
|
LOGREGS("dor = %02x\n", data);
|
|
uint8_t diff = dor ^ data;
|
|
dor = data;
|
|
if(diff & 4)
|
|
soft_reset();
|
|
|
|
for(int i=0; i<4; i++) {
|
|
floppy_info &fi = flopi[i];
|
|
if(fi.dev)
|
|
fi.dev->mon_w(!(dor & (0x10 << i)));
|
|
}
|
|
check_irq();
|
|
}
|
|
|
|
READ8_MEMBER(upd765_family_device::tdr_r)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
WRITE8_MEMBER(upd765_family_device::tdr_w)
|
|
{
|
|
}
|
|
|
|
READ8_MEMBER(upd765_family_device::msr_r)
|
|
{
|
|
return read_msr();
|
|
}
|
|
|
|
uint8_t upd765_family_device::read_msr()
|
|
{
|
|
uint32_t msr = 0;
|
|
switch(main_phase) {
|
|
case PHASE_CMD:
|
|
msr |= MSR_RQM;
|
|
if(command_pos)
|
|
msr |= MSR_CB;
|
|
break;
|
|
case PHASE_EXEC:
|
|
msr |= MSR_CB;
|
|
if(spec & SPEC_ND)
|
|
msr |= MSR_EXM;
|
|
if(internal_drq) {
|
|
msr |= MSR_RQM;
|
|
if(!fifo_write)
|
|
msr |= MSR_DIO;
|
|
}
|
|
break;
|
|
|
|
case PHASE_RESULT:
|
|
msr |= MSR_RQM|MSR_DIO|MSR_CB;
|
|
break;
|
|
}
|
|
for(int i=0; i<4; i++)
|
|
if(flopi[i].main_state == RECALIBRATE || flopi[i].main_state == SEEK) {
|
|
msr |= 1<<i;
|
|
//msr |= MSR_CB;
|
|
}
|
|
msr |= get_drive_busy();
|
|
|
|
if(data_irq) {
|
|
data_irq = false;
|
|
check_irq();
|
|
}
|
|
|
|
return msr;
|
|
}
|
|
|
|
WRITE8_MEMBER(upd765_family_device::dsr_w)
|
|
{
|
|
LOGREGS("dsr_w %02x (%s)\n", data, machine().describe_context());
|
|
if(data & 0x80)
|
|
soft_reset();
|
|
dsr = data & 0x7f;
|
|
cur_rate = rates[dsr & 3];
|
|
}
|
|
|
|
void upd765_family_device::set_rate(int rate)
|
|
{
|
|
cur_rate = rate;
|
|
}
|
|
|
|
uint8_t upd765_family_device::read_fifo()
|
|
{
|
|
uint8_t r = 0xff;
|
|
switch(main_phase) {
|
|
case PHASE_EXEC:
|
|
if(internal_drq)
|
|
return fifo_pop(false);
|
|
LOGFIFO("read_fifo in phase %d\n", main_phase);
|
|
break;
|
|
|
|
case PHASE_RESULT:
|
|
r = result[0];
|
|
result_pos--;
|
|
memmove(result, result+1, result_pos);
|
|
if(!result_pos)
|
|
main_phase = PHASE_CMD;
|
|
else if(result_pos == 1) {
|
|
// clear drive busy bit after the first sense interrupt status result byte is read
|
|
for(floppy_info &fi : flopi)
|
|
if((fi.main_state == RECALIBRATE || fi.main_state == SEEK) && fi.sub_state == IDLE && fi.st0_filled == false)
|
|
fi.main_state = IDLE;
|
|
clr_drive_busy();
|
|
}
|
|
break;
|
|
default:
|
|
LOGFIFO("read_fifo in phase %d\n", main_phase);
|
|
break;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
void upd765_family_device::write_fifo(uint8_t data)
|
|
{
|
|
switch(main_phase) {
|
|
case PHASE_CMD: {
|
|
command[command_pos++] = data;
|
|
other_irq = false;
|
|
check_irq();
|
|
int cmd = check_command();
|
|
if(cmd == C_INCOMPLETE)
|
|
break;
|
|
if(cmd == C_INVALID) {
|
|
LOGFIFO("Invalid on %02x\n", command[0]);
|
|
main_phase = PHASE_RESULT;
|
|
result[0] = ST0_UNK;
|
|
result_pos = 1;
|
|
command_pos = 0;
|
|
return;
|
|
}
|
|
start_command(cmd);
|
|
break;
|
|
}
|
|
case PHASE_EXEC:
|
|
if(internal_drq) {
|
|
fifo_push(data, false);
|
|
return;
|
|
}
|
|
LOGFIFO("write_fifo in phase %d\n", main_phase);
|
|
break;
|
|
|
|
default:
|
|
LOGFIFO("write_fifo in phase %d\n", main_phase);
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t upd765_family_device::do_dir_r()
|
|
{
|
|
floppy_info &fi = flopi[dor & 3];
|
|
if(fi.dev)
|
|
return fi.dev->dskchg_r() ? 0x00 : 0x80;
|
|
return 0x00;
|
|
}
|
|
|
|
READ8_MEMBER(upd765_family_device::dir_r)
|
|
{
|
|
return do_dir_r();
|
|
}
|
|
|
|
WRITE8_MEMBER(upd765_family_device::ccr_w)
|
|
{
|
|
dsr = (dsr & 0xfc) | (data & 3);
|
|
cur_rate = rates[data & 3];
|
|
}
|
|
|
|
void upd765_family_device::set_drq(bool state)
|
|
{
|
|
if(state != drq) {
|
|
drq = state;
|
|
drq_cb(drq);
|
|
}
|
|
}
|
|
|
|
bool upd765_family_device::get_drq() const
|
|
{
|
|
return drq;
|
|
}
|
|
|
|
void upd765_family_device::enable_transfer()
|
|
{
|
|
if(spec & SPEC_ND) {
|
|
// PIO
|
|
if(!internal_drq) {
|
|
internal_drq = true;
|
|
check_irq();
|
|
}
|
|
|
|
} else {
|
|
// DMA
|
|
if(!drq)
|
|
set_drq(true);
|
|
}
|
|
}
|
|
|
|
void upd765_family_device::disable_transfer()
|
|
{
|
|
if(spec & SPEC_ND) {
|
|
internal_drq = false;
|
|
check_irq();
|
|
} else
|
|
set_drq(false);
|
|
}
|
|
|
|
void upd765_family_device::fifo_push(uint8_t data, bool internal)
|
|
{
|
|
// MZ: A bit speculative. These lines help to avoid some FIFO mess-up
|
|
// with the HX5102 that happens when WRITE DATA fails to find the sector
|
|
// but the host already starts pushing the sector data. Should not hurt.
|
|
if (fifo_expected == 0)
|
|
{
|
|
LOGFIFO("Fifo not expecting data, discarding\n");
|
|
return;
|
|
}
|
|
|
|
if(fifo_pos == 16) {
|
|
if(internal) {
|
|
if(!(st1 & ST1_OR))
|
|
LOGFIFO("Fifo overrun\n");
|
|
st1 |= ST1_OR;
|
|
}
|
|
return;
|
|
}
|
|
fifo[fifo_pos++] = data;
|
|
fifo_expected--;
|
|
|
|
int thr = (fifocfg & FIF_THR)+1;
|
|
if(!fifo_write && (!fifo_expected || fifo_pos >= thr || (fifocfg & FIF_DIS)))
|
|
enable_transfer();
|
|
if(fifo_write && (fifo_pos == 16 || !fifo_expected))
|
|
disable_transfer();
|
|
}
|
|
|
|
|
|
uint8_t upd765_family_device::fifo_pop(bool internal)
|
|
{
|
|
if(!fifo_pos) {
|
|
if(internal) {
|
|
if(!(st1 & ST1_OR))
|
|
LOGFIFO("Fifo underrun\n");
|
|
st1 |= ST1_OR;
|
|
}
|
|
return 0;
|
|
}
|
|
uint8_t r = fifo[0];
|
|
fifo_pos--;
|
|
memmove(fifo, fifo+1, fifo_pos);
|
|
if(!fifo_write && !fifo_pos)
|
|
disable_transfer();
|
|
int thr = fifocfg & 15;
|
|
if(fifo_write && fifo_expected && (fifo_pos <= thr || (fifocfg & 0x20)))
|
|
enable_transfer();
|
|
return r;
|
|
}
|
|
|
|
void upd765_family_device::fifo_expect(int size, bool write)
|
|
{
|
|
fifo_expected = size;
|
|
fifo_write = write;
|
|
if(fifo_write)
|
|
enable_transfer();
|
|
}
|
|
|
|
READ8_MEMBER(upd765_family_device::mdma_r)
|
|
{
|
|
return dma_r();
|
|
}
|
|
|
|
WRITE8_MEMBER(upd765_family_device::mdma_w)
|
|
{
|
|
dma_w(data);
|
|
}
|
|
|
|
uint8_t upd765_family_device::dma_r()
|
|
{
|
|
return fifo_pop(false);
|
|
}
|
|
|
|
void upd765_family_device::dma_w(uint8_t data)
|
|
{
|
|
fifo_push(data, false);
|
|
}
|
|
|
|
void upd765_family_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.bit_counter = 0;
|
|
cur_live.data_separator_phase = false;
|
|
cur_live.data_reg = 0;
|
|
cur_live.previous_type = live_info::PT_NONE;
|
|
cur_live.data_bit_context = false;
|
|
cur_live.byte_counter = 0;
|
|
cur_live.pll.reset(cur_live.tm);
|
|
cur_live.pll.set_clock(attotime::from_hz(mfm ? 2*cur_rate : cur_rate));
|
|
checkpoint_live = cur_live;
|
|
fi.live = true;
|
|
|
|
live_run();
|
|
}
|
|
|
|
void upd765_family_device::checkpoint()
|
|
{
|
|
if(cur_live.fi)
|
|
cur_live.pll.commit(cur_live.fi->dev, cur_live.tm);
|
|
checkpoint_live = cur_live;
|
|
}
|
|
|
|
void upd765_family_device::rollback()
|
|
{
|
|
cur_live = checkpoint_live;
|
|
}
|
|
|
|
void upd765_family_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());
|
|
else
|
|
live_sync();
|
|
}
|
|
|
|
void upd765_family_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 upd765_family_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 upd765_family_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));
|
|
}
|
|
}
|
|
|
|
for(;;) {
|
|
switch(cur_live.state) {
|
|
case SEARCH_ADDRESS_MARK_HEADER:
|
|
if(read_one_bit(limit))
|
|
return;
|
|
|
|
LOGSHIFT("%s: shift = %04x data=%02x c=%d\n", tts(cur_live.tm), cur_live.shift_reg,
|
|
(cur_live.shift_reg & 0x4000 ? 0x80 : 0x00) |
|
|
(cur_live.shift_reg & 0x1000 ? 0x40 : 0x00) |
|
|
(cur_live.shift_reg & 0x0400 ? 0x20 : 0x00) |
|
|
(cur_live.shift_reg & 0x0100 ? 0x10 : 0x00) |
|
|
(cur_live.shift_reg & 0x0040 ? 0x08 : 0x00) |
|
|
(cur_live.shift_reg & 0x0010 ? 0x04 : 0x00) |
|
|
(cur_live.shift_reg & 0x0004 ? 0x02 : 0x00) |
|
|
(cur_live.shift_reg & 0x0001 ? 0x01 : 0x00),
|
|
cur_live.bit_counter);
|
|
|
|
if(mfm && cur_live.shift_reg == 0x4489) {
|
|
cur_live.crc = 0x443b;
|
|
cur_live.data_separator_phase = false;
|
|
cur_live.bit_counter = 0;
|
|
cur_live.state = READ_HEADER_BLOCK_HEADER;
|
|
LOGLIVE("%s: Found A1\n", tts(cur_live.tm));
|
|
}
|
|
|
|
if(!mfm && cur_live.shift_reg == 0xf57e) {
|
|
cur_live.crc = 0xef21;
|
|
cur_live.data_separator_phase = false;
|
|
cur_live.bit_counter = 0;
|
|
cur_live.state = READ_ID_BLOCK;
|
|
LOGLIVE("%s: Found IDAM\n", tts(cur_live.tm));
|
|
}
|
|
break;
|
|
|
|
case READ_HEADER_BLOCK_HEADER: {
|
|
if(read_one_bit(limit))
|
|
return;
|
|
|
|
LOGSHIFT("%s: shift = %04x data=%02x counter=%d\n", tts(cur_live.tm), cur_live.shift_reg,
|
|
(cur_live.shift_reg & 0x4000 ? 0x80 : 0x00) |
|
|
(cur_live.shift_reg & 0x1000 ? 0x40 : 0x00) |
|
|
(cur_live.shift_reg & 0x0400 ? 0x20 : 0x00) |
|
|
(cur_live.shift_reg & 0x0100 ? 0x10 : 0x00) |
|
|
(cur_live.shift_reg & 0x0040 ? 0x08 : 0x00) |
|
|
(cur_live.shift_reg & 0x0010 ? 0x04 : 0x00) |
|
|
(cur_live.shift_reg & 0x0004 ? 0x02 : 0x00) |
|
|
(cur_live.shift_reg & 0x0001 ? 0x01 : 0x00),
|
|
cur_live.bit_counter);
|
|
|
|
if(cur_live.bit_counter & 15)
|
|
break;
|
|
|
|
int slot = cur_live.bit_counter >> 4;
|
|
|
|
if(slot < 3) {
|
|
if(cur_live.shift_reg != 0x4489)
|
|
cur_live.state = SEARCH_ADDRESS_MARK_HEADER;
|
|
else
|
|
LOGLIVE("%s: Found A1\n", tts(cur_live.tm));
|
|
break;
|
|
}
|
|
if(cur_live.data_reg != 0xfe) {
|
|
LOGLIVE("%s: No ident byte found after triple-A1, continue search\n", tts(cur_live.tm));
|
|
cur_live.state = SEARCH_ADDRESS_MARK_HEADER;
|
|
break;
|
|
}
|
|
|
|
cur_live.bit_counter = 0;
|
|
cur_live.state = READ_ID_BLOCK;
|
|
|
|
break;
|
|
}
|
|
|
|
case READ_ID_BLOCK: {
|
|
if(read_one_bit(limit))
|
|
return;
|
|
if(cur_live.bit_counter & 15)
|
|
break;
|
|
int slot = (cur_live.bit_counter >> 4)-1;
|
|
|
|
LOGLIVE("%s: slot=%d data=%02x crc=%04x\n", tts(cur_live.tm), slot, cur_live.data_reg, cur_live.crc);
|
|
cur_live.idbuf[slot] = cur_live.data_reg;
|
|
if(slot == 5) {
|
|
live_delay(IDLE);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SEARCH_ADDRESS_MARK_DATA:
|
|
if(read_one_bit(limit))
|
|
return;
|
|
|
|
LOGSHIFT("%s: shift = %04x data=%02x c=%d.%x\n", tts(cur_live.tm), cur_live.shift_reg,
|
|
(cur_live.shift_reg & 0x4000 ? 0x80 : 0x00) |
|
|
(cur_live.shift_reg & 0x1000 ? 0x40 : 0x00) |
|
|
(cur_live.shift_reg & 0x0400 ? 0x20 : 0x00) |
|
|
(cur_live.shift_reg & 0x0100 ? 0x10 : 0x00) |
|
|
(cur_live.shift_reg & 0x0040 ? 0x08 : 0x00) |
|
|
(cur_live.shift_reg & 0x0010 ? 0x04 : 0x00) |
|
|
(cur_live.shift_reg & 0x0004 ? 0x02 : 0x00) |
|
|
(cur_live.shift_reg & 0x0001 ? 0x01 : 0x00),
|
|
cur_live.bit_counter >> 4, cur_live.bit_counter & 15);
|
|
|
|
if(mfm) {
|
|
// Large tolerance due to perpendicular recording at extended density
|
|
if(cur_live.bit_counter > 62*16) {
|
|
live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);
|
|
return;
|
|
}
|
|
|
|
if(cur_live.bit_counter >= 28*16 && cur_live.shift_reg == 0x4489) {
|
|
cur_live.crc = 0x443b;
|
|
cur_live.data_separator_phase = false;
|
|
cur_live.bit_counter = 0;
|
|
cur_live.state = READ_DATA_BLOCK_HEADER;
|
|
}
|
|
|
|
} else {
|
|
if(cur_live.bit_counter > 23*16) {
|
|
live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);
|
|
return;
|
|
}
|
|
|
|
if(cur_live.bit_counter >= 11*16 && (cur_live.shift_reg == 0xf56a || cur_live.shift_reg == 0xf56f)) {
|
|
cur_live.crc = cur_live.shift_reg == 0xf56a ? 0x8fe7 : 0xbf84;
|
|
cur_live.data_separator_phase = false;
|
|
cur_live.bit_counter = 0;
|
|
cur_live.state = READ_SECTOR_DATA;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case READ_DATA_BLOCK_HEADER: {
|
|
if(read_one_bit(limit))
|
|
return;
|
|
|
|
LOGSHIFT("%s: shift = %04x data=%02x counter=%d\n", tts(cur_live.tm), cur_live.shift_reg,
|
|
(cur_live.shift_reg & 0x4000 ? 0x80 : 0x00) |
|
|
(cur_live.shift_reg & 0x1000 ? 0x40 : 0x00) |
|
|
(cur_live.shift_reg & 0x0400 ? 0x20 : 0x00) |
|
|
(cur_live.shift_reg & 0x0100 ? 0x10 : 0x00) |
|
|
(cur_live.shift_reg & 0x0040 ? 0x08 : 0x00) |
|
|
(cur_live.shift_reg & 0x0010 ? 0x04 : 0x00) |
|
|
(cur_live.shift_reg & 0x0004 ? 0x02 : 0x00) |
|
|
(cur_live.shift_reg & 0x0001 ? 0x01 : 0x00),
|
|
cur_live.bit_counter);
|
|
|
|
if(cur_live.bit_counter & 15)
|
|
break;
|
|
|
|
int slot = cur_live.bit_counter >> 4;
|
|
|
|
if(slot < 3) {
|
|
if(cur_live.shift_reg != 0x4489) {
|
|
live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
if(cur_live.data_reg != 0xfb && cur_live.data_reg != 0xf8) {
|
|
live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);
|
|
return;
|
|
}
|
|
|
|
cur_live.bit_counter = 0;
|
|
cur_live.state = READ_SECTOR_DATA;
|
|
break;
|
|
}
|
|
|
|
case SEARCH_ADDRESS_MARK_DATA_FAILED:
|
|
st1 |= ST1_MA;
|
|
st2 |= ST2_MD;
|
|
cur_live.state = IDLE;
|
|
return;
|
|
|
|
case READ_SECTOR_DATA: {
|
|
if(read_one_bit(limit))
|
|
return;
|
|
if(cur_live.bit_counter & 15)
|
|
break;
|
|
int slot = (cur_live.bit_counter >> 4)-1;
|
|
if(slot < sector_size) {
|
|
// Sector data
|
|
if(cur_live.fi->main_state == SCAN_DATA)
|
|
live_delay(SCAN_SECTOR_DATA_BYTE);
|
|
else
|
|
live_delay(READ_SECTOR_DATA_BYTE);
|
|
return;
|
|
|
|
} else if(slot < sector_size+2) {
|
|
// CRC
|
|
if(slot == sector_size+1) {
|
|
live_delay(IDLE);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case READ_SECTOR_DATA_BYTE:
|
|
if(!tc_done)
|
|
fifo_push(cur_live.data_reg, true);
|
|
cur_live.state = READ_SECTOR_DATA;
|
|
checkpoint();
|
|
break;
|
|
|
|
case SCAN_SECTOR_DATA_BYTE:
|
|
if(!scan_done) // TODO: handle stp, x68000 sets it to 0xff (as it would dtl)?
|
|
{
|
|
int slot = (cur_live.bit_counter >> 4)-1;
|
|
uint8_t data = fifo_pop(true);
|
|
if(!slot)
|
|
st2 = (st2 & ~(ST2_SN)) | ST2_SH;
|
|
|
|
if(data != cur_live.data_reg)
|
|
{
|
|
st2 = (st2 & ~(ST2_SH)) | ST2_SN;
|
|
if((data < cur_live.data_reg) && ((command[0] & 0x1f) == 0x19)) // low
|
|
st2 &= ~ST2_SN;
|
|
|
|
if((data > cur_live.data_reg) && ((command[0] & 0x1f) == 0x1d)) // high
|
|
st2 &= ~ST2_SN;
|
|
}
|
|
if((slot == sector_size) && !(st2 & ST2_SN))
|
|
{
|
|
scan_done = true;
|
|
tc_done = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(fifo_pos)
|
|
fifo_pop(true);
|
|
}
|
|
cur_live.state = READ_SECTOR_DATA;
|
|
checkpoint();
|
|
break;
|
|
|
|
case WRITE_SECTOR_SKIP_GAP2:
|
|
cur_live.bit_counter = 0;
|
|
cur_live.byte_counter = 0;
|
|
cur_live.state = WRITE_SECTOR_SKIP_GAP2_BYTE;
|
|
checkpoint();
|
|
break;
|
|
|
|
case WRITE_SECTOR_SKIP_GAP2_BYTE:
|
|
if(read_one_bit(limit))
|
|
return;
|
|
if(mfm && cur_live.bit_counter != 22*16)
|
|
break;
|
|
if(!mfm && cur_live.bit_counter != 11*16)
|
|
break;
|
|
cur_live.bit_counter = 0;
|
|
cur_live.byte_counter = 0;
|
|
live_delay(WRITE_SECTOR_DATA);
|
|
return;
|
|
|
|
case WRITE_SECTOR_DATA:
|
|
if(mfm) {
|
|
if(cur_live.byte_counter < 12)
|
|
live_write_mfm(0x00);
|
|
else if(cur_live.byte_counter < 15)
|
|
live_write_raw(0x4489);
|
|
else if(cur_live.byte_counter < 16) {
|
|
cur_live.crc = 0xcdb4;
|
|
live_write_mfm(command[0] & 0x08 ? 0xf8 : 0xfb);
|
|
} else if(cur_live.byte_counter < 16+sector_size)
|
|
live_write_mfm(tc_done && !fifo_pos? 0x00 : fifo_pop(true));
|
|
else if(cur_live.byte_counter < 16+sector_size+2)
|
|
live_write_mfm(cur_live.crc >> 8);
|
|
else if(cur_live.byte_counter < 16+sector_size+2+command[7])
|
|
live_write_mfm(0x4e);
|
|
else {
|
|
cur_live.pll.stop_writing(cur_live.fi->dev, cur_live.tm);
|
|
cur_live.state = IDLE;
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
if(cur_live.byte_counter < 6)
|
|
live_write_fm(0x00);
|
|
else if(cur_live.byte_counter < 7) {
|
|
cur_live.crc = 0xffff;
|
|
live_write_raw(command[0] & 0x08 ? 0xf56a : 0xf56f);
|
|
} else if(cur_live.byte_counter < 7+sector_size)
|
|
live_write_fm(tc_done && !fifo_pos? 0x00 : fifo_pop(true));
|
|
else if(cur_live.byte_counter < 7+sector_size+2)
|
|
live_write_fm(cur_live.crc >> 8);
|
|
else if(cur_live.byte_counter < 7+sector_size+2+command[7])
|
|
live_write_fm(0xff);
|
|
else {
|
|
cur_live.pll.stop_writing(cur_live.fi->dev, cur_live.tm);
|
|
cur_live.state = IDLE;
|
|
return;
|
|
}
|
|
}
|
|
cur_live.state = WRITE_SECTOR_DATA_BYTE;
|
|
cur_live.bit_counter = 16;
|
|
checkpoint();
|
|
break;
|
|
|
|
case WRITE_TRACK_PRE_SECTORS:
|
|
if(!cur_live.byte_counter && command[3])
|
|
fifo_expect(4, true);
|
|
if(mfm) {
|
|
if(cur_live.byte_counter < 80)
|
|
live_write_mfm(0x4e);
|
|
else if(cur_live.byte_counter < 92)
|
|
live_write_mfm(0x00);
|
|
else if(cur_live.byte_counter < 95)
|
|
live_write_raw(0x5224);
|
|
else if(cur_live.byte_counter < 96)
|
|
live_write_mfm(0xfc);
|
|
else if(cur_live.byte_counter < 146)
|
|
live_write_mfm(0x4e);
|
|
else {
|
|
cur_live.state = WRITE_TRACK_SECTOR;
|
|
cur_live.byte_counter = 0;
|
|
break;
|
|
}
|
|
} else {
|
|
if(cur_live.byte_counter < 40)
|
|
live_write_fm(0xff);
|
|
else if(cur_live.byte_counter < 46)
|
|
live_write_fm(0x00);
|
|
else if(cur_live.byte_counter < 47)
|
|
live_write_raw(0xf77a);
|
|
else if(cur_live.byte_counter < 73)
|
|
live_write_fm(0xff);
|
|
else {
|
|
cur_live.state = WRITE_TRACK_SECTOR;
|
|
cur_live.byte_counter = 0;
|
|
break;
|
|
}
|
|
}
|
|
cur_live.state = WRITE_TRACK_PRE_SECTORS_BYTE;
|
|
cur_live.bit_counter = 16;
|
|
checkpoint();
|
|
break;
|
|
|
|
case WRITE_TRACK_SECTOR:
|
|
if(!cur_live.byte_counter) {
|
|
command[3]--;
|
|
if(command[3])
|
|
fifo_expect(4, true);
|
|
}
|
|
if(mfm) {
|
|
if(cur_live.byte_counter < 12)
|
|
live_write_mfm(0x00);
|
|
else if(cur_live.byte_counter < 15)
|
|
live_write_raw(0x4489);
|
|
else if(cur_live.byte_counter < 16) {
|
|
cur_live.crc = 0xcdb4;
|
|
live_write_mfm(0xfe);
|
|
} else if(cur_live.byte_counter < 20) {
|
|
uint8_t byte = fifo_pop(true);
|
|
command[12+cur_live.byte_counter-16] = byte;
|
|
live_write_mfm(byte);
|
|
if(cur_live.byte_counter == 19)
|
|
LOGFORMAT("formatting sector %02x %02x %02x %02x\n",
|
|
command[12], command[13], command[14], command[15]);
|
|
} else if(cur_live.byte_counter < 22)
|
|
live_write_mfm(cur_live.crc >> 8);
|
|
else if(cur_live.byte_counter < 44)
|
|
live_write_mfm(0x4e);
|
|
else if(cur_live.byte_counter < 56)
|
|
live_write_mfm(0x00);
|
|
else if(cur_live.byte_counter < 59)
|
|
live_write_raw(0x4489);
|
|
else if(cur_live.byte_counter < 60) {
|
|
cur_live.crc = 0xcdb4;
|
|
live_write_mfm(0xfb);
|
|
} else if(cur_live.byte_counter < 60+sector_size)
|
|
live_write_mfm(command[5]);
|
|
else if(cur_live.byte_counter < 62+sector_size)
|
|
live_write_mfm(cur_live.crc >> 8);
|
|
else if(cur_live.byte_counter < 62+sector_size+command[4])
|
|
live_write_mfm(0x4e);
|
|
else {
|
|
cur_live.byte_counter = 0;
|
|
cur_live.state = command[3] ? WRITE_TRACK_SECTOR : WRITE_TRACK_POST_SECTORS;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
if(cur_live.byte_counter < 6)
|
|
live_write_fm(0x00);
|
|
else if(cur_live.byte_counter < 7) {
|
|
cur_live.crc = 0xffff;
|
|
live_write_raw(0xf57e);
|
|
} else if(cur_live.byte_counter < 11) {
|
|
uint8_t byte = fifo_pop(true);
|
|
command[12+cur_live.byte_counter-7] = byte;
|
|
live_write_fm(byte);
|
|
if(cur_live.byte_counter == 10)
|
|
LOGFORMAT("formatting sector %02x %02x %02x %02x\n",
|
|
command[12], command[13], command[14], command[15]);
|
|
} else if(cur_live.byte_counter < 13)
|
|
live_write_fm(cur_live.crc >> 8);
|
|
else if(cur_live.byte_counter < 24)
|
|
live_write_fm(0xff);
|
|
else if(cur_live.byte_counter < 30)
|
|
live_write_fm(0x00);
|
|
else if(cur_live.byte_counter < 31) {
|
|
cur_live.crc = 0xffff;
|
|
live_write_raw(0xf56f);
|
|
} else if(cur_live.byte_counter < 31+sector_size)
|
|
live_write_fm(command[5]);
|
|
else if(cur_live.byte_counter < 33+sector_size)
|
|
live_write_fm(cur_live.crc >> 8);
|
|
else if(cur_live.byte_counter < 33+sector_size+command[4])
|
|
live_write_fm(0xff);
|
|
else {
|
|
cur_live.byte_counter = 0;
|
|
cur_live.state = command[3] ? WRITE_TRACK_SECTOR : WRITE_TRACK_POST_SECTORS;
|
|
break;
|
|
}
|
|
}
|
|
cur_live.state = WRITE_TRACK_SECTOR_BYTE;
|
|
cur_live.bit_counter = 16;
|
|
checkpoint();
|
|
break;
|
|
|
|
case WRITE_TRACK_POST_SECTORS:
|
|
if(mfm)
|
|
live_write_mfm(0x4e);
|
|
else
|
|
live_write_fm(0xff);
|
|
cur_live.state = WRITE_TRACK_POST_SECTORS_BYTE;
|
|
cur_live.bit_counter = 16;
|
|
checkpoint();
|
|
break;
|
|
|
|
case WRITE_TRACK_PRE_SECTORS_BYTE:
|
|
case WRITE_TRACK_SECTOR_BYTE:
|
|
case WRITE_TRACK_POST_SECTORS_BYTE:
|
|
case WRITE_SECTOR_DATA_BYTE:
|
|
if(write_one_bit(limit))
|
|
return;
|
|
if(cur_live.bit_counter == 0) {
|
|
cur_live.byte_counter++;
|
|
live_delay(cur_live.state-1);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
LOGWARN("%s: Unknown live state %d\n", tts(cur_live.tm), cur_live.state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int upd765_family_device::check_command()
|
|
{
|
|
// 0.000010 read track
|
|
// 00000011 specify
|
|
// 00000100 sense drive status
|
|
// ..000101 write data
|
|
// ...00110 read data
|
|
// 00000111 recalibrate
|
|
// 00001000 sense interrupt status
|
|
// ..001001 write deleted data
|
|
// 0.001010 read id
|
|
// ...01100 read deleted data
|
|
// 0.001101 format track
|
|
// 00001110 dumpreg
|
|
// 00101110 save
|
|
// 01001110 restore
|
|
// 10001110 drive specification command
|
|
// 00001111 seek
|
|
// 1.001111 relative seek
|
|
// 00010000 version
|
|
// ...10001 scan equal
|
|
// 00010010 perpendicular mode
|
|
// 00010011 configure
|
|
// 00110011 option
|
|
// .0010100 lock
|
|
// ...10110 verify
|
|
// 00010111 powerdown mode
|
|
// 00011000 part id
|
|
// ...11001 scan low or equal
|
|
// ...11101 scan high or equal
|
|
|
|
// MSDOS 6.22 format uses 0xcd to format a track, which makes one
|
|
// think only the bottom 5 bits are decoded.
|
|
|
|
switch(command[0] & 0x1f) {
|
|
case 0x02:
|
|
return command_pos == 9 ? C_READ_TRACK : C_INCOMPLETE;
|
|
|
|
case 0x03:
|
|
return command_pos == 3 ? C_SPECIFY : C_INCOMPLETE;
|
|
|
|
case 0x04:
|
|
return command_pos == 2 ? C_SENSE_DRIVE_STATUS : C_INCOMPLETE;
|
|
|
|
case 0x05:
|
|
case 0x09:
|
|
return command_pos == 9 ? C_WRITE_DATA : C_INCOMPLETE;
|
|
|
|
case 0x06:
|
|
case 0x0c:
|
|
return command_pos == 9 ? C_READ_DATA : C_INCOMPLETE;
|
|
|
|
case 0x07:
|
|
return command_pos == 2 ? C_RECALIBRATE : C_INCOMPLETE;
|
|
|
|
case 0x08:
|
|
return C_SENSE_INTERRUPT_STATUS;
|
|
|
|
case 0x0a:
|
|
return command_pos == 2 ? C_READ_ID : C_INCOMPLETE;
|
|
|
|
case 0x0d:
|
|
return command_pos == 6 ? C_FORMAT_TRACK : C_INCOMPLETE;
|
|
|
|
case 0x0e:
|
|
return C_DUMP_REG;
|
|
|
|
case 0x0f:
|
|
return command_pos == 3 ? C_SEEK : C_INCOMPLETE;
|
|
|
|
case 0x11:
|
|
return command_pos == 9 ? C_SCAN_EQUAL : C_INCOMPLETE;
|
|
|
|
case 0x12:
|
|
return command_pos == 2 ? C_PERPENDICULAR : C_INCOMPLETE;
|
|
|
|
case 0x13:
|
|
return command_pos == 4 ? C_CONFIGURE : C_INCOMPLETE;
|
|
|
|
case 0x14:
|
|
return C_LOCK;
|
|
|
|
case 0x19:
|
|
return command_pos == 9 ? C_SCAN_LOW : C_INCOMPLETE;
|
|
|
|
case 0x1d:
|
|
return command_pos == 9 ? C_SCAN_HIGH : C_INCOMPLETE;
|
|
|
|
default:
|
|
return C_INVALID;
|
|
}
|
|
}
|
|
|
|
void upd765_family_device::start_command(int cmd)
|
|
{
|
|
command_pos = 0;
|
|
result_pos = 0;
|
|
main_phase = PHASE_EXEC;
|
|
tc_done = false;
|
|
|
|
execute_command(cmd);
|
|
}
|
|
|
|
void upd765_family_device::execute_command(int cmd)
|
|
{
|
|
switch(cmd) {
|
|
case C_CONFIGURE:
|
|
LOGCOMMAND("command configure %02x %02x %02x\n",
|
|
command[1], command[2], command[3]);
|
|
// byte 1 is ignored, byte 3 is precompensation-related
|
|
motorcfg = command[1];
|
|
fifocfg = command[2];
|
|
precomp = command[3];
|
|
main_phase = PHASE_CMD;
|
|
break;
|
|
|
|
case C_DUMP_REG:
|
|
LOGCOMMAND("command dump regs\n");
|
|
main_phase = PHASE_RESULT;
|
|
result[0] = flopi[0].pcn;
|
|
result[1] = flopi[1].pcn;
|
|
result[2] = flopi[2].pcn;
|
|
result[3] = flopi[3].pcn;
|
|
result[4] = (spec & 0xff00) >> 8;
|
|
result[5] = (spec & 0x00ff);
|
|
result[6] = sector_size;
|
|
result[7] = locked ? 0x80 : 0x00;
|
|
result[7] |= (perpmode & 0x30);
|
|
result[8] = fifocfg;
|
|
result[9] = precomp;
|
|
result_pos = 10;
|
|
break;
|
|
|
|
case C_FORMAT_TRACK:
|
|
format_track_start(flopi[command[1] & 3]);
|
|
break;
|
|
|
|
case C_LOCK:
|
|
locked = command[0] & 0x80;
|
|
main_phase = PHASE_RESULT;
|
|
result[0] = locked ? 0x10 : 0x00;
|
|
result_pos = 1;
|
|
LOGCOMMAND("command lock (%s)\n", locked ? "on" : "off");
|
|
break;
|
|
|
|
case C_PERPENDICULAR:
|
|
LOGCOMMAND("command perpendicular\n");
|
|
perpmode = command[1];
|
|
main_phase = PHASE_CMD;
|
|
break;
|
|
|
|
case C_READ_DATA:
|
|
read_data_start(flopi[command[1] & 3]);
|
|
break;
|
|
|
|
case C_READ_ID:
|
|
read_id_start(flopi[command[1] & 3]);
|
|
break;
|
|
|
|
case C_READ_TRACK:
|
|
read_track_start(flopi[command[1] & 3]);
|
|
break;
|
|
|
|
case C_SCAN_EQUAL:
|
|
case C_SCAN_LOW:
|
|
case C_SCAN_HIGH:
|
|
scan_start(flopi[command[1] & 3]);
|
|
break;
|
|
|
|
case C_RECALIBRATE:
|
|
recalibrate_start(flopi[command[1] & 3]);
|
|
main_phase = PHASE_CMD;
|
|
break;
|
|
|
|
case C_SEEK:
|
|
seek_start(flopi[command[1] & 3]);
|
|
main_phase = PHASE_CMD;
|
|
break;
|
|
|
|
case C_SENSE_DRIVE_STATUS: {
|
|
floppy_info &fi = flopi[command[1] & 3];
|
|
main_phase = PHASE_RESULT;
|
|
result[0] = command[1] & 7;
|
|
if(fi.ready)
|
|
result[0] |= ST3_RY;
|
|
if(fi.dev)
|
|
result[0] |=
|
|
(fi.dev->wpt_r() ? ST3_WP : 0x00) |
|
|
(fi.dev->trk00_r() ? 0x00 : ST3_T0) |
|
|
(fi.dev->twosid_r() ? 0x00 : ST3_TS);
|
|
LOGCOMMAND("command sense drive status %d (%02x)\n", fi.id, result[0]);
|
|
result_pos = 1;
|
|
break;
|
|
}
|
|
|
|
case C_SENSE_INTERRUPT_STATUS: {
|
|
// Documentation is somewhat contradictory w.r.t polling
|
|
// and irq. PC bios, especially 5150, requires that only
|
|
// one irq happens. That's also what the ns82077a doc
|
|
// says it does. OTOH, a number of docs says you need to
|
|
// call SIS 4 times, once per drive...
|
|
//
|
|
// There's also the interaction with the seek irq. The
|
|
// somewhat borderline tf20 code seems to think that
|
|
// essentially ignoring the polling irq should work.
|
|
//
|
|
// And the pc98 expects to be able to accumulate irq reasons
|
|
// for different drives and things to work.
|
|
//
|
|
// Current hypothesis:
|
|
// - each drive has its own st0 and irq trigger
|
|
// - SIS drops the irq always, but also returns the first full st0 it finds
|
|
|
|
main_phase = PHASE_RESULT;
|
|
|
|
int fid;
|
|
for(fid=0; fid<4 && !flopi[fid].st0_filled; fid++) {};
|
|
if(fid == 4) {
|
|
result[0] = ST0_UNK;
|
|
result_pos = 1;
|
|
LOGCOMMAND("command sense interrupt status (%02x) (%s)\n", result[0], machine().describe_context());
|
|
break;
|
|
}
|
|
|
|
floppy_info &fi = flopi[fid];
|
|
fi.st0_filled = false;
|
|
|
|
result[0] = fi.st0;
|
|
result[1] = fi.pcn;
|
|
|
|
LOGCOMMAND("command sense interrupt status (fid=%d %02x %02x) (%s)\n", fid, result[0], result[1], machine().describe_context());
|
|
result_pos = 2;
|
|
|
|
other_irq = false;
|
|
check_irq();
|
|
break;
|
|
}
|
|
|
|
case C_SPECIFY:
|
|
spec = (command[1] << 8) | command[2];
|
|
LOGCOMMAND("command specify %02x %02x: step_rate=%d ms, head_unload=%d ms, head_load=%d ms, non_dma=%s\n",
|
|
command[1], command[2], 16-(command[1]>>4), (command[1]&0x0f)<<4, command[2]&0xfe, ((command[2]&1)==1)? "true":"false");
|
|
main_phase = PHASE_CMD;
|
|
break;
|
|
|
|
case C_WRITE_DATA:
|
|
write_data_start(flopi[command[1] & 3]);
|
|
break;
|
|
|
|
default:
|
|
LOGWARN("Unknown command %02x\n", cmd);
|
|
// exit(1);
|
|
}
|
|
}
|
|
|
|
void upd765_family_device::command_end(floppy_info &fi, bool data_completion)
|
|
{
|
|
LOGCOMMAND("command done (%s) - %s\n", data_completion ? "data" : "seek", results());
|
|
fi.main_state = fi.sub_state = IDLE;
|
|
if(data_completion)
|
|
data_irq = true;
|
|
else {
|
|
other_irq = true;
|
|
fi.st0_filled = true;
|
|
}
|
|
check_irq();
|
|
}
|
|
|
|
void upd765_family_device::recalibrate_start(floppy_info &fi)
|
|
{
|
|
LOGCOMMAND("command recalibrate %d\n", command[1] & 3);
|
|
fi.main_state = RECALIBRATE;
|
|
fi.sub_state = SEEK_WAIT_STEP_TIME_DONE;
|
|
fi.dir = 1;
|
|
fi.counter = 77;
|
|
fi.ready = get_ready(command[1] & 3);
|
|
fi.st0 = (fi.ready ? 0 : ST0_NR);
|
|
seek_continue(fi);
|
|
}
|
|
|
|
void upd765_family_device::seek_start(floppy_info &fi)
|
|
{
|
|
LOGCOMMAND("command %sseek %d\n", command[0] & 0x80 ? "relative " : "", command[2]);
|
|
fi.main_state = SEEK;
|
|
fi.sub_state = SEEK_WAIT_STEP_TIME_DONE;
|
|
fi.dir = fi.pcn > command[2] ? 1 : 0;
|
|
fi.ready = get_ready(command[1] & 3);
|
|
fi.st0 = (fi.ready ? 0 : ST0_NR);
|
|
seek_continue(fi);
|
|
}
|
|
|
|
void upd765_family_device::delay_cycles(emu_timer *tm, int cycles)
|
|
{
|
|
tm->adjust(attotime::from_double(double(cycles)/cur_rate));
|
|
}
|
|
|
|
void upd765_family_device::seek_continue(floppy_info &fi)
|
|
{
|
|
for(;;) {
|
|
switch(fi.sub_state) {
|
|
case SEEK_MOVE:
|
|
LOGSTATE("SEEK_MOVE\n");
|
|
if(fi.dev) {
|
|
fi.dev->dir_w(fi.dir);
|
|
fi.dev->stp_w(0);
|
|
}
|
|
fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
|
|
fi.tm->adjust(attotime::from_nsec(2500));
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_SIGNAL_TIME:
|
|
LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME\n");
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_SIGNAL_TIME_DONE:
|
|
LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME_DONE\n");
|
|
if(fi.dev)
|
|
fi.dev->stp_w(1);
|
|
|
|
if(fi.main_state == SEEK) {
|
|
if(fi.pcn > command[2])
|
|
fi.pcn--;
|
|
else
|
|
fi.pcn++;
|
|
}
|
|
fi.sub_state = SEEK_WAIT_STEP_TIME;
|
|
delay_cycles(fi.tm, 500*(16-(spec >> 12)));
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_TIME:
|
|
LOGSTATE("SEEK_WAIT_STEP_TIME\n");
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_TIME_DONE: {
|
|
LOGSTATE("SEEK_WAIT_STEP_TIME_DONE\n");
|
|
bool done = false;
|
|
switch(fi.main_state) {
|
|
case RECALIBRATE:
|
|
LOGSTATE("RECALIBRATE\n");
|
|
fi.counter--;
|
|
done = fi.dev && !fi.dev->trk00_r();
|
|
if(done)
|
|
fi.pcn = 0;
|
|
else if(!fi.counter) {
|
|
fi.st0 |= ST0_FAIL|ST0_SE|ST0_EC | fi.id;
|
|
command_end(fi, false);
|
|
return;
|
|
}
|
|
break;
|
|
case SEEK:
|
|
LOGSTATE("SEEK\n");
|
|
done = fi.pcn == command[2];
|
|
break;
|
|
}
|
|
if(done) {
|
|
fi.sub_state = SEEK_WAIT_DONE;
|
|
// recalibrate and seek takes some time, even if we don't move
|
|
fi.tm->adjust(attotime::from_nsec((fi.main_state == RECALIBRATE) ? 20000 : 10000));
|
|
return;
|
|
}
|
|
fi.sub_state = SEEK_MOVE;
|
|
break;
|
|
}
|
|
|
|
case SEEK_WAIT_DONE:
|
|
LOGSTATE("SEEK_WAIT_DONE\n");
|
|
fi.st0 |= ST0_SE | fi.id;
|
|
command_end(fi, false);
|
|
return;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void upd765_family_device::read_data_start(floppy_info &fi)
|
|
{
|
|
fi.main_state = READ_DATA;
|
|
fi.sub_state = HEAD_LOAD;
|
|
mfm = command[0] & 0x40;
|
|
|
|
LOGCOMMAND("command read%s data%s%s%s%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x dtl=%02x rate=%d\n",
|
|
command[0] & 0x08 ? " deleted" : "",
|
|
command[0] & 0x80 ? " mt" : "",
|
|
command[0] & 0x40 ? " mfm" : "",
|
|
command[0] & 0x20 ? " sk" : "",
|
|
fifocfg & 0x40 ? " seek" : "",
|
|
command[0],
|
|
command[1],
|
|
command[2],
|
|
command[3],
|
|
command[4],
|
|
128 << (command[5] & 7),
|
|
command[6],
|
|
command[7],
|
|
command[8],
|
|
cur_rate);
|
|
|
|
fi.st0 = command[1] & 7;
|
|
st1 = ST1_MA;
|
|
st2 = 0x00;
|
|
hdl_cb(1);
|
|
set_ds(command[1] & 3);
|
|
fi.ready = get_ready(command[1] & 3);
|
|
|
|
if(!fi.ready) {
|
|
fi.st0 |= ST0_NR | ST0_FAIL;
|
|
fi.sub_state = COMMAND_DONE;
|
|
st1 = 0;
|
|
st2 = 0;
|
|
read_data_continue(fi);
|
|
return;
|
|
}
|
|
|
|
if(fi.dev)
|
|
fi.dev->ss_w(command[1] & 4 ? 1 : 0);
|
|
read_data_continue(fi);
|
|
}
|
|
|
|
void upd765_family_device::scan_start(floppy_info &fi)
|
|
{
|
|
fi.main_state = SCAN_DATA;
|
|
fi.sub_state = HEAD_LOAD;
|
|
mfm = command[0] & 0x40;
|
|
|
|
LOGCOMMAND("command scan%s data%s%s%s%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x stp=%02x rate=%d\n",
|
|
command[0] & 0x08 ? " deleted" : "",
|
|
command[0] & 0x80 ? " mt" : "",
|
|
command[0] & 0x40 ? " mfm" : "",
|
|
command[0] & 0x20 ? " sk" : "",
|
|
fifocfg & 0x40 ? " seek" : "",
|
|
command[0],
|
|
command[1],
|
|
command[2],
|
|
command[3],
|
|
command[4],
|
|
128 << (command[5] & 7),
|
|
command[6],
|
|
command[7],
|
|
command[8],
|
|
cur_rate);
|
|
|
|
fi.st0 = command[1] & 7;
|
|
st1 = ST1_MA;
|
|
st2 = 0x00;
|
|
scan_done = false;
|
|
hdl_cb(1);
|
|
set_ds(command[1] & 3);
|
|
fi.ready = get_ready(command[1] & 3);
|
|
|
|
if(!fi.ready) {
|
|
fi.st0 |= ST0_NR | ST0_FAIL;
|
|
fi.sub_state = COMMAND_DONE;
|
|
st1 = 0;
|
|
st2 = 0;
|
|
read_data_continue(fi);
|
|
return;
|
|
}
|
|
|
|
if(fi.dev)
|
|
fi.dev->ss_w(command[1] & 4 ? 1 : 0);
|
|
read_data_continue(fi);
|
|
}
|
|
|
|
void upd765_family_device::read_data_continue(floppy_info &fi)
|
|
{
|
|
for(;;) {
|
|
switch(fi.sub_state) {
|
|
case HEAD_LOAD:
|
|
LOGSTATE("HEAD_LOAD\n");
|
|
delay_cycles(fi.tm, 500*(spec & 0x00fe));
|
|
fi.sub_state = HEAD_LOAD_DONE;
|
|
break;
|
|
case HEAD_LOAD_DONE:
|
|
LOGSTATE("HEAD_LOAD_DONE\n");
|
|
if(fi.pcn == command[2] || !(fifocfg & 0x40)) {
|
|
fi.sub_state = SEEK_DONE;
|
|
break;
|
|
}
|
|
fi.st0 |= ST0_SE;
|
|
if(fi.dev) {
|
|
fi.dev->dir_w(fi.pcn > command[2] ? 1 : 0);
|
|
fi.dev->stp_w(0);
|
|
}
|
|
fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
|
|
fi.tm->adjust(attotime::from_nsec(2500));
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_SIGNAL_TIME:
|
|
LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME\n");
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_SIGNAL_TIME_DONE:
|
|
LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME_DONE\n");
|
|
if(fi.dev)
|
|
fi.dev->stp_w(1);
|
|
|
|
fi.sub_state = SEEK_WAIT_STEP_TIME;
|
|
delay_cycles(fi.tm, 500*(16-(spec >> 12)));
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_TIME:
|
|
LOGSTATE("SEEK_WAIT_STEP_TIME\n");
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_TIME_DONE:
|
|
LOGSTATE("SEEK_WAIT_STEP_TIME_DONE\n");
|
|
if(fi.pcn > command[2])
|
|
fi.pcn--;
|
|
else
|
|
fi.pcn++;
|
|
fi.sub_state = HEAD_LOAD_DONE;
|
|
break;
|
|
|
|
case SEEK_DONE:
|
|
LOGSTATE("SEEK_DONE\n");
|
|
fi.counter = 0;
|
|
fi.sub_state = SCAN_ID;
|
|
LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");
|
|
live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
|
|
return;
|
|
|
|
case SCAN_ID:
|
|
LOGSTATE("SCAN_ID\n");
|
|
if(cur_live.crc) {
|
|
fi.st0 |= ST0_FAIL;
|
|
st1 |= ST1_DE|ST1_ND;
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
}
|
|
st1 &= ~ST1_MA;
|
|
if(!sector_matches()) {
|
|
if(cur_live.idbuf[0] != command[2]) {
|
|
if(cur_live.idbuf[0] == 0xff)
|
|
st2 |= ST2_WC|ST2_BC;
|
|
else
|
|
st2 |= ST2_WC;
|
|
}
|
|
LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");
|
|
live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
|
|
return;
|
|
}
|
|
LOGRW("reading sector %02x %02x %02x %02x\n",
|
|
cur_live.idbuf[0],
|
|
cur_live.idbuf[1],
|
|
cur_live.idbuf[2],
|
|
cur_live.idbuf[3]);
|
|
sector_size = calc_sector_size(cur_live.idbuf[3]);
|
|
if(fi.main_state == SCAN_DATA)
|
|
fifo_expect(sector_size, true);
|
|
else
|
|
fifo_expect(sector_size, false);
|
|
fi.sub_state = SECTOR_READ;
|
|
LOGSTATE("SEARCH_ADDRESS_MARK_DATA\n");
|
|
live_start(fi, SEARCH_ADDRESS_MARK_DATA);
|
|
return;
|
|
|
|
case SCAN_ID_FAILED:
|
|
LOGSTATE("SCAN_ID_FAILED\n");
|
|
fi.st0 |= ST0_FAIL;
|
|
// MZ: The HX5102 does not correctly detect a FM/MFM mismatch
|
|
// when the ND bit is set, because in the firmware the ND bit wins
|
|
// against MA, and thus concludes that the format is correct
|
|
// but the sector is missing.
|
|
// st1 |= ST1_ND;
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
|
|
case SECTOR_READ: {
|
|
LOGSTATE("SECTOR_READ\n");
|
|
if(st2 & ST2_MD) {
|
|
fi.st0 |= ST0_FAIL;
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
}
|
|
if(cur_live.crc) {
|
|
fi.st0 |= ST0_FAIL;
|
|
st1 |= ST1_DE;
|
|
st2 |= ST2_CM;
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
}
|
|
bool done = tc_done;
|
|
if(command[4] == command[6]) {
|
|
if(command[0] & 0x80) {
|
|
command[3] = command[3] ^ 1;
|
|
command[4] = 1;
|
|
if(fi.dev)
|
|
fi.dev->ss_w(command[3] & 1);
|
|
}
|
|
if(!(command[0] & 0x80) || !(command[3] & 1)) {
|
|
if(!tc_done) {
|
|
fi.st0 |= ST0_FAIL;
|
|
st1 |= ST1_EN;
|
|
} else {
|
|
command[2]++;
|
|
command[4] = 1;
|
|
}
|
|
done = true;
|
|
}
|
|
} else
|
|
command[4]++;
|
|
if(!done) {
|
|
fi.sub_state = SEEK_DONE;
|
|
break;
|
|
}
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
}
|
|
|
|
case COMMAND_DONE:
|
|
LOGSTATE("COMMAND_DONE\n");
|
|
main_phase = PHASE_RESULT;
|
|
result[0] = fi.st0;
|
|
result[1] = st1;
|
|
result[2] = st2;
|
|
result[3] = command[2];
|
|
result[4] = command[3];
|
|
result[5] = command[4];
|
|
result[6] = command[5];
|
|
result_pos = 7;
|
|
command_end(fi, true);
|
|
return;
|
|
|
|
default:
|
|
LOGWARN("%s: read sector unknown sub-state %d\n", ttsn(), fi.sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void upd765_family_device::write_data_start(floppy_info &fi)
|
|
{
|
|
fi.main_state = WRITE_DATA;
|
|
fi.sub_state = HEAD_LOAD;
|
|
mfm = command[0] & 0x40;
|
|
LOGRW("command write%s data%s%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x dtl=%02x rate=%d\n",
|
|
command[0] & 0x08 ? " deleted" : "",
|
|
command[0] & 0x80 ? " mt" : "",
|
|
command[0] & 0x40 ? " mfm" : "",
|
|
command[0],
|
|
command[1],
|
|
command[2],
|
|
command[3],
|
|
command[4],
|
|
128 << (command[5] & 7),
|
|
command[6],
|
|
command[7],
|
|
command[8],
|
|
cur_rate);
|
|
|
|
if(fi.dev)
|
|
fi.dev->ss_w(command[1] & 4 ? 1 : 0);
|
|
|
|
fi.st0 = command[1] & 7;
|
|
st1 = ST1_MA;
|
|
st2 = 0x00;
|
|
hdl_cb(1);
|
|
set_ds(command[1] & 3);
|
|
fi.ready = get_ready(command[1] & 3);
|
|
|
|
if(!fi.ready) {
|
|
fi.st0 |= ST0_NR | ST0_FAIL;
|
|
fi.sub_state = COMMAND_DONE;
|
|
st1 = 0;
|
|
st2 = 0;
|
|
write_data_continue(fi);
|
|
return;
|
|
}
|
|
|
|
write_data_continue(fi);
|
|
}
|
|
|
|
void upd765_family_device::write_data_continue(floppy_info &fi)
|
|
{
|
|
for(;;) {
|
|
switch(fi.sub_state) {
|
|
case HEAD_LOAD:
|
|
LOGSTATE("HEAD_LOAD\n");
|
|
delay_cycles(fi.tm, 500*(spec & 0x00fe));
|
|
fi.sub_state = HEAD_LOAD_DONE;
|
|
break;
|
|
case HEAD_LOAD_DONE:
|
|
LOGSTATE("HEAD_LOAD_DONE\n");
|
|
fi.counter = 0;
|
|
fi.sub_state = SCAN_ID;
|
|
LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");
|
|
live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
|
|
return;
|
|
|
|
case SCAN_ID:
|
|
LOGSTATE("SCAN_ID\n");
|
|
if(!sector_matches()) {
|
|
LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");
|
|
live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
|
|
return;
|
|
}
|
|
if(cur_live.crc) {
|
|
fi.st0 |= ST0_FAIL;
|
|
st1 |= ST1_DE|ST1_ND;
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
}
|
|
st1 &= ~ST1_MA;
|
|
sector_size = calc_sector_size(cur_live.idbuf[3]);
|
|
fifo_expect(sector_size, true);
|
|
fi.sub_state = SECTOR_WRITTEN;
|
|
LOGSTATE("WRITE_SECTOR_SKIP_GAP2\n");
|
|
live_start(fi, WRITE_SECTOR_SKIP_GAP2);
|
|
return;
|
|
|
|
case SCAN_ID_FAILED:
|
|
LOGSTATE("SCAN_ID_FAILED\n");
|
|
fi.st0 |= ST0_FAIL;
|
|
// st1 |= ST1_ND;
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
|
|
case SECTOR_WRITTEN: {
|
|
LOGSTATE("SECTOR_WRITTEN\n");
|
|
bool done = tc_done;
|
|
if(command[4] == command[6]) {
|
|
if(command[0] & 0x80) {
|
|
command[3] = command[3] ^ 1;
|
|
command[4] = 1;
|
|
if(fi.dev)
|
|
fi.dev->ss_w(command[3] & 1);
|
|
}
|
|
if(!(command[0] & 0x80) || !(command[3] & 1)) {
|
|
if(!tc_done) {
|
|
fi.st0 |= ST0_FAIL;
|
|
st1 |= ST1_EN;
|
|
} else {
|
|
command[2]++;
|
|
command[4] = 1;
|
|
}
|
|
done = true;
|
|
}
|
|
} else
|
|
command[4]++;
|
|
if(!done) {
|
|
fi.sub_state = HEAD_LOAD_DONE;
|
|
break;
|
|
}
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
}
|
|
|
|
case COMMAND_DONE:
|
|
LOGSTATE("COMMAND_DONE\n");
|
|
main_phase = PHASE_RESULT;
|
|
result[0] = fi.st0;
|
|
result[1] = st1;
|
|
result[2] = st2;
|
|
result[3] = command[2];
|
|
result[4] = command[3];
|
|
result[5] = command[4];
|
|
result[6] = command[5];
|
|
result_pos = 7;
|
|
command_end(fi, true);
|
|
return;
|
|
|
|
default:
|
|
LOGWARN("%s: write sector unknown sub-state %d\n", ttsn(), fi.sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void upd765_family_device::read_track_start(floppy_info &fi)
|
|
{
|
|
fi.main_state = READ_TRACK;
|
|
fi.sub_state = HEAD_LOAD;
|
|
mfm = command[0] & 0x40;
|
|
sectors_read = 0;
|
|
|
|
LOGRW("command read track%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x dtl=%02x rate=%d\n",
|
|
command[0] & 0x40 ? " mfm" : "",
|
|
command[0],
|
|
command[1],
|
|
command[2],
|
|
command[3],
|
|
command[4],
|
|
128 << (command[5] & 7),
|
|
command[6],
|
|
command[7],
|
|
command[8],
|
|
cur_rate);
|
|
fi.st0 = command[1] & 7;
|
|
st1 = ST1_MA;
|
|
st2 = 0x00;
|
|
hdl_cb(1);
|
|
set_ds(command[1] & 3);
|
|
fi.ready = get_ready(command[1] & 3);
|
|
|
|
if(!fi.ready) {
|
|
fi.st0 |= ST0_NR | ST0_FAIL;
|
|
fi.sub_state = COMMAND_DONE;
|
|
st1 = 0;
|
|
st2 = 0;
|
|
read_track_continue(fi);
|
|
return;
|
|
}
|
|
|
|
if(fi.dev)
|
|
fi.dev->ss_w(command[1] & 4 ? 1 : 0);
|
|
read_track_continue(fi);
|
|
}
|
|
|
|
void upd765_family_device::read_track_continue(floppy_info &fi)
|
|
{
|
|
for(;;) {
|
|
switch(fi.sub_state) {
|
|
case HEAD_LOAD:
|
|
LOGSTATE("HEAD_LOAD\n");
|
|
delay_cycles(fi.tm, 500*(spec & 0x00fe));
|
|
fi.sub_state = HEAD_LOAD_DONE;
|
|
break;
|
|
case HEAD_LOAD_DONE:
|
|
LOGSTATE("HEAD_LOAD_DONE\n");
|
|
if(fi.pcn == command[2] || !(fifocfg & 0x40)) {
|
|
fi.sub_state = SEEK_DONE;
|
|
break;
|
|
}
|
|
fi.st0 |= ST0_SE;
|
|
if(fi.dev) {
|
|
fi.dev->dir_w(fi.pcn > command[2] ? 1 : 0);
|
|
fi.dev->stp_w(0);
|
|
}
|
|
fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
|
|
fi.tm->adjust(attotime::from_nsec(2500));
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_SIGNAL_TIME:
|
|
LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME\n");
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_SIGNAL_TIME_DONE:
|
|
LOGSTATE("SEEK_WAIT_STEP_SIGNAL_TIME_DONE\n");
|
|
if(fi.dev)
|
|
fi.dev->stp_w(1);
|
|
|
|
fi.sub_state = SEEK_WAIT_STEP_TIME;
|
|
delay_cycles(fi.tm, 500*(16-(spec >> 12)));
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_TIME:
|
|
LOGSTATE("SEEK_WAIT_STEP_TIME\n");
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_TIME_DONE:
|
|
LOGSTATE("SEEK_WAIT_STEP_TIME_DONE\n");
|
|
if(fi.pcn > command[2])
|
|
fi.pcn--;
|
|
else
|
|
fi.pcn++;
|
|
fi.sub_state = HEAD_LOAD_DONE;
|
|
break;
|
|
|
|
case SEEK_DONE:
|
|
LOGSTATE("SEEK_DONE\n");
|
|
fi.counter = 0;
|
|
fi.sub_state = WAIT_INDEX;
|
|
return;
|
|
|
|
case WAIT_INDEX:
|
|
LOGSTATE("WAIT_INDEX\n");
|
|
return;
|
|
|
|
case WAIT_INDEX_DONE:
|
|
LOGSTATE("WAIT_INDEX_DONE\n");
|
|
fi.sub_state = SCAN_ID;
|
|
LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");
|
|
live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
|
|
return;
|
|
|
|
case SCAN_ID:
|
|
LOGSTATE("SCAN_ID\n");
|
|
if(cur_live.crc) {
|
|
st1 |= ST1_DE;
|
|
}
|
|
st1 &= ~ST1_MA;
|
|
LOGRW("reading sector %02x %02x %02x %02x\n",
|
|
cur_live.idbuf[0],
|
|
cur_live.idbuf[1],
|
|
cur_live.idbuf[2],
|
|
cur_live.idbuf[3]);
|
|
if(!sector_matches())
|
|
st1 |= ST1_ND;
|
|
else
|
|
st1 &= ~ST1_ND;
|
|
|
|
sector_size = calc_sector_size(cur_live.idbuf[3]);
|
|
fifo_expect(sector_size, false);
|
|
fi.sub_state = SECTOR_READ;
|
|
LOGSTATE("SEARCH_ADDRESS_MARK_DATA\n");
|
|
live_start(fi, SEARCH_ADDRESS_MARK_DATA);
|
|
return;
|
|
|
|
case SCAN_ID_FAILED:
|
|
LOGSTATE("SCAN_ID_FAILED\n");
|
|
fi.st0 |= ST0_FAIL;
|
|
// st1 |= ST1_ND;
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
|
|
case SECTOR_READ: {
|
|
LOGSTATE("SECTOR_READ\n");
|
|
if(st2 & ST2_MD) {
|
|
fi.st0 |= ST0_FAIL;
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
}
|
|
if(cur_live.crc) {
|
|
st1 |= ST1_DE;
|
|
st2 |= ST2_CM;
|
|
}
|
|
bool done = tc_done;
|
|
sectors_read++;
|
|
if(sectors_read == command[6]) {
|
|
if(!tc_done) {
|
|
fi.st0 |= ST0_FAIL;
|
|
st1 |= ST1_EN;
|
|
}
|
|
done = true;
|
|
}
|
|
if(!done) {
|
|
fi.sub_state = WAIT_INDEX_DONE;
|
|
break;
|
|
}
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
}
|
|
|
|
case COMMAND_DONE:
|
|
LOGSTATE("COMMAND_DONE\n");
|
|
main_phase = PHASE_RESULT;
|
|
result[0] = fi.st0;
|
|
result[1] = st1;
|
|
result[2] = st2;
|
|
result[3] = command[2];
|
|
result[4] = command[3];
|
|
result[5] = command[4];
|
|
result[6] = command[5];
|
|
result_pos = 7;
|
|
command_end(fi, true);
|
|
return;
|
|
|
|
default:
|
|
LOGWARN("%s: read track unknown sub-state %d\n", ttsn(), fi.sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int upd765_family_device::calc_sector_size(uint8_t size)
|
|
{
|
|
return size > 7 ? 16384 : 128 << size;
|
|
}
|
|
|
|
void upd765_family_device::format_track_start(floppy_info &fi)
|
|
{
|
|
fi.main_state = FORMAT_TRACK;
|
|
fi.sub_state = HEAD_LOAD;
|
|
mfm = command[0] & 0x40;
|
|
|
|
LOGCOMMAND("command format track %s h=%02x n=%02x sc=%02x gpl=%02x d=%02x\n",
|
|
command[0] & 0x40 ? "mfm" : "fm",
|
|
command[1], command[2], command[3], command[4], command[5]);
|
|
|
|
hdl_cb(1);
|
|
set_ds(command[1] & 3);
|
|
fi.ready = get_ready(command[1] & 3);
|
|
|
|
if(!fi.ready) {
|
|
fi.st0 = (command[1] & 7) | ST0_NR | ST0_FAIL;
|
|
fi.sub_state = TRACK_DONE;
|
|
format_track_continue(fi);
|
|
return;
|
|
}
|
|
fi.st0 = command[1] & 7;
|
|
|
|
if(fi.dev)
|
|
fi.dev->ss_w(command[1] & 4 ? 1 : 0);
|
|
sector_size = calc_sector_size(command[2]);
|
|
|
|
format_track_continue(fi);
|
|
}
|
|
|
|
void upd765_family_device::format_track_continue(floppy_info &fi)
|
|
{
|
|
for(;;) {
|
|
switch(fi.sub_state) {
|
|
case HEAD_LOAD:
|
|
LOGSTATE("HEAD_LOAD\n");
|
|
delay_cycles(fi.tm, 500*(spec & 0x00fe));
|
|
fi.sub_state = HEAD_LOAD_DONE;
|
|
break;
|
|
case HEAD_LOAD_DONE:
|
|
LOGSTATE("HEAD_LOAD_DONE\n");
|
|
fi.sub_state = WAIT_INDEX;
|
|
break;
|
|
|
|
case WAIT_INDEX:
|
|
LOGSTATE("WAIT_INDEX\n");
|
|
return;
|
|
|
|
case WAIT_INDEX_DONE:
|
|
LOGSTATE("WAIT_INDEX_DONE\n");
|
|
fi.sub_state = TRACK_DONE;
|
|
cur_live.pll.start_writing(machine().time());
|
|
LOGSTATE("WRITE_TRACK_PRE_SECTORS\n");
|
|
live_start(fi, WRITE_TRACK_PRE_SECTORS);
|
|
return;
|
|
|
|
case TRACK_DONE:
|
|
LOGSTATE("TRACK_DONE\n");
|
|
main_phase = PHASE_RESULT;
|
|
result[0] = fi.st0;
|
|
result[1] = 0;
|
|
result[2] = 0;
|
|
result[3] = 0;
|
|
result[4] = 0;
|
|
result[5] = 0;
|
|
result[6] = command[2];
|
|
result_pos = 7;
|
|
command_end(fi, true);
|
|
return;
|
|
|
|
default:
|
|
LOGWARN("%s: format track unknown sub-state %d\n", ttsn(), fi.sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void upd765_family_device::read_id_start(floppy_info &fi)
|
|
{
|
|
fi.main_state = READ_ID;
|
|
fi.sub_state = HEAD_LOAD;
|
|
mfm = command[0] & 0x40;
|
|
|
|
LOGCOMMAND("command read id%s %d, rate=%d\n",
|
|
command[0] & 0x40 ? " mfm" : "",
|
|
command[1] & 3,
|
|
cur_rate);
|
|
|
|
if(fi.dev)
|
|
fi.dev->ss_w(command[1] & 4 ? 1 : 0);
|
|
|
|
fi.st0 = command[1] & 7;
|
|
st1 = 0x00;
|
|
st2 = 0x00;
|
|
|
|
for(int i=0; i<4; i++)
|
|
cur_live.idbuf[i] = 0x00;
|
|
|
|
hdl_cb(1);
|
|
set_ds(command[1] & 3);
|
|
fi.ready = get_ready(command[1] & 3);
|
|
|
|
if(!fi.ready) {
|
|
fi.st0 |= ST0_NR | ST0_FAIL;
|
|
fi.sub_state = COMMAND_DONE;
|
|
read_id_continue(fi);
|
|
return;
|
|
}
|
|
|
|
read_id_continue(fi);
|
|
}
|
|
|
|
void upd765_family_device::read_id_continue(floppy_info &fi)
|
|
{
|
|
for(;;) {
|
|
switch(fi.sub_state) {
|
|
case HEAD_LOAD:
|
|
LOGSTATE("HEAD_LOAD\n");
|
|
delay_cycles(fi.tm, 500*(spec & 0x00fe));
|
|
fi.sub_state = HEAD_LOAD_DONE;
|
|
break;
|
|
case HEAD_LOAD_DONE:
|
|
LOGSTATE("HEAD_LOAD_DONE\n");
|
|
fi.counter = 0;
|
|
fi.sub_state = SCAN_ID;
|
|
LOGSTATE("SEARCH_ADDRESS_MARK_HEADER\n");
|
|
live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
|
|
return;
|
|
|
|
case SCAN_ID:
|
|
LOGSTATE("SCAN_ID\n");
|
|
if(cur_live.crc) {
|
|
fi.st0 |= ST0_FAIL;
|
|
st1 |= ST1_MA|ST1_DE|ST1_ND;
|
|
}
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
|
|
case SCAN_ID_FAILED:
|
|
LOGSTATE("SCAN_ID_FAILED\n");
|
|
fi.st0 |= ST0_FAIL;
|
|
// st1 |= ST1_ND|ST1_MA;
|
|
st1 = ST1_MA;
|
|
fi.sub_state = COMMAND_DONE;
|
|
break;
|
|
|
|
case COMMAND_DONE:
|
|
LOGSTATE("COMMAND_DONE\n");
|
|
main_phase = PHASE_RESULT;
|
|
result[0] = fi.st0;
|
|
result[1] = st1;
|
|
result[2] = st2;
|
|
result[3] = cur_live.idbuf[0];
|
|
result[4] = cur_live.idbuf[1];
|
|
result[5] = cur_live.idbuf[2];
|
|
result[6] = cur_live.idbuf[3];
|
|
result_pos = 7;
|
|
command_end(fi, true);
|
|
return;
|
|
|
|
default:
|
|
LOGWARN("%s: read id unknown sub-state %d\n", ttsn(), fi.sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void upd765_family_device::check_irq()
|
|
{
|
|
bool old_irq = cur_irq;
|
|
cur_irq = data_irq || other_irq || internal_drq;
|
|
cur_irq = cur_irq && (dor & 4) && (mode != MODE_AT || (dor & 8));
|
|
if(cur_irq != old_irq) {
|
|
LOGTCIRQ("irq = %d\n", cur_irq);
|
|
intrq_cb(cur_irq);
|
|
}
|
|
}
|
|
|
|
bool upd765_family_device::get_irq() const
|
|
{
|
|
return cur_irq;
|
|
}
|
|
|
|
std::string upd765_family_device::tts(attotime t)
|
|
{
|
|
const char *sign = "";
|
|
if(t.seconds() < 0) {
|
|
t = attotime::zero - t;
|
|
sign = "-";
|
|
}
|
|
int const nsec = t.attoseconds() / ATTOSECONDS_PER_NANOSECOND;
|
|
return util::string_format("%s%04d.%03d,%03d,%03d", sign, int(t.seconds()), nsec/1000000, (nsec/1000)%1000, nsec % 1000);
|
|
}
|
|
|
|
std::string upd765_family_device::results() const
|
|
{
|
|
std::ostringstream stream;
|
|
stream << "results=(";
|
|
if (!result_pos) stream << "none";
|
|
else
|
|
{
|
|
stream << std::hex << std::setfill('0') << std::setw(2) << unsigned(result[0]);
|
|
for (int i=1; i < result_pos; i++)
|
|
stream << ',' << std::setw(2) << unsigned(result[i]);
|
|
}
|
|
stream << ')';
|
|
return stream.str();
|
|
}
|
|
|
|
std::string upd765_family_device::ttsn() const
|
|
{
|
|
return tts(machine().time());
|
|
}
|
|
|
|
void upd765_family_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
|
{
|
|
if(id == TIMER_DRIVE_READY_POLLING) {
|
|
run_drive_ready_polling();
|
|
return;
|
|
}
|
|
|
|
live_sync();
|
|
|
|
floppy_info &fi = flopi[id];
|
|
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 upd765_family_device::run_drive_ready_polling()
|
|
{
|
|
if(main_phase != PHASE_CMD || (fifocfg & FIF_POLL) || command_pos)
|
|
return;
|
|
|
|
for(int fid=0; fid<4; fid++) {
|
|
bool ready = get_ready(fid);
|
|
if(ready != flopi[fid].ready) {
|
|
LOGCOMMAND("polled %d : %d -> %d\n", fid, flopi[fid].ready, ready);
|
|
flopi[fid].ready = ready;
|
|
if(!flopi[fid].st0_filled) {
|
|
flopi[fid].st0 = ST0_ABRT | fid;
|
|
flopi[fid].st0_filled = true;
|
|
other_irq = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
check_irq();
|
|
}
|
|
|
|
void upd765_family_device::index_callback(floppy_image_device *floppy, int state)
|
|
{
|
|
LOGSTATE("Index pulse %d\n", state);
|
|
LOGLIVE("%s: Pulse %d\n", ttsn(), state);
|
|
for(floppy_info & fi : flopi) {
|
|
if(fi.dev != floppy)
|
|
continue;
|
|
|
|
if(fi.live)
|
|
live_sync();
|
|
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 HEAD_LOAD:
|
|
case HEAD_LOAD_DONE:
|
|
case SCAN_ID_FAILED:
|
|
case SECTOR_READ:
|
|
break;
|
|
|
|
case WAIT_INDEX:
|
|
fi.sub_state = WAIT_INDEX_DONE;
|
|
break;
|
|
|
|
case SCAN_ID:
|
|
fi.counter++;
|
|
if(fi.counter == 2) {
|
|
fi.sub_state = SCAN_ID_FAILED;
|
|
live_abort();
|
|
}
|
|
break;
|
|
|
|
case TRACK_DONE:
|
|
live_abort();
|
|
break;
|
|
|
|
default:
|
|
LOGWARN("%s: Index pulse on unknown sub-state %d\n", ttsn(), fi.sub_state);
|
|
break;
|
|
}
|
|
|
|
general_continue(fi);
|
|
}
|
|
}
|
|
|
|
|
|
void upd765_family_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 RECALIBRATE:
|
|
case SEEK:
|
|
seek_continue(fi);
|
|
break;
|
|
|
|
case READ_DATA:
|
|
case SCAN_DATA:
|
|
read_data_continue(fi);
|
|
break;
|
|
|
|
case WRITE_DATA:
|
|
write_data_continue(fi);
|
|
break;
|
|
|
|
case READ_TRACK:
|
|
read_track_continue(fi);
|
|
break;
|
|
|
|
case FORMAT_TRACK:
|
|
format_track_continue(fi);
|
|
break;
|
|
|
|
case READ_ID:
|
|
read_id_continue(fi);
|
|
break;
|
|
|
|
default:
|
|
LOGWARN("%s: general_continue on unknown main-state %d\n", ttsn(), fi.main_state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool upd765_family_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 upd765_family_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) {
|
|
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 upd765_family_device::live_write_raw(uint16_t raw)
|
|
{
|
|
LOGLIVE("%s: write %04x %04x\n", tts(cur_live.tm), raw, cur_live.crc);
|
|
cur_live.shift_reg = raw;
|
|
cur_live.data_bit_context = raw & 1;
|
|
}
|
|
|
|
void upd765_family_device::live_write_mfm(uint8_t mfm)
|
|
{
|
|
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;
|
|
}
|
|
cur_live.data_reg = mfm;
|
|
cur_live.shift_reg = raw;
|
|
cur_live.data_bit_context = context;
|
|
LOGLIVE("%s: write %02x %04x %04x\n", tts(cur_live.tm), mfm, cur_live.crc, raw);
|
|
}
|
|
|
|
void upd765_family_device::live_write_fm(uint8_t fm)
|
|
{
|
|
uint16_t raw = 0xaaaa;
|
|
for(int i=0; i<8; i++)
|
|
if(fm & (0x80 >> i))
|
|
raw |= 0x4000 >> (2*i);
|
|
cur_live.data_reg = fm;
|
|
cur_live.shift_reg = raw;
|
|
cur_live.data_bit_context = fm & 1;
|
|
LOGLIVE("%s: write %02x %04x %04x\n", tts(cur_live.tm), fm, cur_live.crc, raw);
|
|
}
|
|
|
|
bool upd765_family_device::sector_matches() const
|
|
{
|
|
LOGMATCH("matching %02x %02x %02x %02x - %02x %02x %02x %02x\n",
|
|
cur_live.idbuf[0], cur_live.idbuf[1], cur_live.idbuf[2], cur_live.idbuf[3],
|
|
command[2], command[3], command[4], command[5]);
|
|
return
|
|
cur_live.idbuf[0] == command[2] &&
|
|
cur_live.idbuf[1] == command[3] &&
|
|
cur_live.idbuf[2] == command[4] &&
|
|
cur_live.idbuf[3] == command[5];
|
|
}
|
|
|
|
upd765a_device::upd765a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, UPD765A, tag, owner, clock)
|
|
{
|
|
dor_reset = 0x0c;
|
|
}
|
|
|
|
upd765b_device::upd765b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, UPD765B, tag, owner, clock)
|
|
{
|
|
dor_reset = 0x0c;
|
|
}
|
|
|
|
i8272a_device::i8272a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, I8272A, tag, owner, clock)
|
|
{
|
|
dor_reset = 0x0c;
|
|
}
|
|
|
|
upd72065_device::upd72065_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, UPD72065, tag, owner, clock)
|
|
{
|
|
dor_reset = 0x0c;
|
|
}
|
|
|
|
i82072_device::i82072_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, I82072, tag, owner, clock)
|
|
{
|
|
dor_reset = 0x0c;
|
|
}
|
|
|
|
void i82072_device::device_start()
|
|
{
|
|
upd765_family_device::device_start();
|
|
|
|
save_item(NAME(motor_off_counter));
|
|
save_item(NAME(motor_on_counter));
|
|
save_item(NAME(drive_busy));
|
|
save_item(NAME(delayed_command));
|
|
}
|
|
|
|
void i82072_device::soft_reset()
|
|
{
|
|
motorcfg = 0x60;
|
|
|
|
upd765_family_device::soft_reset();
|
|
}
|
|
|
|
int i82072_device::check_command()
|
|
{
|
|
// ...00110 read data
|
|
// ...01100 read deleted data
|
|
// ..000101 write data
|
|
// ..001001 write deleted data
|
|
// 0.000010 read track
|
|
// 0.001010 read id
|
|
// 0.001101 format track
|
|
// 00000111 recalibrate
|
|
// 00001000 sense interrupt status
|
|
// 00000011 specify
|
|
// 00000100 sense drive status
|
|
// 00001111 seek
|
|
// 00010011 configure
|
|
// ...01011 motor on/off
|
|
// 1.001111 relative seek
|
|
// 00001110 dumpreg
|
|
|
|
switch(command[0] & 0x1f) {
|
|
case 0x02:
|
|
return command_pos == 9 ? C_READ_TRACK : C_INCOMPLETE;
|
|
|
|
case 0x03:
|
|
return command_pos == 3 ? C_SPECIFY : C_INCOMPLETE;
|
|
|
|
case 0x04:
|
|
return command_pos == 2 ? C_SENSE_DRIVE_STATUS : C_INCOMPLETE;
|
|
|
|
case 0x05:
|
|
case 0x09:
|
|
return command_pos == 9 ? C_WRITE_DATA : C_INCOMPLETE;
|
|
|
|
case 0x06:
|
|
case 0x0c:
|
|
return command_pos == 9 ? C_READ_DATA : C_INCOMPLETE;
|
|
|
|
case 0x07:
|
|
return command_pos == 2 ? C_RECALIBRATE : C_INCOMPLETE;
|
|
|
|
case 0x08:
|
|
return C_SENSE_INTERRUPT_STATUS;
|
|
|
|
case 0x0a:
|
|
return command_pos == 2 ? C_READ_ID : C_INCOMPLETE;
|
|
|
|
case 0x0b:
|
|
return C_MOTOR_ONOFF;
|
|
|
|
case 0x0d:
|
|
return command_pos == 6 ? C_FORMAT_TRACK : C_INCOMPLETE;
|
|
|
|
case 0x0e:
|
|
return C_DUMP_REG;
|
|
|
|
case 0x0f:
|
|
return command_pos == 3 ? C_SEEK : C_INCOMPLETE;
|
|
|
|
case 0x13:
|
|
return command_pos == 4 ? C_CONFIGURE : C_INCOMPLETE;
|
|
|
|
default:
|
|
return C_INVALID;
|
|
}
|
|
}
|
|
|
|
void i82072_device::start_command(int cmd)
|
|
{
|
|
// check if the command specifies a target drive
|
|
switch(cmd) {
|
|
case C_READ_TRACK:
|
|
case C_SENSE_DRIVE_STATUS:
|
|
case C_WRITE_DATA:
|
|
case C_READ_DATA:
|
|
case C_RECALIBRATE:
|
|
//case C_WRITE_DELETED_DATA:
|
|
case C_READ_ID:
|
|
//case C_READ_DELETED_DATA:
|
|
case C_FORMAT_TRACK:
|
|
case C_SEEK:
|
|
// start the motor
|
|
motor_control(command[1] & 0x3, true);
|
|
break;
|
|
}
|
|
|
|
// execute the command immediately if there's no motor on delay
|
|
if(motor_on_counter == 0) {
|
|
upd765_family_device::start_command(cmd);
|
|
|
|
// set motor off counter if command execution has completed
|
|
if (main_phase != PHASE_EXEC && motorcfg)
|
|
motor_off_counter = (2 + ((motorcfg & MOFF) >> 4)) << (motorcfg & HSDA ? 1 : 0);
|
|
} else
|
|
delayed_command = cmd;
|
|
}
|
|
|
|
void i82072_device::execute_command(int cmd)
|
|
{
|
|
switch(cmd) {
|
|
case C_DUMP_REG:
|
|
upd765_family_device::execute_command(cmd);
|
|
|
|
// i82072 dumps motor configuration at offset 7
|
|
result[7] = motorcfg;
|
|
break;
|
|
|
|
case C_MOTOR_ONOFF: {
|
|
bool motor_on = command[0] & 0x80;
|
|
floppy_info &fi = flopi[(command[0] >> 5) & 0x3];
|
|
|
|
LOGCOMMAND("command motor %s drive %d\n", motor_on ? "on" : "off", fi.id);
|
|
|
|
// if we are selecting a different drive, stop the motor on the previously selected drive
|
|
if (selected_drive != fi.id && flopi[selected_drive].dev && flopi[selected_drive].dev->mon_r() == 0)
|
|
flopi[selected_drive].dev->mon_w(1);
|
|
|
|
// select the drive
|
|
if(motor_on)
|
|
set_ds(fi.id);
|
|
|
|
// start the motor
|
|
if(fi.dev)
|
|
fi.dev->mon_w(motor_on ? 0 : 1);
|
|
|
|
main_phase = PHASE_CMD;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
upd765_family_device::execute_command(cmd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The Intel datasheet says that the drive busy bits in the MSR are supposed to remain
|
|
* set after a seek or recalibrate until a sense interrupt status status command is
|
|
* executed. The InterPro 2000 diagnostic routine goes further, and tests the drive
|
|
* status bits before and after the first sense interrupt status result byte is read,
|
|
* and expects the drive busy bit to clear only after.
|
|
*
|
|
* The Amstrad CPC6128 uses a upd765a and seems to expect the busy bits to be cleared
|
|
* immediately after the seek/recalibrate interrupt is generated.
|
|
*
|
|
* Special casing the i82072 here seems the only way to reconcile this apparently
|
|
* different behaviour for now.
|
|
*/
|
|
void i82072_device::command_end(floppy_info &fi, bool data_completion)
|
|
{
|
|
if(!data_completion)
|
|
drive_busy |= (1 << fi.id);
|
|
|
|
// set motor off counter
|
|
if(motorcfg)
|
|
motor_off_counter = (2 + ((motorcfg & MOFF) >> 4)) << (motorcfg & HSDA ? 1 : 0);
|
|
|
|
// clear existing interrupt sense data
|
|
for(floppy_info &fi : flopi)
|
|
fi.st0_filled = false;
|
|
|
|
upd765_family_device::command_end(fi, data_completion);
|
|
}
|
|
|
|
void i82072_device::motor_control(int fid, bool start_motor)
|
|
{
|
|
// check if motor control is enabled
|
|
if(motorcfg == 0)
|
|
return;
|
|
|
|
floppy_info &fi = flopi[fid];
|
|
|
|
if(start_motor) {
|
|
// if we are selecting a different drive, stop the motor on the previously selected drive
|
|
if(selected_drive != fid && flopi[selected_drive].dev && flopi[selected_drive].dev->mon_r() == 0)
|
|
flopi[selected_drive].dev->mon_w(1);
|
|
|
|
// start the motor on the selected drive
|
|
if(fi.dev && fi.dev->mon_r() == 1) {
|
|
LOGCOMMAND("motor_control: switching on motor for drive %d\n", fid);
|
|
|
|
// select the drive and enable the motor
|
|
set_ds(fid);
|
|
fi.dev->mon_w(0);
|
|
|
|
// set motor on counter
|
|
motor_on_counter = (motorcfg & MON) << (motorcfg & HSDA ? 1 : 0);
|
|
}
|
|
} else {
|
|
// motor off timer only applies to the selected drive
|
|
if(selected_drive != fid)
|
|
return;
|
|
|
|
// decrement motor on counter
|
|
if(motor_on_counter)
|
|
motor_on_counter--;
|
|
|
|
// execute the command if the motor on counter has expired
|
|
if(motor_on_counter == 0 && main_phase == PHASE_CMD && delayed_command) {
|
|
upd765_family_device::start_command(delayed_command);
|
|
|
|
// set motor off counter if command execution has completed
|
|
if(main_phase != PHASE_EXEC && motorcfg)
|
|
motor_off_counter = (2 + ((motorcfg & MOFF) >> 4)) << (motorcfg & HSDA ? 1 : 0);
|
|
|
|
delayed_command = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
// ignore motor off timer while drive is busy
|
|
if(fi.main_state == SEEK || fi.main_state == RECALIBRATE)
|
|
return;
|
|
|
|
// check if the motor is already off
|
|
if(motor_off_counter == 0 || (fi.dev && fi.dev->mon_r() == 1))
|
|
return;
|
|
|
|
// decrement the counter
|
|
motor_off_counter--;
|
|
|
|
// if the motor off timer has expired, stop the motor
|
|
if(motor_off_counter == 0 && fi.dev) {
|
|
LOGCOMMAND("motor_control: switching off motor for drive %d\n", fid);
|
|
fi.dev->mon_w(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void i82072_device::index_callback(floppy_image_device *floppy, int state)
|
|
{
|
|
if(state)
|
|
for(floppy_info &fi : flopi) {
|
|
if(fi.dev != floppy)
|
|
continue;
|
|
|
|
// update motor on/off counters and stop motor if necessary
|
|
motor_control(fi.id, false);
|
|
}
|
|
|
|
upd765_family_device::index_callback(floppy, state);
|
|
}
|
|
|
|
smc37c78_device::smc37c78_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, SMC37C78, tag, owner, clock)
|
|
{
|
|
ready_connected = false;
|
|
select_connected = true;
|
|
}
|
|
|
|
n82077aa_device::n82077aa_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, N82077AA, tag, owner, clock)
|
|
{
|
|
ready_connected = false;
|
|
select_connected = true;
|
|
}
|
|
|
|
pc_fdc_superio_device::pc_fdc_superio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, PC_FDC_SUPERIO, tag, owner, clock)
|
|
{
|
|
ready_polled = false;
|
|
ready_connected = false;
|
|
select_connected = true;
|
|
}
|
|
|
|
dp8473_device::dp8473_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, DP8473, tag, owner, clock)
|
|
{
|
|
ready_polled = false;
|
|
ready_connected = false;
|
|
select_connected = true;
|
|
}
|
|
|
|
pc8477a_device::pc8477a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, PC8477A, tag, owner, clock)
|
|
{
|
|
ready_polled = true;
|
|
ready_connected = false;
|
|
select_connected = true;
|
|
}
|
|
|
|
wd37c65c_device::wd37c65c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, WD37C65C, tag, owner, clock)
|
|
{
|
|
ready_polled = true;
|
|
ready_connected = false;
|
|
select_connected = true;
|
|
}
|
|
|
|
mcs3201_device::mcs3201_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
|
|
upd765_family_device(mconfig, MCS3201, tag, owner, clock),
|
|
m_input_handler(*this)
|
|
{
|
|
dor_reset = 0x0c;
|
|
ready_polled = false;
|
|
ready_connected = false;
|
|
select_connected = true;
|
|
}
|
|
|
|
void mcs3201_device::device_start()
|
|
{
|
|
upd765_family_device::device_start();
|
|
m_input_handler.resolve_safe(0);
|
|
}
|
|
|
|
READ8_MEMBER( mcs3201_device::input_r )
|
|
{
|
|
return m_input_handler();
|
|
}
|
|
|
|
tc8566af_device::tc8566af_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: upd765_family_device(mconfig, TC8566AF, tag, owner, clock)
|
|
, m_cr1(0)
|
|
{
|
|
ready_polled = true;
|
|
ready_connected = true;
|
|
select_connected = true;
|
|
}
|
|
|
|
void tc8566af_device::device_start()
|
|
{
|
|
upd765_family_device::device_start();
|
|
save_item(NAME(m_cr1));
|
|
}
|
|
|
|
WRITE8_MEMBER(tc8566af_device::cr1_w)
|
|
{
|
|
m_cr1 = data;
|
|
|
|
if(m_cr1 & 0x02) {
|
|
// Not sure if this inverted or not
|
|
tc_w((m_cr1 & 0x01) ? true : false);
|
|
}
|
|
}
|