diff options
Diffstat (limited to 'src/devices/machine/swim2.cpp')
-rw-r--r-- | src/devices/machine/swim2.cpp | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/src/devices/machine/swim2.cpp b/src/devices/machine/swim2.cpp new file mode 100644 index 00000000000..5d51dba66ad --- /dev/null +++ b/src/devices/machine/swim2.cpp @@ -0,0 +1,565 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/********************************************************************* + + Implementation of the Apple SWIM2 floppy disk controller + +*********************************************************************/ + +#include "emu.h" +#include "swim2.h" + +#define LOG_SETUP (1U << 1) +#define LOG_MODE (1U << 2) +#define VERBOSE 0 +#include "logmacro.h" + +#define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__) +#define LOGMODE(...) LOGMASKED(LOG_MODE, __VA_ARGS__) + +DEFINE_DEVICE_TYPE(SWIM2, swim2_device, "swim2", "Apple SWIM2 (Sander/Wozniak Integrated Machine) version 2 floppy controller") + +swim2_device::swim2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + applefdintf_device(mconfig, SWIM2, tag, owner, clock), + m_floppy(nullptr) +{ +} + +void swim2_device::device_start() +{ + applefdintf_device::device_start(); + + save_item(NAME(m_mode)); + save_item(NAME(m_setup)); + save_item(NAME(m_param_idx)); + save_item(NAME(m_param)); + save_item(NAME(m_last_sync)); + save_item(NAME(m_crc)); + save_item(NAME(m_fifo)); + save_item(NAME(m_fifo_pos)); + save_item(NAME(m_error)); + save_item(NAME(m_half_cycles_before_change)); + save_item(NAME(m_current_bit)); + save_item(NAME(m_tss_sr)); + save_item(NAME(m_tss_output)); + save_item(NAME(m_sr)); + save_item(NAME(m_mfm_sync_counter)); + save_item(NAME(m_flux_write_start)); + save_item(NAME(m_flux_write)); + save_item(NAME(m_flux_write_count)); +} + +void swim2_device::device_reset() +{ + applefdintf_device::device_reset(); + + m_mode = 0x40; + m_setup = 0x00; + m_param_idx = 0; + memset(m_param, 0, sizeof(m_param)); + m_floppy = nullptr; + m_crc = 0xffff; + m_fifo[0] = m_fifo[1] = 0; + m_fifo_pos = 0; + m_error = 0; + m_half_cycles_before_change = 0; + m_current_bit = 0; + m_tss_sr = 0; + m_tss_output = 0; + m_sr = 0; + m_mfm_sync_counter = 0; + m_devsel_cb(0); + m_sel35_cb(true); + m_hdsel_cb(false); + m_dat1byte_cb(CLEAR_LINE); + m_flux_write_start = 0; + m_flux_write_count = 0; + std::fill(m_flux_write.begin(), m_flux_write.end(), 0); + + m_last_sync = machine().time().as_ticks(clock()); +} + +void swim2_device::set_floppy(floppy_image_device *floppy) +{ + if(m_floppy == floppy) + return; + + sync(); + flush_write(); + + m_floppy = floppy; + update_phases(); + m_hdsel_cb((m_mode >> 5) & 1); +} + +floppy_image_device *swim2_device::get_floppy() const +{ + return m_floppy; +} + +void swim2_device::flush_write(u64 when) +{ + if(!m_flux_write_start) + return; + + if(!when) + when = m_last_sync; + + if(m_floppy && 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]); + 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 swim2_device::show_mode() const +{ + LOGMODE("mode%s hdsel=%c %c%s %c%c%s\n", + m_mode & 0x80 ? " motoron" : "", + m_mode & 0x20 ? '1' : '0', + m_mode & 0x10 ? 'w' : 'r', + m_mode & 0x08 ? " action" : "", + m_mode & 0x04 ? 'b' : '-', + m_mode & 0x02 ? 'a' : '-', + m_mode & 0x01 ? " clear" : ""); + +} + +u8 swim2_device::read(offs_t offset) +{ + sync(); + + static const char *const names[] = { + "data", "mark", "crc", "param", "phases", "setup", "status", "handshake" + }; + switch(offset & 7) { + case 0x0: { // data + u16 r = fifo_pop(); + if(!m_error) { + if(r == 0xffff) + m_error |= 4; + else if(r & M_MARK) + m_error |= 2; + } + return r; + } + + case 0x1: { // mark + u16 r = fifo_pop(); + if(!m_error && r == 0xffff) + m_error |= 4; + return r; + } + + case 0x2: { // error + u8 err = m_error; + m_error = 0; + return err; + } + + case 0x3: { // param + u8 r = m_param[m_param_idx]; + m_param_idx = (m_param_idx + 1) & 3; + return r; + } + + case 0x4: // phases + return m_phases; + + case 0x5: // setup + return m_setup; + + case 0x6: // mode + return m_mode; + + case 0x7: { // handshake + u8 h = 0; + if(m_fifo_pos > 0) { + if(m_fifo[m_fifo_pos - 1] & M_MARK) + h |= 0x01; + if(!(m_fifo[m_fifo_pos - 1] & M_CRC0)) + h |= 0x02; + } + // rddata on 4 + if(!m_floppy || m_floppy->wpt_r()) + h |= 0x08; + if(m_error) + h |= 0x20; + if(m_mode & 0x10) { + // write + if(m_fifo_pos == 0) + h |= 0xc0; + else if(m_fifo_pos == 1) + h |= 0x80; + } else { + // read + if(m_fifo_pos == 2) + h |= 0xc0; + else if(m_fifo_pos == 1) + h |= 0x80; + } + return h; + } + + default: + LOG("read %s\n", names[offset & 7]); + break; + } + return 0xff; +} + +void swim2_device::write(offs_t offset, u8 data) +{ + sync(); + + u8 prev_mode = m_mode; + + static const char *const names[] = { + "data", "mark", "crc", "param", "phases", "setup", "mode0", "mode1", + }; + + switch(offset & 7) { + case 0: + if(fifo_push(data) && !m_error) + m_error |= 0x04; + break; + + case 1: + if(fifo_push(M_MARK | data) && !m_error) + m_error |= 0x04; + break; + + case 2: + if(fifo_push(M_CRC) && !m_error) + m_error |= 0x04; + break; + + case 3: { // param + static const char *const pname[4] = { + "late", "time0", "early", "time1" + }; + LOG("param[%s] = %02x\n", pname[m_param_idx], data); + m_param[m_param_idx] = data; + m_param_idx = (m_param_idx + 1) & 3; + break; + } + case 4: { // phases + m_phases = data; + update_phases(); + break; + } + + case 5: // setup + m_setup = data; + m_sel35_cb((m_setup >> 1) & 1); + LOGSETUP("setup write=%s %s test=%s %s %s 3.5=%s %s\n", + m_setup & 0x40 ? "gcr" : "mfm", + m_setup & 0x20 ? "ibm" : "apple", + m_setup & 0x10 ? "on" : "off", + m_setup & 0x08 ? "fclk/2" : "fclk", + m_setup & 0x04 ? "gcr" : "mfm", + m_setup & 0x02 ? "off" : "on", + m_setup & 0x01 ? "wrinvert" : "wrdirect"); + break; + + case 6: // mode clear + m_mode &= ~data; + m_mode |= 0x40; + m_param_idx = 0; + show_mode(); + update_dat1byte(); + break; + + case 7: // mode set + m_mode |= data; + show_mode(); + update_dat1byte(); + break; + + default: + LOG("write %s, %02x\n", names[offset & 7], data); + break; + } + + if(m_mode & 0x01) + fifo_clear(); + + if((m_mode ^ prev_mode) & 0x86) + m_devsel_cb(m_mode & 0x80 ? (m_mode >> 1) & 3 : 0); + if((m_mode ^ prev_mode) & 0x20) + m_hdsel_cb((m_mode >> 5) & 1); + + if((m_mode & 0x18) == 0x18 && ((prev_mode & 0x18) != 0x18)) { + // Entering write mode + m_current_bit = 0; + LOG("%s write start %s %s floppy=%p\n", machine().time().to_string(), m_setup & 0x40 ? "gcr" : "mfm", m_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_mode & 0x18) != 0x18) { + // Exiting write mode + flush_write(); + m_flux_write_start = 0; + m_current_bit = 0xff; + m_half_cycles_before_change = 0; + LOG("%s write end\n", machine().time().to_string()); + } + + if((m_mode & 0x18) == 0x08 && ((prev_mode & 0x18) != 0x08)) { + // Entering read mode + m_current_bit = 0; + m_sr = 0; + m_mfm_sync_counter = 0; + LOG("%s read start %s %s floppy=%p\n", machine().time().to_string(), m_setup & 0x04 ? "gcr" : "mfm", m_setup & 0x08 ? "fclk/2" : "fclk", m_floppy); + + m_pll.reset(machine().time()); + static const int cycles_per_cell[4] = { 16, 31, 31, 63 }; + + m_pll.set_clock(attotime::from_ticks(cycles_per_cell[(m_setup >> 2) & 3], clock())); + LOG("PLL read clock %s\n", attotime::from_ticks(cycles_per_cell[(m_setup >> 2) & 3], clock()).to_string()); + + } else if((prev_mode & 0x18) == 0x08 && (m_mode & 0x18) != 0x08) { + // Exiting read mode + flush_write(); + m_current_bit = 0xff; + m_half_cycles_before_change = 0; + LOG("%s read end\n", machine().time().to_string()); + } +} + +void swim2_device::crc_clear() +{ + m_crc = 0xcdb4; +} + +void swim2_device::crc_update(int bit) +{ + if((m_crc ^ (bit ? 0x8000 : 0x0000)) & 0x8000) + m_crc = (m_crc << 1) ^ 0x1021; + else + m_crc = m_crc << 1; + +} + +u64 swim2_device::time_to_cycles(const attotime &tm) const +{ + return tm.as_ticks(clock()); +} + +attotime swim2_device::cycles_to_time(u64 cycles) const +{ + return attotime::from_ticks(cycles, clock()); +} + +void swim2_device::fifo_clear() +{ + m_fifo_pos = 0; + update_dat1byte(); + crc_clear(); +} + +bool swim2_device::fifo_push(u16 data) +{ + if(m_fifo_pos == 2) + return true; + m_fifo[m_fifo_pos ++] = data; + update_dat1byte(); + return false; +} + +u16 swim2_device::fifo_pop() +{ + if(m_fifo_pos == 0) + return 0xffff; + u16 r = m_fifo[0]; + m_fifo[0] = m_fifo[1]; + m_fifo_pos --; + update_dat1byte(); + return r; +} + +void swim2_device::sync() +{ + u64 next_sync = time_to_cycles(machine().time()); + if(!(m_mode & 0x08)) { + m_last_sync = next_sync; + return; + } + + if(m_mode & 0x10) { + // We count in half-cycles but only toggle write on full cycles + u32 cycles = (next_sync - m_last_sync) << 1; + + // Write mode + while(cycles) { + if(m_half_cycles_before_change) { + if(cycles >= m_half_cycles_before_change) { + cycles -= m_half_cycles_before_change; + m_half_cycles_before_change = 0; + } else { + m_half_cycles_before_change -= cycles; + cycles = 0; + break; + } + } + + if(m_tss_output & 0xc) { + bool bit; + if(m_tss_output & 8) { + bit = (m_tss_output >> 1) & 1; + m_tss_output &= ~0xa; + } else { + bit = m_tss_output & 1; + m_tss_output = 0; + } + if(bit) { + if(m_flux_write_count == m_flux_write.size()) + flush_write(next_sync - (cycles >> 1)); + m_flux_write[m_flux_write_count ++] = next_sync - (cycles >> 1); + m_half_cycles_before_change = 63; + } else + m_half_cycles_before_change = m_setup & 0x40 ? 63 : 31; + if(m_setup & 8) + m_half_cycles_before_change <<= 1; + continue; + } + if(m_current_bit == 0xff) + fatalerror("Sequence break on write\n"); + + if(m_current_bit == 0) { + if(m_sr & M_CRC) + m_sr = m_crc >> 8; + else { + u16 r = fifo_pop(); + if(r == 0xffff && !m_error) { + m_error |= 0x01; + flush_write(); + m_current_bit = 0xff; + m_half_cycles_before_change = 0; + m_mode &= ~8; + logerror("write end on underrun\n"); + break; + } + if(r & M_CRC) + m_sr = M_CRC | (m_crc >> 8); + else + m_sr = r & (M_MARK | M_CRC | 0xff); + } + m_current_bit = 8; + if(m_sr & M_MARK) + crc_clear(); + } + m_current_bit --; + int bit = (m_sr >> m_current_bit) & 1; + if(!(m_sr & M_MARK)) + crc_update(bit); + m_tss_sr = (m_tss_sr << 1) | bit; + if(m_setup & 0x40) + m_tss_output = 4 | bit; + else { + static const u8 tss[4] = { 5, 0xd, 4, 5 }; + if((m_sr & M_MARK) && ((m_tss_sr & 0xf) == 8)) + m_tss_output = 0xc; + else + m_tss_output = tss[m_tss_sr & 3]; + } + + continue; + } + } else { + attotime limit = machine().time(); + if(m_setup & 0x04) { + // GCR mode + for(;;) { + attotime when; + int bit = m_pll.get_next_bit(when, m_floppy, limit); + if(bit == -1) + break; + m_sr = ((m_sr << 1) | bit) & 0xff; + if(m_sr & 0x80) { + if(fifo_push(m_sr) && !m_error) + m_error |= 0x01; + m_sr = 0; + } + } + } else { + // MFM mode + for(;;) { + attotime when; + int bit = m_pll.get_next_bit(when, m_floppy, limit); + if(bit == -1) + break; + if(m_mfm_sync_counter < 64) { + if(bit != (m_mfm_sync_counter & 1)) + m_mfm_sync_counter ++; + else + m_mfm_sync_counter = 0; + } else { + if(m_mfm_sync_counter == 64 && bit) + m_mfm_sync_counter --; + else { + if(m_mfm_sync_counter == 65 || m_mfm_sync_counter == 81) { + m_tss_sr = 0xff; + m_sr = 0; + } + if(m_mfm_sync_counter & 1) { + m_sr |= bit << (((96 - m_mfm_sync_counter) >> 1) & 7); + crc_update(bit); + } + m_tss_sr = (m_tss_sr << 1) | bit; + if((m_tss_sr & 0xf) == 1 && !(m_mfm_sync_counter & 1)) + m_sr |= M_MARK; + + m_mfm_sync_counter ++; + if(m_mfm_sync_counter == 80) { + if(!(m_sr & M_MARK)) + m_mfm_sync_counter = 0; + else { + crc_clear(); + if(fifo_push(m_sr) && !m_error) + m_error |= 0x01; + } + } else if(m_mfm_sync_counter == 96) { + m_mfm_sync_counter -= 16; + if(m_sr & M_MARK) + crc_clear(); + else if(!m_crc) + m_sr |= M_CRC0; + if(fifo_push(m_sr) && !m_error) + m_error |= 0x01; + } + } + } + } + } + } + + m_last_sync = next_sync; +} + +void swim2_device::update_dat1byte() +{ + if (m_mode & 0x10) + { + // write: Does FIFO have room? + m_dat1byte_cb((m_fifo_pos < 2) ? ASSERT_LINE : CLEAR_LINE); + } + else + { + // read: is FIFO not empty? + m_dat1byte_cb((m_fifo_pos > 0) ? ASSERT_LINE : CLEAR_LINE); + } +} |