DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/serialport/serialport.cpp
00001 /*
00002  *  Copyright (C) 2002-2019  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, 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 ((Bit8u)val);
00184                         return;
00185                 case IER_OFFSET:
00186                         serialports[i]->Write_IER ((Bit8u)val);
00187                         return;
00188                 case FCR_OFFSET:
00189                         serialports[i]->Write_FCR ((Bit8u)val);
00190                         return;
00191                 case LCR_OFFSET:
00192                         serialports[i]->Write_LCR ((Bit8u)val);
00193                         return;
00194                 case MCR_OFFSET:
00195                         serialports[i]->Write_MCR ((Bit8u)val);
00196                         return;
00197                 case MSR_OFFSET:
00198                         serialports[i]->Write_MSR ((Bit8u)val);
00199                         return;
00200                 case SPR_OFFSET:
00201                         serialports[i]->Write_SPR ((Bit8u)val);
00202                         return;
00203                 default:
00204                         serialports[i]->Write_reserved ((Bit8u)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((Bit16u)(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 new_dtr = data & MCR_DTR_MASK? true:false;
00737         bool new_rts = data & MCR_RTS_MASK? true:false;
00738         bool new_op1 = data & MCR_OP1_MASK? true:false;
00739         bool new_op2 = data & MCR_OP2_MASK? true:false;
00740         bool new_loopback = data & MCR_LOOPBACK_Enable_MASK? true:false;
00741         if (loopback != new_loopback) {
00742                 if (new_loopback) setRTSDTR(false,false);
00743                 else setRTSDTR(new_rts,new_dtr);
00744         }
00745 
00746         if (new_loopback) {     // is on:
00747                 // DTR->DSR
00748                 // RTS->CTS
00749                 // OP1->RI
00750                 // OP2->CD
00751                 if (new_dtr != dtr && !d_dsr) {
00752                         d_dsr = true;
00753                         rise (MSR_PRIORITY);
00754                 }
00755                 if (new_dtr != dtr && !d_dsr) {
00756                         d_dsr = true;
00757                         rise (MSR_PRIORITY);
00758                 }
00759                 if (new_op1 != op1 && !d_ri) {
00760                         // interrupt only at trailing edge
00761                         if (!new_op1) {
00762                                 d_ri = true;
00763                                 rise (MSR_PRIORITY);
00764                         }
00765                 }
00766                 if (new_op2 != op2 && !d_cd) {
00767                         d_cd = true;
00768                         rise (MSR_PRIORITY);
00769                 }
00770         } else {
00771                 // loopback is off
00772                 if (new_rts != rts) {
00773                         // RTS difference
00774                         if (new_dtr != dtr) {
00775                                 // both difference
00776 
00777 #if SERIAL_DEBUG
00778                                 log_ser(dbg_modemcontrol,"RTS %x.",new_rts);
00779                                 log_ser(dbg_modemcontrol,"DTR %x.",new_dtr);
00780 #endif
00781                                 setRTSDTR(new_rts, new_dtr);
00782                         } else {
00783                                 // only RTS
00784 
00785 #if SERIAL_DEBUG
00786                                 log_ser(dbg_modemcontrol,"RTS %x.",new_rts);
00787 #endif
00788                                 setRTS(new_rts);
00789                         }
00790                 } else if (new_dtr != dtr) {
00791                         // only DTR
00792 #if SERIAL_DEBUG
00793                                 log_ser(dbg_modemcontrol,"%DTR %x.",new_dtr);
00794 #endif
00795                         setDTR(new_dtr);
00796                 }
00797         }
00798         // interrupt logic: if new_OP2 is 0, the IRQ line is tristated (pulled high)
00799         // which turns off the IRQ generation.
00800         if ((!op2) && new_op2) {
00801                 // irq has been enabled (tristate high -> irq level)
00802                 // Generate one if ComputeInterrupts has set irq_active to true
00803                 if (irq_active) PIC_ActivateIRQ(irq);
00804         } else if (op2 && (!new_op2)) {
00805                 // irq has been disabled (irq level -> tristate) 
00806                 // Remove the IRQ signal if the irq was being generated before
00807                 if (irq_active) PIC_DeActivateIRQ(irq); 
00808         }
00809 
00810         dtr=new_dtr;
00811         rts=new_rts;
00812         op1=new_op1;
00813         op2=new_op2;
00814         loopback=new_loopback;
00815 }
00816 
00817 /*****************************************************************************/
00818 /* Line Status Register (r)                                                 **/
00819 /*****************************************************************************/
00820 // errors, tx registers status, rx register status
00821 // modified by:
00822 // - event from real serial port
00823 // - loopback
00824 Bitu CSerial::Read_LSR () {
00825         Bitu retval = LSR & (LSR_ERROR_MASK|LSR_TX_EMPTY_MASK);
00826         if(txfifo->isEmpty()) retval |= LSR_TX_HOLDING_EMPTY_MASK;
00827         if(!(rxfifo->isEmpty()))retval |= LSR_RX_DATA_READY_MASK;
00828         if(errors_in_fifo) retval |= FIFO_ERROR;
00829         LSR &= (~LSR_ERROR_MASK);                       // clear error bits on read
00830         clear (ERROR_PRIORITY);
00831         return retval;
00832 }
00833 
00834 void CSerial::Write_MSR (Bit8u val) {
00835         d_cts = (val&MSR_dCTS_MASK)?true:false;
00836         d_dsr = (val&MSR_dDSR_MASK)?true:false;
00837         d_cd = (val&MSR_dCD_MASK)?true:false;
00838         d_ri = (val&MSR_dRI_MASK)?true:false;
00839 }
00840 
00841 /*****************************************************************************/
00842 /* Modem Status Register (r)                                                **/
00843 /*****************************************************************************/
00844 // Contains status of the control input lines (CD, RI, DSR, CTS) and
00845 // their "deltas": if level changed since last read delta = 1.
00846 // modified by:
00847 // - real values
00848 // - write operation to MCR in loopback mode
00849 Bitu CSerial::Read_MSR () {
00850         Bit8u retval=0;
00851         
00852         if (loopback) {
00853                 
00854                 if (rts) retval |= MSR_CTS_MASK;
00855                 if (dtr) retval |= MSR_DSR_MASK;
00856                 if (op1) retval |= MSR_RI_MASK;
00857                 if (op2) retval |= MSR_CD_MASK;
00858         
00859         } else {
00860 
00861                 updateMSR();
00862                 if (cd) retval |= MSR_CD_MASK;
00863                 if (ri) retval |= MSR_RI_MASK;
00864                 if (dsr) retval |= MSR_DSR_MASK;
00865                 if (cts) retval |= MSR_CTS_MASK;
00866         
00867         }
00868         // new delta flags
00869         if(d_cd) retval|=MSR_dCD_MASK;
00870         if(d_ri) retval|=MSR_dRI_MASK;
00871         if(d_cts) retval|=MSR_dCTS_MASK;
00872         if(d_dsr) retval|=MSR_dDSR_MASK;
00873         
00874         d_cd = false;
00875         d_ri = false;
00876         d_cts = false;
00877         d_dsr = false;
00878         
00879         clear (MSR_PRIORITY);
00880         return retval;
00881 }
00882 
00883 /*****************************************************************************/
00884 /* Scratchpad Register (r/w)                                                **/
00885 /*****************************************************************************/
00886 // Just a memory register. Not much to do here.
00887 Bitu CSerial::Read_SPR () {
00888         return SPR;
00889 }
00890 
00891 void CSerial::Write_SPR (Bit8u data) {
00892         SPR = data;
00893 }
00894 
00895 /*****************************************************************************/
00896 /* Write_reserved                                                           **/
00897 /*****************************************************************************/
00898 void CSerial::Write_reserved (Bit8u data, Bit8u address) {
00899     (void)data;//UNUSED
00900     (void)address;//UNUSED
00901         /*LOG_UART("Serial%d: Write to reserved register, value 0x%x, register %x",
00902                 COMNUMBER, data, address);*/
00903 }
00904 
00905 /*****************************************************************************/
00906 /* MCR Access: returns cirquit state as boolean.                            **/
00907 /*****************************************************************************/
00908 bool CSerial::getDTR () {
00909         if(loopback) return false;
00910         else return dtr;
00911 }
00912 
00913 bool CSerial::getRTS () {
00914         if(loopback) return false;
00915         else return rts;
00916 }
00917 
00918 /*****************************************************************************/
00919 /* MSR Access                                                               **/
00920 /*****************************************************************************/
00921 bool CSerial::getRI () {
00922         return ri;
00923 }
00924 
00925 bool CSerial::getCD () {
00926         return cd;
00927 }
00928 
00929 bool CSerial::getDSR () {
00930         return dsr;
00931 }
00932 
00933 bool CSerial::getCTS () {
00934         return cts;
00935 }
00936 
00937 void CSerial::setRI (bool value) {
00938         if (value != ri) {
00939 
00940 #if SERIAL_DEBUG
00941                 log_ser(dbg_modemcontrol,"%RI  %x.",value);
00942 #endif
00943                 // don't change delta when in loopback mode
00944                 ri=value;
00945                 if(!loopback) {
00946             if(value==false) d_ri=true;
00947                         rise (MSR_PRIORITY);
00948                 }
00949         }
00950         //else no change
00951 }
00952 void CSerial::setDSR (bool value) {
00953         if (value != dsr) {
00954 #if SERIAL_DEBUG
00955                 log_ser(dbg_modemcontrol,"DSR %x.",value);
00956 #endif
00957                 // don't change delta when in loopback mode
00958                 dsr=value;
00959                 if(!loopback) {
00960             d_dsr=true;
00961                         rise (MSR_PRIORITY);
00962                 }
00963         }
00964         //else no change
00965 }
00966 void CSerial::setCD (bool value) {
00967         if (value != cd) {
00968 #if SERIAL_DEBUG
00969                 log_ser(dbg_modemcontrol,"CD  %x.",value);
00970 #endif
00971                 // don't change delta when in loopback mode
00972                 cd=value;
00973                 if(!loopback) {
00974             d_cd=true;
00975                         rise (MSR_PRIORITY);
00976                 }
00977         }
00978         //else no change
00979 }
00980 void CSerial::setCTS (bool value) {
00981         if (value != cts) {
00982 #if SERIAL_DEBUG
00983                 log_ser(dbg_modemcontrol,"CTS %x.",value);
00984 #endif
00985                 // don't change delta when in loopback mode
00986                 cts=value;
00987                 if(!loopback) {
00988             d_cts=true;
00989                         rise (MSR_PRIORITY);
00990                 }
00991         }
00992         //else no change
00993 }
00994 
00995 /*****************************************************************************/
00996 /* Initialisation                                                           **/
00997 /*****************************************************************************/
00998 void CSerial::Init_Registers () {
00999         // The "power on" settings
01000         irq_active=false;
01001         waiting_interrupts = 0x0;
01002 
01003         Bit32u initbps = 9600;
01004         Bit8u bytesize = 8;
01005         char parity = 'N';
01006                                                           
01007         Bit8u lcrresult = 0;
01008         Bit16u baudresult = 0;
01009 
01010         IER = 0;
01011         ISR = 0x1;
01012         LCR = 0;
01013         //MCR = 0xff;
01014         loopback = true;
01015         dtr=true;
01016         rts=true;
01017         op1=true;
01018         op2=true;
01019 
01020         sync_guardtime=false;
01021         FCR=0xff;
01022         Write_FCR(0x00);
01023 
01024 
01025         LSR = 0x60;
01026         d_cts = true;   
01027         d_dsr = true;   
01028         d_ri = true;
01029         d_cd = true;    
01030         cts = true;     
01031         dsr = true;     
01032         ri = true;      
01033         cd = true;      
01034 
01035         SPR = 0xFF;
01036 
01037         baud_divider=0x0;
01038 
01039         // make lcr: byte size, parity, stopbits, baudrate
01040 
01041         if (bytesize == 5)
01042                 lcrresult |= LCR_DATABITS_5;
01043         else if (bytesize == 6)
01044                 lcrresult |= LCR_DATABITS_6;
01045         else if (bytesize == 7)
01046                 lcrresult |= LCR_DATABITS_7;
01047         else
01048                 lcrresult |= LCR_DATABITS_8;
01049 
01050         switch(parity)
01051         {
01052         case 'N':
01053         case 'n':
01054                 lcrresult |= LCR_PARITY_NONE;
01055                 break;
01056         case 'O':
01057         case 'o':
01058                 lcrresult |= LCR_PARITY_ODD;
01059                 break;
01060         case 'E':
01061         case 'e':
01062                 lcrresult |= LCR_PARITY_EVEN;
01063                 break;
01064         case 'M':
01065         case 'm':
01066                 lcrresult |= LCR_PARITY_MARK;
01067                 break;
01068         case 'S':
01069         case 's':
01070                 lcrresult |= LCR_PARITY_SPACE;
01071                 break;
01072         }
01073 
01074         // baudrate
01075         if (initbps > 0)
01076                 baudresult = (Bit16u) (115200 / initbps);
01077         else
01078                 baudresult = 12;                        // = 9600 baud
01079 
01080         Write_MCR (0);
01081         Write_LCR (LCR_DIVISOR_Enable_MASK);
01082         Write_THR ((Bit8u) baudresult & 0xff);
01083         Write_IER ((Bit8u) (baudresult >> 8));
01084         Write_LCR (lcrresult);
01085         updateMSR();
01086         Read_MSR();
01087         PIC_DeActivateIRQ(irq);
01088 }
01089 
01090 CSerial::CSerial(Bitu id, CommandLine* cmd) {
01091         idnumber=id;
01092         Bit16u base = serial_baseaddr[id];
01093 
01094     d_cts=false;                // bit0: deltaCTS
01095     d_dsr=false;                // bit1: deltaDSR
01096     d_ri=false;                 // bit2: deltaRI
01097     d_cd=false;                 // bit3: deltaCD
01098     cts=false;                  // bit4: CTS
01099     dsr=false;                  // bit5: DSR
01100     ri=false;                   // bit6: RI
01101     cd=false;                   // bit7: CD
01102 
01103         irq = serial_defaultirq[id];
01104         getBituSubstring("irq:",&irq, cmd);
01105         if (irq < 2 || irq > 15) irq = serial_defaultirq[id];
01106 
01107 #if SERIAL_DEBUG
01108         dbg_serialtraffic = cmd->FindExist("dbgtr", false);
01109         dbg_modemcontrol  = cmd->FindExist("dbgmd", false);
01110         dbg_register      = cmd->FindExist("dbgreg", false);
01111         dbg_interrupt     = cmd->FindExist("dbgirq", false);
01112         dbg_aux                   = cmd->FindExist("dbgaux", false);
01113         
01114         if(cmd->FindExist("dbgall", false)) {
01115                 dbg_serialtraffic= 
01116                 dbg_modemcontrol= 
01117                 dbg_register=
01118                 dbg_interrupt=
01119                 dbg_aux= true;
01120         }
01121 
01122 
01123         if(dbg_serialtraffic|dbg_modemcontrol|dbg_register|dbg_interrupt|dbg_aux)
01124                 debugfp=OpenCaptureFile("serlog",".serlog.txt");
01125         else debugfp=0;
01126 
01127         if(debugfp == 0) {
01128                 dbg_serialtraffic= 
01129                 dbg_modemcontrol= 
01130                 dbg_register=
01131                 dbg_interrupt=
01132                 dbg_aux= false;
01133         } else {
01134                 std::string cleft;
01135                 cmd->GetStringRemain(cleft);
01136 
01137                 log_ser(true,"Serial%d: BASE %3x, IRQ %d, initstring \"%s\"\r\n\r\n",
01138                         COMNUMBER,base,irq,cleft.c_str());
01139         }
01140 #endif
01141         fifosize=16;
01142 
01143         errorfifo = new MyFifo(fifosize);
01144         rxfifo = new MyFifo(fifosize);
01145         txfifo = new MyFifo(fifosize);
01146 
01147         mydosdevice=NULL;
01148         errormsg_pending=false;
01149         framingErrors=0;
01150         parityErrors=0;
01151         overrunErrors=0;
01152         txOverrunErrors=0;
01153         overrunIF0=0;
01154         breakErrors=0;
01155         
01156         for (Bitu i = 0; i <= 7; i++) {
01157                 WriteHandler[i].Install (i + base, SERIAL_Write, IO_MB);
01158                 ReadHandler[i].Install (i + base, SERIAL_Read, IO_MB);
01159         }
01160 }
01161 
01162 bool CSerial::getBituSubstring(const char* name,Bitu* data, CommandLine* cmd) {
01163         std::string tmpstring;
01164         if(!(cmd->FindStringBegin(name,tmpstring,false))) return false;
01165         const char* tmpchar=tmpstring.c_str();
01166 
01167         unsigned int tmp;
01168         if(sscanf(tmpchar,"%u",&tmp)!=1) return false;
01169         *data = (Bitu)tmp;
01170         return true;
01171 }
01172 
01173 void CSerial::registerDOSDevice() {
01174         if (mydosdevice == NULL) {
01175                 LOG(LOG_MISC,LOG_DEBUG)("COM%d: Registering DOS device",(int)idnumber+1);
01176                 mydosdevice = new device_COM(this);
01177                 DOS_AddDevice(mydosdevice);
01178         }
01179 }
01180 
01181 void CSerial::unregisterDOSDevice() {
01182         if (mydosdevice != NULL) {
01183                 LOG(LOG_MISC,LOG_DEBUG)("COM%d: Unregistering DOS device",(int)idnumber+1);
01184                 DOS_DelDevice(mydosdevice); // deletes the pointer for us!
01185                 mydosdevice=NULL;
01186         }
01187 }
01188 
01189 CSerial::~CSerial(void) {
01190         unregisterDOSDevice();
01191         for(Bit8u i = 0; i <= SERIAL_BASE_EVENT_COUNT; i++)
01192                 removeEvent(i);
01193 
01194         if (rxfifo != NULL) {
01195                 delete rxfifo;
01196                 rxfifo = NULL;
01197         }
01198         if (txfifo != NULL) {
01199                 delete txfifo;
01200                 txfifo = NULL;
01201         }
01202         if (errorfifo != NULL) {
01203                 delete errorfifo;
01204                 errorfifo = NULL;
01205         }
01206 }
01207 
01208 bool CSerial::Getchar(Bit8u* data, Bit8u* lsr, bool wait_dsr, Bitu timeout) {
01209         double starttime=PIC_FullIndex();
01210         // wait for DSR on
01211         if(wait_dsr) {
01212                 while((!(Read_MSR()&0x20))&&(starttime>PIC_FullIndex()-timeout))
01213                         CALLBACK_Idle();
01214                 if(!(starttime>PIC_FullIndex()-timeout)) {
01215 #if SERIAL_DEBUG
01216                         log_ser(dbg_aux,"Getchar status timeout: MSR 0x%x",Read_MSR());
01217 #endif
01218                         return false;
01219                 }
01220         }
01221         // wait for a byte to arrive
01222         while((!((*lsr=(Bit8u)Read_LSR())&0x1))&&(starttime>PIC_FullIndex()-timeout))
01223                 CALLBACK_Idle();
01224         
01225         if(!(starttime>PIC_FullIndex()-timeout)) {
01226 #if SERIAL_DEBUG
01227                 log_ser(dbg_aux,"Getchar data timeout: MSR 0x%x",Read_MSR());
01228 #endif
01229                 return false;
01230         }
01231         *data=(Bit8u)Read_RHR();
01232 
01233 #if SERIAL_DEBUG
01234         log_ser(dbg_aux,"Getchar read 0x%x",*data);
01235 #endif
01236         return true;
01237 }
01238 
01239 
01240 bool CSerial::Putchar(Bit8u data, bool wait_dsr, bool wait_cts, Bitu timeout) {
01241         
01242         double starttime=PIC_FullIndex();
01243         // wait for it to become empty
01244         while(!(Read_LSR()&0x20)) {
01245                 CALLBACK_Idle();
01246         }
01247         // wait for DSR+CTS on
01248         if(wait_dsr||wait_cts) {
01249                 if(wait_dsr||wait_cts) {
01250                         while(((Read_MSR()&0x30)!=0x30)&&(starttime>PIC_FullIndex()-timeout))
01251                                 CALLBACK_Idle();
01252                 } else if(wait_dsr) {
01253                         while(!(Read_MSR()&0x20)&&(starttime>PIC_FullIndex()-timeout))
01254                                 CALLBACK_Idle();
01255                 } else if(wait_cts) {
01256                         while(!(Read_MSR()&0x10)&&(starttime>PIC_FullIndex()-timeout))
01257                                 CALLBACK_Idle();
01258                 } 
01259                 if(!(starttime>PIC_FullIndex()-timeout)) {
01260 #if SERIAL_DEBUG
01261                         log_ser(dbg_aux,"Putchar timeout: MSR 0x%x",Read_MSR());
01262 #endif
01263                         return false;
01264                 }
01265         }
01266         Write_THR(data);
01267 
01268 #if SERIAL_DEBUG
01269         log_ser(dbg_aux,"Putchar 0x%x",data);
01270 #endif
01271 
01272         return true;
01273 }
01274 
01275 void BIOS_PnP_ComPortRegister(Bitu port,Bitu irq);
01276 void BIOS_SetCOMPort(Bitu port, Bit16u baseaddr);
01277 
01278 void BIOS_Post_register_comports_PNP() {
01279         unsigned int i;
01280 
01281         for (i=0;i < 4;i++) {
01282                 if (serialports[i] != NULL) {
01283                         BIOS_PnP_ComPortRegister(serial_baseaddr[i],serialports[i]->irq);
01284                 }
01285         }
01286 }
01287 
01288 Bitu bios_post_comport_count() {
01289         Bitu count = 0;
01290         unsigned int i;
01291 
01292         for (i=0;i < 4;i++) {
01293                 if (serialports[i] != NULL)
01294                         count++;
01295         }
01296 
01297         return count;
01298 }
01299 
01300 /* at BIOS POST stage, write serial ports to bios data area */
01301 void BIOS_Post_register_comports() {
01302         unsigned int i;
01303 
01304         for (i=0;i < 4;i++) {
01305                 if (serialports[i] != NULL)
01306                         BIOS_SetCOMPort(i,serial_baseaddr[i]);
01307         }
01308 }
01309 
01310 class SERIALPORTS:public Module_base {
01311 public:
01312         SERIALPORTS (Section * configuration):Module_base (configuration) {
01313                 Section_prop *section = static_cast <Section_prop*>(configuration);
01314 
01315         // TODO: PC-98 does have serial ports, though differently.
01316         //       COM1 is a 8251 UART, while COM2 and higher if they exist are 8250/16xxx UARTs
01317         if (IS_PC98_ARCH) return;
01318 
01319                 char s_property[] = "serialx"; 
01320                 for(Bit8u i = 0; i < 4; i++) {
01321                         // get the configuration property
01322                         s_property[6] = '1' + i;
01323                         Prop_multival* p = section->Get_multival(s_property);
01324                         std::string type = p->GetSection()->Get_string("type");
01325                         CommandLine cmd(0,p->GetSection()->Get_string("parameters"));
01326                         
01327                         // detect the type
01328                         if (type=="dummy") {
01329                                 serialports[i] = new CSerialDummy (i, &cmd);
01330                         }
01331                         else if (type=="log") {
01332                                 serialports[i] = new CSerialLog (i, &cmd);
01333                         }
01334                         else if (type=="serialmouse") {
01335                                 serialports[i] = new CSerialMouse (i, &cmd);
01336                         }
01337 #ifdef DIRECTSERIAL_AVAILIBLE
01338                         else if (type=="directserial") {
01339                                 serialports[i] = new CDirectSerial (i, &cmd);
01340                                 if (!serialports[i]->InstallationSuccessful)  {
01341                                         // serial port name was wrong or already in use
01342                                         delete serialports[i];
01343                                         serialports[i] = NULL;
01344                                 }
01345                         }
01346 #endif
01347 #if C_MODEM
01348                         else if(type=="modem") {
01349                                 serialports[i] = new CSerialModem (i, &cmd);
01350                                 if (!serialports[i]->InstallationSuccessful)  {
01351                                         delete serialports[i];
01352                                         serialports[i] = NULL;
01353                                 }
01354                         }
01355                         else if(type=="nullmodem") {
01356                                 serialports[i] = new CNullModem (i, &cmd);
01357                                 if (!serialports[i]->InstallationSuccessful)  {
01358                                         delete serialports[i];
01359                                         serialports[i] = NULL;
01360                                 }
01361                         }
01362 #endif
01363                         else if(type=="disabled") {
01364                                 serialports[i] = NULL;
01365                         } else {
01366                                 serialports[i] = NULL;
01367                                 LOG_MSG("Invalid type for serial%d",(int)i+1);
01368                         }
01369                 } // for 1-4
01370         }
01371 
01372         ~SERIALPORTS () {
01373                 for (Bitu i = 0; i < 4; i++)
01374                         if (serialports[i]) {
01375                                 delete serialports[i];
01376                                 serialports[i] = 0;
01377                         }
01378         }
01379 };
01380 
01381 static SERIALPORTS *testSerialPortsBaseclass;
01382 
01383 void SERIAL_Destroy (Section * sec) {
01384     (void)sec;//UNUSED
01385         if (testSerialPortsBaseclass) {
01386                 LOG(LOG_MISC,LOG_DEBUG)("Deleting serial port base class");
01387                 delete testSerialPortsBaseclass;
01388                 testSerialPortsBaseclass = NULL;
01389         }
01390 }
01391 
01392 void SERIAL_OnPowerOn (Section * sec) {
01393     (void)sec;//UNUSED
01394         // should never happen
01395         LOG(LOG_MISC,LOG_DEBUG)("Reinitializing serial emulation");
01396         if (testSerialPortsBaseclass) delete testSerialPortsBaseclass;
01397         testSerialPortsBaseclass = new SERIALPORTS (control->GetSection("serial"));
01398 }
01399 
01400 void SERIAL_OnDOSKernelInit (Section * sec) {
01401     (void)sec;//UNUSED
01402         unsigned int i;
01403 
01404         LOG(LOG_MISC,LOG_DEBUG)("DOS kernel initializing, creating COMx devices");
01405 
01406         for (i=0;i < 3;i++) {
01407                 if (serialports[i] != NULL)
01408                         serialports[i]->registerDOSDevice();
01409         }
01410 }
01411 
01412 void SERIAL_OnDOSKernelExit (Section * sec) {
01413     (void)sec;//UNUSED
01414         unsigned int i;
01415 
01416         for (i=0;i < 3;i++) {
01417                 if (serialports[i] != NULL)
01418                         serialports[i]->unregisterDOSDevice();
01419         }
01420 }
01421 
01422 void SERIAL_OnReset (Section * sec) {
01423     (void)sec;//UNUSED
01424         unsigned int i;
01425 
01426         // FIXME: Unregister/destroy the DOS devices, but consider that the DOS kernel at reset is gone.
01427         for (i=0;i < 3;i++) {
01428                 if (serialports[i] != NULL)
01429                         serialports[i]->unregisterDOSDevice();
01430         }
01431 }
01432 
01433 void SERIAL_Init () {
01434         LOG(LOG_MISC,LOG_DEBUG)("Initializing serial port emulation");
01435 
01436         AddExitFunction(AddExitFunctionFuncPair(SERIAL_Destroy),true);
01437 
01438     if (!IS_PC98_ARCH) {
01439         AddVMEventFunction(VM_EVENT_POWERON,AddVMEventFunctionFuncPair(SERIAL_OnPowerOn));
01440         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(SERIAL_OnReset));
01441         AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(SERIAL_OnDOSKernelExit));
01442         AddVMEventFunction(VM_EVENT_DOS_INIT_KERNEL_READY,AddVMEventFunctionFuncPair(SERIAL_OnDOSKernelInit));
01443     }
01444 }
01445