Files
VirtualC64-Core/C64/IEC.cpp
T
2015-11-18 00:34:41 -06:00

390 lines
10 KiB
C++
Executable File

/*
* (C) 2006 Dirk W. Hoffmann. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "C64.h"
IEC::IEC()
{
name = "IEC";
debug(2, " Creating IEC bus at address %p...\n", this);
// Register snapshot items
SnapshotItem items[] = {
{ &driveConnected, sizeof(driveConnected), CLEAR_ON_RESET },
{ &atnLine, sizeof(atnLine), CLEAR_ON_RESET },
{ &oldAtnLine, sizeof(oldAtnLine), CLEAR_ON_RESET },
{ &clockLine, sizeof(clockLine), CLEAR_ON_RESET },
{ &oldClockLine, sizeof(oldClockLine), CLEAR_ON_RESET },
{ &dataLine, sizeof(dataLine), CLEAR_ON_RESET },
{ &oldDataLine, sizeof(oldDataLine), CLEAR_ON_RESET },
{ &deviceAtnPin, sizeof(deviceAtnPin), CLEAR_ON_RESET },
{ &deviceAtnIsOutput, sizeof(deviceAtnIsOutput), CLEAR_ON_RESET },
{ &deviceDataPin, sizeof(deviceDataPin), CLEAR_ON_RESET },
{ &deviceDataIsOutput, sizeof(deviceDataIsOutput), CLEAR_ON_RESET },
{ &deviceClockPin, sizeof(deviceClockPin), CLEAR_ON_RESET },
{ &deviceClockIsOutput, sizeof(deviceClockIsOutput), CLEAR_ON_RESET },
{ &ciaDataPin, sizeof(ciaDataPin), CLEAR_ON_RESET },
{ &ciaDataIsOutput, sizeof(ciaDataIsOutput), CLEAR_ON_RESET },
{ &ciaClockPin, sizeof(ciaClockPin), CLEAR_ON_RESET },
{ &ciaClockIsOutput, sizeof(ciaClockIsOutput), CLEAR_ON_RESET },
{ &ciaAtnPin, sizeof(ciaAtnPin), CLEAR_ON_RESET },
{ &ciaAtnIsOutput, sizeof(ciaAtnIsOutput), CLEAR_ON_RESET },
{ &busActivity, sizeof(busActivity), CLEAR_ON_RESET },
{ NULL, 0, 0 }};
registerSnapshotItems(items, sizeof(items));
}
IEC::~IEC()
{
debug(2, " Releasing IEC bus...\n");
}
void
IEC::reset()
{
VirtualComponent::reset();
// Establish bindings
drive = c64->floppy;
driveConnected = 1;
atnLine = 1;
oldAtnLine = 1;
clockLine = 1;
oldClockLine = 1;
dataLine = 1;
oldDataLine = 1;
deviceDataPin = 1;
deviceClockPin = 1;
ciaDataPin = 1;
ciaDataIsOutput = 1;
ciaClockPin = 1;
ciaClockIsOutput = 1;
ciaAtnPin = 1;
ciaAtnIsOutput = 1;
setDeviceClockPin(1);
setDeviceDataPin(1);
}
void
IEC::ping()
{
drive->c64->putMessage(MSG_VC1541_ATTACHED, driveConnected);
drive->c64->putMessage(MSG_VC1541_DATA, busActivity > 0);
}
void
IEC::dumpState()
{
msg("IEC bus\n");
msg("-------\n");
msg("\n");
dumpTrace();
msg("\n");
msg("Drive connected : %s\n", driveConnected ? "yes" : "no");
msg(" old ATN : %d\n", oldAtnLine);
msg(" old CLK : %d\n", oldClockLine);
msg(" old DATA : %d\n", oldDataLine);
msg("\n");
}
void
IEC::dumpTrace()
{
debug(1, "ATN: %s[%s%s%s%s] CLK: %s[%s%s%s%s] DATA: %s[%s%s%s%s]\n",
atnLine ? "1 F" : "0 T",
deviceAtnPin ? "1" : "0",
deviceAtnIsOutput ? "<-" : "->",
ciaAtnPin ? "1" : "0",
ciaAtnIsOutput ? "<-" : "->",
clockLine ? "1 F" : "0 T",
deviceClockPin ? "1" : "0",
deviceClockIsOutput ? "<-" : "->",
ciaClockPin ? "1" : "0",
ciaClockIsOutput ? "<-" : "->",
dataLine ? "1 F" : "0 T",
deviceDataPin ? "1" : "0",
deviceDataIsOutput ? "<-" : "->",
ciaDataPin ? "1" : "0",
ciaDataIsOutput ? "<-" : "->");
}
void
IEC::connectDrive()
{
driveConnected = true;
drive->c64->putMessage(MSG_VC1541_ATTACHED, 1);
if (drive->soundMessagesEnabled())
drive->c64->putMessage(MSG_VC1541_ATTACHED_SOUND, 1);
}
void
IEC::disconnectDrive()
{
driveConnected = false;
drive->c64->putMessage(MSG_VC1541_ATTACHED, 0);
if (drive->soundMessagesEnabled())
drive->c64->putMessage(MSG_VC1541_ATTACHED_SOUND, 0);
}
bool IEC::_updateIecLines(bool *atnedge)
{
// save current values
oldAtnLine = atnLine;
oldClockLine = clockLine;
oldDataLine = dataLine;
// determine new values
atnLine = 1;
if (ciaAtnIsOutput) atnLine &= ciaAtnPin;
clockLine = 1;
if (ciaClockIsOutput) clockLine &= ciaClockPin;
if (driveConnected && deviceClockIsOutput) clockLine &= deviceClockPin;
dataLine = 1;
if (ciaDataIsOutput) dataLine &= ciaDataPin;
if (driveConnected && deviceDataIsOutput) dataLine &= deviceDataPin;
// Note: The device atn pin is not connected to the ATN line.
// It implements an auto acknowledge feature. When set to 1, the ATN signal
// is automatically acknowledged by the drive. This feature allows the C64
// to detect a connected drive without any interaction by the drive itself.
if (driveConnected && deviceAtnPin == 1)
dataLine &= atnLine;
// Check atn line for a negative edge
if (atnedge != NULL)
*atnedge = (oldAtnLine == 1 && atnLine == 0);
// did any signal change its value?
return (oldAtnLine != atnLine || oldClockLine != clockLine || oldDataLine != dataLine);
}
void IEC::updateIecLines()
{
bool signals_changed;
bool atn_edge;
// Update port lines
signals_changed = _updateIecLines(&atn_edge);
// Check if ATN edge occurred
if (atn_edge) {
drive->simulateAtnInterrupt();
}
if (signals_changed) {
if (busActivity == 0) {
// Bus activity detected
drive->c64->putMessage(MSG_VC1541_DATA, 1);
drive->c64->setWarp(drive->c64->getAlwaysWarp() || drive->c64->getWarpLoad());
}
busActivity = 30;
}
if (signals_changed && tracingEnabled()) {
dumpTrace();
}
}
void IEC::updateCiaPins(uint8_t cia_data, uint8_t cia_direction)
{
// Note: On the pyhsical pins, 0 is dominant.
// I.e., a single 0-source will bring the signal down to 0
ciaAtnIsOutput = (cia_direction & 0x08) ? 1 : 0;
ciaClockIsOutput = (cia_direction & 0x10) ? 1 : 0;
ciaDataIsOutput = (cia_direction & 0x20) ? 1 : 0;
ciaAtnPin = (cia_data & 0x08) ? 0 : 1; // Pin and line are connected via an inverter
ciaClockPin = (cia_data & 0x10) ? 0 : 1; // Pin and line are connected via an inverter
ciaDataPin = (cia_data & 0x20) ? 0 : 1; // Pin and line are connected via an inverter
updateIecLines();
}
void IEC::updateDevicePins(uint8_t device_data, uint8_t device_direction)
{
// Note: On the pyhsical pins, 0 is dominant.
// I.e., a single 0-source will bring the signal down to 0
deviceAtnIsOutput = (device_direction & 0x10) ? 1 : 0;
deviceClockIsOutput = (device_direction & 0x08) ? 1 : 0;
deviceDataIsOutput = (device_direction & 0x02) ? 1 : 0;
deviceAtnPin = (device_data & 0x10) ? 0 : 1; // Pin and line are connected via an inverter
deviceClockPin = (device_data & 0x08) ? 0 : 1; // Pin and line are connected via an inverter
deviceDataPin = (device_data & 0x02) ? 0 : 1; // Pin and line are connected via an inverter
updateIecLines();
}
void IEC::execute()
{
if (busActivity > 0) {
busActivity--;
if (busActivity == 0) {
// Bus is idle
drive->c64->putMessage(MSG_VC1541_DATA, 0);
drive->c64->setWarp(drive->c64->getAlwaysWarp());
}
}
}
// -------------------------------------------------------------------
// Fast loader
// -------------------------------------------------------------------
uint8_t IEC::IECOutATN(uint8_t byte)
{
// The upper four bits contain the command
// -01- : LISTEN
// -10- : TALK
// ---0 : on
// ---1 : off
// The lower four bits contain the device number
switch (byte >> 4) {
case 2: /* LISTEN */
debug(2, "Device %d is now listening\n", byte & 0x0F);
if ((byte & 0x0F) == 8) { // We only support device number 8
listening = true;
filename[0] = 0;
return IEC_OK;
} else {
listening = false;
return IEC_NOTPRESENT;
}
case 3: /* UNLISTEN */
debug(2, "No longer listening\n");
listening = false;
return IEC_OK;
case 4: /* TALK */
debug(2, "Device %d is now listening\n", byte & 0x0F);
if ((byte & 0x0F) == 8) { // We only support device number 8
talking = true;
return IEC_OK;
} else {
talking = false;
return IEC_NOTPRESENT;
}
case 5: /* UNTALK */
debug(2, "No longer talking\n");
talking = false;
return IEC_OK;
}
return IEC_TIMEOUT;
}
uint8_t IEC::IECOutSec(uint8_t byte)
{
// byte: xxxx---- : Command to execute
// ----xxxx : Secondary address
if (listening) {
return IECOutSecWhileListening(byte);
}
if (talking) {
return IECOutSecWhileTalking(byte);
}
return IEC_TIMEOUT;
}
uint8_t IEC::IECOutSecWhileListening(uint8_t byte)
{
command = (byte >> 4);
secondary = (byte & 0xF);
switch (command) {
case IEC_CMD_OPEN:
debug(2, "Received command: OPEN\n");
return IEC_OK;
case IEC_CMD_CLOSE:
debug(2, "Received command: CLOSE\n");
// Turn on LED
return IEC_OK;
}
return IEC_OK;
}
uint8_t IEC::IECOutSecWhileTalking(uint8_t byte)
{
command = (byte >> 4);
secondary = (byte & 0xF);
return IEC_OK;
}
uint8_t IEC::IECOut(uint8_t byte, bool eoi)
{
char tmp[2] = { byte, 0 };
strncat(filename, tmp, 16);
if (eoi) {
printf("Filename: ");
for (unsigned i = 0; i < strlen(filename); i++)
printf("%c", pet2ascii(filename[i]));
printf("\n");
}
return IEC_OK;
}
uint8_t IEC::IECIn(uint8_t *byte)
{
*byte = 42;
return IEC_OK;
}
void IEC::IECSetATN()
{
}
void IEC::IECRelATN()
{
}
void IEC::IECTurnaround()
{
}
void IEC::IECRelease()
{
}