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