390 lines
10 KiB
C++
Executable File
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()
|
|
{
|
|
|
|
}
|
|
|