diff options
Diffstat (limited to 'src/mame/machine/applefdc.cpp')
-rw-r--r-- | src/mame/machine/applefdc.cpp | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/src/mame/machine/applefdc.cpp b/src/mame/machine/applefdc.cpp new file mode 100644 index 00000000000..dac524308ba --- /dev/null +++ b/src/mame/machine/applefdc.cpp @@ -0,0 +1,586 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods, Raphael Nabet, R. Belmont +/********************************************************************* + + applefdc.c + + Implementation of various Apple Floppy Disk Controllers, including + the classic Apple controller and the IWM (Integrated Woz Machine) + chip + + The IWM chip was used as the floppy disk controller for early Macs and the + Apple IIgs, and was eventually superseded by the SWIM chp. + + Nate Woods + Raphael Nabet + + Writing this code would not be possible if it weren't for the work of the + XGS and KEGS emulators which also contain IWM emulations. + + TODO + - Implement the unimplemented IWM modes + - IWM_MODE_CLOCKSPEED + - IWM_MODE_BITCELLTIME + - IWM_MODE_HANDSHAKEPROTOCOL + - IWM_MODE_LATCHMODE + - Investigate the differences between the IWM and the classic Apple II + controller more fully. It is currently unclear what are genuine + differences and what are effectively hacks that "just seem" to work. + - Figure out iwm_readenable2handshake() and iwm_enable2(); they are + hackish at best + - Support the SWIM chip + - Proper timing + - This code was originally IWM specific; we need to clean up IWMisms in + the code + - Make it faster? + - Add sound? + +*********************************************************************/ + +#include "applefdc.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +// logging +#define LOG_APPLEFDC 0 +#define LOG_APPLEFDC_EXTRA 0 + +// mask for FDC lines +#define IWM_MOTOR 0x10 +#define IWM_DRIVE 0x20 +#define IWM_Q6 0x40 +#define IWM_Q7 0x80 + +const device_timer_id TIMER_MOTOR_ONOFF = 1; + + + + + +/*************************************************************************** + IWM MODE + + The IWM mode has the following values: + + Bit 7 Reserved + Bit 6 Reserved + Bit 5 Reserved + Bit 4 ! Clock speed + 0=7MHz; used by Apple IIgs + 1=8MHz; used by Mac (I believe) + Bit 3 ! Bit cell time + 0=4usec/bit (used for 5.25" drives) + 1=2usec/bit (used for 3.5" drives) + Bit 2 Motor-off delay + 0=leave on for 1 sec after system turns it off + 1=turn off immediately + Bit 1 ! Handshake protocol + 0=synchronous (software supplies timing for writing data; used for 5.25" drives) + 1=asynchronous (IWM supplies timing; used for 3.5" drives) + Bit 0 ! Latch mode + 0=read data stays valid for 7usec (used for 5.25" drives) + 1=read data stays valid for full byte time (used for 3.5" drives) + + ***************************************************************************/ + +enum +{ + IWM_MODE_CLOCKSPEED = 0x10, + IWM_MODE_BITCELLTIME = 0x08, + IWM_MODE_MOTOROFFDELAY = 0x04, + IWM_MODE_HANDSHAKEPROTOCOL = 0x02, + IWM_MODE_LATCHMODE = 0x01 +}; + + + +/*************************************************************************** + BASE DEVICE +***************************************************************************/ + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +applefdc_base_device::applefdc_base_device(applefdc_base_device::applefdc_t fdc_type, const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source) +{ + m_type = fdc_type; +} + + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void applefdc_base_device::device_start() +{ + // timer + m_motor_timer = timer_alloc(TIMER_MOTOR_ONOFF); + + // state + m_write_byte = 0x00; + m_lines = 0x00; + m_mode = 0x1F; // default value needed by Lisa 2 - no, I don't know if it is true + m_handshake_hack = 0x00; + + // register save states + save_item(NAME(m_write_byte)); + save_item(NAME(m_lines)); + save_item(NAME(m_mode)); + save_item(NAME(m_handshake_hack)); +} + + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void applefdc_base_device::device_reset(void) +{ + m_handshake_hack = 0x00; + m_write_byte = 0x00; + m_lines = 0x00; + m_mode = 0x1F; /* default value needed by Lisa 2 - no, I don't know if it is true */ + m_motor_timer->reset(); +} + + + +//------------------------------------------------- +// device_timer - device-specific timer callbacks +//------------------------------------------------- + +void applefdc_base_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch(id) + { + case TIMER_MOTOR_ONOFF: + turn_motor_onoff(param != 0); + break; + } +} + + + +//------------------------------------------------- +// get_interface - gets the interface +//------------------------------------------------- + +const applefdc_interface *applefdc_base_device::get_interface() +{ + static const applefdc_interface dummy_interface = {0, }; + + return (static_config() != NULL) + ? (const applefdc_interface *) static_config() + : &dummy_interface; +} + + + +//------------------------------------------------- +// iwm_enable2 - hackish function +//------------------------------------------------- + +int applefdc_base_device::iwm_enable2() +{ + /* R. Nabet : This function looks more like a hack than a real feature of the IWM; */ + /* it is not called from the Mac Plus driver */ + return (m_lines & APPLEFDC_PH1) && (m_lines & APPLEFDC_PH3); +} + + + +//------------------------------------------------- +// iwm_readenable2handshake - hackish function +//------------------------------------------------- + +UINT8 applefdc_base_device::iwm_readenable2handshake() +{ + /* R. Nabet : This function looks more like a hack than a real feature of the IWM; */ + /* it is not called from the Mac Plus driver */ + m_handshake_hack++; + m_handshake_hack %= 4; + return (m_handshake_hack != 0) ? 0xc0 : 0x80; +} + + + +//------------------------------------------------- +// statusreg_r - reads the status register +//------------------------------------------------- + +UINT8 applefdc_base_device::statusreg_r() +{ + UINT8 result; + int status; + const applefdc_interface *intf = get_interface(); + + /* IWM status: + * + * Bit 7 Sense input (write protect for 5.25" drive and general status line for 3.5") + * Bit 6 Reserved + * Bit 5 Drive enable (is 1 if drive is on) + * Bits 4-0 Same as IWM mode bits 4-0 + */ + + status = iwm_enable2() ? 1 : (intf->read_status ? intf->read_status(this) : 0); + + result = (status ? 0x80 : 0x00); + + if (m_type != APPLEFDC_APPLE2) + result |= (((m_lines & IWM_MOTOR) ? 1 : 0) << 5) | m_mode; + return result; +} + + + +//------------------------------------------------- +// iwm_modereg_w - changes the mode register +//------------------------------------------------- + +void applefdc_base_device::iwm_modereg_w(UINT8 data) +{ + m_mode = data & 0x1f; /* write mode register */ + + if (LOG_APPLEFDC_EXTRA) + logerror("iwm_modereg_w: iwm_mode=0x%02x\n", (unsigned) m_mode); +} + + + +//------------------------------------------------- +// read_reg - reads a register +//------------------------------------------------- + +UINT8 applefdc_base_device::read_reg(int lines) +{ + const applefdc_interface *intf = get_interface(); + UINT8 result = 0; + + switch(lines) + { + case 0: + // read data register + if ((m_type != APPLEFDC_APPLE2) && (iwm_enable2() || !(m_lines & IWM_MOTOR))) + { + result = 0xFF; + } + else + { + // Right now, this function assumes latch mode; which is always used for + // 3.5 inch drives. Eventually we should check to see if latch mode is + // off + if (LOG_APPLEFDC) + { + if ((m_mode & IWM_MODE_LATCHMODE) == 0x00) + logerror("applefdc_read_reg(): latch mode off not implemented\n"); + } + + result = (intf->read_data ? intf->read_data(this) : 0x00); + } + break; + + case IWM_Q6: + // read status register + result = statusreg_r(); + break; + + case IWM_Q7: + // Classic Apple II: Read status register + // IWM: Read handshake register + if (m_type == APPLEFDC_APPLE2) + result = statusreg_r(); + else + result = iwm_enable2() ? iwm_readenable2handshake() : 0x80; + break; + } + return result; +} + + + +//------------------------------------------------- +// write_reg - writes a register +//------------------------------------------------- + +void applefdc_base_device::write_reg(UINT8 data) +{ + const applefdc_interface *intf = get_interface(); + + switch(m_lines & (IWM_Q6 | IWM_Q7)) + { + case IWM_Q6 | IWM_Q7: + if (!(m_lines & IWM_MOTOR)) + { + iwm_modereg_w(data); + } + else if (!iwm_enable2()) + { + // Right now, this function assumes latch mode; which is always used for + // 3.5 inch drives. Eventually we should check to see if latch mode is + // off + if (LOG_APPLEFDC) + { + if ((m_mode & IWM_MODE_LATCHMODE) == 0) + logerror("applefdc_write_reg(): latch mode off not implemented\n"); + } + + if (intf->write_data != NULL) + intf->write_data(this, data); + } + break; + } +} + + + +//------------------------------------------------- +// turn_motor_onoff - timer callback for turning +// motor on or off +//------------------------------------------------- + +void applefdc_base_device::turn_motor_onoff(bool status) +{ + const applefdc_interface *intf = get_interface(); + int enable_lines; + + if (status) + { + m_lines |= IWM_MOTOR; + enable_lines = (m_lines & IWM_DRIVE) ? 2 : 1; + } + else + { + m_lines &= ~IWM_MOTOR; + + if (m_type == APPLEFDC_APPLE2) + enable_lines = (m_lines & IWM_DRIVE) ? 2 : 1; + else + enable_lines = 0; + } + + /* invoke callback, if present */ + if (intf->set_enable_lines != NULL) + intf->set_enable_lines(this, enable_lines); + + if (LOG_APPLEFDC_EXTRA) + logerror("iwm_turnmotor_onoff(): Turning motor %s\n", status ? "on" : "off"); +} + + + +//------------------------------------------------- +// iwm_access +//------------------------------------------------- + +void applefdc_base_device::iwm_access(int offset) +{ + static const char *const lines[] = + { + "PH0", + "PH1", + "PH2", + "PH3", + "MOTOR", + "DRIVE", + "Q6", + "Q7" + }; + + const applefdc_interface *intf = get_interface(); + + if (offset & 1) + m_lines |= (1 << (offset >> 1)); + else + m_lines &= ~(1 << (offset >> 1)); + + if (LOG_APPLEFDC_EXTRA) + { + logerror("iwm_access(): %s line %s => %02x\n", + (offset & 1) ? "setting" : "clearing", lines[offset >> 1], m_lines); + } + + if ((offset < 0x08) && (intf->set_lines != NULL)) + intf->set_lines(this, m_lines & 0x0f); + + switch(offset) + { + case 0x08: + /* turn off motor */ + m_motor_timer->adjust( + (m_mode & IWM_MODE_MOTOROFFDELAY) ? attotime::zero : attotime::from_seconds(1), 0); + break; + + case 0x09: + /* turn on motor */ + m_motor_timer->adjust(attotime::zero, 1); + break; + + case 0x0A: + /* turn off IWM_DRIVE */ + if ((m_lines & IWM_MOTOR) && (intf->set_enable_lines != NULL)) + intf->set_enable_lines(this, 1); + break; + + case 0x0B: + /* turn on IWM_DRIVE */ + if ((m_lines & IWM_MOTOR) && (intf->set_enable_lines != NULL)) + intf->set_enable_lines(this, 2); + break; + } +} + + + +//------------------------------------------------- +// read - reads a byte from the FDC +//------------------------------------------------- + +UINT8 applefdc_base_device::read(UINT8 offset) +{ + const applefdc_interface *intf = get_interface(); + UINT8 result = 0; + + // normalize offset + offset &= 0xf; + + if (LOG_APPLEFDC_EXTRA) + logerror("applefdc_r: offset=%i\n", offset); + + iwm_access(offset); + + switch(m_type) + { + case APPLEFDC_APPLE2: + switch(offset) + { + case 0x0C: + if (m_lines & IWM_Q7) + { + if (intf->write_data != NULL) + intf->write_data(this, m_write_byte); + result = 0; + } + else + result = read_reg(0); + break; + + case 0x0D: + result = read_reg(IWM_Q6); + break; + + case 0x0E: + result = read_reg(IWM_Q7); + break; + + case 0x0F: + result = read_reg(IWM_Q7 | IWM_Q6); + break; + } + break; + + case APPLEFDC_IWM: + if ((offset & 1) == 0) + result = read_reg(m_lines & (IWM_Q6 | IWM_Q7)); + break; + + case APPLEFDC_SWIM: + if ((offset & 1) == 0) + result = read_reg(m_lines & (IWM_Q6 | IWM_Q7)); + break; + } + + return result; +} + + + +//------------------------------------------------- +// write - writes a byte to the FDC +//------------------------------------------------- + +void applefdc_base_device::write(UINT8 offset, UINT8 data) +{ + const applefdc_interface *intf = get_interface(); + + /* normalize offset */ + offset &= 15; + + if (LOG_APPLEFDC_EXTRA) + logerror("applefdc_w: offset=%i data=0x%02x\n", offset, data); + + iwm_access(offset); + + switch(m_type) + { + case APPLEFDC_APPLE2: + switch(offset) + { + case 0x0C: + if (m_lines & IWM_Q7) + { + if (intf->write_data != NULL) + intf->write_data(this, m_write_byte); + } + break; + + case 0x0D: + m_write_byte = data; + break; + } + break; + + case APPLEFDC_IWM: + if (offset & 1) + write_reg(data); + break; + + case APPLEFDC_SWIM: + if (offset & 1) + write_reg(data); + break; + } +} + + + +//------------------------------------------------- +// get_lines - accessor +//------------------------------------------------- + +UINT8 applefdc_base_device::get_lines() +{ + return m_lines & 0x0f; +} + + + +/*************************************************************************** + APPLE FDC - Used on Apple II +***************************************************************************/ + +const device_type APPLEFDC = &device_creator<applefdc_device>; + +applefdc_device::applefdc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : applefdc_base_device(APPLEFDC_APPLE2, mconfig, APPLEFDC, "Apple FDC", tag, owner, clock, "apple_fdc", __FILE__) +{ +} + + + +/*************************************************************************** + IWM - Used on early Macs +***************************************************************************/ + +const device_type IWM = &device_creator<iwm_device>; + +iwm_device::iwm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : applefdc_base_device(APPLEFDC_IWM, mconfig, IWM, "Apple IWM (Integrated Woz Machine)", tag, owner, clock, "iwm", __FILE__) +{ +} |