Main Page | Class Hierarchy | Compound List | File List | Compound Members | File Members | Related Pages

IEC.cpp

Go to the documentation of this file.
00001 /*
00002  *  IEC.cpp - IEC bus routines, 1541 emulation (DOS level)
00003  *
00004  *  Frodo (C) 1994-1997 Christian Bauer
00005  *
00006 
00007  *
00008  * Notes:
00009  * ------
00010  *
00011  *  - There are three kinds of devices on the IEC bus: controllers,
00012  *    listeners and talkers. We are always the controller and we
00013  *    can additionally be either listener or talker. There can be
00014  *    only one listener and one talker active at the same time (the
00015  *    real IEC bus allows multiple listeners, but we don't).
00016  *  - There is one Drive object for every emulated drive (8..11).
00017  *    A pointer to one of them is stored in "listener"/"talker"
00018  *    when talk()/listen() is called and is used by the functions
00019  *    called afterwards.
00020  *  - The Drive objects have four virtual functions so that the
00021  *    interface to them is independent of their implementation:
00022  *      Open() opens a channel
00023  *      Close() closes a channel
00024  *      Read() reads from a channel
00025  *      Write() writes to a channel
00026  *  - The EOI/EOF signal is special on the IEC bus in that it is
00027  *    Sent before the last byte, not after it.
00028  */
00029 
00030 #include "sysdeps.h"
00031 
00032 #include "IEC.h"
00033 #include "1541fs.h"
00034 #include "1541d64.h"
00035 #include "1541t64.h"
00036 #include "Prefs.h"
00037 #include "Display.h"
00038 
00039 
00040 /*
00041  *  Constructor: Initialize variables
00042  */
00043 
00044 IEC::IEC(C64Display *display) : the_display(display), ThePrefs(display->TheC64->ThePrefs)
00045 {
00046         __CHECK_NULL(display);
00047 
00048         int i;
00049 
00050         // Create drives 8..11
00051         for (i=0; i<4; i++)
00052                 drive[i] = NULL;        // Important because UpdateLEDs is called from the drive constructors (via set_error)
00053 
00054         if (!ThePrefs.Emul1541Proc)
00055                 for (i=0; i<4; i++) {
00056                         if (ThePrefs.DriveType[i] == DRVTYPE_DIR)
00057                                 drive[i] = new FSDrive(this, ThePrefs.DrivePath[i]);
00058                         else if (ThePrefs.DriveType[i] == DRVTYPE_D64)
00059                                 drive[i] = new D64Drive(this, ThePrefs.DrivePath[i]);
00060                         else
00061                                 drive[i] = new T64Drive(this, ThePrefs.DrivePath[i]);
00062                 }
00063 
00064         listener_active = talker_active = false;
00065         listening = false;
00066 
00067         CTOR(IEC);
00068 }
00069 
00070 
00071 /*
00072  *  Destructor: Delete drives
00073  */
00074 
00075 IEC::~IEC()
00076 {
00077         for (int i=0; i<4; i++)
00078                 delete drive[i];
00079 
00080         DTOR(IEC);
00081 }
00082 
00083 
00084 /*
00085  *  Reset all drives
00086  */
00087 
00088 void IEC::Reset(void)
00089 {
00090         for (int i=0; i<4; i++)
00091                 if (drive[i] != NULL && drive[i]->Ready)
00092                         drive[i]->Reset();
00093 
00094         UpdateLEDs();
00095 
00096         ELOG1(_L8("IEC reset\n"));
00097 }
00098 
00099 
00100 /*
00101  *  Preferences have changed, prefs points to new preferences,
00102  *  ThePrefs still holds the previous ones. Check if drive settings
00103  *  have changed.
00104  */
00105 
00106 void IEC::NewPrefs(Prefs *prefs)
00107 {
00108         ELOG1(_L8("IEC::NewPrefs\n"));
00109 
00110         // Delete and recreate all changed drives
00111         for (int i=0; i<4; i++)
00112                 if ((ThePrefs.DriveType[i] != prefs->DriveType[i]) || strcmp(ThePrefs.DrivePath[i], prefs->DrivePath[i]) || ThePrefs.Emul1541Proc != prefs->Emul1541Proc) {
00113                 ELOG2(_L8("  drive %d changed\n"), i+8);
00114                         delete drive[i];
00115                         drive[i] = NULL;        // Important because UpdateLEDs is called from drive constructors (via set_error())
00116                         if (!prefs->Emul1541Proc) {
00117                                 if (prefs->DriveType[i] == DRVTYPE_DIR)
00118                                         drive[i] = new FSDrive(this, prefs->DrivePath[i]);
00119                                 else if (prefs->DriveType[i] == DRVTYPE_D64)
00120                                         drive[i] = new D64Drive(this, prefs->DrivePath[i]);
00121                                 else
00122                                         drive[i] = new T64Drive(this, prefs->DrivePath[i]);
00123                         }
00124                 }
00125 
00126         UpdateLEDs();
00127 }
00128 
00129 
00130 /*
00131  *  Update drive LED display
00132  */
00133 
00134 void IEC::UpdateLEDs(void)
00135 {
00136         if (drive[0] != NULL && drive[1] != NULL && drive[2] != NULL && drive[3] != NULL)
00137                 the_display->UpdateLEDs(drive[0]->LED, drive[1]->LED, drive[2]->LED, drive[3]->LED);
00138 }
00139 
00140 
00141 /*
00142  *  Output one byte
00143  */
00144 
00145 uint8 IEC::Out(uint8 byte, bool eoi)
00146 {
00147         if (listener_active) {
00148                 if (received_cmd == CMD_OPEN)
00149                         return open_out(byte, eoi);
00150                 if (received_cmd == CMD_DATA)
00151                         return data_out(byte, eoi);
00152                 return ST_TIMEOUT;
00153         } else
00154                 return ST_TIMEOUT;
00155 }
00156 
00157 
00158 /*
00159  *  Output one byte with ATN (Talk/Listen/Untalk/Unlisten)
00160  */
00161 
00162 uint8 IEC::OutATN(uint8 byte)
00163 {
00164         received_cmd = sec_addr = 0;    // Command is sent with secondary address
00165         switch (byte & 0xf0) {
00166                 case ATN_LISTEN:
00167                         listening = true;
00168                         return listen(byte & 0x0f);
00169                 case ATN_UNLISTEN:
00170                         listening = false;
00171                         return unlisten();
00172                 case ATN_TALK:
00173                         listening = false;
00174                         return talk(byte & 0x0f);
00175                 case ATN_UNTALK:
00176                         listening = false;
00177                         return untalk();
00178         }
00179         return ST_TIMEOUT;
00180 }
00181 
00182 
00183 /*
00184  *  Output secondary address
00185  */
00186 
00187 uint8 IEC::OutSec(uint8 byte)
00188 {
00189         if (listening) {
00190                 if (listener_active) {
00191                         sec_addr = byte & 0x0f;
00192                         received_cmd = byte & 0xf0;
00193                         return sec_listen();
00194                 }
00195         } else {
00196                 if (talker_active) {
00197                         sec_addr = byte & 0x0f;
00198                         received_cmd = byte & 0xf0;
00199                         return sec_talk();
00200                 }
00201         }
00202         return ST_TIMEOUT;
00203 }
00204 
00205 
00206 /*
00207  *  Read one byte
00208  */
00209 
00210 uint8 IEC::In(uint8 *byte)
00211 {
00212         if (talker_active && (received_cmd == CMD_DATA))
00213                 return data_in(byte);
00214 
00215         *byte = 0;
00216         return ST_TIMEOUT;
00217 }
00218 
00219 
00220 /*
00221  *  Assert ATN (for Untalk)
00222  */
00223 
00224 void IEC::SetATN(void)
00225 {
00226         // Only needed for real IEC
00227 }
00228 
00229 
00230 /*
00231  *  Release ATN
00232  */
00233 
00234 void IEC::RelATN(void)
00235 {
00236         // Only needed for real IEC
00237 }
00238 
00239 
00240 /*
00241  *  Talk-attention turn-around
00242  */
00243 
00244 void IEC::Turnaround(void)
00245 {
00246         // Only needed for real IEC
00247 }
00248 
00249 
00250 /*
00251  *  System line release
00252  */
00253 
00254 void IEC::Release(void)
00255 {
00256         // Only needed for real IEC
00257 }
00258 
00259 
00260 /*
00261  *  Listen
00262  */
00263 
00264 uint8 IEC::listen(int device)
00265 {
00266         if ((device >= 8) && (device <= 11)) {
00267                 if ((listener = drive[device-8]) != NULL && listener->Ready) {
00268                         listener_active = true;
00269                         return ST_OK;
00270                 }
00271         }
00272 
00273         listener_active = false;
00274         return ST_NOTPRESENT;
00275 }
00276 
00277 
00278 /*
00279  *  Talk
00280  */
00281 
00282 uint8 IEC::talk(int device)
00283 {
00284         if ((device >= 8) && (device <= 11)) {
00285                 if ((talker = drive[device-8]) != NULL && talker->Ready) {
00286                         talker_active = true;
00287                         return ST_OK;
00288                 }
00289         }
00290 
00291         talker_active = false;
00292         return ST_NOTPRESENT;
00293 }
00294 
00295 
00296 /*
00297  *  Unlisten
00298  */
00299 
00300 uint8 IEC::unlisten(void)
00301 {
00302         listener_active = false;
00303         return ST_OK;
00304 }
00305 
00306 
00307 /*
00308  *  Untalk
00309  */
00310 
00311 uint8 IEC::untalk(void)
00312 {
00313         talker_active = false;
00314         return ST_OK;
00315 }
00316 
00317 
00318 /*
00319  *  Secondary address after Listen
00320  */
00321 
00322 uint8 IEC::sec_listen(void)
00323 {
00324         switch (received_cmd) {
00325 
00326                 case CMD_OPEN:  // Prepare for receiving the file name
00327                         name_ptr = name_buf;
00328                         name_len = 0;
00329                         return ST_OK;
00330 
00331                 case CMD_CLOSE: // Close channel
00332                         if (listener->LED != DRVLED_ERROR) {
00333                                 listener->LED = DRVLED_OFF;             // Turn off drive LED
00334                                 UpdateLEDs();
00335                         }
00336                         return listener->Close(sec_addr);
00337         }
00338         return ST_OK;
00339 }
00340 
00341 
00342 /*
00343  *  Secondary address after Talk
00344  */
00345 
00346 uint8 IEC::sec_talk(void)
00347 {
00348         return ST_OK;
00349 }
00350 
00351 
00352 /*
00353  *  Byte after Open command: Store character in file name, open file on EOI
00354  */
00355 
00356 uint8 IEC::open_out(uint8 byte, bool eoi)
00357 {
00358         if (name_len < NAMEBUF_LENGTH) {
00359                 *name_ptr++ = byte;
00360                 name_len++;
00361         }
00362 
00363         if (eoi) {
00364                 *name_ptr = 0;                          // End string
00365                 listener->LED = DRVLED_ON;      // Turn on drive LED
00366                 UpdateLEDs();
00367                 return listener->Open(sec_addr, name_buf);
00368         }
00369 
00370         return ST_OK;
00371 }
00372 
00373 
00374 /*
00375  *  Write byte to channel
00376  */
00377 
00378 uint8 IEC::data_out(uint8 byte, bool eoi)
00379 {
00380         return listener->Write(sec_addr, byte, eoi);
00381 }
00382 
00383 
00384 /*
00385  *  Read byte from channel
00386  */
00387 
00388 uint8 IEC::data_in(uint8 *byte)
00389 {
00390         return talker->Read(sec_addr, byte);
00391 }
00392 
00393 
00394 /*
00395  *  Drive constructor
00396  */
00397 
00398 Drive::Drive(IEC *iec)
00399 {
00400         the_iec = iec;
00401         LED = DRVLED_OFF;
00402         Ready = false;
00403         set_error(ERR_STARTUP);
00404 }
00405 
00406 
00407 /*
00408  *  Set error message on drive
00409  */
00410 
00411 // 1541 error messages
00412 const char* const Errors_1541[] = {
00413         "00, OK,00,00\r",
00414         "25,WRITE ERROR,00,00\r",
00415         "26,WRITE PROTECT ON,00,00\r",
00416         "30,SYNTAX ERROR,00,00\r",
00417         "33,SYNTAX ERROR,00,00\r",
00418         "60,WRITE FILE OPEN,00,00\r",
00419         "61,FILE NOT OPEN,00,00\r",
00420         "62,FILE NOT FOUND,00,00\r",
00421         "67,ILLEGAL TRACK OR SECTOR,00,00\r",
00422         "70,NO CHANNEL,00,00\r",
00423         "73,CBM DOS V2.6 1541,00,00\r",
00424         "74,DRIVE NOT READY,00,00\r"
00425 };
00426 
00427 void Drive::set_error(int error)
00428 {
00429         error_ptr = (char*)Errors_1541[error];
00430         error_len = strlen(error_ptr);
00431 
00432         // Set drive condition
00433         if (error != ERR_OK)
00434                 if (error == ERR_STARTUP)
00435                         LED = DRVLED_OFF;
00436                 else
00437                         LED = DRVLED_ERROR;
00438         else if (LED == DRVLED_ERROR)
00439                 LED = DRVLED_OFF;
00440         the_iec->UpdateLEDs();
00441 }

Generated on Tue Feb 8 04:07:55 2005 for E32frodo by doxygen 1.3.3