DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/serialport/serialport.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include <ctype.h>
00022 
00023 #include "dosbox.h"
00024 
00025 #include "inout.h"
00026 #include "pic.h"
00027 #include "setup.h"
00028 #include "bios.h"                                       // SetComPorts(..)
00029 #include "callback.h"                           // CALLBACK_Idle
00030 #include "control.h"
00031 
00032 #include "serialport.h"
00033 #include "serialmouse.h"
00034 #include "directserial.h"
00035 #include "serialdummy.h"
00036 #include "softmodem.h"
00037 #include "nullmodem.h"
00038 #include "seriallog.h"
00039 #include "serialfile.h"
00040 
00041 #include "cpu.h"
00042 
00043 #define LOG_SER(x) log_ser 
00044 
00045 bool device_COM::Read(Bit8u * data,Bit16u * size) {
00046         // DTR + RTS on
00047         sclass->Write_MCR(0x03);
00048         for (Bit16u i=0; i<*size; i++)
00049         {
00050                 Bit8u status;
00051                 if(!(sclass->Getchar(&data[i],&status,true,1000))) {
00052                         *size=i;
00053                         return true;
00054                 }
00055         }
00056         return true;
00057 }
00058 
00059 
00060 bool device_COM::Write(const Bit8u * data,Bit16u * size) {
00061         // DTR + RTS on
00062         sclass->Write_MCR(0x03);
00063         for (Bit16u i=0; i<*size; i++)
00064         {
00065                 if(!(sclass->Putchar(data[i],true,true,1000))) {
00066                         *size=i;
00067                         sclass->Write_MCR(0x01);
00068                         return false;
00069                 }
00070         }
00071         // RTS off
00072         sclass->Write_MCR(0x01);
00073         return true;
00074 }
00075 
00076 bool device_COM::Seek(Bit32u * pos,Bit32u type) {
00077     (void)type;//UNUSED
00078         *pos = 0;
00079         return true;
00080 }
00081 
00082 bool device_COM::Close() {
00083         return false;
00084 }
00085 
00086 Bit16u device_COM::GetInformation(void) {
00087         return 0x80A0;
00088 }
00089 
00090 device_COM::device_COM(class CSerial* sc) {
00091         sclass = sc;
00092         SetName(serial_comname[sclass->idnumber]);
00093 }
00094 
00095 device_COM::~device_COM() {
00096         /* clear reference to myself so that we're not deleted twice (once by DOS_DelDevice the other by CSerial) */
00097         if (sclass != NULL && sclass->mydosdevice == this)
00098                 sclass->mydosdevice = NULL;
00099 }
00100 
00101 
00102 
00103 // COM1 - COM4 objects
00104 CSerial* serialports[4] ={0,0,0,0};
00105 
00106 static Bitu SERIAL_Read (Bitu port, Bitu iolen) {
00107     (void)iolen;//UNUSED
00108         Bitu i;
00109         Bitu retval;
00110         Bitu index = port & 0x7;
00111         switch(port&0xff8) {
00112                 case 0x3f8: i=0; break;
00113                 case 0x2f8: i=1; break;
00114                 case 0x3e8: i=2; break;
00115                 case 0x2e8: i=3; break;
00116                 default: return 0xff;
00117         }
00118         if(serialports[i]==0) return 0xff;
00119 
00120         switch (index) {
00121                 case RHR_OFFSET:
00122                         retval = serialports[i]->Read_RHR();
00123                         break;
00124                 case IER_OFFSET:
00125                         retval = serialports[i]->Read_IER();
00126                         break;
00127                 case ISR_OFFSET:
00128                         retval = serialports[i]->Read_ISR();
00129                         break;
00130                 case LCR_OFFSET:
00131                         retval = serialports[i]->Read_LCR();
00132                         break;
00133                 case MCR_OFFSET:
00134                         retval = serialports[i]->Read_MCR();
00135                         break;
00136                 case LSR_OFFSET:
00137                         retval = serialports[i]->Read_LSR();
00138                         break;
00139                 case MSR_OFFSET:
00140                         retval = serialports[i]->Read_MSR();
00141                         break;
00142                 case SPR_OFFSET:
00143                         retval = serialports[i]->Read_SPR();
00144                         break;
00145         }
00146 
00147 #if SERIAL_DEBUG
00148         const char* const dbgtext[]=
00149                 {"RHR","IER","ISR","LCR","MCR","LSR","MSR","SPR","DLL","DLM"};
00150         if(serialports[i]->dbg_register) {
00151                 if((index<2) && ((serialports[i]->LCR)&LCR_DIVISOR_Enable_MASK))
00152                         index += 8;
00153                 serialports[i]->log_ser(serialports[i]->dbg_register,
00154                         "read  0x%2x from %s.",retval,dbgtext[index]);
00155         }
00156 #endif
00157         return retval;  
00158 }
00159 static void SERIAL_Write (Bitu port, Bitu val, Bitu) {
00160         Bitu i;
00161         Bitu index = port & 0x7;
00162         switch(port&0xff8) {
00163                 case 0x3f8: i=0; break;
00164                 case 0x2f8: i=1; break;
00165                 case 0x3e8: i=2; break;
00166                 case 0x2e8: i=3; break;
00167                 default: return;
00168         }
00169         if(serialports[i]==0) return;
00170         
00171 #if SERIAL_DEBUG
00172                 const char* const dbgtext[]={"THR","IER","FCR",
00173                         "LCR","MCR","!LSR","MSR","SPR","DLL","DLM"};
00174                 if(serialports[i]->dbg_register) {
00175                         Bitu debugindex=index;
00176                         if((index<2) && ((serialports[i]->LCR)&LCR_DIVISOR_Enable_MASK))
00177                                 debugindex += 8;
00178                         serialports[i]->log_ser(serialports[i]->dbg_register,
00179                                 "write 0x%2x to %s.",val,dbgtext[debugindex]);
00180                 }
00181 #endif
00182         switch (index) {
00183                 case THR_OFFSET:
00184                         serialports[i]->Write_THR ((Bit8u)val);
00185                         return;
00186                 case IER_OFFSET:
00187                         serialports[i]->Write_IER ((Bit8u)val);
00188                         return;
00189                 case FCR_OFFSET:
00190                         serialports[i]->Write_FCR ((Bit8u)val);
00191                         return;
00192                 case LCR_OFFSET:
00193                         serialports[i]->Write_LCR ((Bit8u)val);
00194                         return;
00195                 case MCR_OFFSET:
00196                         serialports[i]->Write_MCR ((Bit8u)val);
00197                         return;
00198                 case MSR_OFFSET:
00199                         serialports[i]->Write_MSR ((Bit8u)val);
00200                         return;
00201                 case SPR_OFFSET:
00202                         serialports[i]->Write_SPR ((Bit8u)val);
00203                         return;
00204                 default:
00205                         serialports[i]->Write_reserved ((Bit8u)val, port & 0x7);
00206         }
00207 }
00208 #if SERIAL_DEBUG
00209 void CSerial::log_ser(bool active, char const* format,...) {
00210         if(active) {
00211                 // copied from DEBUG_SHOWMSG
00212                 char buf[512];
00213                 buf[0]=0;
00214                 sprintf(buf,"%12.3f [%7u] ",PIC_FullIndex(), SDL_GetTicks());
00215                 va_list msg;
00216                 va_start(msg,format);
00217                 vsprintf(buf+strlen(buf),format,msg);
00218                 va_end(msg);
00219                 // Add newline if not present
00220                 Bitu len=(Bitu)strlen(buf);
00221                 if(buf[len-1]!='\n') strcat(buf,"\r\n");
00222                 fputs(buf,debugfp);
00223         }
00224 }
00225 #endif
00226 
00227 void CSerial::changeLineProperties() {
00228         // update the event wait time
00229         float bitlen;
00230 
00231         if(baud_divider==0) bitlen=(1000.0f/115200.0f);
00232         else bitlen = (1000.0f/115200.0f)*(float)baud_divider;
00233         bytetime=bitlen*(float)(1+5+1);         // startbit + minimum length + stopbit
00234         bytetime+= bitlen*(float)(LCR&0x3); // databits
00235         if(LCR&0x4) bytetime+=bitlen;           // 2nd stopbit
00236         if(LCR&0x8) bytetime+=bitlen;           // parity
00237 
00238 #if SERIAL_DEBUG
00239         const char* const dbgtext[]={"none","odd","none","even","none","mark","none","space"};
00240         log_ser(dbg_serialtraffic,"New COM parameters: baudrate %5.0f, parity %s, wordlen %d, stopbits %d",
00241                 1.0/bitlen*1000.0f,dbgtext[(LCR&0x38)>>3],(LCR&0x3)+5,((LCR&0x4)>>2)+1);
00242 #endif  
00243         updatePortConfig (baud_divider, LCR);
00244 }
00245 
00246 static void Serial_EventHandler(Bitu val) {
00247         Bitu serclassid=val&0x3;
00248         if(serialports[serclassid]!=0)
00249                 serialports[serclassid]->handleEvent((Bit16u)(val>>2));
00250 }
00251 
00252 void CSerial::setEvent(Bit16u type, float duration) {
00253     PIC_AddEvent(Serial_EventHandler,duration,(Bitu)(((unsigned int)type<<2u)|(unsigned int)idnumber));
00254 }
00255 
00256 void CSerial::removeEvent(Bit16u type) {
00257     // TODO
00258         PIC_RemoveSpecificEvents(Serial_EventHandler,(Bitu)(((unsigned int)type<<2u)|(unsigned int)idnumber));
00259 }
00260 
00261 void CSerial::handleEvent(Bit16u type) {
00262         switch(type) {
00263                 case SERIAL_TX_LOOPBACK_EVENT: {
00264 
00265 #if SERIAL_DEBUG
00266                         log_ser(dbg_serialtraffic,loopback_data<0x10?
00267                                 "tx 0x%02x (%u) (loopback)":"tx 0x%02x (%c) (loopback)",
00268                                 loopback_data, loopback_data);
00269 #endif
00270                         receiveByte (loopback_data);
00271                         ByteTransmitted ();
00272                         break;
00273                 }
00274                 case SERIAL_THR_LOOPBACK_EVENT: {
00275                         loopback_data=txfifo->probeByte();
00276                         ByteTransmitting();
00277                         setEvent(SERIAL_TX_LOOPBACK_EVENT,bytetime);    
00278                         break;
00279                 }
00280                 case SERIAL_ERRMSG_EVENT: {
00281                         LOG_MSG("Serial%d: Errors: "\
00282                                 "Framing %d, Parity %d, Overrun RX:%d (IF0:%d), TX:%d, Break %d",
00283                                 (int)COMNUMBER,
00284                                 (int)framingErrors,
00285                                 (int)parityErrors,
00286                                 (int)overrunErrors,
00287                                 (int)overrunIF0,
00288                                 (int)txOverrunErrors,
00289                                 (int)breakErrors);
00290                         errormsg_pending=false;
00291                         framingErrors=0;
00292                         parityErrors=0;
00293                         overrunErrors=0;
00294                         txOverrunErrors=0;
00295                         overrunIF0=0;
00296                         breakErrors=0;
00297                         break;                                    
00298                 }
00299                 case SERIAL_RX_TIMEOUT_EVENT: {
00300                         rise(TIMEOUT_PRIORITY);
00301                         break;
00302                 }
00303                 default: handleUpperEvent(type);
00304         }
00305 }
00306 
00307 /*****************************************************************************/
00308 /* Interrupt control routines                                               **/
00309 /*****************************************************************************/
00310 void CSerial::rise (Bit8u priority) {
00311 #if SERIAL_DEBUG
00312         if(priority&TX_PRIORITY && !(waiting_interrupts&TX_PRIORITY))
00313                 log_ser(dbg_interrupt,"tx interrupt on.");
00314         if(priority&RX_PRIORITY && !(waiting_interrupts&RX_PRIORITY))
00315                 log_ser(dbg_interrupt,"rx interrupt on.");
00316         if(priority&MSR_PRIORITY && !(waiting_interrupts&MSR_PRIORITY))
00317                 log_ser(dbg_interrupt,"msr interrupt on.");
00318         if(priority&TIMEOUT_PRIORITY && !(waiting_interrupts&TIMEOUT_PRIORITY))
00319                 log_ser(dbg_interrupt,"fifo rx timeout interrupt on.");
00320 #endif
00321         
00322         waiting_interrupts |= priority;
00323         ComputeInterrupts();
00324 }
00325 
00326 // clears the pending interrupt, triggers other waiting interrupt
00327 void CSerial::clear (Bit8u priority) {
00328         
00329 #if SERIAL_DEBUG
00330         if(priority&TX_PRIORITY && (waiting_interrupts&TX_PRIORITY))
00331                 log_ser(dbg_interrupt,"tx interrupt off.");
00332         if(priority&RX_PRIORITY && (waiting_interrupts&RX_PRIORITY))
00333                 log_ser(dbg_interrupt,"rx interrupt off.");
00334         if(priority&MSR_PRIORITY && (waiting_interrupts&MSR_PRIORITY))
00335                 log_ser(dbg_interrupt,"msr interrupt off.");
00336         if(priority&ERROR_PRIORITY && (waiting_interrupts&ERROR_PRIORITY))
00337                 log_ser(dbg_interrupt,"error interrupt off.");
00338 #endif
00339         waiting_interrupts &= (~priority);
00340         ComputeInterrupts();
00341 }
00342 
00343 void CSerial::ComputeInterrupts () {
00344 
00345         Bitu val = IER & waiting_interrupts;
00346 
00347         if (val & ERROR_PRIORITY)                       ISR = ISR_ERROR_VAL;
00348         else if (val & TIMEOUT_PRIORITY)        ISR = ISR_FIFOTIMEOUT_VAL;
00349         else if (val & RX_PRIORITY)                     ISR = ISR_RX_VAL;
00350         else if (val & TX_PRIORITY)                     ISR = ISR_TX_VAL;
00351         else if (val & MSR_PRIORITY)            ISR = ISR_MSR_VAL;
00352         else ISR = ISR_CLEAR_VAL;
00353 
00354         if(val && !irq_active) 
00355         {
00356                 irq_active=true;
00357                 if(op2) {
00358                         PIC_ActivateIRQ(irq);
00359 #if SERIAL_DEBUG
00360                         log_ser(dbg_interrupt,"IRQ%d on.",irq);
00361 #endif
00362                 }
00363         } else if((!val) && irq_active) {
00364                 irq_active=false;
00365                 if(op2) { 
00366                         PIC_DeActivateIRQ(irq);
00367 #if SERIAL_DEBUG
00368                         log_ser(dbg_interrupt,"IRQ%d off.",irq);
00369 #endif
00370                 }
00371         }
00372 }
00373 
00374 /*****************************************************************************/
00375 /* Can a byte be received?                                                  **/
00376 /*****************************************************************************/
00377 bool CSerial::CanReceiveByte() {
00378         return !rxfifo->isFull();
00379 }
00380 
00381 /*****************************************************************************/
00382 /* A byte was received                                                      **/
00383 /*****************************************************************************/
00384 void CSerial::receiveByteEx (Bit8u data, Bit8u error) {
00385 #if SERIAL_DEBUG
00386         log_ser(dbg_serialtraffic,data<0x10 ? "\t\t\t\trx 0x%02x (%u)":
00387                 "\t\t\t\trx 0x%02x (%c)", data, data);
00388 #endif
00389         if (!(rxfifo->addb(data))) {
00390                 // Overrun error ;o
00391                 error |= LSR_OVERRUN_ERROR_MASK;
00392         }
00393         removeEvent(SERIAL_RX_TIMEOUT_EVENT);
00394         if(rxfifo->getUsage()==rx_interrupt_threshold) rise (RX_PRIORITY);
00395         else setEvent(SERIAL_RX_TIMEOUT_EVENT,bytetime*4.0f);
00396 
00397         if(error) {
00398                 // A lot of UART chips generate a framing error too when receiving break
00399                 if(error&LSR_RX_BREAK_MASK) error |= LSR_FRAMING_ERROR_MASK;
00400 #if SERIAL_DEBUG
00401                 log_ser(dbg_serialtraffic,"with error: framing=%d,overrun=%d,break=%d,parity=%d",
00402                         (error&LSR_FRAMING_ERROR_MASK)>0,(error&LSR_OVERRUN_ERROR_MASK)>0,
00403                         (error&LSR_RX_BREAK_MASK)>0,(error&LSR_PARITY_ERROR_MASK)>0);
00404 #endif
00405                 if(FCR&FCR_ACTIVATE) {
00406                         // error and FIFO active
00407                         if(!errorfifo->isFull()) {
00408                                 errors_in_fifo++;
00409                                 errorfifo->addb(error);
00410                         }
00411                         else {
00412                                 Bit8u toperror=errorfifo->getTop();
00413                                 if(!toperror) errors_in_fifo++;
00414                                 errorfifo->addb(error|toperror);
00415                         }
00416                         if(errorfifo->probeByte()) {
00417                                 // the next byte in the error fifo has an error
00418                                 rise (ERROR_PRIORITY);
00419                                 LSR |= error;
00420                         }
00421                 } else {
00422                         // error and FIFO inactive
00423                         rise (ERROR_PRIORITY);
00424                         LSR |= error;
00425                 }
00426         if(error&LSR_PARITY_ERROR_MASK) {
00427                         parityErrors++;
00428                 }
00429                 if(error&LSR_OVERRUN_ERROR_MASK) {
00430                         overrunErrors++;
00431                         if(!GETFLAG(IF)) overrunIF0++;
00432 #if SERIAL_DEBUG
00433                         log_ser(dbg_serialtraffic,"rx overrun (IF=%d)", GETFLAG(IF)>0);
00434 #endif
00435                 }
00436                 if(error&LSR_FRAMING_ERROR_MASK) {
00437                         framingErrors++;
00438                 }
00439                 if(error&LSR_RX_BREAK_MASK) {
00440                         breakErrors++;
00441                 }
00442                 // trigger status window error notification
00443                 if(!errormsg_pending) {
00444                         errormsg_pending=true;
00445                         setEvent(SERIAL_ERRMSG_EVENT,1000);
00446                 }
00447         } else {
00448                 // no error
00449                 if(FCR&FCR_ACTIVATE) {
00450                         errorfifo->addb(error);
00451                 }
00452         }
00453 }
00454 
00455 void CSerial::receiveByte (Bit8u data) {
00456         receiveByteEx(data,0);
00457 }
00458 
00459 /*****************************************************************************/
00460 /* ByteTransmitting: Byte has made it from THR to TX.                       **/
00461 /*****************************************************************************/
00462 void CSerial::ByteTransmitting() {
00463         if(sync_guardtime) {
00464                 //LOG_MSG("byte transmitting after guard");
00465                 //if(txfifo->isEmpty()) LOG_MSG("Serial port: FIFO empty when it should not");
00466                 sync_guardtime=false;
00467                 txfifo->getb();
00468         } //else LOG_MSG("byte transmitting");
00469         if(txfifo->isEmpty())rise (TX_PRIORITY);
00470 }
00471 
00472 
00473 /*****************************************************************************/
00474 /* ByteTransmitted: When a byte was sent, notify here.                      **/
00475 /*****************************************************************************/
00476 void CSerial::ByteTransmitted () {
00477         if(!txfifo->isEmpty()) {
00478                 // there is more data
00479                 Bit8u data = txfifo->getb();
00480 #if SERIAL_DEBUG
00481                 log_ser(dbg_serialtraffic,data<0x10?
00482                         "\t\t\t\t\ttx 0x%02x (%u) (from buffer)":
00483                         "\t\t\t\t\ttx 0x%02x (%c) (from buffer)",data,data);
00484 #endif
00485                 if (loopback) setEvent(SERIAL_TX_LOOPBACK_EVENT, bytetime);
00486                 else transmitByte(data,false);
00487                 if(txfifo->isEmpty())rise (TX_PRIORITY);
00488 
00489         } else {
00490 #if SERIAL_DEBUG
00491                 log_ser(dbg_serialtraffic,"tx buffer empty.");
00492 #endif
00493                 LSR |= LSR_TX_EMPTY_MASK;
00494         }
00495 }
00496 
00497 /*****************************************************************************/
00498 /* Transmit Holding Register, also LSB of Divisor Latch (r/w)               **/
00499 /*****************************************************************************/
00500 void CSerial::Write_THR (Bit8u data) {
00501         // 0-7 transmit data
00502         
00503         if (LCR & LCR_DIVISOR_Enable_MASK) {
00504                 // write to DLL
00505                 baud_divider&=0xFF00;
00506                 baud_divider |= data;
00507                 changeLineProperties();
00508         } else {
00509                 // write to THR
00510         clear (TX_PRIORITY);
00511 
00512                 if(LSR & LSR_TX_EMPTY_MASK)
00513                 {       // we were idle before
00514                         //LOG_MSG("starting new transmit cycle");
00515                         //if(sync_guardtime) LOG_MSG("Serial port internal error 1");
00516                         //if(!(LSR & LSR_TX_EMPTY_MASK)) LOG_MSG("Serial port internal error 2");
00517                         //if(txfifo->getUsage()) LOG_MSG("Serial port internal error 3");
00518                         
00519                         // need "warming up" time
00520                         sync_guardtime=true;
00521                         // block the fifo so it returns THR full (or not in case of FIFO on)
00522                         txfifo->addb(data); 
00523                         // transmit shift register is busy
00524                         LSR &= (~LSR_TX_EMPTY_MASK);
00525                         if(loopback) setEvent(SERIAL_THR_LOOPBACK_EVENT, bytetime/10);
00526                         else {
00527 #if SERIAL_DEBUG
00528                                 log_ser(dbg_serialtraffic,data<0x10?
00529                                         "\t\t\t\t\ttx 0x%02x (%u) [FIFO=%2d]":
00530                                         "\t\t\t\t\ttx 0x%02x (%c) [FIFO=%2d]",data,data,txfifo->getUsage());
00531 #endif
00532                                 transmitByte (data,true);
00533                         }
00534                 } else {
00535                         //  shift register is transmitting
00536                         if(!txfifo->addb(data)) {
00537                                 // TX overflow
00538 #if SERIAL_DEBUG
00539                                 log_ser(dbg_serialtraffic,"tx overflow");
00540 #endif
00541                                 txOverrunErrors++;
00542                                 if(!errormsg_pending) {
00543                                         errormsg_pending=true;
00544                                         setEvent(SERIAL_ERRMSG_EVENT,1000);
00545                                 }
00546                         }
00547                 }
00548         }
00549 }
00550 
00551 
00552 /*****************************************************************************/
00553 /* Receive Holding Register, also LSB of Divisor Latch (r/w)                **/
00554 /*****************************************************************************/
00555 Bitu CSerial::Read_RHR () {
00556         // 0-7 received data
00557         if (LCR & LCR_DIVISOR_Enable_MASK) return baud_divider&0xff;
00558         else {
00559                 Bit8u data=rxfifo->getb();
00560                 if(FCR&FCR_ACTIVATE) {
00561                         Bit8u error=errorfifo->getb();
00562                         if(error) errors_in_fifo--;
00563                         // new error
00564                         if(!rxfifo->isEmpty()) {
00565                                 error=errorfifo->probeByte();
00566                                 if(error) {
00567                                         LSR |= error;
00568                                         rise(ERROR_PRIORITY);
00569                                 }
00570                         }
00571                 }
00572                 // Reading RHR resets the FIFO timeout
00573                 clear (TIMEOUT_PRIORITY);
00574                 // RX int. is cleared if the buffer holds less data than the threshold
00575                 if(rxfifo->getUsage()<rx_interrupt_threshold)clear(RX_PRIORITY);
00576                 removeEvent(SERIAL_RX_TIMEOUT_EVENT);
00577                 if(!rxfifo->isEmpty()) setEvent(SERIAL_RX_TIMEOUT_EVENT,bytetime*4.0f);
00578                 return data;
00579         }
00580 }
00581 
00582 /*****************************************************************************/
00583 /* Interrupt Enable Register, also MSB of Divisor Latch (r/w)               **/
00584 /*****************************************************************************/
00585 // Modified by:
00586 // - writing to it.
00587 Bitu CSerial::Read_IER () {
00588         // 0    receive holding register (byte received)
00589         // 1    transmit holding register (byte sent)
00590         // 2    receive line status (overrun, parity error, frame error, break)
00591         // 3    modem status 
00592         // 4-7  0
00593 
00594         if (LCR & LCR_DIVISOR_Enable_MASK) return (Bitu)baud_divider>>8u;
00595         else return IER&0x0f;
00596 }
00597 
00598 void CSerial::Write_IER (Bit8u data) {
00599         if (LCR & LCR_DIVISOR_Enable_MASK) {    // write to DLM
00600                 baud_divider&=0xff;
00601                 baud_divider |= ((Bit16u)data)<<8;
00602                 changeLineProperties();
00603         } else {
00604                 // Retrigger TX interrupt
00605                 if (txfifo->isEmpty()&& (data&TX_PRIORITY))
00606                         waiting_interrupts |= TX_PRIORITY;
00607                 
00608                 IER = data&0xF;
00609                 if((FCR&FCR_ACTIVATE)&&data&RX_PRIORITY) IER |= TIMEOUT_PRIORITY; 
00610                 ComputeInterrupts();
00611         }
00612 }
00613 
00614 /*****************************************************************************/
00615 /* Interrupt Status Register (r)                                            **/
00616 /*****************************************************************************/
00617 // modified by:
00618 // - incoming interrupts
00619 // - loopback mode
00620 Bitu CSerial::Read_ISR () {
00621         // 0    0:interrupt pending 1: no interrupt
00622         // 1-3  identification
00623         //      011 LSR
00624         //              010 RXRDY
00625         //              110 RX_TIMEOUT
00626         //              001 TXRDY
00627         //              000 MSR
00628         // 4-7  0
00629 
00630         if(IER&Modem_Status_INT_Enable_MASK) updateMSR();
00631         Bit8u retval = ISR;
00632 
00633         // clear changes ISR!! mean..
00634         if(ISR==ISR_TX_VAL) clear(TX_PRIORITY);
00635         if(FCR&FCR_ACTIVATE) retval |= FIFO_STATUS_ACTIVE;
00636 
00637         return retval;
00638 }
00639 
00640 #define BIT_CHANGE_H(oldv,newv,bitmask) (!(oldv&bitmask) && (newv&bitmask))
00641 #define BIT_CHANGE_L(oldv,newv,bitmask) ((oldv&bitmask) && !(newv&bitmask))
00642 
00643 void CSerial::Write_FCR (Bit8u data) {
00644         if(BIT_CHANGE_H(FCR,data,FCR_ACTIVATE)) {
00645                 // FIFO was switched on
00646                 errors_in_fifo=0; // should already be 0
00647                 errorfifo->setSize(fifosize);
00648                 rxfifo->setSize(fifosize);
00649                 txfifo->setSize(fifosize);
00650         } else if(BIT_CHANGE_L(FCR,data,FCR_ACTIVATE)) {
00651                 // FIFO was switched off
00652                 errors_in_fifo=0;
00653                 errorfifo->setSize(1);
00654                 rxfifo->setSize(1);
00655                 txfifo->setSize(1);
00656                 rx_interrupt_threshold=1;
00657         }
00658         FCR=data&0xCF;
00659         if(FCR&FCR_CLEAR_RX) {
00660                 errors_in_fifo=0;
00661                 errorfifo->clear();
00662                 rxfifo->clear();
00663         }
00664         if(FCR&FCR_CLEAR_TX) txfifo->clear();
00665         if(FCR&FCR_ACTIVATE) {
00666                 switch(FCR>>6) {
00667                         case 0: rx_interrupt_threshold=1; break;
00668                         case 1: rx_interrupt_threshold=4; break;
00669                         case 2: rx_interrupt_threshold=8; break;
00670                         case 3: rx_interrupt_threshold=14; break;
00671                 }
00672         }
00673 }
00674 
00675 /*****************************************************************************/
00676 /* Line Control Register (r/w)                                              **/
00677 /*****************************************************************************/
00678 // signal decoder configuration:
00679 // - parity, stopbits, word length
00680 // - send break
00681 // - switch between RHR/THR and baud rate registers
00682 // Modified by:
00683 // - writing to it.
00684 Bitu CSerial::Read_LCR () {
00685         // 0-1  word length
00686         // 2    stop bits
00687         // 3    parity enable
00688         // 4-5  parity type
00689         // 6    set break
00690         // 7    divisor latch enable
00691         return LCR;
00692 }
00693 
00694 void CSerial::Write_LCR (Bit8u data) {
00695         Bit8u lcr_old = LCR;
00696         LCR = data;
00697         if (((data ^ lcr_old) & LCR_PORTCONFIG_MASK) != 0) {
00698                 changeLineProperties();
00699         }
00700         if (((data ^ lcr_old) & LCR_BREAK_MASK) != 0) {
00701                 if(!loopback) setBreak ((LCR & LCR_BREAK_MASK)!=0);
00702                 else {
00703                         // TODO: set loopback break event to reveiveError after
00704                 }
00705 #if SERIAL_DEBUG
00706                 log_ser(dbg_serialtraffic,((LCR & LCR_BREAK_MASK)!=0) ?
00707                         "break on.":"break off.");
00708 #endif
00709         }
00710 }
00711 
00712 /*****************************************************************************/
00713 /* Modem Control Register (r/w)                                             **/
00714 /*****************************************************************************/
00715 // Set levels of RTS and DTR, as well as loopback-mode.
00716 // Modified by: 
00717 // - writing to it.
00718 Bitu CSerial::Read_MCR () {
00719         // 0    -DTR
00720         // 1    -RTS
00721         // 2    -OP1
00722         // 3    -OP2
00723         // 4    loopback enable
00724         // 5-7  0
00725         Bit8u retval=0;
00726         if(dtr) retval|=MCR_DTR_MASK;
00727         if(rts) retval|=MCR_RTS_MASK;
00728         if(op1) retval|=MCR_OP1_MASK;
00729         if(op2) retval|=MCR_OP2_MASK;
00730         if(loopback) retval|=MCR_LOOPBACK_Enable_MASK;
00731         return retval;
00732 }
00733 
00734 void CSerial::Write_MCR (Bit8u data) {
00735         // WARNING: At the time setRTSDTR is called rts and dsr members are still wrong.
00736         if (data&FIFO_FLOWCONTROL) LOG_MSG("Warning: tried to activate hardware handshake.");
00737         bool new_dtr = (data & MCR_DTR_MASK)? true:false;
00738         bool new_rts = (data & MCR_RTS_MASK)? true:false;
00739         bool new_op1 = (data & MCR_OP1_MASK)? true:false;
00740         bool new_op2 = (data & MCR_OP2_MASK)? true:false;
00741         bool new_loopback = (data & MCR_LOOPBACK_Enable_MASK)? true:false;
00742         if (loopback != new_loopback) {
00743                 if (new_loopback) setRTSDTR(false,false);
00744                 else setRTSDTR(new_rts,new_dtr);
00745         }
00746 
00747         if (new_loopback) {     // is on:
00748                 // DTR->DSR
00749                 // RTS->CTS
00750                 // OP1->RI
00751                 // OP2->CD
00752                 if (new_dtr != dtr && !d_dsr) {
00753                         d_dsr = true;
00754                         rise (MSR_PRIORITY);
00755                 }
00756                 if (new_dtr != dtr && !d_dsr) {
00757                         d_dsr = true;
00758                         rise (MSR_PRIORITY);
00759                 }
00760                 if (new_op1 != op1 && !d_ri) {
00761                         // interrupt only at trailing edge
00762                         if (!new_op1) {
00763                                 d_ri = true;
00764                                 rise (MSR_PRIORITY);
00765                         }
00766                 }
00767                 if (new_op2 != op2 && !d_cd) {
00768                         d_cd = true;
00769                         rise (MSR_PRIORITY);
00770                 }
00771         } else {
00772                 // loopback is off
00773                 if (new_rts != rts) {
00774                         // RTS difference
00775                         if (new_dtr != dtr) {
00776                                 // both difference
00777 
00778 #if SERIAL_DEBUG
00779                                 log_ser(dbg_modemcontrol,"RTS %x.",new_rts);
00780                                 log_ser(dbg_modemcontrol,"DTR %x.",new_dtr);
00781 #endif
00782                                 setRTSDTR(new_rts, new_dtr);
00783                         } else {
00784                                 // only RTS
00785 
00786 #if SERIAL_DEBUG
00787                                 log_ser(dbg_modemcontrol,"RTS %x.",new_rts);
00788 #endif
00789                                 setRTS(new_rts);
00790                         }
00791                 } else if (new_dtr != dtr) {
00792                         // only DTR
00793 #if SERIAL_DEBUG
00794                                 log_ser(dbg_modemcontrol,"%DTR %x.",new_dtr);
00795 #endif
00796                         setDTR(new_dtr);
00797                 }
00798         }
00799         // interrupt logic: if new_OP2 is 0, the IRQ line is tristated (pulled high)
00800         // which turns off the IRQ generation.
00801         if ((!op2) && new_op2) {
00802                 // irq has been enabled (tristate high -> irq level)
00803                 // Generate one if ComputeInterrupts has set irq_active to true
00804                 if (irq_active) PIC_ActivateIRQ(irq);
00805         } else if (op2 && (!new_op2)) {
00806                 // irq has been disabled (irq level -> tristate) 
00807                 // Remove the IRQ signal if the irq was being generated before
00808                 if (irq_active) PIC_DeActivateIRQ(irq); 
00809         }
00810 
00811         dtr=new_dtr;
00812         rts=new_rts;
00813         op1=new_op1;
00814         op2=new_op2;
00815         loopback=new_loopback;
00816 }
00817 
00818 /*****************************************************************************/
00819 /* Line Status Register (r)                                                 **/
00820 /*****************************************************************************/
00821 // errors, tx registers status, rx register status
00822 // modified by:
00823 // - event from real serial port
00824 // - loopback
00825 Bitu CSerial::Read_LSR () {
00826         Bitu retval = LSR & (LSR_ERROR_MASK|LSR_TX_EMPTY_MASK);
00827         if(txfifo->isEmpty()) retval |= LSR_TX_HOLDING_EMPTY_MASK;
00828         if(!(rxfifo->isEmpty()))retval |= LSR_RX_DATA_READY_MASK;
00829         if(errors_in_fifo) retval |= FIFO_ERROR;
00830         LSR &= (~LSR_ERROR_MASK);                       // clear error bits on read
00831         clear (ERROR_PRIORITY);
00832         return retval;
00833 }
00834 
00835 void CSerial::Write_MSR (Bit8u val) {
00836         d_cts = (val&MSR_dCTS_MASK)?true:false;
00837         d_dsr = (val&MSR_dDSR_MASK)?true:false;
00838         d_cd = (val&MSR_dCD_MASK)?true:false;
00839         d_ri = (val&MSR_dRI_MASK)?true:false;
00840 }
00841 
00842 /*****************************************************************************/
00843 /* Modem Status Register (r)                                                **/
00844 /*****************************************************************************/
00845 // Contains status of the control input lines (CD, RI, DSR, CTS) and
00846 // their "deltas": if level changed since last read delta = 1.
00847 // modified by:
00848 // - real values
00849 // - write operation to MCR in loopback mode
00850 Bitu CSerial::Read_MSR () {
00851         Bit8u retval=0;
00852         
00853         if (loopback) {
00854                 
00855                 if (rts) retval |= MSR_CTS_MASK;
00856                 if (dtr) retval |= MSR_DSR_MASK;
00857                 if (op1) retval |= MSR_RI_MASK;
00858                 if (op2) retval |= MSR_CD_MASK;
00859         
00860         } else {
00861 
00862                 updateMSR();
00863                 if (cd) retval |= MSR_CD_MASK;
00864                 if (ri) retval |= MSR_RI_MASK;
00865                 if (dsr) retval |= MSR_DSR_MASK;
00866                 if (cts) retval |= MSR_CTS_MASK;
00867         
00868         }
00869         // new delta flags
00870         if(d_cd) retval|=MSR_dCD_MASK;
00871         if(d_ri) retval|=MSR_dRI_MASK;
00872         if(d_cts) retval|=MSR_dCTS_MASK;
00873         if(d_dsr) retval|=MSR_dDSR_MASK;
00874         
00875         d_cd = false;
00876         d_ri = false;
00877         d_cts = false;
00878         d_dsr = false;
00879         
00880         clear (MSR_PRIORITY);
00881         return retval;
00882 }
00883 
00884 /*****************************************************************************/
00885 /* Scratchpad Register (r/w)                                                **/
00886 /*****************************************************************************/
00887 // Just a memory register. Not much to do here.
00888 Bitu CSerial::Read_SPR () {
00889         return SPR;
00890 }
00891 
00892 void CSerial::Write_SPR (Bit8u data) {
00893         SPR = data;
00894 }
00895 
00896 /*****************************************************************************/
00897 /* Write_reserved                                                           **/
00898 /*****************************************************************************/
00899 void CSerial::Write_reserved (Bit8u data, Bit8u address) {
00900     (void)data;//UNUSED
00901     (void)address;//UNUSED
00902         /*LOG_UART("Serial%d: Write to reserved register, value 0x%x, register %x",
00903                 COMNUMBER, data, address);*/
00904 }
00905 
00906 /*****************************************************************************/
00907 /* MCR Access: returns cirquit state as boolean.                            **/
00908 /*****************************************************************************/
00909 bool CSerial::getDTR () {
00910         if(loopback) return false;
00911         else return dtr;
00912 }
00913 
00914 bool CSerial::getRTS () {
00915         if(loopback) return false;
00916         else return rts;
00917 }
00918 
00919 /*****************************************************************************/
00920 /* MSR Access                                                               **/
00921 /*****************************************************************************/
00922 bool CSerial::getRI () {
00923         return ri;
00924 }
00925 
00926 bool CSerial::getCD () {
00927         return cd;
00928 }
00929 
00930 bool CSerial::getDSR () {
00931         return dsr;
00932 }
00933 
00934 bool CSerial::getCTS () {
00935         return cts;
00936 }
00937 
00938 void CSerial::setRI (bool value) {
00939         if (value != ri) {
00940 
00941 #if SERIAL_DEBUG
00942                 log_ser(dbg_modemcontrol,"%RI  %x.",value);
00943 #endif
00944                 // don't change delta when in loopback mode
00945                 ri=value;
00946                 if(!loopback) {
00947             if(value==false) d_ri=true;
00948                         rise (MSR_PRIORITY);
00949                 }
00950         }
00951         //else no change
00952 }
00953 void CSerial::setDSR (bool value) {
00954         if (value != dsr) {
00955 #if SERIAL_DEBUG
00956                 log_ser(dbg_modemcontrol,"DSR %x.",value);
00957 #endif
00958                 // don't change delta when in loopback mode
00959                 dsr=value;
00960                 if(!loopback) {
00961             d_dsr=true;
00962                         rise (MSR_PRIORITY);
00963                 }
00964         }
00965         //else no change
00966 }
00967 void CSerial::setCD (bool value) {
00968         if (value != cd) {
00969 #if SERIAL_DEBUG
00970                 log_ser(dbg_modemcontrol,"CD  %x.",value);
00971 #endif
00972                 // don't change delta when in loopback mode
00973                 cd=value;
00974                 if(!loopback) {
00975             d_cd=true;
00976                         rise (MSR_PRIORITY);
00977                 }
00978         }
00979         //else no change
00980 }
00981 void CSerial::setCTS (bool value) {
00982         if (value != cts) {
00983 #if SERIAL_DEBUG
00984                 log_ser(dbg_modemcontrol,"CTS %x.",value);
00985 #endif
00986                 // don't change delta when in loopback mode
00987                 cts=value;
00988                 if(!loopback) {
00989             d_cts=true;
00990                         rise (MSR_PRIORITY);
00991                 }
00992         }
00993         //else no change
00994 }
00995 
00996 /*****************************************************************************/
00997 /* Initialisation                                                           **/
00998 /*****************************************************************************/
00999 void CSerial::Init_Registers () {
01000         // The "power on" settings
01001         irq_active=false;
01002         waiting_interrupts = 0x0;
01003 
01004         Bit32u initbps = 9600;
01005         Bit8u bytesize = 8;
01006         char parity = 'N';
01007                                                           
01008         Bit8u lcrresult = 0;
01009         Bit16u baudresult = 0;
01010 
01011         IER = 0;
01012         ISR = 0x1;
01013         LCR = 0;
01014         //MCR = 0xff;
01015         loopback = true;
01016         dtr=true;
01017         rts=true;
01018         op1=true;
01019         op2=true;
01020 
01021         sync_guardtime=false;
01022         FCR=0xff;
01023         Write_FCR(0x00);
01024 
01025 
01026         LSR = 0x60;
01027         d_cts = true;   
01028         d_dsr = true;   
01029         d_ri = true;
01030         d_cd = true;    
01031         cts = true;     
01032         dsr = true;     
01033         ri = true;      
01034         cd = true;      
01035 
01036         SPR = 0xFF;
01037 
01038         baud_divider=0x0;
01039 
01040         // make lcr: byte size, parity, stopbits, baudrate
01041 
01042         if (bytesize == 5)
01043                 lcrresult |= LCR_DATABITS_5;
01044         else if (bytesize == 6)
01045                 lcrresult |= LCR_DATABITS_6;
01046         else if (bytesize == 7)
01047                 lcrresult |= LCR_DATABITS_7;
01048         else
01049                 lcrresult |= LCR_DATABITS_8;
01050 
01051         switch(parity)
01052         {
01053         case 'N':
01054         case 'n':
01055                 lcrresult |= LCR_PARITY_NONE;
01056                 break;
01057         case 'O':
01058         case 'o':
01059                 lcrresult |= LCR_PARITY_ODD;
01060                 break;
01061         case 'E':
01062         case 'e':
01063                 lcrresult |= LCR_PARITY_EVEN;
01064                 break;
01065         case 'M':
01066         case 'm':
01067                 lcrresult |= LCR_PARITY_MARK;
01068                 break;
01069         case 'S':
01070         case 's':
01071                 lcrresult |= LCR_PARITY_SPACE;
01072                 break;
01073         }
01074 
01075         // baudrate
01076         if (initbps > 0)
01077                 baudresult = (Bit16u) (115200 / initbps);
01078         else
01079                 baudresult = 12;                        // = 9600 baud
01080 
01081         Write_MCR (0);
01082         Write_LCR (LCR_DIVISOR_Enable_MASK);
01083         Write_THR ((Bit8u) baudresult & 0xff);
01084         Write_IER ((Bit8u) (baudresult >> 8));
01085         Write_LCR (lcrresult);
01086         updateMSR();
01087         Read_MSR();
01088         PIC_DeActivateIRQ(irq);
01089 }
01090 
01091 CSerial::CSerial(Bitu id, CommandLine* cmd) {
01092         idnumber=id;
01093         Bit16u base = serial_baseaddr[id];
01094     InstallationSuccessful = false;
01095     bytetime = 0;
01096     waiting_interrupts = 0;
01097     baud_divider = 0;
01098     IER = 0;
01099     irq_active = false;
01100     ISR = 0;
01101     LCR = 0;
01102     dtr = false;
01103     rts = false;
01104     op1 = false;
01105     op2 = false;
01106     loopback = false;
01107     LSR = 0;
01108     SPR = 0;
01109     loopback_data = 0;
01110     errors_in_fifo = 0;
01111     rx_interrupt_threshold = 0;
01112     FCR = 0;
01113     sync_guardtime = false;
01114 
01115     d_cts=false;                // bit0: deltaCTS
01116     d_dsr=false;                // bit1: deltaDSR
01117     d_ri=false;                 // bit2: deltaRI
01118     d_cd=false;                 // bit3: deltaCD
01119     cts=false;                  // bit4: CTS
01120     dsr=false;                  // bit5: DSR
01121     ri=false;                   // bit6: RI
01122     cd=false;                   // bit7: CD
01123 
01124         irq = serial_defaultirq[id];
01125         getBituSubstring("irq:",&irq, cmd);
01126         if (irq < 2 || irq > 15) irq = serial_defaultirq[id];
01127 
01128 #if SERIAL_DEBUG
01129         dbg_serialtraffic = cmd->FindExist("dbgtr", false);
01130         dbg_modemcontrol  = cmd->FindExist("dbgmd", false);
01131         dbg_register      = cmd->FindExist("dbgreg", false);
01132         dbg_interrupt     = cmd->FindExist("dbgirq", false);
01133         dbg_aux                   = cmd->FindExist("dbgaux", false);
01134         
01135         if(cmd->FindExist("dbgall", false)) {
01136                 dbg_serialtraffic= 
01137                 dbg_modemcontrol= 
01138                 dbg_register=
01139                 dbg_interrupt=
01140                 dbg_aux= true;
01141         }
01142 
01143 
01144         if(dbg_serialtraffic|dbg_modemcontrol|dbg_register|dbg_interrupt|dbg_aux)
01145                 debugfp=OpenCaptureFile("serlog",".serlog.txt");
01146         else debugfp=0;
01147 
01148         if(debugfp == 0) {
01149                 dbg_serialtraffic= 
01150                 dbg_modemcontrol= 
01151                 dbg_register=
01152                 dbg_interrupt=
01153                 dbg_aux= false;
01154         } else {
01155                 std::string cleft;
01156                 cmd->GetStringRemain(cleft);
01157 
01158                 log_ser(true,"Serial%d: BASE %3x, IRQ %d, initstring \"%s\"\r\n\r\n",
01159                         COMNUMBER,base,irq,cleft.c_str());
01160         }
01161 #endif
01162         fifosize=16;
01163 
01164         errorfifo = new MyFifo(fifosize);
01165         rxfifo = new MyFifo(fifosize);
01166         txfifo = new MyFifo(fifosize);
01167 
01168         mydosdevice=NULL;
01169         errormsg_pending=false;
01170         framingErrors=0;
01171         parityErrors=0;
01172         overrunErrors=0;
01173         txOverrunErrors=0;
01174         overrunIF0=0;
01175         breakErrors=0;
01176         
01177         for (Bitu i = 0; i <= 7; i++) {
01178                 WriteHandler[i].Install (i + base, SERIAL_Write, IO_MB);
01179                 ReadHandler[i].Install (i + base, SERIAL_Read, IO_MB);
01180         }
01181 }
01182 
01183 bool CSerial::getBituSubstring(const char* name,Bitu* data, CommandLine* cmd) {
01184         std::string tmpstring;
01185         if(!(cmd->FindStringBegin(name,tmpstring,false))) return false;
01186         const char* tmpchar=tmpstring.c_str();
01187 
01188         unsigned int tmp;
01189         if(sscanf(tmpchar,"%u",&tmp)!=1) return false;
01190         *data = (Bitu)tmp;
01191         return true;
01192 }
01193 
01194 void CSerial::registerDOSDevice() {
01195         if (mydosdevice == NULL) {
01196                 LOG(LOG_MISC,LOG_DEBUG)("COM%d: Registering DOS device",(int)idnumber+1);
01197                 mydosdevice = new device_COM(this);
01198                 DOS_AddDevice(mydosdevice);
01199         }
01200 }
01201 
01202 void CSerial::unregisterDOSDevice() {
01203         if (mydosdevice != NULL) {
01204                 LOG(LOG_MISC,LOG_DEBUG)("COM%d: Unregistering DOS device",(int)idnumber+1);
01205                 DOS_DelDevice(mydosdevice); // deletes the pointer for us!
01206                 mydosdevice=NULL;
01207         }
01208 }
01209 
01210 CSerial::~CSerial(void) {
01211         unregisterDOSDevice();
01212         for(Bit8u i = 0; i <= SERIAL_BASE_EVENT_COUNT; i++)
01213                 removeEvent(i);
01214 
01215         if (rxfifo != NULL) {
01216                 delete rxfifo;
01217                 rxfifo = NULL;
01218         }
01219         if (txfifo != NULL) {
01220                 delete txfifo;
01221                 txfifo = NULL;
01222         }
01223         if (errorfifo != NULL) {
01224                 delete errorfifo;
01225                 errorfifo = NULL;
01226         }
01227 }
01228 
01229 bool CSerial::Getchar(Bit8u* data, Bit8u* lsr, bool wait_dsr, Bitu timeout) {
01230         double starttime=PIC_FullIndex();
01231         // wait for DSR on
01232         if(wait_dsr) {
01233                 while((!(Read_MSR()&0x20))&&(starttime>PIC_FullIndex()-timeout))
01234                         CALLBACK_Idle();
01235                 if(!(starttime>PIC_FullIndex()-timeout)) {
01236 #if SERIAL_DEBUG
01237                         log_ser(dbg_aux,"Getchar status timeout: MSR 0x%x",Read_MSR());
01238 #endif
01239                         return false;
01240                 }
01241         }
01242         // wait for a byte to arrive
01243         while((!((*lsr=(Bit8u)Read_LSR())&0x1))&&(starttime>PIC_FullIndex()-timeout))
01244                 CALLBACK_Idle();
01245         
01246         if(!(starttime>PIC_FullIndex()-timeout)) {
01247 #if SERIAL_DEBUG
01248                 log_ser(dbg_aux,"Getchar data timeout: MSR 0x%x",Read_MSR());
01249 #endif
01250                 return false;
01251         }
01252         *data=(Bit8u)Read_RHR();
01253 
01254 #if SERIAL_DEBUG
01255         log_ser(dbg_aux,"Getchar read 0x%x",*data);
01256 #endif
01257         return true;
01258 }
01259 
01260 
01261 bool CSerial::Putchar(Bit8u data, bool wait_dsr, bool wait_cts, Bitu timeout) {
01262         
01263         double starttime=PIC_FullIndex();
01264         // wait for it to become empty
01265         while(!(Read_LSR()&0x20)) {
01266                 CALLBACK_Idle();
01267         }
01268         // wait for DSR+CTS on
01269         if(wait_dsr||wait_cts) {
01270                 if(wait_dsr||wait_cts) {
01271                         while(((Read_MSR()&0x30)!=0x30)&&(starttime>PIC_FullIndex()-timeout))
01272                                 CALLBACK_Idle();
01273                 } else if(wait_dsr) {
01274                         while(!(Read_MSR()&0x20)&&(starttime>PIC_FullIndex()-timeout))
01275                                 CALLBACK_Idle();
01276                 } else if(wait_cts) {
01277                         while(!(Read_MSR()&0x10)&&(starttime>PIC_FullIndex()-timeout))
01278                                 CALLBACK_Idle();
01279                 } 
01280                 if(!(starttime>PIC_FullIndex()-timeout)) {
01281 #if SERIAL_DEBUG
01282                         log_ser(dbg_aux,"Putchar timeout: MSR 0x%x",Read_MSR());
01283 #endif
01284                         return false;
01285                 }
01286         }
01287         Write_THR(data);
01288 
01289 #if SERIAL_DEBUG
01290         log_ser(dbg_aux,"Putchar 0x%x",data);
01291 #endif
01292 
01293         return true;
01294 }
01295 
01296 void BIOS_PnP_ComPortRegister(Bitu port,Bitu irq);
01297 void BIOS_SetCOMPort(Bitu port, Bit16u baseaddr);
01298 
01299 void BIOS_Post_register_comports_PNP() {
01300         unsigned int i;
01301 
01302         for (i=0;i < 4;i++) {
01303                 if (serialports[i] != NULL) {
01304                         BIOS_PnP_ComPortRegister(serial_baseaddr[i],serialports[i]->irq);
01305                 }
01306         }
01307 }
01308 
01309 Bitu bios_post_comport_count() {
01310         Bitu count = 0;
01311         unsigned int i;
01312 
01313         for (i=0;i < 4;i++) {
01314                 if (serialports[i] != NULL)
01315                         count++;
01316         }
01317 
01318         return count;
01319 }
01320 
01321 /* at BIOS POST stage, write serial ports to bios data area */
01322 void BIOS_Post_register_comports() {
01323         unsigned int i;
01324 
01325         for (i=0;i < 4;i++) {
01326                 if (serialports[i] != NULL)
01327                         BIOS_SetCOMPort(i,serial_baseaddr[i]);
01328         }
01329 }
01330 
01331 class SERIALPORTS:public Module_base {
01332 public:
01333         SERIALPORTS (Section * configuration):Module_base (configuration) {
01334                 Section_prop *section = static_cast <Section_prop*>(configuration);
01335 
01336         // TODO: PC-98 does have serial ports, though differently.
01337         //       COM1 is a 8251 UART, while COM2 and higher if they exist are 8250/16xxx UARTs
01338         if (IS_PC98_ARCH) return;
01339 
01340 #if C_MODEM
01341                 const Prop_path *pbFilename = section->Get_path("phonebookfile");
01342                 MODEM_ReadPhonebook(pbFilename->realpath);
01343 #endif
01344                 
01345                 char s_property[] = "serialx"; 
01346                 for(Bit8u i = 0; i < 4; i++) {
01347                         // get the configuration property
01348                         s_property[6] = '1' + i;
01349                         Prop_multival* p = section->Get_multival(s_property);
01350                         std::string type = p->GetSection()->Get_string("type");
01351                         CommandLine cmd(0,p->GetSection()->Get_string("parameters"));
01352                         
01353                         // detect the type
01354                         if (type=="dummy") {
01355                                 serialports[i] = new CSerialDummy (i, &cmd);
01356                         }
01357                         else if (type=="log") {
01358                                 serialports[i] = new CSerialLog (i, &cmd);
01359                         }
01360                         else if (type=="file") {
01361                                 serialports[i] = new CSerialFile (i, &cmd);
01362                         }
01363                         else if (type=="serialmouse") {
01364                                 serialports[i] = new CSerialMouse (i, &cmd);
01365                         }
01366 #ifdef DIRECTSERIAL_AVAILIBLE
01367                         else if (type=="directserial") {
01368                                 serialports[i] = new CDirectSerial (i, &cmd);
01369                                 if (!serialports[i]->InstallationSuccessful)  {
01370                                         // serial port name was wrong or already in use
01371                                         delete serialports[i];
01372                                         serialports[i] = NULL;
01373                                 }
01374                         }
01375 #endif
01376 #if C_MODEM
01377                         else if(type=="modem") {
01378                                 serialports[i] = new CSerialModem (i, &cmd);
01379                                 if (!serialports[i]->InstallationSuccessful)  {
01380                                         delete serialports[i];
01381                                         serialports[i] = NULL;
01382                                 }
01383                         }
01384                         else if(type=="nullmodem") {
01385                                 serialports[i] = new CNullModem (i, &cmd);
01386                                 if (!serialports[i]->InstallationSuccessful)  {
01387                                         delete serialports[i];
01388                                         serialports[i] = NULL;
01389                                 }
01390                         }
01391 #endif
01392                         else if(type=="disabled") {
01393                                 serialports[i] = NULL;
01394                         } else {
01395                                 serialports[i] = NULL;
01396                                 LOG_MSG("Invalid type for serial%d",(int)i+1);
01397                         }
01398                 } // for 1-4
01399         }
01400 
01401         ~SERIALPORTS () {
01402                 for (Bitu i = 0; i < 4; i++)
01403                         if (serialports[i]) {
01404                                 delete serialports[i];
01405                                 serialports[i] = 0;
01406                         }
01407         }
01408 };
01409 
01410 static SERIALPORTS *testSerialPortsBaseclass;
01411 
01412 void SERIAL_Destroy (Section * sec) {
01413     (void)sec;//UNUSED
01414         if (testSerialPortsBaseclass) {
01415                 LOG(LOG_MISC,LOG_DEBUG)("Deleting serial port base class");
01416                 delete testSerialPortsBaseclass;
01417                 testSerialPortsBaseclass = NULL;
01418         }
01419 }
01420 
01421 void SERIAL_OnPowerOn (Section * sec) {
01422     (void)sec;//UNUSED
01423         // should never happen
01424         LOG(LOG_MISC,LOG_DEBUG)("Reinitializing serial emulation");
01425         if (testSerialPortsBaseclass) delete testSerialPortsBaseclass;
01426         testSerialPortsBaseclass = new SERIALPORTS (control->GetSection("serial"));
01427 }
01428 
01429 void SERIAL_OnDOSKernelInit (Section * sec) {
01430     (void)sec;//UNUSED
01431         unsigned int i;
01432 
01433         LOG(LOG_MISC,LOG_DEBUG)("DOS kernel initializing, creating COMx devices");
01434 
01435         for (i=0;i < 3;i++) {
01436                 if (serialports[i] != NULL)
01437                         serialports[i]->registerDOSDevice();
01438         }
01439 }
01440 
01441 void SERIAL_OnDOSKernelExit (Section * sec) {
01442     (void)sec;//UNUSED
01443         unsigned int i;
01444 
01445         for (i=0;i < 3;i++) {
01446                 if (serialports[i] != NULL)
01447                         serialports[i]->unregisterDOSDevice();
01448         }
01449 }
01450 
01451 void SERIAL_OnReset (Section * sec) {
01452     (void)sec;//UNUSED
01453         unsigned int i;
01454 
01455         // FIXME: Unregister/destroy the DOS devices, but consider that the DOS kernel at reset is gone.
01456         for (i=0;i < 3;i++) {
01457                 if (serialports[i] != NULL)
01458                         serialports[i]->unregisterDOSDevice();
01459         }
01460 }
01461 
01462 void SERIAL_Init () {
01463         LOG(LOG_MISC,LOG_DEBUG)("Initializing serial port emulation");
01464 
01465         AddExitFunction(AddExitFunctionFuncPair(SERIAL_Destroy),true);
01466 
01467     if (!IS_PC98_ARCH) {
01468         AddVMEventFunction(VM_EVENT_POWERON,AddVMEventFunctionFuncPair(SERIAL_OnPowerOn));
01469         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(SERIAL_OnReset));
01470         AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(SERIAL_OnDOSKernelExit));
01471         AddVMEventFunction(VM_EVENT_DOS_INIT_KERNEL_READY,AddVMEventFunctionFuncPair(SERIAL_OnDOSKernelInit));
01472     }
01473 }
01474 
01475 // save state support
01476 void *Serial_EventHandler_PIC_Event = (void*)((uintptr_t)Serial_EventHandler);