Serial.cpp

Go to the documentation of this file.
00001 /* ES40 emulator.
00002  * Copyright (C) 2007-2008 by the ES40 Emulator Project
00003  *
00004  * WWW    : http://sourceforge.net/projects/es40
00005  * E-mail : camiel@camicom.com
00006  * 
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  * 
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00020  * 
00021  * Although this is not required, the author would appreciate being notified of, 
00022  * and receiving any modifications you may make to the source code that might serve
00023  * the general public.
00024  */
00025 
00184 #include "StdAfx.h"
00185 #include "Serial.h"
00186 #include "System.h"
00187 #include "AliM1543C.h"
00188 
00189 #include "lockstep.h"
00190 
00191 #define RECV_TICKS  10
00192 
00193 int iCounter = 0;
00194 
00195 #define FIFO_SIZE 1024
00196 
00197 //#define DEBUG_SERIAL 1
00198 
00202 CSerial::CSerial(CConfigurator* cfg, CSystem* c, u16 number) : CSystemComponent(cfg, c)
00203 {
00204   state.iNumber = number;
00205   breakHit = false;
00206 }
00207 
00211 void CSerial::init()
00212 {
00213   listenPort = (int) myCfg->get_num_value("port", false, 8000 + state.iNumber);
00214 
00215   char    s[1000];
00216   char*   nargv = s;
00217   int     i = 0;
00218 
00219   cSystem->RegisterMemory(this, 0,
00220                           U64(0x00000801fc0003f8) - (0x100 * state.iNumber), 8);
00221 
00222   // Start Telnet server
00223 #if defined(_WIN32)
00224 
00225   // Windows Sockets only work after calling WSAStartup.
00226   WSADATA wsa;
00227   WSAStartup(0x0101, &wsa);
00228 #endif // defined (_WIN32)
00229   struct sockaddr_in  Address;
00230 
00231   socklen_t           nAddressSize = sizeof(struct sockaddr_in);
00232 
00233   listenSocket = (int) socket(AF_INET, SOCK_STREAM, 0);
00234   if(listenSocket == INVALID_SOCKET)
00235   {
00236     printf("Could not open socket to listen on!\n");
00237   }
00238 
00239   Address.sin_addr.s_addr = INADDR_ANY;
00240   Address.sin_port = htons((u16) (listenPort));
00241   Address.sin_family = AF_INET;
00242 
00243   int optval = 1;
00244   setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, (char*) &optval,
00245              sizeof(optval));
00246   bind(listenSocket, (struct sockaddr*) &Address, sizeof(Address));
00247   listen(listenSocket, 1);
00248 
00249   printf("%s: Waiting for connection on port %d.\n", devid_string, listenPort);
00250 
00251   WaitForConnection();
00252 
00253 #if defined(IDB) && defined(LS_MASTER)
00254   struct sockaddr_in  dest_addr;
00255   int                 result = -1;
00256 
00257   throughSocket = socket(AF_INET, SOCK_STREAM, 0);
00258 
00259   dest_addr.sin_family = AF_INET;
00260   dest_addr.sin_port = htons((u16) (base + number));
00261   dest_addr.sin_addr.s_addr = inet_addr(ls_IP);
00262 
00263   printf("%s: Waiting to initiate remote connection to %s.\n", devid_string,
00264          ls_IP);
00265 
00266   while(result == -1)
00267   {
00268     result = connect(throughSocket, (struct sockaddr*) &dest_addr,
00269                      sizeof(struct sockaddr));
00270   }
00271 #endif
00272   state.rcvW = 0;
00273   state.rcvR = 0;
00274 
00275   state.bLCR = 0x00;
00276   state.bLSR = 0x60;  // THRE, TSRE
00277   state.bMSR = 0x30;  // CTS, DSR
00278   state.bIIR = 0x01;  // no interrupt
00279   state.irq_active = false;
00280   myThread = 0;
00281 
00282   printf("%s: $Id: Serial.cpp,v 1.48 2008/03/31 19:13:28 iamcamiel Exp $\n",
00283          devid_string);
00284 }
00285 
00286 void CSerial::start_threads()
00287 {
00288   char  buffer[5];
00289   if(!myThread)
00290   {
00291     sprintf(buffer, "srl%d", state.iNumber);
00292     myThread = new Poco::Thread(buffer);
00293     printf(" %s", myThread->getName().c_str());
00294     StopThread = false;
00295     myThread->start(*this);
00296   }
00297 }
00298 
00299 void CSerial::stop_threads()
00300 {
00301   StopThread = true;
00302   if(myThread)
00303   {
00304     printf(" %s", myThread->getName().c_str());
00305     myThread->join();
00306     delete myThread;
00307     myThread = 0;
00308   }
00309 }
00310 
00314 CSerial::~CSerial()
00315 {
00316   stop_threads();
00317 }
00318 
00319 u64 CSerial::ReadMem(int index, u64 address, int dsize)
00320 {
00321   u8  d;
00322 
00323   switch(address)
00324   {
00325   case 0: // data buffer
00326     if(state.bLCR & 0x80)
00327     {
00328       return state.bBRB_LSB;
00329     }
00330     else
00331     {
00332       if(state.rcvR != state.rcvW)
00333       {
00334         state.bRDR = state.rcvBuffer[state.rcvR];
00335         state.rcvR++;
00336         if(state.rcvR == FIFO_SIZE)
00337           state.rcvR = 0;
00338         TRC_DEV4("Read character %02x (%c) on serial port %d\n", state.bRDR,
00339                  printable(state.bRDR), state.iNumber);
00340 #if defined(DEBUG_SERIAL)
00341         printf("Read character %02x (%c) on serial port %d\n", state.bRDR,
00342                printable(state.bRDR), state.iNumber);
00343 #endif
00344       }
00345       else
00346       {
00347         TRC_DEV2("Read past FIFO on serial port %d\n", state.iNumber);
00348 #if defined(DEBUG_SERIAL)
00349         printf("Read past FIFO on serial port %d\n", state.iNumber);
00350 #endif
00351       }
00352 
00353       return state.bRDR;
00354     }
00355 
00356   case 1:
00357     if(state.bLCR & 0x80)
00358     {
00359       return state.bBRB_MSB;
00360     }
00361     else
00362     {
00363       return state.bIER;
00364     }
00365 
00366   case 2: //interrupt cause
00367     d = state.bIIR;
00368     state.bIIR = 0x01;
00369     return d;
00370 
00371   case 3:
00372     return state.bLCR;
00373 
00374   case 4:
00375     return state.bMCR;
00376 
00377   case 5: //serialization state
00378     if(state.rcvR != state.rcvW)
00379       state.bLSR = 0x61;    // THRE, TSRE, RxRD
00380     else
00381       state.bLSR = 0x60;    // THRE, TSRE
00382     return state.bLSR;
00383 
00384   case 6:
00385     return state.bMSR;
00386 
00387   default:
00388     return state.bSPR;
00389   }
00390 }
00391 
00392 void CSerial::WriteMem(int index, u64 address, int dsize, u64 data)
00393 {
00394   u8    d;
00395   char  s[5];
00396   d = (u8) data;
00397 
00398   switch(address)
00399   {
00400   case 0:
00401     if(state.bLCR & 0x80)   // divisor latch access bit set
00402     {
00403 
00404       // LSB of divisor latch
00405       state.bBRB_LSB = d;
00406     }
00407     else
00408     {
00409 
00410       // Transmit Hold Register
00411       sprintf(s, "%c", d);
00412       write(s);
00413       TRC_DEV4("Write character %02x (%c) on serial port %d\n", d, printable(d),
00414                state.iNumber);
00415 #if defined(DEBUG_SERIAL)
00416       printf("Write character %02x (%c) on serial port %d\n", d, printable(d),
00417              state.iNumber);
00418 #endif
00419       eval_interrupts();
00420     }
00421     break;
00422 
00423   case 1:
00424     if(state.bLCR & 0x80)   // divisor latch access bit set
00425     {
00426 
00427       // MSB of divisor latch
00428       state.bBRB_MSB = d;
00429     }
00430     else
00431     {
00432 
00433       // Interrupt Enable Register
00434       state.bIER = d;
00435       eval_interrupts();
00436     }
00437     break;
00438 
00439   case 2:
00440     state.bFCR = d;
00441     break;
00442 
00443   case 3:
00444     state.bLCR = d;
00445     break;
00446 
00447   case 4:
00448     state.bMCR = d;
00449     break;
00450 
00451   default:
00452     state.bSPR = d;
00453   }
00454 }
00455 
00456 void CSerial::eval_interrupts()
00457 {
00458   state.bIIR = 0x01;        // no interrupt
00459   if((state.bIER & 0x01) && (state.rcvR != state.rcvW))
00460     state.bIIR = 0x04;
00461   else if(state.bIER & 0x2) //transmitter buffer empty enabled?
00462     state.bIIR = 0x02;      //transmitter buffer empty
00463   else
00464     state.bIIR = 0x01;      // no interrupt
00465   if(state.bIIR > 0x01)
00466   {
00467     if(!state.irq_active)
00468       theAli->pic_interrupt(0, 4 - state.iNumber);
00469   }
00470   else
00471   {
00472     if(state.irq_active)
00473       theAli->pic_deassert(0, 4 - state.iNumber);
00474   }
00475 }
00476 
00477 void CSerial::write(const char* s)
00478 {
00479   int val = send(connectSocket, s, (int) strlen(s) + 1, 0);
00480 }
00481 
00482 void CSerial::receive(const char* data)
00483 {
00484   char*   x;
00485 
00486   x = (char*) data;
00487 
00488   while(*x)
00489   {
00490     state.rcvBuffer[state.rcvW++] = *x;
00491     if(state.rcvW == FIFO_SIZE)
00492       state.rcvW = 0;
00493     x++;
00494     eval_interrupts();
00495   }
00496 }
00497 
00501 void CSerial::run()
00502 {
00503   try
00504   {
00505     for(;;)
00506     {
00507       if(StopThread)
00508         return;
00509       execute();
00510       Poco::Thread::sleep(20);
00511     }
00512   }
00513 
00514   catch(Poco::Exception & e)
00515   {
00516     printf("Exception in Serial thread: %s.\n", e.displayText().c_str());
00517 
00518     // Let the thread die...
00519   }
00520 }
00521 
00527 void CSerial::check_state()
00528 {
00529   if(breakHit)
00530     serial_menu();
00531 
00532   if(myThread && !myThread->isRunning())
00533     FAILURE(Thread, "Serial thread has died");
00534 }
00535 
00536 void CSerial::serial_menu()
00537 {
00538   fd_set          readset;
00539   unsigned char   buffer[FIFO_SIZE + 1];
00540   ssize_t         size;
00541   struct timeval  tv;
00542   bool            exitLoop = false;
00543 
00544   cSystem->stop_threads();
00545 
00546   write("\r\n<BREAK> received. What do you want to do?\r\n");
00547   write("     0. Continue\r\n");
00548 #if defined(IDB)
00549   write("     1. End run\r\n");
00550 #else
00551   write("     1. Exit emulator gracefully\r\n");
00552   write("     2. Abort emulator (no changes saved)\r\n");
00553   write("     3. Save state to autosave.axp and continue\r\n");
00554   write("     4. Load state from autosave.axp and continue\r\n");
00555 #endif
00556   while(!exitLoop)
00557   {
00558     FD_ZERO(&readset);
00559     FD_SET(connectSocket, &readset);
00560     tv.tv_sec = 60;
00561     tv.tv_usec = 0;
00562     if(select(connectSocket + 1, &readset, NULL, NULL, &tv) <= 0)
00563     {
00564       write("%SRL-I-TIMEOUT: no timely answer received. Continuing emulation.\r\n");
00565       break;  // leave loop
00566     }
00567 
00568 #if defined(_WIN32) || defined(__VMS)
00569     size = recv(connectSocket, (char*) buffer, FIFO_SIZE, 0);
00570 #else
00571     size = read(connectSocket, &buffer, FIFO_SIZE);
00572 #endif
00573     switch(buffer[0])
00574     {
00575     case '0':
00576       write("%SRL-I-CONTINUE: continuing emulation.\r\n");
00577       exitLoop = true;
00578       break;
00579 
00580     case '1':
00581       write("%SRL-I-EXIT: exiting emulation gracefully.\r\n");
00582       FAILURE(Graceful, "Graceful exit");
00583       exitLoop = true;
00584       break;
00585 
00586     case '2':
00587       write("%SRL-I-ABORT: aborting emulation.\r\n");
00588       FAILURE(Abort, "Aborting");
00589       exitLoop = true;
00590       break;
00591 
00592     case '3':
00593       write("%SRL-I-SAVESTATE: Saving state to autosave.axp.\r\n");
00594       cSystem->SaveState("autosave.axp");
00595       write("%SRL-I-CONTINUE: continuing emulation.\r\n");
00596       exitLoop = true;
00597       break;
00598 
00599     case '4':
00600       write("%SRL-I-LOADSTATE: Loading state from autosave.axp.\r\n");
00601       cSystem->RestoreState("autosave.axp");
00602       write("%SRL-I-CONTINUE: continuing emulation.\r\n");
00603       exitLoop = true;
00604       break;
00605 
00606     default:
00607       write("%SRL-W-INVALID: Not a valid answer.\r\n");
00608     }
00609   }
00610 
00611   breakHit = false;
00612   cSystem->start_threads();
00613 }
00614 
00615 void CSerial::execute()
00616 {
00617   fd_set          readset;
00618   unsigned char   buffer[FIFO_SIZE + 1];
00619   unsigned char   cbuffer[FIFO_SIZE + 1]; // cooked buffer
00620   unsigned char*  b;
00621 
00622   // cooked buffer
00623   unsigned char *c;
00624   ssize_t         size;
00625   struct timeval  tv;
00626 
00627   state.serial_cycles++;
00628   if(state.serial_cycles >= RECV_TICKS)
00629   {
00630     FD_ZERO(&readset);
00631     FD_SET(connectSocket, &readset);
00632     tv.tv_sec = 0;
00633     tv.tv_usec = 0;
00634     if(select(connectSocket + 1, &readset, NULL, NULL, &tv) > 0)
00635     {
00636 #if defined(_WIN32) || defined(__VMS)
00637 
00638       // Windows Sockets has no direct equivalent of BSD's read
00639       size = recv(connectSocket, (char*) buffer, FIFO_SIZE, 0);
00640 #else
00641       size = read(connectSocket, &buffer, FIFO_SIZE);
00642 #endif
00643 
00644       extern int  got_sigint;
00645       if(size == 0 && !got_sigint)
00646       {
00647         printf("%%SRL-W-DISCONNECT: Write socket closed on other end for serial port %d.\n",
00648              state.iNumber);
00649         printf("-SRL-I-WAITFOR: Waiting for a new connection on port %d.\n",
00650                listenPort);
00651         WaitForConnection();
00652         return;
00653       }
00654 
00655       buffer[size + 1] = 0; // force null termination.
00656       b = buffer;
00657       c = cbuffer;
00658       while((ssize_t) (b - buffer) < size)
00659       {
00660         if(*b == 0x0a)
00661         {
00662           b++;      // skip LF
00663           continue;
00664         }
00665 
00666         if(*b == IAC)
00667         {
00668           if(*(b + 1) == IAC)
00669           {         // escaped IAC.
00670             b++;
00671           }
00672           else if(*(b + 1) >= WILL)
00673           {         // will/won't/do/don't
00674             b += 3; // skip this byte, and following two. (telnet escape)
00675             continue;
00676           }
00677           else if(*(b + 1) == SB)
00678           {         // skip until IAC SE
00679             b += 2; // now we're at start of subnegotiation.
00680             while(*b != IAC && *(b + 1) != SE)
00681               b++;
00682             b += 2;
00683             continue;
00684           }
00685           else if(*(b + 1) == BREAK)
00686           {         // break (== halt button?)
00687             b += 2;
00688             breakHit = true;
00689           }
00690           else if(*(b + 1) == AYT)
00691           {         // are you there?
00692           }
00693           else
00694           {         // misc single byte command.
00695             b += 2;
00696             continue;
00697           }
00698         }
00699 
00700         *c = *b;
00701         c++;
00702         b++;
00703       }
00704 
00705       *c = 0;       // null terminate it.
00706       this->receive((const char*) &cbuffer);
00707     }
00708 
00709     state.serial_cycles = 0;
00710   }
00711 
00712   eval_interrupts();
00713 }
00714 
00715 static u32  srl_magic1 = 0x5A15A15A;
00716 static u32  srl_magic2 = 0x1A51A51A;
00717 
00721 int CSerial::SaveState(FILE* f)
00722 {
00723   long  ss = sizeof(state);
00724 
00725   fwrite(&srl_magic1, sizeof(u32), 1, f);
00726   fwrite(&ss, sizeof(long), 1, f);
00727   fwrite(&state, sizeof(state), 1, f);
00728   fwrite(&srl_magic2, sizeof(u32), 1, f);
00729   printf("%s: %d bytes saved.\n", devid_string, (int) ss);
00730   return 0;
00731 }
00732 
00736 int CSerial::RestoreState(FILE* f)
00737 {
00738   long    ss;
00739   u32     m1;
00740   u32     m2;
00741   size_t  r;
00742 
00743   r = fread(&m1, sizeof(u32), 1, f);
00744   if(r != 1)
00745   {
00746     printf("%s: unexpected end of file!\n", devid_string);
00747     return -1;
00748   }
00749 
00750   if(m1 != srl_magic1)
00751   {
00752     printf("%s: MAGIC 1 does not match!\n", devid_string);
00753     return -1;
00754   }
00755 
00756   fread(&ss, sizeof(long), 1, f);
00757   if(r != 1)
00758   {
00759     printf("%s: unexpected end of file!\n", devid_string);
00760     return -1;
00761   }
00762 
00763   if(ss != sizeof(state))
00764   {
00765     printf("%s: STRUCT SIZE does not match!\n", devid_string);
00766     return -1;
00767   }
00768 
00769   fread(&state, sizeof(state), 1, f);
00770   if(r != 1)
00771   {
00772     printf("%s: unexpected end of file!\n", devid_string);
00773     return -1;
00774   }
00775 
00776   r = fread(&m2, sizeof(u32), 1, f);
00777   if(r != 1)
00778   {
00779     printf("%s: unexpected end of file!\n", devid_string);
00780     return -1;
00781   }
00782 
00783   if(m2 != srl_magic2)
00784   {
00785     printf("%s: MAGIC 1 does not match!\n", devid_string);
00786     return -1;
00787   }
00788 
00789   printf("%s: %d bytes restored.\n", devid_string, (int) ss);
00790   return 0;
00791 }
00792 
00793 void CSerial::WaitForConnection()
00794 {
00795   struct sockaddr_in  Address;
00796   socklen_t           nAddressSize = sizeof(struct sockaddr_in);
00797   const char*               telnet_options = "%c%c%c";
00798   char                buffer[8];
00799   char                s[1000];
00800   char*               nargv = s;
00801   int                 i = 0;
00802 
00803 #if !defined(LS_SLAVE)
00804   char                s2[200];
00805   char*               argv[20];
00806 
00807   strncpy(s, myCfg->get_text_value("action", ""), 999);
00808   s[999] = '\0';
00809 
00810   //printf("%s: Specified : %s\n",devid_string,s);
00811   if(strcmp(s, ""))
00812   {
00813 
00814     // spawn external program (telnet client)...
00815     while(*nargv)
00816     {
00817       argv[i] = nargv;
00818       if(nargv[0] == '\"')
00819         nargv = strchr(nargv + 1, '\"');
00820       if(nargv)
00821         nargv = strchr(nargv, ' ');
00822       if(!nargv)
00823         break;
00824       *nargv++ = '\0';
00825       i++;
00826       argv[i] = NULL;
00827     }
00828 
00829     argv[i + 1] = NULL;
00830     strcpy(s2, argv[0]);
00831     nargv = s2;
00832     if(nargv[0] == '\"')
00833     {
00834       nargv++;
00835       *(strchr(nargv, '\"')) = '\0';
00836     }
00837 
00838     //printf("%s: Starting %s\n", devid_string,nargv);
00839 #if defined(_WIN32)
00840     _spawnvp(_P_NOWAIT, nargv, argv);
00841 #elif !defined(__VMS)
00842     pid_t child;
00843     int   status;
00844     if(!(child = fork()))
00845     {
00846       execvp(argv[0], argv);
00847       FAILURE_1(Runtime,"Exec of '%s' failed.\n", argv[0]);
00848     }
00849     else
00850     {
00851       sleep(1); // give it a chance to start up.
00852       waitpid(child, &status, WNOHANG); // reap it, if needed.
00853       if(kill(child, 0) < 0)
00854       { // uh oh, no kiddo.
00855         FAILURE_1(Runtime,"Exec of '%s' has failed.\n", argv[0]);
00856       }
00857     }
00858 #endif
00859   }
00860 #endif
00861   Address.sin_addr.s_addr = INADDR_ANY;
00862   Address.sin_port = htons((u16) listenPort);
00863   Address.sin_family = AF_INET;
00864 
00865   //  Wait until we have a connection
00866   connectSocket = INVALID_SOCKET;
00867   while(connectSocket == INVALID_SOCKET)
00868   {
00869     connectSocket = (int) accept(listenSocket, (struct sockaddr*) &Address,
00870                                  &nAddressSize);
00871   }
00872 
00873   state.serial_cycles = 0;
00874 
00875   // Send some control characters to the telnet client to handle
00876   // character-at-a-time mode.
00877   sprintf(buffer, telnet_options, IAC, DO, TELOPT_ECHO);
00878   this->write(buffer);
00879 
00880   sprintf(buffer, telnet_options, IAC, DO, TELOPT_NAWS);
00881   write(buffer);
00882 
00883   sprintf(buffer, telnet_options, IAC, DO, TELOPT_LFLOW);
00884   this->write(buffer);
00885 
00886   sprintf(buffer, telnet_options, IAC, WILL, TELOPT_ECHO);
00887   this->write(buffer);
00888 
00889   sprintf(buffer, telnet_options, IAC, WILL, TELOPT_SGA);
00890   this->write(buffer);
00891 
00892   sprintf(s, "This is serial port #%d on AlphaSim\r\n", state.iNumber);
00893   this->write(s);
00894 }

SourceForge.net Logo
Project space on SourceForge.net