diff options
Diffstat (limited to 'src/devices/machine/swim1.cpp')
-rw-r--r-- | src/devices/machine/swim1.cpp | 1230 |
1 files changed, 1230 insertions, 0 deletions
diff --git a/src/devices/machine/swim1.cpp b/src/devices/machine/swim1.cpp new file mode 100644 index 00000000000..f8450f45bef --- /dev/null +++ b/src/devices/machine/swim1.cpp @@ -0,0 +1,1230 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/********************************************************************* + + Implementation of the Apple SWIM1 floppy disk controller + +*********************************************************************/ + +#include "emu.h" +#include "swim1.h" + +#define VERBOSE (0) +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(SWIM1, swim1_device, "swim1", "Apple SWIM1 (Sander/Wozniak Integrated Machine) version 1 floppy controller") + +swim1_device::swim1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + applefdintf_device(mconfig, SWIM1, tag, owner, clock), + m_floppy(nullptr), + m_timer(nullptr) +{ +} + +void swim1_device::device_start() +{ + applefdintf_device::device_start(); + + m_timer = timer_alloc(FUNC(swim1_device::update), this); + m_sync_timer = timer_alloc(FUNC(swim1_device::ism_periodic_sync), this); + + save_item(NAME(m_last_sync)); + save_item(NAME(m_flux_write_start)); + save_item(NAME(m_flux_write)); + save_item(NAME(m_flux_write_count)); + + save_item(NAME(m_ism_param)); + save_item(NAME(m_ism_mode)); + save_item(NAME(m_ism_setup)); + save_item(NAME(m_ism_error)); + save_item(NAME(m_ism_param_idx)); + save_item(NAME(m_ism_fifo_pos)); + save_item(NAME(m_ism_tss_sr)); + save_item(NAME(m_ism_tss_output)); + save_item(NAME(m_ism_current_bit)); + save_item(NAME(m_ism_fifo)); + save_item(NAME(m_ism_sr)); + save_item(NAME(m_ism_crc)); + save_item(NAME(m_ism_half_cycles_before_change)); + save_item(NAME(m_ism_correction_factor)); + save_item(NAME(m_ism_latest_edge)); + save_item(NAME(m_ism_prev_ls)); + save_item(NAME(m_ism_csm_state)); + save_item(NAME(m_ism_csm_error_counter)); + save_item(NAME(m_ism_csm_pair_side)); + save_item(NAME(m_ism_csm_min_count)); + save_item(NAME(m_ism_tsm_out)); + save_item(NAME(m_ism_tsm_bits)); + save_item(NAME(m_ism_tsm_mark)); + + save_item(NAME(m_iwm_next_state_change)); + save_item(NAME(m_iwm_sync_update)); + save_item(NAME(m_iwm_async_update)); + save_item(NAME(m_iwm_active)); + save_item(NAME(m_iwm_rw)); + save_item(NAME(m_iwm_rw_state)); + save_item(NAME(m_iwm_data)); + save_item(NAME(m_iwm_whd)); + save_item(NAME(m_iwm_mode)); + save_item(NAME(m_iwm_status)); + save_item(NAME(m_iwm_control)); + save_item(NAME(m_iwm_rsh)); + save_item(NAME(m_iwm_wsh)); + save_item(NAME(m_iwm_rw_bit_count)); + save_item(NAME(m_iwm_to_ism_counter)); + save_item(NAME(m_iwm_devsel)); +} + +void swim1_device::device_reset() +{ + applefdintf_device::device_reset(); + m_ism_mode = 0x00; + m_ism_setup = 0x00; + m_ism_param_idx = 0; + memset(m_ism_param, 0, sizeof(m_ism_param)); + m_floppy = nullptr; + + m_last_sync = machine().time().as_ticks(clock()); + m_flux_write_start = 0; + m_flux_write_count = 0; + + m_iwm_next_state_change = 0; + m_iwm_active = MODE_IDLE; + m_iwm_rw = MODE_IDLE; + m_iwm_rw_state = S_IDLE; + m_iwm_data = 0x00; + m_iwm_whd = 0xbf; + m_iwm_mode = 0x00; + m_iwm_status = 0x00; + m_iwm_control = 0x00; + m_iwm_wsh = 0x00; + m_iwm_rsh = 0x00; + m_iwm_rw_bit_count = 0; + m_iwm_to_ism_counter = 0; + m_iwm_devsel = 0; + + m_devsel_cb(0); + m_sel35_cb(true); + m_hdsel_cb(false); + m_dat1byte_cb(CLEAR_LINE); +} + +void swim1_device::set_floppy(floppy_image_device *floppy) +{ + if(m_floppy == floppy) + return; + + sync(); + flush_write(); + + LOG("floppy %s\n", floppy ? floppy->tag() : "-"); + + m_floppy = floppy; + update_phases(); + if(m_ism_mode & 0x40) + m_hdsel_cb((m_ism_mode >> 5) & 1); +} + +floppy_image_device *swim1_device::get_floppy() const +{ + return m_floppy; +} + +void swim1_device::ism_show_mode() const +{ + LOG("ism mode%s %s hdsel=%c %c%s %c%c%s\n", + m_ism_mode & 0x80 ? " motoron" : "", + m_ism_mode & 0x40 ? "ism" : "iwm", + m_ism_mode & 0x20 ? '1' : '0', + m_ism_mode & 0x10 ? 'w' : 'r', + m_ism_mode & 0x08 ? " action" : "", + m_ism_mode & 0x04 ? 'a' : '-', + m_ism_mode & 0x02 ? 'b' : '-', + m_ism_mode & 0x01 ? " clear" : ""); + +} + +u8 swim1_device::read(offs_t offset) +{ + if(m_ism_mode & 0x40) + return ism_read(offset); + else + { + if(!machine().side_effects_disabled()) + iwm_control(offset, 0x00); + + switch(m_iwm_control & 0xc0) { + case 0x00: return m_iwm_active ? m_iwm_data : 0xff; + case 0x40: return (m_iwm_status & 0x7f) | ((!m_floppy || m_floppy->wpt_r()) ? 0x80 : 0x00); + case 0x80: return m_iwm_whd; + case 0xc0: return 0xff; + } + + abort(); + } +} + +void swim1_device::write(offs_t offset, u8 data) +{ + if(m_ism_mode & 0x40) + ism_write(offset, data); + else + iwm_control(offset, data); +} + +u8 swim1_device::ism_read(offs_t offset) +{ + ism_sync(); + + static const char *const names[] = { + "data", "mark", "crc", "param", "phases", "setup", "status", "handshake" + }; + + LOG("read ism %s\n", names[offset & 7]); + switch(offset & 7) { + case 0x0: { // data + u16 r = ism_fifo_pop(); + if(!m_ism_error) { + if(r == 0xffff) + m_ism_error |= 4; + else if(r & M_MARK) + m_ism_error |= 2; + } + return r; + } + + case 0x1: { // mark + u16 r = ism_fifo_pop(); + if(!m_ism_error && r == 0xffff) + m_ism_error |= 4; + return r; + } + + case 0x2: { // error + u8 err = m_ism_error; + m_ism_error = 0; + return err; + } + + case 0x3: { + u8 r = m_ism_param[m_ism_param_idx]; + m_ism_param_idx = (m_ism_param_idx + 1) & 15; + return r; + } + + case 0x4: // phases + return m_phases; + + case 0x5: // setup + return m_ism_setup; + + case 0x6: // mode + return m_ism_mode; + + case 0x7: { // handshake + u8 h = 0; + if(m_ism_fifo_pos > 0) { + if(m_ism_fifo[m_ism_fifo_pos - 1] & M_MARK) + h |= 0x01; + if(!(m_ism_fifo[m_ism_fifo_pos - 1] & M_CRC0)) + h |= 0x02; + } + // rddata on 3 + if(!m_floppy || m_floppy->wpt_r()) + h |= 0x0c; + if(m_ism_error) + h |= 0x20; + if(m_ism_mode & 0x10) { + // write + if(m_ism_fifo_pos == 0) + h |= 0xc0; + else if(m_ism_fifo_pos == 1) + h |= 0x80; + } else { + // read + if(m_ism_fifo_pos == 2) + h |= 0xc0; + else if(m_ism_fifo_pos == 1) + h |= 0x80; + } + return h; + } + + default: + logerror("read %s\n", names[offset & 7]); + break; + } + return 0xff; +} + +void swim1_device::ism_write(offs_t offset, u8 data) +{ + ism_sync(); + + u8 prev_mode = m_ism_mode; + + static const char *const names[] = { + "data", "mark", "crc", "param", "phases", "setup", "mode0", "mode1", + }; + switch(offset) { + case 0: + if(ism_fifo_push(data) && !m_ism_error) + m_ism_error |= 0x04; + break; + + case 1: + if(ism_fifo_push(M_MARK | data) && !m_ism_error) + m_ism_error |= 0x04; + break; + + case 2: + if(ism_fifo_push(M_CRC) && !m_ism_error) + m_ism_error |= 0x04; + break; + + case 0x3: { + static const char *const pname[16] = { + "minct", "mult", "ssl", "sss", "sll", "sls", "rpt", "csls", + "lsl", "lss", "lll", "lls", "late", "time0", "early", "time1" + }; + LOG("param[%s] = %02x\n", pname[m_ism_param_idx], data); + m_ism_param[m_ism_param_idx] = data; + m_ism_param_idx = (m_ism_param_idx + 1) & 15; + break; + } + + case 0x4: { + LOG("ism phases %02x\n", data); + m_phases = data; + update_phases(); + break; + } + + case 0x5: + m_ism_setup = data; + LOG("setup timer=%s tsm=%s %s ecm=%s %s %s 3.5=%s %s\n", + m_ism_setup & 0x80 ? "on" : "off", + m_ism_setup & 0x40 ? "off" : "on", + m_ism_setup & 0x20 ? "ibm" : "apple", + m_ism_setup & 0x10 ? "on" : "off", + m_ism_setup & 0x08 ? "fclk/2" : "fclk", + m_ism_setup & 0x04 ? "gcr" : "mfm", + m_ism_setup & 0x02 ? "off" : "on", + m_ism_setup & 0x01 ? "hdsel" : "q3"); + break; + + case 0x6: + m_ism_mode &= ~data; + m_ism_param_idx = 0; + ism_show_mode(); + ism_update_dat1byte(); + if(!(m_ism_mode & 0x40)) { + LOG("switch to iwm\n"); + u8 ism_devsel = m_ism_mode & 0x80 ? (m_ism_mode >> 1) & 3 : 0; + if(ism_devsel != m_iwm_devsel) + m_devsel_cb(m_iwm_devsel); + } + break; + + case 0x7: + m_ism_mode |= data; + ism_show_mode(); + ism_update_dat1byte(); + break; + + default: + logerror("write %s, %02x\n", names[offset & 7], data); + break; + } + + if(m_ism_mode & 0x01) + ism_fifo_clear(); + + if((m_ism_mode ^ prev_mode) & 0x86) + m_devsel_cb(m_ism_mode & 0x80 ? (m_ism_mode >> 1) & 3 : 0); + if((m_ism_mode ^ prev_mode) & 0x20) + m_hdsel_cb((m_ism_mode >> 5) & 1); + + if ((m_ism_mode ^ prev_mode) & 0x40) { + if (m_ism_mode & 0x40) + m_sync_timer->adjust(attotime::zero, 0, attotime::from_hz(clock()/16)); + else + m_sync_timer->adjust(attotime::never); + } + + if((m_ism_mode & 0x18) == 0x18 && ((prev_mode & 0x18) != 0x18)) { + // Entering write mode + m_ism_current_bit = 0; + LOG("%s write start %s %s floppy=%p\n", machine().time().to_string(), m_ism_setup & 0x40 ? "gcr" : "mfm", m_ism_setup & 0x08 ? "fclk/2" : "fclk", m_floppy); + m_flux_write_start = m_last_sync; + m_flux_write_count = 0; + + } else if((prev_mode & 0x18) == 0x18 && (m_ism_mode & 0x18) != 0x18) { + // Exiting write mode + flush_write(); + m_flux_write_start = 0; + m_ism_current_bit = 0xff; + m_ism_half_cycles_before_change = 0; + LOG("%s write end\n", machine().time().to_string()); + } + + if((m_ism_mode & 0x18) == 0x08 && ((prev_mode & 0x18) != 0x08)) { + // Entering read mode + m_ism_current_bit = 0; + m_ism_sr = 0; + m_ism_latest_edge = m_last_sync; + m_ism_prev_ls = (1<<2) | 1; + m_ism_csm_state = m_ism_setup & 0x04 ? CSM_SYNCHRONIZED : CSM_INIT; + m_ism_csm_error_counter[0] = m_ism_csm_error_counter[1] = 0; + m_ism_correction_factor[0] = m_ism_correction_factor[1] = 0; + m_ism_csm_pair_side = 0; + m_ism_csm_min_count = 0; + + LOG("%s read start %s %s floppy=%p\n", machine().time().to_string(), m_ism_setup & 0x04 ? "gcr" : "mfm", m_ism_setup & 0x08 ? "fclk/2" : "fclk", m_floppy); + + } else if((prev_mode & 0x18) == 0x08 && (m_ism_mode & 0x18) != 0x08) { + // Exiting read mode + flush_write(); + m_ism_current_bit = 0xff; + m_ism_half_cycles_before_change = 0; + LOG("%s read end\n", machine().time().to_string()); + } +} + +TIMER_CALLBACK_MEMBER(swim1_device::update) +{ + if(m_iwm_active == MODE_DELAY) { + flush_write(); + m_iwm_active = MODE_IDLE; + m_iwm_rw = MODE_IDLE; + m_iwm_rw_state = S_IDLE; + if(!(m_ism_mode & 0x40)) + m_devsel_cb(0); + m_iwm_devsel = 0; + m_iwm_status &= ~0x20; + m_iwm_whd &= ~0x40; + } +} + +void swim1_device::flush_write(u64 when) +{ + if(!m_flux_write_start) + return; + + if(!when) + when = m_last_sync; + + if(when > m_flux_write_start) { + bool last_on_edge = m_flux_write_count && m_flux_write[m_flux_write_count-1] == when; + if(last_on_edge) + m_flux_write_count--; + + attotime start = cycles_to_time(m_flux_write_start); + attotime end = cycles_to_time(when); + std::vector<attotime> fluxes(m_flux_write_count); + for(u32 i=0; i != m_flux_write_count; i++) + fluxes[i] = cycles_to_time(m_flux_write[i]); + + if(m_floppy) + m_floppy->write_flux(start, end, m_flux_write_count, m_flux_write_count ? &fluxes[0] : nullptr); + + m_flux_write_count = 0; + if(last_on_edge) + m_flux_write[m_flux_write_count++] = when; + m_flux_write_start = when; + + } else + m_flux_write_count = 0; +} + +void swim1_device::iwm_control(int offset, u8 data) +{ + iwm_sync(); + + u8 prev_iwm_to_ism_counter = m_iwm_to_ism_counter; + + if(offset < 8) { + if(offset & 1) + m_phases |= 1 << (offset >> 1); + else + m_phases &= ~(1 << (offset >> 1)); + update_phases(); + } else { + if(offset & 1) + m_iwm_control |= 1 << (offset >> 1); + else + m_iwm_control &= ~(1 << (offset >> 1)); + } + + if(m_iwm_control & 0x10) { + if(m_iwm_active != MODE_ACTIVE) { + m_iwm_active = MODE_ACTIVE; + m_iwm_status |= 0x20; + if(m_floppy) + m_floppy->mon_w(false); + } + + if((m_iwm_control & 0x80) == 0x00) { + if(m_iwm_rw != MODE_READ) { + if(m_iwm_rw == MODE_WRITE) { + flush_write(); + m_flux_write_start = 0; + } + m_iwm_rw = MODE_READ; + m_iwm_rw_state = S_IDLE; + m_iwm_next_state_change = 0; + m_iwm_sync_update = 0; + m_iwm_async_update = 0; + m_iwm_data = 0x00; + } + + } else { + if(m_iwm_rw != MODE_WRITE) { + m_iwm_rw = MODE_WRITE; + m_iwm_rw_state = S_IDLE; + m_iwm_whd |= 0x40; + m_iwm_next_state_change = 0; + m_flux_write_start = m_last_sync; + m_flux_write_count = 0; + if(m_floppy) + m_floppy->set_write_splice(cycles_to_time(m_flux_write_start)); + } + } + } else { + if(m_iwm_active == MODE_ACTIVE) { + flush_write(); + if(m_iwm_mode & 0x04) { + m_flux_write_start = 0; + m_iwm_active = MODE_IDLE; + m_iwm_rw = MODE_IDLE; + m_iwm_rw_state = S_IDLE; + m_iwm_status &= ~0x20; + m_iwm_whd &= ~0x40; + if(m_floppy) + m_floppy->mon_w(true); + } else { + m_devsel_cb(m_iwm_control & 0x20 ? 2 : 1); + m_iwm_active = MODE_DELAY; + m_timer->adjust(cycles_to_time(8388608)); + } + } + } + + u8 devsel = m_iwm_active != MODE_IDLE ? m_iwm_control & 0x20 ? 2 : 1 : 0; + if(devsel != m_iwm_devsel) { + m_iwm_devsel = devsel; + m_devsel_cb(devsel); + } + + if(0) { + u8 s = m_iwm_control & 0xc0; + const char *slot = "?"; + if(s == 0x00 && !m_iwm_active) + slot = "idle / ff"; + if(s == 0x00 && m_iwm_active) + slot = "read / read data"; + if(s == 0x40) + slot = "wp sense / read status"; + if(s == 0x80) + slot = "write / read whd"; + if(s == 0xc0 && !m_iwm_active) + slot = "mode set / write mode"; + if(s == 0xc0 && m_iwm_active) + slot = "write load / write data"; + + LOG("%s control %c%c %c%c %c%c%c%c (%s) [%s, %s] whd=%02x data=%02x\n", + machine().time().to_string(), + m_iwm_control & 0x80 ? '1' : '0', + m_iwm_control & 0x40 ? '1' : '0', + m_iwm_control & 0x20 ? 'b' : 'a', + m_iwm_control & 0x10 ? '#' : '.', + m_phases & 0x08 ? '#' : '.', + m_phases & 0x04 ? '#' : '.', + m_phases & 0x02 ? '#' : '.', + m_phases & 0x01 ? '#' : '.', + slot, + m_iwm_active == MODE_IDLE ? "idle" : m_iwm_active == MODE_DELAY ? "delay" : "active", + m_iwm_rw == MODE_IDLE ? "idle" : m_iwm_rw == MODE_READ ? "read" : "write", + m_iwm_whd, m_iwm_data); + } + + if(m_iwm_active && !(m_iwm_control & 0x80) && !iwm_is_sync() && (m_iwm_data & 0x80)) + m_iwm_async_update = m_last_sync + 14; + + if(offset == 0xf) { + switch(m_iwm_to_ism_counter) { + case 0: case 2: + if(data & 0x40) + m_iwm_to_ism_counter++; + break; + case 1: + if(!(data & 0x40)) + m_iwm_to_ism_counter++; + break; + case 3: + if(data & 0x40) { + m_ism_mode |= 0x40; + LOG("switch to ism\n"); + + u8 ism_devsel = m_ism_mode & 0x80 ? (m_ism_mode >> 1) & 3 : 0; + if(ism_devsel != m_iwm_devsel) + m_devsel_cb(ism_devsel); + + m_sync_timer->adjust(attotime::zero, 0, attotime::from_hz(clock()/16)); + } + break; + } + } + if(m_iwm_to_ism_counter != prev_iwm_to_ism_counter+1) + m_iwm_to_ism_counter = 0; + else + LOG("iwm counter = %d\n", m_iwm_to_ism_counter); + + if((m_iwm_control & 0xc0) == 0xc0 && (offset & 1)) + { + if(m_iwm_active) + iwm_data_w(data); + else + iwm_mode_w(data); + } +} + +void swim1_device::ism_crc_clear() +{ + m_ism_crc = 0xcdb4; +} + +void swim1_device::ism_crc_update(int bit) +{ + if((m_ism_crc ^ (bit ? 0x8000 : 0x0000)) & 0x8000) + m_ism_crc = (m_ism_crc << 1) ^ 0x1021; + else + m_ism_crc = m_ism_crc << 1; + +} + +// The ism part of the swim1 works in half-clocks +u64 swim1_device::time_to_cycles(const attotime &tm) const +{ + return tm.as_ticks(2*clock()); +} + +attotime swim1_device::cycles_to_time(u64 cycles) const +{ + return attotime::from_ticks(cycles, 2*clock()); +} + +void swim1_device::ism_fifo_clear() +{ + m_ism_fifo_pos = 0; + ism_update_dat1byte(); + ism_crc_clear(); +} + +bool swim1_device::ism_fifo_push(u16 data) +{ + if(m_ism_fifo_pos == 2) + { + return true; + } + m_ism_fifo[m_ism_fifo_pos ++] = data; + ism_update_dat1byte(); + return false; +} + +u16 swim1_device::ism_fifo_pop() +{ + if(m_ism_fifo_pos == 0) + return 0xffff; + u16 r = m_ism_fifo[0]; + m_ism_fifo[0] = m_ism_fifo[1]; + m_ism_fifo_pos --; + ism_update_dat1byte(); + return r; +} + +void swim1_device::iwm_mode_w(u8 data) +{ + m_iwm_mode = data; + m_iwm_status = (m_iwm_status & 0xe0) | (data & 0x1f); + LOG("mode %02x%s%s%s%s%s%s%s\n", m_iwm_mode, + m_iwm_mode & 0x80 ? " b7" : "", + m_iwm_mode & 0x40 ? " mz-reset" : "", + m_iwm_mode & 0x20 ? " test" : " normal", + m_iwm_mode & 0x10 ? " 8MHz" : " 7MHz", + m_iwm_mode & 0x08 ? " fast" : " slow", + m_iwm_mode & 0x04 ? "" : " timer", + m_iwm_mode & 0x02 ? " async" : " sync", + m_iwm_mode & 0x01 ? " latched" : ""); +} + +void swim1_device::iwm_data_w(u8 data) +{ + m_iwm_data = data; + if(iwm_is_sync() && m_iwm_rw == MODE_WRITE) + m_iwm_wsh = data; + if(m_iwm_mode & 0x01) + m_iwm_whd &= 0x7f; +} + +bool swim1_device::iwm_is_sync() const +{ + return m_iwm_mode & 0x02 ? false : true; +} + +u64 swim1_device::iwm_half_window_size() const +{ + switch(m_iwm_mode & 0x18) { + case 0x00: return 2*14; + case 0x08: return 2* 7; + case 0x10: return 2*16; + case 0x18: return 2* 8; + } + abort(); +} + +u64 swim1_device::iwm_window_size() const +{ + switch(m_iwm_mode & 0x18) { + case 0x00: return 2*28; + case 0x08: return 2*14; + case 0x10: return 2*36; + case 0x18: return 2*16; + } + abort(); +} + +u64 swim1_device::iwm_read_register_update_delay() const +{ + return m_iwm_mode & 0x08 ? 4 : 8; +} + +void swim1_device::iwm_sync() +{ + if(!m_iwm_active) + return; + + u64 next_sync = time_to_cycles(machine().time()); + switch(m_iwm_rw) { + case MODE_IDLE: + m_last_sync = next_sync; + break; + + case MODE_READ: { + u64 next_flux_change = 0; + while(next_sync > m_last_sync) { + if(next_flux_change <= m_last_sync) { + attotime flux = m_floppy ? m_floppy->get_next_transition(cycles_to_time(m_last_sync+1)) : attotime::never; + next_flux_change = flux.is_never() ? u64(-1) : time_to_cycles(flux); + if(next_flux_change <= m_last_sync) + next_flux_change = m_last_sync+1; + } + if(next_sync < m_iwm_next_state_change) { + m_last_sync = next_sync; + break; + } + if(m_last_sync < m_iwm_next_state_change) + m_last_sync = m_iwm_next_state_change; + switch(m_iwm_rw_state) { + case S_IDLE: + m_iwm_rsh = 0x00; + m_iwm_rw_state = SR_WINDOW_EDGE_0; + m_iwm_next_state_change = m_last_sync + iwm_window_size(); + m_iwm_sync_update = 0; + m_iwm_async_update = 0; + break; + + case SR_WINDOW_EDGE_0: + case SR_WINDOW_EDGE_1: { + u64 endw = m_iwm_next_state_change + (m_iwm_rw_state == SR_WINDOW_EDGE_0 ? iwm_window_size() : iwm_half_window_size()); + if(m_iwm_rw_state == SR_WINDOW_EDGE_0 && endw >= next_flux_change && next_sync >= next_flux_change) { + m_last_sync = m_iwm_next_state_change = next_flux_change; + m_iwm_rw_state = SR_WINDOW_EDGE_1; + break; + } + if(next_sync < endw) { + m_last_sync = next_sync; + break; + } + m_iwm_rsh = (m_iwm_rsh << 1) | (m_iwm_rw_state == SR_WINDOW_EDGE_1 ? 1 : 0); + m_iwm_next_state_change = m_last_sync = endw; + m_iwm_rw_state = SR_WINDOW_EDGE_0; + if(iwm_is_sync()) { + if(m_iwm_rsh >= 0x80) { + m_iwm_data = m_iwm_rsh; + m_iwm_rsh = 0; + } else if(m_iwm_rsh >= 0x04) { + m_iwm_data = m_iwm_rsh; + m_iwm_sync_update = 0; + } else if(m_iwm_rsh >= 0x02) + m_iwm_sync_update = m_last_sync + iwm_read_register_update_delay(); + + } else if(m_iwm_rsh >= 0x80) { + m_iwm_data = m_iwm_rsh; + m_iwm_async_update = 0; + m_iwm_rsh = 0; + } + break; + } + } + } + if(m_iwm_sync_update && m_iwm_sync_update <= m_last_sync) { + if(iwm_is_sync()) + m_iwm_data = m_iwm_rsh; + m_iwm_sync_update = 0; + } + if(m_iwm_async_update && m_iwm_async_update <= m_last_sync) { + if(!iwm_is_sync()) + m_iwm_data = 0; + m_iwm_async_update = 0; + } + break; + } + + case MODE_WRITE: { + while(next_sync > m_last_sync) { + if(next_sync < m_iwm_next_state_change || !(m_iwm_whd & 0x40)) { + m_last_sync = next_sync; + break; + } + if(m_last_sync < m_iwm_next_state_change) + m_last_sync = m_iwm_next_state_change; + switch(m_iwm_rw_state) { + case S_IDLE: + m_flux_write_count = 0; + if(m_iwm_mode & 0x02) { + m_iwm_rw_state = SW_WINDOW_LOAD; + m_iwm_rw_bit_count = 8; + m_iwm_next_state_change = m_last_sync + 7; + } else { + m_iwm_wsh = m_iwm_data; + m_iwm_rw_state = SW_WINDOW_MIDDLE; + m_iwm_next_state_change = m_last_sync + iwm_half_window_size(); + } + break; + + case SW_WINDOW_LOAD: + if(m_iwm_whd & 0x80) { + LOG("underrun\n"); + flush_write(); + m_flux_write_start = 0; + m_iwm_whd &= ~0x40; + m_last_sync = next_sync; + m_iwm_rw_state = SW_UNDERRUN; + + } else { + m_iwm_wsh = m_iwm_data; + m_iwm_rw_state = SW_WINDOW_MIDDLE; + m_iwm_whd |= 0x80; + m_iwm_next_state_change = m_last_sync + iwm_half_window_size() - 7; + } + break; + + case SW_WINDOW_MIDDLE: + if(m_iwm_wsh & 0x80) + m_flux_write[m_flux_write_count++] = m_last_sync; + m_iwm_wsh <<= 1; + m_iwm_rw_state = SW_WINDOW_END; + m_iwm_next_state_change = m_last_sync + iwm_half_window_size(); + break; + + case SW_WINDOW_END: + if(m_flux_write_count == m_flux_write.size()) + flush_write(); + if(m_iwm_mode & 0x02) { + m_iwm_rw_bit_count --; + if(m_iwm_rw_bit_count == 0) { + m_iwm_rw_state = SW_WINDOW_LOAD; + m_iwm_rw_bit_count = 8; + m_iwm_next_state_change = m_last_sync + 7; + } else { + m_iwm_rw_state = SW_WINDOW_MIDDLE; + m_iwm_next_state_change = m_last_sync + iwm_half_window_size(); + } + } else { + m_iwm_next_state_change = m_last_sync + iwm_half_window_size(); + m_iwm_rw_state = SW_WINDOW_MIDDLE; + } + break; + + case SW_UNDERRUN: + m_last_sync = next_sync; + break; + } + } + break; + } + } +} + +TIMER_CALLBACK_MEMBER(swim1_device::ism_periodic_sync) +{ + if (m_ism_mode & 0x40) + { + ism_sync(); + } +} + +void swim1_device::ism_sync() +{ + u64 next_sync = time_to_cycles(machine().time()); + if(!(m_ism_mode & 0x08)) { + m_last_sync = next_sync; + return; + } + + if(m_ism_mode & 0x10) { + u32 cycles = next_sync - m_last_sync; + + // Write mode + while(cycles) { + if(m_ism_half_cycles_before_change) { + if(cycles >= m_ism_half_cycles_before_change) { + cycles -= m_ism_half_cycles_before_change; + m_ism_half_cycles_before_change = 0; + } else { + m_ism_half_cycles_before_change -= cycles; + cycles = 0; + break; + } + } + + if(m_ism_tss_output & 0xc) { + bool bit; + if(m_ism_tss_output & 8) { + bit = (m_ism_tss_output >> 1) & 1; + m_ism_tss_output &= ~0xa; + } else { + bit = m_ism_tss_output & 1; + m_ism_tss_output = 0; + } + if(bit) { + if(m_flux_write_count == m_flux_write.size()) + flush_write(next_sync - cycles); + m_flux_write[m_flux_write_count ++] = next_sync - cycles; + m_ism_half_cycles_before_change = m_ism_param[P_TIME1] + 2*2; + } else + m_ism_half_cycles_before_change = m_ism_param[P_TIME0] + 2*2; + if(m_ism_setup & 8) + m_ism_half_cycles_before_change <<= 1; + continue; + } + if(m_ism_current_bit == 0xff) + fatalerror("Sequence break on write\n"); + + if(m_ism_current_bit == 0) { + if(m_ism_sr & M_CRC) + m_ism_sr = m_ism_crc >> 8; + else { + u16 r = ism_fifo_pop(); + if(r == 0xffff && !m_ism_error) { + m_ism_error |= 0x01; + flush_write(); + m_ism_current_bit = 0xff; + m_ism_half_cycles_before_change = 0; + m_ism_mode &= ~8; + LOG("write end on underrun\n"); + break; + } + if(r & M_CRC) + m_ism_sr = M_CRC | (m_ism_crc >> 8); + else + m_ism_sr = r & (M_MARK | M_CRC | 0xff); + } + m_ism_current_bit = 8; + if(m_ism_sr & M_MARK) + ism_crc_clear(); + } + m_ism_current_bit --; + int bit = (m_ism_sr >> m_ism_current_bit) & 1; + if(!(m_ism_sr & M_MARK)) + ism_crc_update(bit); + m_ism_tss_sr = (m_ism_tss_sr << 1) | bit; + if(m_ism_setup & 0x40) + m_ism_tss_output = 4 | bit; + else { + static const u8 tss[4] = { 5, 0xd, 4, 5 }; + if((m_ism_sr & M_MARK) && ((m_ism_tss_sr & 0xf) == 8)) + m_ism_tss_output = 0xc; + else + m_ism_tss_output = tss[m_ism_tss_sr & 3]; + } + + continue; + } + } else { + while(m_last_sync < next_sync) { + // Find when in the future the next edge happens + u64 cycles_to_next; + bool will_hit_edge; + if(!m_floppy) { + cycles_to_next = next_sync - m_ism_latest_edge; + will_hit_edge = false; + } else { + auto when = m_floppy->get_next_transition(cycles_to_time(m_ism_latest_edge+2)); + if(when == attotime::never || when > cycles_to_time(next_sync)) { + cycles_to_next = next_sync - m_ism_latest_edge; + will_hit_edge = false; + } else { + cycles_to_next = time_to_cycles(when) - m_ism_latest_edge; + will_hit_edge = true; + } + } + + // Pick up the current rescaling factor + int scale = m_ism_correction_factor[m_ism_csm_pair_side]; + if(scale < 192) + scale |= 256; + + // Count the number of cells in the L and the S hypothesis + u32 sct, lct; + if(m_ism_prev_ls == 0x5) { + // Previous was a short + if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2)) >> 8) + sct = 0; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_SSS] + 2*2)) >> 8) + sct = 1; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_SSS] + 2*2 + m_ism_param[P_SLS] + 2*2)) >> 8) + sct = 2; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_SSS] + 2*2 + m_ism_param[P_SLS] + 2*2 + m_ism_param[P_RPT] + 2*2)) >> 8) + sct = 3; + else + sct = 4; + if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2)) >> 8) + lct = 0; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_SSL] + 2*2)) >> 8) + lct = 1; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_SSL] + 2*2 + m_ism_param[P_SLL] + 2*2)) >> 8) + lct = 2; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_SSL] + 2*2 + m_ism_param[P_SLL] + 2*2 + m_ism_param[P_RPT] + 2*2)) >> 8) + lct = 3; + else + lct = 4; + + } else if(m_ism_prev_ls == 0x6 || m_ism_prev_ls == 0x7 || m_ism_prev_ls == 0x9 || m_ism_prev_ls == 0xd) { + // Previous was marginal + if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2)) >> 8) + sct = 0; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSS] + 2*2)) >> 8) + sct = 1; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSS] + 2*2 + m_ism_param[P_CSLS] + 2*2)) >> 8) + sct = 2; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSS] + 2*2 + m_ism_param[P_CSLS] + 2*2 + m_ism_param[P_RPT] + 2*2)) >> 8) + sct = 3; + else + sct = 4; + if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2)) >> 8) + lct = 0; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSL] + 2*2)) >> 8) + lct = 1; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSL] + 2*2 + m_ism_param[P_CSLS] + 2*2)) >> 8) + lct = 2; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSL] + 2*2 + m_ism_param[P_CSLS] + 2*2 + m_ism_param[P_RPT] + 2*2)) >> 8) + lct = 3; + else + lct = 4; + + } else { + // Previous was long + if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2)) >> 8) + sct = 0; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSS] + 2*2)) >> 8) + sct = 1; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSS] + 2*2 + m_ism_param[P_LLS] + 2*2)) >> 8) + sct = 2; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSS] + 2*2 + m_ism_param[P_LLS] + 2*2 + m_ism_param[P_RPT] + 2*2)) >> 8) + sct = 3; + else + sct = 4; + if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2)) >> 8) + lct = 0; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSL] + 2*2)) >> 8) + lct = 1; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSL] + 2*2 + m_ism_param[P_LLL] + 2*2)) >> 8) + lct = 2; + else if(cycles_to_next <= (scale * (m_ism_param[P_MINCT] + 3*2 + m_ism_param[P_LSL] + 2*2 + m_ism_param[P_LLL] + 2*2 + m_ism_param[P_RPT] + 2*2)) >> 8) + lct = 3; + else + lct = 4; + } + + // Actually resolve the cell lengths according to the counts + int resolved_ls_count = 0; + int resolved_ls_type[2] = { 0, 0 }; + if((sct == 4 || lct == 4) && !m_ism_error) + m_ism_error |= 0x20; + + if(will_hit_edge) { + if(sct == 0) { + // Don't do the short cell error, write splices often trigger it and the physical media + // probably doesn't actually allow for it. + sct = lct = 1; + } + if(sct == 4) + sct = 3; + if(lct == 4) + lct = 3; + + bool previous_marginal = m_ism_prev_ls == 0x6 || m_ism_prev_ls == 0x7 || m_ism_prev_ls == 0x9 || m_ism_prev_ls == 0xd; + bool current_marginal = (sct == 1 && lct > 1) || (lct == 1 && sct > 1); + + if(previous_marginal && current_marginal) { + if(!m_ism_error) + m_ism_error |= 0x40; + resolved_ls_count = 2; + resolved_ls_type[0] = (m_ism_prev_ls >> 2) & 3; + resolved_ls_type[1] = lct; + } else { + if(previous_marginal) { + if(sct == 1) + resolved_ls_type[resolved_ls_count++] = m_ism_prev_ls & 3; + else + resolved_ls_type[resolved_ls_count++] = (m_ism_prev_ls >> 2) & 3; + } + if(!current_marginal) { + if(sct == 1) + resolved_ls_type[resolved_ls_count++] = sct; + else + resolved_ls_type[resolved_ls_count++] = lct; + } + } + + m_ism_prev_ls = (lct << 2) | sct; + } + + // If we have cell lengths, run the Correction State Machine and the Trans-Space Machine appropriately + for(int i=0; i != resolved_ls_count; i++) { + int type = resolved_ls_type[i]; + bool drop_one_bit = false; + switch(m_ism_csm_state) { + case CSM_INIT: + m_ism_csm_error_counter[0] = m_ism_csm_error_counter[1] = 0; + m_ism_csm_pair_side = 0; + m_ism_csm_min_count = 0; + m_ism_csm_state = CSM_COUNT_MIN; + break; + + case CSM_COUNT_MIN: + if(type != 1) { + m_ism_csm_state = CSM_INIT; + break; + } + m_ism_csm_error_counter[m_ism_csm_pair_side] += m_ism_param[P_MULT] * (cycles_to_next >> 1); + m_ism_csm_min_count ++; + if(m_ism_csm_min_count == 64) { + for(int i=0; i != 2; i++) { + m_ism_correction_factor[i] = m_ism_csm_error_counter[i] >> 8; + if(!m_ism_error && (m_ism_csm_error_counter[i] < 0xc000 || m_ism_csm_error_counter[i] >= 0x1c000)) + m_ism_error |= 0x08; + } + m_ism_csm_state = CSM_WAIT_NON_MIN; + } + break; + + case CSM_WAIT_NON_MIN: + if(type == 1) { + break; + } + m_ism_csm_state = CSM_CHECK_MARK; + m_ism_tsm_out = 0; + m_ism_tsm_mark = false; + m_ism_tsm_bits = 0; + ism_crc_clear(); + drop_one_bit = true; + [[fallthrough]]; + + // 2 1 3 1 3 2 2 2 1 + // 2 3 2 3 2 + + case CSM_CHECK_MARK: + case CSM_SYNCHRONIZED: + if(m_ism_setup & 0x04) { + for(int i=0; i != type; i++) { + int bit = (i+1 == type) ? 1 : 0; + m_ism_tsm_out = (m_ism_tsm_out << 1) | bit; + if(m_ism_tsm_out & 0x80) { + if(ism_fifo_push(m_ism_tsm_out) && !m_ism_error) + m_ism_error |= 0x01; + m_ism_tsm_out = 0; + } + } + } else { + static u32 nb[6] = { 1, 1, 2, 1, 2, 2 }; + static u32 bb[6] = { 1, 0, 1, 0, 1, 0 }; + int idx = (m_ism_tsm_out & 1 ? 0 : 3) + type - 1; + int nbc = nb[idx]; + int bbc = bb[idx]; + if(drop_one_bit) { + nbc--; + drop_one_bit = false; + } + if(idx == 5) + m_ism_tsm_mark = true; + for(int i=0; i != nbc; i++) { + int bit = (bbc >> (nbc-1-i)) & 1; + m_ism_tsm_out = (m_ism_tsm_out << 1) | bit; + m_ism_tsm_bits ++; + ism_crc_update(bit); + + if(m_ism_tsm_bits == 8) { + if(m_ism_csm_state == CSM_CHECK_MARK) { + if(!m_ism_tsm_mark) { + m_ism_csm_state = CSM_INIT; + break; + } + m_ism_csm_state = CSM_SYNCHRONIZED; + } + u16 val = m_ism_tsm_out & 0xff; + if(m_ism_tsm_mark) { + m_ism_tsm_mark = false; + val |= M_MARK; + ism_crc_clear(); + } + if(!m_ism_crc) + val |= M_CRC0; + if(ism_fifo_push(val) && !m_ism_error) + m_ism_error |= 0x01; + m_ism_tsm_bits = 0; + } + } + } + break; + } + + m_ism_csm_pair_side = !m_ism_csm_pair_side; + } + + + // Go to the next sync point + if(will_hit_edge) { + m_ism_latest_edge += cycles_to_next; + m_last_sync = m_ism_latest_edge; + } else + m_last_sync = next_sync; + } + } + + m_last_sync = next_sync; +} + +void swim1_device::sync() +{ + if(m_ism_mode & 0x40) + return ism_sync(); + else + return iwm_sync(); +} + +void swim1_device::ism_update_dat1byte() +{ + if (m_ism_mode & 0x10) + { + // write: Does FIFO have room? + m_dat1byte_cb((m_ism_fifo_pos < 2) ? ASSERT_LINE : CLEAR_LINE); + } + else + { + // read: is FIFO not empty? + m_dat1byte_cb((m_ism_fifo_pos > 0) ? ASSERT_LINE : CLEAR_LINE); + } +} |