diff --git a/configure.in b/configure.in index 5591b7ac..7028e908 100644 --- a/configure.in +++ b/configure.in @@ -60,7 +60,18 @@ AC_MSG_CHECKING(if environ can be linked) AC_LINK_IFELSE([AC_LANG_PROGRAM([[extern char ** environ;]],[[*environ;]])], [AC_MSG_RESULT(yes);AC_DEFINE(ENVIRON_LINKED,1,[environ can be linked])],AC_MSG_RESULT(no)) +dnl Check for powf +if test x$target = xi386-pc-os2-emx ; then + AC_MSG_CHECKING(for powf in libm); + LIBS_BACKUP=$LIBS; + LIBS="$LIBS -lm"; + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ + powf(1.0f, 1.0f); + ]])], [AC_MSG_RESULT(yes)], [AC_DEFINE([DB_HAVE_NO_POWF],[1],[libm doesn't include powf])]) + LIBS=$LIBS_BACKUP +else AC_CHECK_LIB([m],[powf],,[AC_DEFINE([DB_HAVE_NO_POWF],[1],[libm doesn't include powf])]) +fi dnl Checks for libraries. @@ -230,7 +241,7 @@ else AC_CHECK_LIB(SDL_net, SDLNet_Init, have_sdl_net_lib=yes, , ) fi if test x$have_sdl_net_lib = xyes -a x$have_sdl_net_h = xyes ; then - LIBS="$LIBS -lSDL_net" + LIBS="$LIBS -lSDL_net" AC_DEFINE(C_MODEM,1) AC_DEFINE(C_IPX,1) else @@ -239,20 +250,31 @@ fi AH_TEMPLATE(C_OPENGL,[Define to 1 to use opengl display output support]) AC_ARG_ENABLE(opengl,AC_HELP_STRING([--disable-opengl],[Disable opengl support]),,enable_opengl=yes) -AC_CHECK_LIB(GL, main, have_gl_lib=yes, have_gl_lib=no , ) -AC_CHECK_LIB(opengl32, main, have_opengl32_lib=yes,have_opengl32_lib=no , ) -AC_CHECK_HEADER(GL/gl.h, have_gl_h=yes , have_gl_h=no , ) AC_MSG_CHECKING(whether opengl display output will be enabled) -if test x$enable_opengl = xyes -a x$have_gl_h = xyes -a x$have_gl_lib = xyes ; then - AC_MSG_RESULT(yes) - LIBS="$LIBS -lGL" - AC_DEFINE(C_OPENGL,1) -elif test x$enable_opengl = xyes -a x$have_gl_h = xyes -a x$have_opengl32_lib = xyes ; then - AC_MSG_RESULT(yes) - LIBS="$LIBS -lopengl32" - AC_DEFINE(C_OPENGL,1) -else - AC_MSG_RESULT(no) +if test x$enable_opengl = xyes; then +case "$target" in + *-*-darwin*) + AC_MSG_RESULT(yes) + LIBS="$LIBS -framework OpenGL" + AC_DEFINE(C_OPENGL,1) + ;; + *) + AC_CHECK_LIB(GL, main, have_gl_lib=yes, have_gl_lib=no , ) + AC_CHECK_LIB(opengl32, main, have_opengl32_lib=yes,have_opengl32_lib=no , ) + AC_CHECK_HEADER(GL/gl.h, have_gl_h=yes , have_gl_h=no , ) + if test x$have_gl_h = xyes -a x$have_gl_lib = xyes ; then + AC_MSG_RESULT(yes) + LIBS="$LIBS -lGL" + AC_DEFINE(C_OPENGL,1) + elif test x$have_gl_h = xyes -a x$have_opengl32_lib = xyes ; then + AC_MSG_RESULT(yes) + LIBS="$LIBS -lopengl32" + AC_DEFINE(C_OPENGL,1) + else + AC_MSG_RESULT(no) + fi + ;; +esac fi AH_TEMPLATE(C_SDL_SOUND,[Define to 1 to enable SDL_sound support]) @@ -292,7 +314,10 @@ case "$target" in *-*-cygwin* | *-*-mingw32*) LIBS="$LIBS -lwinmm" AC_CHECK_HEADERS(ddraw.h) - AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32 and OS/2 only).]) + AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2 only).]) + if test x$have_sdl_net_lib = xyes -a x$have_sdl_net_h = xyes ; then + LIBS="$LIBS -lws2_32" + fi ;; *-*-darwin*) dnl We have a problem here: both MacOS X and Darwin report @@ -304,10 +329,11 @@ case "$target" in ;; *-*-linux-gnu*) AC_DEFINE(LINUX, 1, [Compiling on GNU/Linux]) + AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2).]) ;; *-*-os2-emx*) AC_DEFINE(OS2, 1, [Compiling on OS/2 EMX]) - AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32 and OS/2 only).]) + AC_DEFINE(C_DIRECTSERIAL, 1, [ Define to 1 if you want serial passthrough support (Win32, Posix and OS/2).]) ;; esac diff --git a/include/bios.h b/include/bios.h index 0184fc8f..39b06531 100644 --- a/include/bios.h +++ b/include/bios.h @@ -177,4 +177,6 @@ bool BIOS_AddKeyToBuffer(Bit16u code); void INT10_ReloadRomFonts(); +void BIOS_SetComPorts (Bit16u baseaddr[]); + #endif diff --git a/include/ipx.h b/include/ipx.h index 962a751a..884b4946 100644 --- a/include/ipx.h +++ b/include/ipx.h @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: ipx.h,v 1.11 2007-01-08 19:45:37 qbix79 Exp $ */ +/* $Id: ipx.h,v 1.12 2007-01-13 08:35:49 qbix79 Exp $ */ #ifndef DOSBOX_IPX_H #define DOSBOX_IPX_H @@ -27,8 +27,12 @@ #ifdef IPX_DEBUGMSG #define LOG_IPX LOG_MSG #else +#if defined (_MSC_VER) +#define LOG_IPX +#else #define LOG_IPX(...) #endif +#endif #ifndef DOSBOX_DOSBOX_H #include "dosbox.h" diff --git a/include/serialport.h b/include/serialport.h index 53142c64..bc55b0cd 100644 --- a/include/serialport.h +++ b/include/serialport.h @@ -16,13 +16,15 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: serialport.h,v 1.13 2007-01-08 19:45:37 qbix79 Exp $ */ +/* $Id: serialport.h,v 1.14 2007-01-13 08:35:49 qbix79 Exp $ */ #ifndef DOSBOX_SERIALPORT_H #define DOSBOX_SERIALPORT_H +#define SERIAL_DEBUG 0 + // Uncomment this for a lot of debug messages: -// #define SERIALPORT_DEBUGMSG +//#define LOG_UART #ifndef DOSBOX_DOSBOX_H #include "dosbox.h" @@ -34,31 +36,70 @@ #include "timer.h" #endif +#include "dos_inc.h" +#include "setup.h" -// Serial port interface // +#if SERIAL_DEBUG +#include "hardware.h" +#endif + +// Serial port interface class CSerial { public: - // Constructor takes base port (0x3f8, 0x2f8, 0x2e8, etc.), IRQ, and initial bps // - CSerial(IO_ReadHandler* rh, IO_WriteHandler* wh, - TIMER_TickHandler TimerHandler, - Bit16u initbase, Bit8u initirq, Bit32u initbps, - Bit8u bytesize, const char* parity, Bit8u stopbits); +#if SERIAL_DEBUG + FILE * debugfp; + bool dbg_modemcontrol; // RTS,CTS,DTR,DSR,RI,CD + bool dbg_serialtraffic; + bool dbg_register; + bool dbg_interrupt; + bool dbg_aux; + +#endif + + static bool getBituSubstring(const char* name,Bitu* data, CommandLine* cmd); + + bool InstallationSuccessful;// check after constructing. If + // something was wrong, delete it right away. + + // Constructor takes com port number (0-3) + CSerial(Bitu id, CommandLine* cmd); - TIMER_TickHandler TimerHnd; virtual ~CSerial(); - void InstallTimerHandler(TIMER_TickHandler); - + IO_ReadHandleObject ReadHandler[8]; IO_WriteHandleObject WriteHandler[8]; - void Timer(void); - virtual void Timer2(void)=0; + float bytetime; // how long a byte takes to transmit/receive in milliseconds + void changeLineProperties(); + Bitu idnumber; + + void setEvent(Bit16u type, float duration); + void removeEvent(Bit16u type); + void handleEvent(Bit16u type); + virtual void handleUpperEvent(Bit16u type)=0; - Bitu base; + // defines for event type +#define SERIAL_TX_LOOPBACK_EVENT 0 +#define SERIAL_THR_LOOPBACK_EVENT 1 +#define SERIAL_ERRMSG_EVENT 2 + +#define SERIAL_TX_EVENT 3 +#define SERIAL_RX_EVENT 4 +#define SERIAL_POLLING_EVENT 5 +#define SERIAL_THR_EVENT 6 + +#define SERIAL_BASE_EVENT_COUNT 6 + +#define COMNUMBER idnumber+1 + Bitu irq; + // CSerial requests an update of the input lines + virtual void updateMSR()=0; + + // Control lines from prepherial to serial port bool getDTR(); bool getRTS(); @@ -72,75 +113,75 @@ public: void setCD(bool value); void setCTS(bool value); + // From serial port to prepherial + // set output lines + virtual void setRTSDTR(bool rts, bool dtr)=0; + virtual void setRTS(bool val)=0; + virtual void setDTR(bool val)=0; + + // Register access void Write_THR(Bit8u data); - Bitu Read_RHR(); - Bitu Read_IER(); void Write_IER(Bit8u data); - Bitu Read_ISR(); - Bitu Read_LCR(); + void Write_FCR(Bit8u data); void Write_LCR(Bit8u data); - Bitu Read_MCR(); void Write_MCR(Bit8u data); - Bitu Read_LSR(); - // Really old hardware seems to have the delta part of this register writable void Write_MSR(Bit8u data); - - Bitu Read_MSR(); - Bitu Read_SPR(); void Write_SPR(Bit8u data); void Write_reserved(Bit8u data, Bit8u address); + + Bitu Read_RHR(); + Bitu Read_IER(); + Bitu Read_ISR(); + Bitu Read_LCR(); + Bitu Read_MCR(); + Bitu Read_LSR(); + Bitu Read_MSR(); + Bitu Read_SPR(); - // If a byte comes from wherever(loopback or real port or maybe - // that softmodem thingy), put it in here. + // If a byte comes from loopback or prepherial, put it in here. void receiveByte(Bit8u data); // If an error was received, put it here (in LSR register format) void receiveError(Bit8u errorword); + // depratched // connected device checks, if port can receive data: bool CanReceiveByte(); + // when THR was shifted to TX + void ByteTransmitting(); + // When done sending, notify here void ByteTransmitted(); - // Virtual app has read the received data - virtual void RXBufferEmpty()=0; - - // real transmit - virtual void transmitByte(Bit8u val)=0; + // Transmit byte to prepherial + virtual void transmitByte(Bit8u val, bool first)=0; // switch break state to the passed value virtual void setBreak(bool value)=0; - // set output lines - virtual void updateModemControlLines(/*Bit8u mcr*/)=0; - // change baudrate, number of bits, parity, word length al at once - virtual void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr)=0; + virtual void updatePortConfig(Bit16u divider, Bit8u lcr)=0; - // CSerial requests an update of the input lines - virtual void updateMSR()=0; + void Init_Registers(); + + bool Putchar(Bit8u data, bool wait_dtr, bool wait_rts, Bitu timeout); + bool Getchar(Bit8u* data, bool wait_dsr, Bitu timeout); - // after update request, or some "real" changes, - // modify MSR here - void changeMSR(Bit8u data); // make public - - void Init_Registers(Bit32u initbps, - Bit8u bytesize, const char* parity, Bit8u stopbits); private: + DOS_Device* mydosdevice; + // I used this spec: http://www.exar.com/products/st16c450v420.pdf - void changeMSR_Loopback(Bit8u data); + void ComputeInterrupts(); - void WriteRealIER(Bit8u data); - // reason for an interrupt has occured - functions triggers interrupt - // if it is enabled and no higher-priority irq pending + // a sub-interrupt is triggered void rise(Bit8u priority); - // clears the pending interrupt + // clears the pending sub-interrupt void clear(Bit8u priority); #define ERROR_PRIORITY 4 // overrun, parity error, frame error, break @@ -149,18 +190,12 @@ private: #define MSR_PRIORITY 8 // CRS, DSR, RI, DCD change #define NONE_PRIORITY 0 - - Bit8u pending_interrupts; // stores triggered interupts - Bit8u current_priority; Bit8u waiting_interrupts; // these are on, but maybe not enabled // 16C450 (no FIFO) // read/write name - - Bit8u DLL; // r Baudrate divider low byte - Bit8u DLM; // r "" high byte - + Bit16u baud_divider; Bit8u RHR; // r Receive Holding Register, also LSB of Divisor Latch (r/w) #define RHR_OFFSET 0 // Data: whole byte @@ -169,13 +204,10 @@ private: #define THR_OFFSET 0 // Data: whole byte - Bit8u IER; // r/w Interrupt Enable Register, also MSB of Divisor Latch (r/w) + Bit8u IER; // r/w Interrupt Enable Register, also MSB of Divisor Latch #define IER_OFFSET 1 - // Data: - // bit0 receive holding register - // bit1 transmit holding register - // bit2 receive line status interrupt - // bit3 modem status interrupt + + bool irq_active; #define RHR_INT_Enable_MASK 0x1 #define THR_INT_Enable_MASK 0x2 @@ -222,23 +254,24 @@ private: #define LCR_STOPBITS_1 0x0 #define LCR_STOPBITS_MORE_THAN_1 0x4 - Bit8u MCR; // r/w Modem Control Register + // Modem Control Register + // r/w #define MCR_OFFSET 4 - // bit0: DTR - // bit1: RTS - // bit2: OP1 - // bit3: OP2 - // bit4: loop back enable + bool dtr; // bit0: DTR + bool rts; // bit1: RTS + bool op1; // bit2: OP1 + bool op2; // bit3: OP2 + bool loopback; // bit4: loop back enable - #define MCR_LOOPBACK_Enable_MASK 0x10 - #define MCR_LEVELS_MASK 0xf - #define MCR_DTR_MASK 0x1 #define MCR_RTS_MASK 0x2 #define MCR_OP1_MASK 0x4 - #define MCR_OP2_MASK 0x8 - + #define MCR_OP2_MASK 0x8 + #define MCR_LOOPBACK_Enable_MASK 0x10 +public: Bit8u LSR; // r Line Status Register +private: + #define LSR_OFFSET 5 #define LSR_RX_DATA_READY_MASK 0x1 @@ -251,17 +284,26 @@ private: #define LSR_ERROR_MASK 0x1e + // error printing + bool errormsg_pending; + Bitu framingErrors; + Bitu parityErrors; + Bitu overrunErrors; + Bitu overrunIF0; + Bitu breakErrors; - Bit8u MSR; // r Modem Status Register + + // Modem Status Register + // r #define MSR_OFFSET 6 - // bit0: deltaCTS - // bit1: deltaDSR - // bit2: deltaRI - // bit3: deltaCD - // bit4: CTS - // bit5: DSR - // bit6: RI - // bit7: CD + bool d_cts; // bit0: deltaCTS + bool d_dsr; // bit1: deltaDSR + bool d_ri; // bit2: deltaRI + bool d_cd; // bit3: deltaCD + bool cts; // bit4: CTS + bool dsr; // bit5: DSR + bool ri; // bit6: RI + bool cd; // bit7: CD #define MSR_delta_MASK 0xf #define MSR_LINE_MASK 0xf0 @@ -280,20 +322,37 @@ private: // For loopback purposes... - bool loopback_pending; Bit8u loopback_data; - void transmitLoopbackByte(Bit8u val); + void transmitLoopbackByte(Bit8u val, bool value); // 16C550 (FIFO) // TODO + #define FCR_OFFSET 2 + bool fifo_warn; //Bit8u FCR; // FIFO Control Register }; -#define COM1_BASE 0x3f8 -#define COM2_BASE 0x2f8 -#define COM3_BASE 0x3e8 -#define COM4_BASE 0x2e8 +extern CSerial* serialports[]; +const Bit8u serial_defaultirq[4] = { 4, 3, 4, 3 }; +const Bit16u serial_baseaddr[4] = {0x3f8,0x2f8,0x3e8,0x2e8}; +const char* const serial_comname[]={"COM1","COM2","COM3","COM4"}; + +// the COM devices + +class device_COM : public DOS_Device { +public: + // Creates a COM device that communicates with the num-th parallel port, i.e. is LPTnum + device_COM(class CSerial* sc); + ~device_COM(); + bool Read(Bit8u * data,Bit16u * size); + bool Write(Bit8u * data,Bit16u * size); + bool Seek(Bit32u * pos,Bit32u type); + bool Close(); + Bit16u GetInformation(void); +private: + CSerial* sclass; +}; #endif diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index 942fabe4..53b01ec9 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: cpu.cpp,v 1.93 2007-01-11 16:31:10 c2woody Exp $ */ +/* $Id: cpu.cpp,v 1.94 2007-01-13 08:35:49 qbix79 Exp $ */ #include #include "dosbox.h" @@ -33,9 +33,13 @@ extern void GFX_SetTitle(Bit32s cycles ,Bits frameskip,bool paused); #if 1 #undef LOG +#if defined (_MSC_VER) +#define LOG(X,Y) +#else #define LOG(X,Y) CPU_LOG #define CPU_LOG(...) #endif +#endif CPU_Regs cpu_regs; CPUBlock cpu; diff --git a/src/dos/dos.cpp b/src/dos/dos.cpp index 46fdb74d..98237bec 100644 --- a/src/dos/dos.cpp +++ b/src/dos/dos.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: dos.cpp,v 1.98 2007-01-08 20:36:53 qbix79 Exp $ */ +/* $Id: dos.cpp,v 1.99 2007-01-13 08:35:49 qbix79 Exp $ */ #include #include @@ -30,6 +30,7 @@ #include "dos_inc.h" #include "setup.h" #include "support.h" +#include "serialport.h" DOS_Block dos; DOS_InfoBlock dos_infoblock; @@ -79,7 +80,27 @@ static Bitu DOS_21Handler(void) { } break; case 0x03: /* Read character from STDAUX */ + { + Bit16u port = real_readw(0x40,0); + if(port!=0 && serialports[0]) { + // RTS/DTR on + IO_WriteB(port+4,0x3); + serialports[0]->Getchar(®_al,true, 0xFFFFFFFF); + } + } + break; case 0x04: /* Write Character to STDAUX */ + { + Bit16u port = real_readw(0x40,0); + if(port!=0 && serialports[0]) { + // RTS/DTR on + IO_WriteB(port+4,0x3); + serialports[0]->Putchar(reg_dl,true,true, 0xFFFFFFFF); + // RTS off + IO_WriteB(port+4,0x1); + } + } + break; case 0x05: /* Write Character to PRINTER */ E_Exit("DOS:Unhandled call %02X",reg_ah); break; diff --git a/src/dos/dos_devices.cpp b/src/dos/dos_devices.cpp index 933f0cb1..d20ce408 100644 --- a/src/dos/dos_devices.cpp +++ b/src/dos/dos_devices.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: dos_devices.cpp,v 1.15 2007-01-08 19:45:39 qbix79 Exp $ */ +/* $Id: dos_devices.cpp,v 1.16 2007-01-13 08:35:49 qbix79 Exp $ */ #include #include "dosbox.h" @@ -126,6 +126,10 @@ DOS_File & DOS_File::operator= (const DOS_File & orig) { Bit8u DOS_FindDevice(char * name) { /* should only check for the names before the dot and spacepadded */ + // STDAUX is alias for COM1 + // A bit of a hack, but no application will probably use stdaux to determine wether a directory exists + if (strcasecmp(name, "STDAUX") == 0) name = "COM1"; + char temp[CROSS_LEN];//TODO if(!name || !(*name)) return DOS_DEVICES; strcpy(temp,name); diff --git a/src/dosbox.cpp b/src/dosbox.cpp index 8ed5e690..23433d71 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: dosbox.cpp,v 1.110 2007-01-11 16:47:10 qbix79 Exp $ */ +/* $Id: dosbox.cpp,v 1.111 2007-01-13 08:35:49 qbix79 Exp $ */ #include #include @@ -428,12 +428,13 @@ void DOSBOX_Init(void) { secprop->Add_string("serial4","disabled"); MSG_Add("SERIAL_CONFIGFILE_HELP", "serial1-4 -- set type of device connected to com port.\n" - " Can be disabled, dummy, modem, directserial.\n" + " Can be disabled, dummy, modem, nullmodem, directserial.\n" " Additional parameters must be in the same line in the form of\n" - " parameter:value. Parameters for all types are irq, startbps, bytesize,\n" - " stopbits, parity (all optional).\n" - " for directserial: realport (required).\n" + " parameter:value. Parameter for all types is irq.\n" + " for directserial: realport (required), rxdelay (optional).\n" " for modem: listenport (optional).\n" + " for nullmodem: server, rxdelay, txdelay, telnet, usedtr,\n" + " transparent, port, inhsocket (all optional).\n" " Example: serial1=modem listenport:5000\n" ); diff --git a/src/hardware/serialport/Makefile.am b/src/hardware/serialport/Makefile.am index 4ae53b23..af7b803c 100644 --- a/src/hardware/serialport/Makefile.am +++ b/src/hardware/serialport/Makefile.am @@ -4,5 +4,7 @@ noinst_LIBRARIES = libserial.a libserial_a_SOURCES = directserial_win32.cpp directserial_win32.h \ serialdummy.cpp serialdummy.h serialport.cpp \ - softmodem.cpp softmodem.h \ - directserial_os2.h directserial_os2.cpp + softmodem.cpp softmodem.h misc_util.cpp misc_util.h \ + directserial_os2.h directserial_os2.cpp \ + directserial_posix.h directserial_posix.cpp \ + nullmodem.cpp nullmodem.h diff --git a/src/hardware/serialport/directserial_posix.cpp b/src/hardware/serialport/directserial_posix.cpp new file mode 100644 index 00000000..58f2d0bc --- /dev/null +++ b/src/hardware/serialport/directserial_posix.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2002-2007 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* $Id: directserial_posix.cpp,v 1.1 2007-01-13 08:35:49 qbix79 Exp $ */ + +#include "dosbox.h" + +#if C_DIRECTSERIAL + +// Posix version +#if defined (LINUX) + +#include "serialport.h" +#include "directserial_posix.h" +#include "pic.h" + +#include +#include +#include + +#include +#include + +/* This is a serial passthrough class. Its amazingly simple to */ +/* write now that the serial ports themselves were abstracted out */ + +CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd) + :CSerial (id, cmd) { + InstallationSuccessful = false; + + rx_retry = 0; + rx_retry_max = 0; + + std::string prefix="/dev/"; + std::string tmpstring; + if(!cmd->FindStringBegin("realport:",tmpstring,false)) return; + +#if SERIAL_DEBUG + if(dbg_modemcontrol) + fprintf(debugfp,"%12.3f Port type directserial realport %s\r\n", + PIC_FullIndex(),tmpstring.c_str()); +#endif + + prefix.append(tmpstring); + + // rxdelay: How many milliseconds to wait before causing an + // overflow when the application is unresponsive. + if(getBituSubstring("rxdelay:", &rx_retry_max, cmd)) { + if(!(rx_retry_max<=10000)) rx_retry_max=0; + } + + const char* tmpchar=prefix.c_str(); + + LOG_MSG ("Serial%d: Opening %s", COMNUMBER, tmpchar); + + fileHandle = open (tmpchar, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (fileHandle < 0) { + LOG_MSG ("Serial%d: Serial Port \"%s\" could not be opened.", + COMNUMBER, tmpchar); + if (errno == 2) { + LOG_MSG ("The specified port does not exist."); + } else if (errno == EBUSY) { + LOG_MSG ("The specified port is already in use."); + } else { + LOG_MSG ("Errno %d occurred.", errno); + } + return; + } + + int result = tcgetattr(fileHandle, &termInfo); + + + if (result==-1) { + // Handle the error. + LOG_MSG ("tcgetattr failed with error %d.\n", errno); + return; + } + + // save it here to restore in destructor + tcgetattr(fileHandle,&backup); + + // initialize the port + termInfo.c_cflag = CS8 | CREAD | CLOCAL; // noparity, 1 stopbit + termInfo.c_iflag = PARMRK | INPCK; + termInfo.c_oflag = 0; + termInfo.c_lflag = 0; + + cfsetospeed (&termInfo, B9600); + cfsetispeed (&termInfo, B9600); + + termInfo.c_cc[VMIN] = 0; + termInfo.c_cc[VTIME] = 0; + + tcflush (fileHandle, TCIFLUSH); + tcsetattr (fileHandle, TCSANOW, &termInfo); + + //initialise base class + CSerial::Init_Registers(); + InstallationSuccessful = true; + receiveblock=false; + + setEvent(SERIAL_POLLING_EVENT, 1); // millisecond tick +} + +CDirectSerial::~CDirectSerial () { + if (fileHandle >= 0) + { + tcsetattr(fileHandle, TCSANOW, &backup); + close(fileHandle); + } + // We do not use own events so we don't have to clear them. +} + +void CDirectSerial::handleUpperEvent(Bit16u type) { + + switch(type) { + case SERIAL_POLLING_EVENT: { + setEvent(SERIAL_POLLING_EVENT, 1); + if(!receiveblock) { + if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max )) + { + ReadCharacter(); + } else rx_retry++; + } + // check for errors + CheckErrors(); + // update Modem input line states + updateMSR (); + break; + } + case 40: { + // receive time is up + receiveblock=false; + // check if there is something to receive + if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max )) + { + ReadCharacter(); + } else rx_retry++; + break; + } + case SERIAL_TX_EVENT: { + if(!receiveblock) { + if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max )) + { + ReadCharacter(); + } else rx_retry++; + } + ByteTransmitted(); + break; + } + case SERIAL_THR_EVENT: { + ByteTransmitting(); + setEvent(SERIAL_TX_EVENT,bytetime+0.03f); + break; + } + } +} + +void CDirectSerial::ReadCharacter() +{ + Bit8u chRead = 0; + int dwRead = 0; + rx_retry=0; + + dwRead=read(fileHandle,&chRead,1); + if (dwRead==1) { + if(chRead==0xff) // error escape + { + dwRead=read(fileHandle,&chRead,1); + if(chRead==0x00) // an error + { + dwRead=read(fileHandle,&chRead,1); + if(chRead==0x0)receiveError(LSR_RX_BREAK_MASK); + else receiveError(LSR_PARITY_ERROR_MASK); + } + } + receiveByte (chRead); + setEvent(40, bytetime-0.03f); // receive timing + receiveblock=true; + } +} + +void CDirectSerial::CheckErrors() { + +} + +/*****************************************************************************/ +/* updatePortConfig is called when emulated app changes the serial port **/ +/* parameters baudrate, stopbits, number of databits, parity. **/ +/*****************************************************************************/ +void CDirectSerial::updatePortConfig (Bit16u divider, Bit8u lcr) { + Bit8u parity = 0; + Bit8u bytelength = 0; + int baudrate=0; + + // baud + termInfo.c_cflag = CREAD | CLOCAL; + + if (divider == 0x1) baudrate = B115200; + else if (divider == 0x2) baudrate = B57600; + else if (divider == 0x3) baudrate = B38400; + else if (divider == 0x6) baudrate = B19200; + else if (divider == 0xc) baudrate = B9600; + else if (divider == 0x18) baudrate = B4800; + else if (divider == 0x30) baudrate = B2400; + else if (divider == 0x60) baudrate = B1200; + else if (divider == 0xc0) baudrate = B600; + else if (divider == 0x180) baudrate = B300; + else if (divider == 0x417) baudrate = B110; + + // Don't think termios supports nonstandard baudrates + else baudrate = B9600; + + // byte length + bytelength = lcr & 0x3; + bytelength += 5; + + switch (bytelength) { + case 5: + termInfo.c_cflag |= CS5; + break; + + case 6: + termInfo.c_cflag |= CS6; + break; + + case 7: + termInfo.c_cflag |= CS7; + break; + + case 8: + default: + termInfo.c_cflag |= CS8; + break; + } + + // parity + parity = lcr & 0x38; + parity >>= 3; + switch (parity) { + case 0x1: + termInfo.c_cflag |= PARODD; + termInfo.c_cflag |= PARENB; + break; + case 0x3: + termInfo.c_cflag |= PARENB; + break; + case 0x5: + +// "works on many systems" +#define CMSPAR 010000000000 + + termInfo.c_cflag |= PARODD; + termInfo.c_cflag |= PARENB; + termInfo.c_cflag |= CMSPAR; + //LOG_MSG("Serial%d: Mark parity not supported.", COMNUMBER); + break; + case 0x7: + termInfo.c_cflag |= PARENB; + termInfo.c_cflag |= CMSPAR; + //LOG_MSG("Serial%d: Space parity not supported.", COMNUMBER); + break; + default: // no parity + break; + } + + // stopbits + if (lcr & 0x4) termInfo.c_cflag |= CSTOPB; + + cfsetospeed (&termInfo, baudrate); + cfsetispeed (&termInfo, baudrate); + + int retval = tcsetattr(fileHandle, TCSANOW, &termInfo); + + if(retval==-1) + LOG_MSG ("Serial%d: Desired serial mode not supported", COMNUMBER); + +} + +void CDirectSerial::updateMSR () { + long flags = 0; + ioctl (fileHandle, TIOCMGET, &flags); + + if (flags & TIOCM_CTS) setCTS(true); + else setCTS(false); + + if (flags & TIOCM_DSR) setDSR(true); + else setDSR(false); + + if (flags & TIOCM_RI) setRI(true); + else setRI(false); + + if (flags & TIOCM_CD) setCD(true); + else setCD(false); +} + +void CDirectSerial::transmitByte (Bit8u val, bool first) { + if((LCR&LCR_BREAK_MASK) == 0) { + + int bytesWritten = write(fileHandle, &val, 1); + if (bytesWritten != 1) + LOG_MSG ("Serial%d: COM port error: write failed!", idnumber); + } + if(first) setEvent(SERIAL_THR_EVENT, bytetime/8); + else setEvent(SERIAL_TX_EVENT, bytetime); +} + +/*****************************************************************************/ +/* setBreak(val) switches break on or off **/ +/*****************************************************************************/ +void CDirectSerial::setBreak (bool value) { + if (value) ioctl (fileHandle, TIOCSBRK); + else ioctl (fileHandle, TIOCCBRK); +} + +/*****************************************************************************/ +/* updateModemControlLines(mcr) sets DTR and RTS. **/ +/*****************************************************************************/ +void CDirectSerial::setRTSDTR(bool rts, bool dtr) { + + long setflags = 0; + long clearflags = 0; + + if(rts) setflags |= TIOCM_RTS; + else clearflags |= TIOCM_RTS; + + if(dtr) setflags |= TIOCM_DTR; + else clearflags |= TIOCM_DTR; + + if(setflags) ioctl (fileHandle, TIOCMBIS, &setflags); + if(clearflags) ioctl (fileHandle, TIOCMBIC, &clearflags); +} +void CDirectSerial::setRTS(bool val) { + long flag = TIOCM_RTS; + if(val) ioctl(fileHandle, TIOCMBIS, &flag); + else ioctl(fileHandle, TIOCMBIC, &flag); +} +void CDirectSerial::setDTR(bool val) { + long flag = TIOCM_DTR; + if(val) ioctl(fileHandle, TIOCMBIS, &flag); + else ioctl(fileHandle, TIOCMBIC, &flag); +} + +#endif +#endif diff --git a/src/hardware/serialport/directserial_posix.h b/src/hardware/serialport/directserial_posix.h new file mode 100644 index 00000000..f57102a2 --- /dev/null +++ b/src/hardware/serialport/directserial_posix.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2002-2007 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* $Id: directserial_posix.h,v 1.1 2007-01-13 08:35:49 qbix79 Exp $ */ + +// include guard +#ifndef DOSBOX_DIRECTSERIAL_POSIX_H +#define DOSBOX_DIRECTSERIAL_POSIX_H + +#include "dosbox.h" + +#if C_DIRECTSERIAL +#ifdef LINUX + + + +#define DIRECTSERIAL_AVAILIBLE +#include "serialport.h" +#include +#include + +class CDirectSerial : public CSerial { +public: + termios termInfo; + termios backup; + int fileHandle; + + CDirectSerial(Bitu id, CommandLine* cmd); + ~CDirectSerial(); + bool receiveblock; // It's not a block of data it rather blocks + + Bitu rx_retry; // counter of retries + + Bitu rx_retry_max; // how many POLL_EVENTS to wait before causing + // a overrun error. + + void ReadCharacter(); + void CheckErrors(); + + void updatePortConfig(Bit16u divider, Bit8u lcr); + void updateMSR(); + void transmitByte(Bit8u val, bool first); + void setBreak(bool value); + + void setRTSDTR(bool rts, bool dtr); + void setRTS(bool val); + void setDTR(bool val); + void handleUpperEvent(Bit16u type); + +}; + +#endif // WIN32 +#endif // C_DIRECTSERIAL +#endif // include guard diff --git a/src/hardware/serialport/directserial_win32.cpp b/src/hardware/serialport/directserial_win32.cpp index ee546370..5b88721d 100644 --- a/src/hardware/serialport/directserial_win32.cpp +++ b/src/hardware/serialport/directserial_win32.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: directserial_win32.cpp,v 1.4 2007-01-08 19:45:41 qbix79 Exp $ */ +/* $Id: directserial_win32.cpp,v 1.5 2007-01-13 08:35:49 qbix79 Exp $ */ #include "dosbox.h" @@ -27,6 +27,8 @@ #include "serialport.h" #include "directserial_win32.h" +#include "misc_util.h" +#include "pic.h" // Win32 related headers #include @@ -34,17 +36,41 @@ /* This is a serial passthrough class. Its amazingly simple to */ /* write now that the serial ports themselves were abstracted out */ -CDirectSerial::CDirectSerial (IO_ReadHandler * rh, IO_WriteHandler * wh, - TIMER_TickHandler th, Bit16u baseAddr, Bit8u initIrq, - Bit32u initBps, Bit8u bytesize, const char *parity, - Bit8u stopbits,const char *realPort) - :CSerial (rh, wh, th,baseAddr,initIrq, initBps, - bytesize, parity,stopbits) { +CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd) + :CSerial (id, cmd) { InstallationSuccessful = false; - InstallTimerHandler(th); - lastChance = 0; - LOG_MSG ("Serial port at %x: Opening %s", base, realPort); - hCom = CreateFile (realPort, GENERIC_READ | GENERIC_WRITE, 0, // must be opened with exclusive-access + + rx_retry = 0; + rx_retry_max = 0; + + // open the port in NT object space (recommended by Microsoft) + // allows the user to open COM10+ and custom port names. + std::string prefix="\\\\.\\"; + std::string tmpstring; + if(!cmd->FindStringBegin("realport:",tmpstring,false)) return; + +#if SERIAL_DEBUG + if(dbg_modemcontrol) + fprintf(debugfp,"%12.3f Port type directserial realport %s\r\n", + PIC_FullIndex(),tmpstring.c_str()); +#endif + + prefix.append(tmpstring); + + // rxdelay: How many milliseconds to wait before causing an + // overflow when the application is unresponsive. + if(getBituSubstring("rxdelay:", &rx_retry_max, cmd)) { + if(!(rx_retry_max<=10000)) { + rx_retry_max=0; + } + } + + const char* tmpchar=prefix.c_str(); + + LOG_MSG ("Serial%d: Opening %s", COMNUMBER, tmpstring.c_str()); + hCom = CreateFile (tmpchar, + GENERIC_READ | GENERIC_WRITE, 0, + // must be opened with exclusive-access NULL, // no security attributes OPEN_EXISTING, // must use OPEN_EXISTING 0, // non overlapped I/O @@ -53,7 +79,8 @@ CDirectSerial::CDirectSerial (IO_ReadHandler * rh, IO_WriteHandler * wh, if (hCom == INVALID_HANDLE_VALUE) { int error = GetLastError (); - LOG_MSG ("Serial port \"%s\" could not be opened.", realPort); + LOG_MSG ("Serial%d: Serial Port \"%s\" could not be opened.", + COMNUMBER, tmpstring.c_str()); if (error == 2) { LOG_MSG ("The specified port does not exist."); } else if (error == 5) { @@ -61,19 +88,48 @@ CDirectSerial::CDirectSerial (IO_ReadHandler * rh, IO_WriteHandler * wh, } else { LOG_MSG ("Windows error %d occurred.", error); } - - hCom = 0; return; } + dcb.DCBlength=sizeof(dcb); fSuccess = GetCommState (hCom, &dcb); if (!fSuccess) { // Handle the error. LOG_MSG ("GetCommState failed with error %d.\n", GetLastError ()); - hCom = 0; + hCom = INVALID_HANDLE_VALUE; return; } + + // initialize the port + dcb.BaudRate=CBR_9600; + dcb.fBinary=true; + dcb.fParity=true; + dcb.fOutxCtsFlow=false; + dcb.fOutxDsrFlow=false; + dcb.fDtrControl=DTR_CONTROL_DISABLE; + dcb.fDsrSensitivity=false; + + dcb.fOutX=false; + dcb.fInX=false; + dcb.fErrorChar=0; + dcb.fNull=false; + dcb.fRtsControl=RTS_CONTROL_DISABLE; + dcb.fAbortOnError=false; + + dcb.ByteSize=8; + dcb.Parity=NOPARITY; + dcb.StopBits=ONESTOPBIT; + + fSuccess = SetCommState (hCom, &dcb); + + if (!fSuccess) { + // Handle the error. + LOG_MSG ("SetCommState failed with error %d.\n", GetLastError ()); + hCom = INVALID_HANDLE_VALUE; + return; + } + // Configure timeouts to effectively use polling COMMTIMEOUTS ct; ct.ReadIntervalTimeout = MAXDWORD; @@ -83,50 +139,103 @@ CDirectSerial::CDirectSerial (IO_ReadHandler * rh, IO_WriteHandler * wh, ct.WriteTotalTimeoutMultiplier = 0; SetCommTimeouts (hCom, &ct); - CSerial::Init_Registers (initBps, bytesize, parity, stopbits); + CSerial::Init_Registers(); InstallationSuccessful = true; - //LOG_MSG("InstSuccess"); + receiveblock=false; + + ClearCommBreak (hCom); + setEvent(SERIAL_POLLING_EVENT, 1); // millisecond tick } CDirectSerial::~CDirectSerial () { - if (hCom != INVALID_HANDLE_VALUE) - CloseHandle (hCom); + if (hCom != INVALID_HANDLE_VALUE) CloseHandle (hCom); + // We do not use own events so we don't have to clear them. } -Bitu lastChance; - -void CDirectSerial::RXBufferEmpty () { - DWORD dwRead; - DWORD errors; - Bit8u chRead; - if (lastChance > 0) { - receiveByte (ChanceChar); - lastChance = 0; - } else { - // update RX - if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) { - if (dwRead != 0) { - //LOG_MSG("UART 0x%x: RX 0x%x", base,chRead); - receiveByte (chRead); +void CDirectSerial::handleUpperEvent(Bit16u type) { + + switch(type) { + case SERIAL_POLLING_EVENT: { + DWORD dwRead = 0; + DWORD errors = 0; + Bit8u chRead = 0; + + setEvent(SERIAL_POLLING_EVENT, 1); + if(!receiveblock) { + if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max )) + { + rx_retry=0; + if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) { + if (dwRead) { + receiveByte (chRead); + setEvent(40, bytetime-0.03f); // receive timing + receiveblock=true; + } + } + } else rx_retry++; } + // check for errors + CheckErrors(); + // update Modem input line states + updateMSR (); + break; + } + case 40: { + // receive time is up + DWORD dwRead = 0; + Bit8u chRead = 0; + receiveblock=false; + // check if there is something to receive + if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max )) + { + rx_retry=0; + if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) { + if (dwRead) { + receiveByte (chRead); + setEvent(40, bytetime-0.03f); // receive timing + receiveblock=true; + } + } + } else rx_retry++; + break; + } + case SERIAL_TX_EVENT: { + DWORD dwRead = 0; + Bit8u chRead = 0; + if(!receiveblock) { + if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max )) + { + rx_retry=0; + if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) { + if (dwRead) { + receiveByte (chRead); + setEvent(40, bytetime-0.03f); // receive timing + receiveblock=true; + } + } + } else rx_retry++; + } + ByteTransmitted(); + break; + } + case SERIAL_THR_EVENT: { + ByteTransmitting(); + setEvent(SERIAL_TX_EVENT,bytetime+0.03f); + break; } } +} + +void CDirectSerial::CheckErrors() { + + DWORD errors=0; // check for errors if (ClearCommError (hCom, &errors, NULL)) if (errors & (CE_BREAK | CE_FRAME | CE_RXPARITY)) { Bit8u errreg = 0; - if (errors & CE_BREAK) { - LOG_MSG ("Serial port at 0x%x: line error: break received.", base); - errreg |= LSR_RX_BREAK_MASK; - } - if (errors & CE_FRAME) { - LOG_MSG ("Serial port at 0x%x: line error: framing error.", base); - errreg |= LSR_FRAMING_ERROR_MASK; - } - if (errors & CE_RXPARITY) { - LOG_MSG ("Serial port at 0x%x: line error: parity error.", base); - errreg |= LSR_PARITY_ERROR_MASK; - } + if (errors & CE_BREAK) errreg |= LSR_RX_BREAK_MASK; + if (errors & CE_FRAME) errreg |= LSR_FRAMING_ERROR_MASK; + if (errors & CE_RXPARITY) errreg |= LSR_PARITY_ERROR_MASK; receiveError (errreg); } } @@ -135,45 +244,37 @@ void CDirectSerial::RXBufferEmpty () { /* updatePortConfig is called when emulated app changes the serial port **/ /* parameters baudrate, stopbits, number of databits, parity. **/ /*****************************************************************************/ -void CDirectSerial::updatePortConfig (Bit8u dll, Bit8u dlm, Bit8u lcr) { +void CDirectSerial::updatePortConfig (Bit16u divider, Bit8u lcr) { Bit8u parity = 0; Bit8u bytelength = 0; - Bit16u baudrate = 0; // baud - baudrate = dlm; - baudrate = baudrate << 8; - baudrate |= dll; - if (baudrate <= 0x1) + if (divider == 0x1) dcb.BaudRate = CBR_115200; - else if (baudrate <= 0x2) + else if (divider == 0x2) dcb.BaudRate = CBR_57600; - else if (baudrate <= 0x3) + else if (divider == 0x3) dcb.BaudRate = CBR_38400; - else if (baudrate <= 0x6) + else if (divider == 0x6) dcb.BaudRate = CBR_19200; - else if (baudrate <= 0xc) + else if (divider == 0xc) dcb.BaudRate = CBR_9600; - else if (baudrate <= 0x18) + else if (divider == 0x18) dcb.BaudRate = CBR_4800; - else if (baudrate <= 0x30) + else if (divider == 0x30) dcb.BaudRate = CBR_2400; - else if (baudrate <= 0x60) + else if (divider == 0x60) dcb.BaudRate = CBR_1200; - else if (baudrate <= 0xc0) + else if (divider == 0xc0) dcb.BaudRate = CBR_600; - else if (baudrate <= 0x180) + else if (divider == 0x180) dcb.BaudRate = CBR_300; - else if (baudrate <= 0x417) + else if (divider == 0x417) dcb.BaudRate = CBR_110; // I read that windows can handle nonstandard baudrates: else - dcb.BaudRate = 115200 / baudrate; - -#ifdef SERIALPORT_DEBUGMSG - LOG_MSG ("Serial port at %x: new baud rate: %d", base, dcb.BaudRate); -#endif + dcb.BaudRate = 115200 / divider; // byte length bytelength = lcr & 0x3; @@ -211,9 +312,25 @@ void CDirectSerial::updatePortConfig (Bit8u dll, Bit8u dlm, Bit8u lcr) { dcb.StopBits = ONESTOPBIT; } - if (!SetCommState (hCom, &dcb)) - LOG_MSG ("Serial port at 0x%x: API did not like the new values.", base); - //LOG_MSG("Serial port at 0x%x: Port params changed: %d Baud", base,dcb.BaudRate); +#ifdef SERIALPORT_DEBUGMSG + LOG_MSG ("__________________________"); + LOG_MSG ("Serial%d: new baud rate: %d", COMNUMBER, dcb.BaudRate); + LOG_MSG ("Serial%d: new bytelen: %d", COMNUMBER, dcb.ByteSize); + LOG_MSG ("Serial%d: new parity: %d", COMNUMBER, dcb.Parity); + LOG_MSG ("Serial%d: new stopbits: %d", COMNUMBER, dcb.StopBits); +#endif + + if (!SetCommState (hCom, &dcb)) { + +#if SERIAL_DEBUG + if(dbg_modemcontrol) + fprintf(debugfp,"%12.3f serial mode not supported: rate=%d,LCR=%x.\r\n", + PIC_FullIndex(),dcb.BaudRate,lcr); +#endif + + LOG_MSG ("Serial%d: Desired serial mode not supported (%d,%d,%d,%d", + dcb.BaudRate,dcb.ByteSize,dcb.Parity,dcb.StopBits, COMNUMBER); + } } void CDirectSerial::updateMSR () { @@ -226,153 +343,53 @@ void CDirectSerial::updateMSR () { #endif //return; } - if (dptr & MS_CTS_ON) - newmsr |= MSR_CTS_MASK; - if (dptr & MS_DSR_ON) - newmsr |= MSR_DSR_MASK; - if (dptr & MS_RING_ON) - newmsr |= MSR_RI_MASK; - if (dptr & MS_RLSD_ON) - newmsr |= MSR_CD_MASK; - changeMSR (newmsr); + setCTS((dptr & MS_CTS_ON)!=0); + setDSR((dptr & MS_DSR_ON)!=0); + setRI ((dptr & MS_RING_ON)!=0); + setCD((dptr & MS_RLSD_ON)!=0); } -void CDirectSerial::transmitByte (Bit8u val) { +void CDirectSerial::transmitByte (Bit8u val, bool first) { // mean bug: with break = 1, WriteFile will never return. if((LCR&LCR_BREAK_MASK) == 0) { - DWORD bytesWritten = 0; WriteFile (hCom, &val, 1, &bytesWritten, NULL); - if (bytesWritten > 0) { - ByteTransmitted (); - //LOG_MSG("UART 0x%x: TX 0x%x", base,val); - } else { - LOG_MSG ("UART 0x%x: NO BYTE WRITTEN! PORT HANGS NOW!", base); - } - } else { - // have a delay here, it's the only sense of sending - // data with break=1 - Bitu ticks; - Bitu elapsed = 0; - ticks = GetTicks(); - - while(elapsed < 10) { - elapsed = GetTicks() - ticks; - } - ByteTransmitted(); + if (bytesWritten != 1) + LOG_MSG ("Serial%d: COM port error: write failed!", idnumber); } + if(first) setEvent(SERIAL_THR_EVENT, bytetime/8); + else setEvent(SERIAL_TX_EVENT, bytetime); } /*****************************************************************************/ /* setBreak(val) switches break on or off **/ /*****************************************************************************/ - void CDirectSerial::setBreak (bool value) { - //#ifdef SERIALPORT_DEBUGMSG - //LOG_MSG("UART 0x%x: Break toggeled: %d", base, value); - //#endif - if (value) - SetCommBreak (hCom); - else - ClearCommBreak (hCom); + if (value) SetCommBreak (hCom); + else ClearCommBreak (hCom); } /*****************************************************************************/ /* updateModemControlLines(mcr) sets DTR and RTS. **/ /*****************************************************************************/ -void CDirectSerial::updateModemControlLines ( /*Bit8u mcr */ ) { - bool change = false; +void CDirectSerial::setRTSDTR(bool rts, bool dtr) { + if(rts) dcb.fRtsControl = RTS_CONTROL_ENABLE; + else dcb.fRtsControl = RTS_CONTROL_DISABLE; + if(dtr) dcb.fDtrControl = DTR_CONTROL_ENABLE; + else dcb.fDtrControl = DTR_CONTROL_DISABLE; + SetCommState (hCom, &dcb); - /*** DTR ***/ - if (CSerial::getDTR ()) { // DTR on - if (dcb.fDtrControl == DTR_CONTROL_DISABLE) { - dcb.fDtrControl = DTR_CONTROL_ENABLE; - change = true; - } - } else { - if (dcb.fDtrControl == DTR_CONTROL_ENABLE) { - dcb.fDtrControl = DTR_CONTROL_DISABLE; - change = true; - } - } - /*** RTS ***/ - if (CSerial::getRTS ()) { // RTS on - if (dcb.fRtsControl == RTS_CONTROL_DISABLE) { - dcb.fRtsControl = RTS_CONTROL_ENABLE; - change = true; - } - } else { - if (dcb.fRtsControl == RTS_CONTROL_ENABLE) { - dcb.fRtsControl = RTS_CONTROL_DISABLE; - change = true; - } - } - if (change) - SetCommState (hCom, &dcb); } - -void CDirectSerial::Timer2(void) { - DWORD dwRead = 0; - DWORD errors = 0; - Bit8u chRead = 0; - - - if (lastChance == 0) { // lastChance = 0 - if (CanReceiveByte ()) { - if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) { - if (dwRead) - receiveByte (chRead); - } - } else { - if (ReadFile (hCom, &chRead, 1, &dwRead, NULL)) { - if (dwRead) { - ChanceChar = chRead; - lastChance++; - } - } - } - } else if (lastChance > 10) { - receiveByte (0); // this causes RX Overrun now - lastChance = 0; - // empty serial buffer - dwRead = 1; - while (dwRead > 0) { // throw away bytes in buffer - ReadFile (hCom, &chRead, 1, &dwRead, NULL); - } - } else { // lastChance>0 // already one waiting - if (CanReceiveByte ()) { // chance used - receiveByte (ChanceChar); - lastChance = 0; - } else - lastChance++; - } - - // check for errors - if (ClearCommError (hCom, &errors, NULL)) - if (errors & (CE_BREAK | CE_FRAME | CE_RXPARITY)) { - Bit8u errreg = 0; - - if (errors & CE_BREAK) { - LOG_MSG ("Serial port at 0x%x: line error: break received.", base); - errreg |= LSR_RX_BREAK_MASK; - } - if (errors & CE_FRAME) { - LOG_MSG ("Serial port at 0x%x: line error: framing error.", base); - errreg |= LSR_FRAMING_ERROR_MASK; - } - if (errors & CE_RXPARITY) { - LOG_MSG ("Serial port at 0x%x: line error: parity error.", base); - errreg |= LSR_PARITY_ERROR_MASK; - } - - receiveError (errreg); - } - // update Modem input line states - updateMSR (); +void CDirectSerial::setRTS(bool val) { + if(val) dcb.fRtsControl = RTS_CONTROL_ENABLE; + else dcb.fRtsControl = RTS_CONTROL_DISABLE; + SetCommState (hCom, &dcb); +} +void CDirectSerial::setDTR(bool val) { + if(val) dcb.fDtrControl = DTR_CONTROL_ENABLE; + else dcb.fDtrControl = DTR_CONTROL_DISABLE; + SetCommState (hCom, &dcb); } - - -#else /*linux and others oneday maybe */ #endif #endif diff --git a/src/hardware/serialport/directserial_win32.h b/src/hardware/serialport/directserial_win32.h index e78beaea..225da117 100644 --- a/src/hardware/serialport/directserial_win32.h +++ b/src/hardware/serialport/directserial_win32.h @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: directserial_win32.h,v 1.3 2007-01-08 19:45:41 qbix79 Exp $ */ +/* $Id: directserial_win32.h,v 1.4 2007-01-13 08:35:49 qbix79 Exp $ */ // include guard #ifndef DOSBOX_DIRECTSERIAL_WIN32_H @@ -39,43 +39,27 @@ public: DCB dcb; BOOL fSuccess; - CDirectSerial( - IO_ReadHandler* rh, - IO_WriteHandler* wh, - TIMER_TickHandler th, - Bit16u baseAddr, - Bit8u initIrq, - Bit32u initBps, - Bit8u bytesize, - const char *parity, - Bit8u stopbits, - const char * realPort - ); - - + CDirectSerial(Bitu id, CommandLine* cmd/*const char* configstring*/); ~CDirectSerial(); + bool receiveblock; // It's not a block of data it rather blocks + + Bitu rx_retry; // counter of retries + + Bitu rx_retry_max; // how many POLL_EVENTS to wait before causing + // a overrun error. + + + void CheckErrors(); - - Bitu lastChance; // If there is no space for new - // received data, it gets a little chance - Bit8u ChanceChar; - - bool CanRecv(void); - bool CanSend(void); - - bool InstallationSuccessful; // check after constructing. If - // something was wrong, delete it right away. - - void RXBufferEmpty(); - - - void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr); + void updatePortConfig(Bit16u divider, Bit8u lcr); void updateMSR(); - void transmitByte(Bit8u val); + void transmitByte(Bit8u val, bool first); void setBreak(bool value); - void updateModemControlLines(/*Bit8u mcr*/); - void Timer2(void); + void setRTSDTR(bool rts, bool dtr); + void setRTS(bool val); + void setDTR(bool val); + void handleUpperEvent(Bit16u type); }; diff --git a/src/hardware/serialport/misc_util.cpp b/src/hardware/serialport/misc_util.cpp new file mode 100644 index 00000000..84ee29d3 --- /dev/null +++ b/src/hardware/serialport/misc_util.cpp @@ -0,0 +1,318 @@ +#include "config.h" + +#include "misc_util.h" + +#if C_MODEM + +/*****************************************************************************/ +// C++ SDLnet wrapper + +// Socket inheritance +#ifdef LINUX +#define CAPWORD (NETWRAPPER_TCP|NETWRAPPER_TCP_NATIVESOCKET) +#include +#include +#include +#define SOCKET int + +#elif defined WIN32 +#define CAPWORD (NETWRAPPER_TCP|NETWRAPPER_TCP_NATIVESOCKET) +#include +typedef int socklen_t; + +#else +#define CAPWORD NETWRAPPER_TCP +#endif + +struct _TCPsocketX { + int ready; + SOCKET channel; + IPaddress remoteAddress; + IPaddress localAddress; + int sflag; +}; + +Bit32u Netwrapper_GetCapabilities() +{ + Bit32u retval=0; + retval = CAPWORD; + return retval; +} + +#ifdef NATIVESOCKETS +TCPClientSocket::TCPClientSocket(int platformsocket) { + sendbuffer=0; + nativetcpstruct = new Bit8u[sizeof(struct _TCPsocketX)]; + + mysock = (TCPsocket)nativetcpstruct; + isopen = false; + if(!SDLNetInited) { + if(SDLNet_Init()==-1) { + LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError()); + return; + } + SDLNetInited = true; + } + // fill the SDL socket manually + ((struct _TCPsocketX*)nativetcpstruct)->ready=0; + ((struct _TCPsocketX*)nativetcpstruct)->sflag=0; + ((struct _TCPsocketX*)nativetcpstruct)->channel=platformsocket; + sockaddr_in sa; + socklen_t sz; + sz=sizeof(sa); + if(getpeername(platformsocket, (sockaddr *)(&sa), &sz)==0) { + ((struct _TCPsocketX*)nativetcpstruct)-> + remoteAddress.host=/*ntohl(*/sa.sin_addr.s_addr;//); + ((struct _TCPsocketX*)nativetcpstruct)-> + remoteAddress.port=/*ntohs(*/sa.sin_port;//); + } + else { + mysock=0; + return; + } + sz=sizeof(sa); + if(getsockname(platformsocket, (sockaddr *)(&sa), &sz)==0) { + ((struct _TCPsocketX*)nativetcpstruct)-> + localAddress.host=/*ntohl(*/sa.sin_addr.s_addr;//); + ((struct _TCPsocketX*)nativetcpstruct)-> + localAddress.port=/*ntohs(*/sa.sin_port;//); + } + else { + mysock=0; + return; + } + if(mysock!=0) { + listensocketset = SDLNet_AllocSocketSet(1); + if(!listensocketset) return; + SDLNet_TCP_AddSocket(listensocketset, mysock); + isopen=true; + return; + } + mysock=0; + return; +} +#endif // NATIVESOCKETS + +TCPClientSocket::TCPClientSocket(TCPsocket source) { +#ifdef NATIVESOCKETS + nativetcpstruct=0; +#endif + sendbuffer=0; + isopen = false; + if(!SDLNetInited) { + if(SDLNet_Init()==-1) { + LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError()); + return; + } + SDLNetInited = true; + } + + mysock=0; + listensocketset=0; + if(source!=0) { + mysock = source; + listensocketset = SDLNet_AllocSocketSet(1); + if(!listensocketset) return; + SDLNet_TCP_AddSocket(listensocketset, source); + + isopen=true; + } +} +TCPClientSocket::TCPClientSocket(const char* destination, Bit16u port) { +#ifdef NATIVESOCKETS + nativetcpstruct=0; +#endif + sendbuffer=0; + isopen = false; + if(!SDLNetInited) { + if(SDLNet_Init()==-1) { + LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError()); + return; + } + SDLNetInited = true; + } + mysock=0; + listensocketset=0; + + IPaddress openip; + if (!SDLNet_ResolveHost(&openip,destination,port)) { + listensocketset = SDLNet_AllocSocketSet(1); + if(!listensocketset) return; + mysock = SDLNet_TCP_Open(&openip); + if(!mysock) return; + SDLNet_TCP_AddSocket(listensocketset, mysock); + isopen=true; + } +} + +TCPClientSocket::~TCPClientSocket() { + + if(sendbuffer) delete [] sendbuffer; +#ifdef NATIVESOCKETS + if(nativetcpstruct) delete [] nativetcpstruct; + else +#endif + if(mysock) { + if(listensocketset) SDLNet_TCP_DelSocket(listensocketset,mysock); + SDLNet_TCP_Close(mysock); + } + + if(listensocketset) SDLNet_FreeSocketSet(listensocketset); +} +bool TCPClientSocket::GetRemoteAddressString(Bit8u* buffer) { + IPaddress* remote_ip; + Bit8u b1, b2, b3, b4; + remote_ip=SDLNet_TCP_GetPeerAddress(mysock); + if(!remote_ip) return false; + b4=remote_ip->host>>24; + b3=(remote_ip->host>>16)&0xff; + b2=(remote_ip->host>>8)&0xff; + b1=remote_ip->host&0xff; + sprintf((char*)buffer,"%u.%u.%u.%u",b1,b2,b3,b4); + return true; +} + +bool TCPClientSocket::ReceiveArray(Bit8u* data, Bitu* size) { + if(SDLNet_CheckSockets(listensocketset,0)) + { + Bitu retval = SDLNet_TCP_Recv(mysock, data, *size); + if(retval<1) { + isopen=false; + *size=0; + return false; + } else { + *size=retval; + return true; + } + } + else { + *size=0; + return true; + } +} + + +Bits TCPClientSocket::GetcharNonBlock() { +// return: +// -1: no data +// -2: socket closed +// 0..255: data + if(SDLNet_CheckSockets(listensocketset,0)) + { + Bitu retval =0; + if(SDLNet_TCP_Recv(mysock, &retval, 1)!=1) { + isopen=false; + return -2; + } else return retval; + } + else return -1; +} +bool TCPClientSocket::Putchar(Bit8u data) { + if(SDLNet_TCP_Send(mysock, &data, 1)!=1) { + isopen=false; + return false; + } + return true; +} + +bool TCPClientSocket::SendArray(Bit8u* data, Bitu bufsize) { + if(SDLNet_TCP_Send(mysock, data, bufsize)!=bufsize) { + isopen=false; + return false; + } + return true; +} + +bool TCPClientSocket::SendByteBuffered(Bit8u data) { + + if(sendbufferindex==(sendbuffersize-1)) { + // buffer is full, get rid of it + sendbuffer[sendbufferindex]=data; + sendbufferindex=0; + + if(SDLNet_TCP_Send(mysock, sendbuffer, sendbuffersize)!=sendbuffersize) { + isopen=false; + return false; + } + } else { + sendbuffer[sendbufferindex]=data; + sendbufferindex++; + } + return true; +} +/* +bool TCPClientSocket::SendArrayBuffered(Bit8u* data, Bitu bufsize) { + + Bitu bytes + while( + + // first case, buffer already full + /*if(sendbufferindex==(sendbuffersize-1)) { + // buffer is full, get rid of it + sendbuffer[sendbufferindex]=data; + sendbufferindex=0; + + if(SDLNet_TCP_Send(mysock, sendbuffer, sendbuffersize)!=sendbuffersize) { + isopen=false; + return false; + } + }*/ +//} + +void TCPClientSocket::FlushBuffer() { + if(sendbufferindex) { + if(SDLNet_TCP_Send(mysock, sendbuffer, + sendbufferindex)!=sendbufferindex) { + isopen=false; + return; + } + sendbufferindex=0; + } +} + +void TCPClientSocket::SetSendBufferSize(Bitu bufsize) { + if(sendbuffer) delete [] sendbuffer; + sendbuffer = new Bit8u[bufsize]; + sendbuffersize=bufsize; + sendbufferindex=0; +} + + +TCPServerSocket::TCPServerSocket(Bit16u port) +{ + isopen = false; + mysock = 0; + if(!SDLNetInited) { + if(SDLNet_Init()==-1) { + LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError()); + return; + } + SDLNetInited = true; + } + if (port) { + IPaddress listen_ip; + SDLNet_ResolveHost(&listen_ip, NULL, port); + mysock=SDLNet_TCP_Open(&listen_ip); + if(!mysock) return; + } + else return; + isopen = true; +} + +TCPServerSocket::~TCPServerSocket() { + if(mysock) SDLNet_TCP_Close(mysock); +} + +TCPClientSocket* TCPServerSocket::Accept() { + + TCPsocket new_tcpsock; + + new_tcpsock=SDLNet_TCP_Accept(mysock); + if(!new_tcpsock) { + //printf("SDLNet_TCP_Accept: %s\n", SDLNet_GetError()); + return 0; + } + + return new TCPClientSocket(new_tcpsock); +} +#endif // #if C_MODEM diff --git a/src/hardware/serialport/misc_util.h b/src/hardware/serialport/misc_util.h new file mode 100644 index 00000000..62f8809d --- /dev/null +++ b/src/hardware/serialport/misc_util.h @@ -0,0 +1,79 @@ +#ifndef SDLNETWRAPPER_H +#define SDLNETWRAPPER_H + +#if C_MODEM + +#include "SDL_net.h" +#include "support.h" + +#ifdef LINUX +#define NATIVESOCKETS + +#elif defined WIN32 +#define NATIVESOCKETS + +#else +#endif + +// Netwrapper Capabilities +#define NETWRAPPER_TCP 1 +#define NETWRAPPER_TCP_NATIVESOCKET 2 + +Bit32u Netwrapper_GetCapabilities(); + + +class TCPClientSocket { + public: + TCPClientSocket(TCPsocket source); + TCPClientSocket(const char* destination, Bit16u port); +#ifdef NATIVESOCKETS + void* nativetcpstruct; + TCPClientSocket(int platformsocket); +#endif + ~TCPClientSocket(); + + // return: + // -1: no data + // -2: socket closed + // >0: data char + Bits GetcharNonBlock(); + + + bool Putchar(Bit8u data); + bool SendArray(Bit8u* data, Bitu bufsize); + bool ReceiveArray(Bit8u* data, Bitu* size); + bool isopen; + + bool GetRemoteAddressString(Bit8u* buffer); + + void FlushBuffer(); + void SetSendBufferSize(Bitu bufsize); + + // buffered send functions + bool SendByteBuffered(Bit8u data); + bool SendArrayBuffered(Bit8u* data, Bitu bufsize); + + private: + TCPsocket mysock; + SDLNet_SocketSet listensocketset; + + // Items for send buffering + Bitu sendbuffersize; + Bitu sendbufferindex; + + Bit8u* sendbuffer; +}; + +class TCPServerSocket { + public: + bool isopen; + TCPsocket mysock; + TCPServerSocket(Bit16u port); + ~TCPServerSocket(); + TCPClientSocket* Accept(); +}; + + +#endif + +#endif //#if C_MODEM diff --git a/src/hardware/serialport/nullmodem.cpp b/src/hardware/serialport/nullmodem.cpp new file mode 100644 index 00000000..7f8cb47b --- /dev/null +++ b/src/hardware/serialport/nullmodem.cpp @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2002-2007 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "dosbox.h" + +#if C_MODEM + +#include "setup.h" // CommandLine +#include "serialport.h" +#include "nullmodem.h" + +CNullModem::CNullModem(Bitu id, CommandLine* cmd):CSerial (id, cmd) { + Bitu temptcpport=23; + memset(&telClient, 0, sizeof(telClient)); + InstallationSuccessful = false; + serversocket = 0; + clientsocket = 0; + serverport = 0; + clientport = 0; + + rx_retry = 0; + rx_retry_max = 100; + + tx_gather = 12; + + dtrrespect=false; + tx_block=false; + receiveblock=false; + transparent=false; + telnet=false; + + Bitu bool_temp=0; + + // usedtr: The nullmodem will + // 1) when it is client connect to the server not immediately but + // as soon as a modem-aware application is started (DTR is switched on). + // 2) only transfer data when DTR is on. + if(getBituSubstring("usedtr:", &bool_temp, cmd)) { + if(bool_temp==1) { + dtrrespect=true; + transparent=true; + } + } + // transparent: don't add additional handshake control. + if(getBituSubstring("transparent:", &bool_temp, cmd)) { + if(bool_temp==1) transparent=true; + else transparent=false; + } + // telnet: interpret telnet commands. + if(getBituSubstring("telnet:", &bool_temp, cmd)) { + if(bool_temp==1) { + transparent=true; + telnet=true; + } + } + // rxdelay: How many milliseconds to wait before causing an + // overflow when the application is unresponsive. + if(getBituSubstring("rxdelay:", &rx_retry_max, cmd)) { + if(!(rx_retry_max<=10000)) { + rx_retry_max=50; + } + } + // txdelay: How many milliseconds to wait before sending data. + // This reduces network overhead quite a lot. + if(getBituSubstring("txdelay:", &tx_gather, cmd)) { + if(!(tx_gather<=500)) { + tx_gather=12; + } + } + // port is for both server and client + if(getBituSubstring("port:", &temptcpport, cmd)) { + if(!(temptcpport>0&&temptcpport<65536)) { + temptcpport=23; + } + } + // socket inheritance + if(getBituSubstring("inhsocket:", &bool_temp, cmd)) { + if(Netwrapper_GetCapabilities()&NETWRAPPER_TCP_NATIVESOCKET) { + if(bool_temp==1) { + int sock; + if (control->cmdline->FindInt("-socket",sock,true)) { + dtrrespect=false; + transparent=true; + // custom connect + Bit8u peernamebuf[16]; + LOG_MSG("inheritance port: %d",sock); + clientsocket = new TCPClientSocket(sock); + if(!clientsocket->isopen) { + LOG_MSG("Serial%d: Connection failed.",COMNUMBER); + delete clientsocket; + clientsocket=0; + return; + } + clientsocket->SetSendBufferSize(256); + clientsocket->GetRemoteAddressString(peernamebuf); + // transmit the line status + if(!transparent) setRTSDTR(getRTS(), getDTR()); + + LOG_MSG("Serial%d: Connected to %s",COMNUMBER,peernamebuf); + setEvent(SERIAL_POLLING_EVENT, 1); + + CSerial::Init_Registers (); + InstallationSuccessful = true; + + setCTS(true); + setDSR(true); + setRI (false); + setCD (true); + return; + } else { + LOG_MSG("Serial%d: -socket start parameter missing.",COMNUMBER); + return; + } + } + } else { + LOG_MSG("Serial%d: socket inheritance not supported on this platform.", + COMNUMBER); + return; + } + } + std::string tmpstring; + if(cmd->FindStringBegin("server:",tmpstring,false)) { + // we are a client + const char* hostnamechar=tmpstring.c_str(); + Bitu hostlen=strlen(hostnamechar)+1; + if(hostlen>sizeof(hostnamebuffer)) { + hostlen=sizeof(hostnamebuffer); + hostnamebuffer[sizeof(hostnamebuffer)-1]=0; + } + memcpy(hostnamebuffer,hostnamechar,hostlen); + clientport=temptcpport; + if(dtrrespect) { + // we connect as soon as DTR is switched on + setEvent(SERIAL_NULLMODEM_DTR_EVENT, 50); + LOG_MSG("Serial%d: Waiting for DTR...",COMNUMBER); + } else ClientConnect(); + } else { + // we are a server + serverport = (Bit16u)temptcpport; + serversocket = new TCPServerSocket(serverport); + if(!serversocket->isopen) return; + LOG_MSG("Serial%d: Nullmodem server waiting for connection on port %d...", + COMNUMBER,serverport); + setEvent(SERIAL_SERVER_POLLING_EVENT, 50); + } + + // .... + + CSerial::Init_Registers (); + InstallationSuccessful = true; + + setCTS(dtrrespect||transparent); + setDSR(dtrrespect||transparent); + setRI (false); + setCD (dtrrespect); +} + +CNullModem::~CNullModem () { + if(serversocket) delete serversocket; + if(clientsocket) delete clientsocket; + // remove events + for(Bitu i = SERIAL_BASE_EVENT_COUNT+1; + i <= SERIAL_NULLMODEM_EVENT_COUNT; i++) + removeEvent(i); +} + +void CNullModem::WriteChar(Bit8u data) { + + if(clientsocket)clientsocket->SendByteBuffered(data); + if(!tx_block) { + //LOG_MSG("setevreduct"); + setEvent(SERIAL_TX_REDUCTION, (float)tx_gather); + tx_block=true; + } +} + +Bits CNullModem::readChar() { + + Bits rxchar = clientsocket->GetcharNonBlock(); + if(telnet && rxchar>=0) return TelnetEmulation(rxchar); + else if(rxchar==0xff && !transparent) {// escape char + // get the next char + Bits rxchar = clientsocket->GetcharNonBlock(); + if(rxchar==0xff) return rxchar; // 0xff 0xff -> 0xff was meant + rxchar&0x1? setCTS(true) : setCTS(false); + rxchar&0x2? setDSR(true) : setDSR(false); + if(rxchar&0x4) receiveError(0x10); + return -1; // no "payload" received + } else return rxchar; +} + +void CNullModem::ClientConnect(){ + Bit8u peernamebuf[16]; + clientsocket = new TCPClientSocket((char*)hostnamebuffer, + (Bit16u)clientport); + if(!clientsocket->isopen) { + LOG_MSG("Serial%d: Connection failed.",idnumber+1); + delete clientsocket; + clientsocket=0; + return; + } + clientsocket->SetSendBufferSize(256); + clientsocket->GetRemoteAddressString(peernamebuf); + // transmit the line status + if(!transparent) setRTSDTR(getRTS(), getDTR()); + + LOG_MSG("Serial%d: Connected to %s",idnumber+1,peernamebuf); + setEvent(SERIAL_POLLING_EVENT, 1); +} + +void CNullModem::Disconnect() { + // it was disconnected; free the socket and restart the server socket + LOG_MSG("Serial%d: Disconnected.",idnumber+1); + delete clientsocket; + clientsocket=0; + setDTR(false); + setCTS(false); + if(serverport) { + serversocket = new TCPServerSocket(serverport); + if(serversocket->isopen) + setEvent(SERIAL_SERVER_POLLING_EVENT, 50); + else delete serversocket; + } +} + +void CNullModem::handleUpperEvent(Bit16u type) { + + switch(type) { + case SERIAL_POLLING_EVENT: { + // periodically check if new data arrived, disconnect + // if required. Add it back. + if(!receiveblock && clientsocket) { + if(((!(LSR&LSR_RX_DATA_READY_MASK)) || rx_retry>=rx_retry_max ) + &&(!dtrrespect | (dtrrespect&& getDTR()) )) { + rx_retry=0; + Bits rxchar = readChar(); + if(rxchar>=0) { + receiveblock=true; + setEvent(SERIAL_RX_EVENT, bytetime-0.01f); + receiveByte((Bit8u)rxchar); + } + else if(rxchar==-2) Disconnect(); + else setEvent(SERIAL_POLLING_EVENT, 1); + } else { + rx_retry++; + setEvent(SERIAL_POLLING_EVENT, 1); + } + } + break; + } + case SERIAL_RX_EVENT: { + // receive time is up, try to receive another byte. + receiveblock=false; + + if((!(LSR&LSR_RX_DATA_READY_MASK) || rx_retry>=rx_retry_max) + &&(!dtrrespect | (dtrrespect&& getDTR()) ) + ) { + rx_retry=0; + Bits rxchar = readChar(); + if(rxchar>=0) { + receiveblock=true; + setEvent(SERIAL_RX_EVENT, bytetime-0.01f); + receiveByte((Bit8u)rxchar); + } + else if(rxchar==-2) Disconnect(); + else setEvent(SERIAL_POLLING_EVENT, 1); + } else { + setEvent(SERIAL_POLLING_EVENT, 1); + rx_retry++; + } + break; + } + case SERIAL_TX_EVENT: { + ByteTransmitted(); + break; + } + case SERIAL_THR_EVENT: { + ByteTransmitting(); + // actually send it + setEvent(SERIAL_TX_EVENT,bytetime+0.01f); + break; + } + case SERIAL_SERVER_POLLING_EVENT: { + // As long as nothing is connected to out server poll the + // connection. + if((clientsocket=serversocket->Accept())) { + Bit8u peeripbuf[16]; + clientsocket->GetRemoteAddressString(peeripbuf); + LOG_MSG("Serial%d: A client (%s) has connected.",idnumber+1,peeripbuf); + // new socket found... + clientsocket->SetSendBufferSize(256); + setEvent(SERIAL_POLLING_EVENT, 1); + + // we don't accept further connections + delete serversocket; + serversocket=0; + + // transmit the line status + setRTSDTR(getRTS(), getDTR()); + } else { + // continue looking + setEvent(SERIAL_SERVER_POLLING_EVENT, 50); + } + break; + } + case SERIAL_TX_REDUCTION: { + // Flush the data in the transmitting buffer. + if(clientsocket) clientsocket->FlushBuffer(); + tx_block=false; + break; + } + case SERIAL_NULLMODEM_DTR_EVENT: { + if(getDTR()) ClientConnect(); + else setEvent(SERIAL_NULLMODEM_DTR_EVENT,50); + break; + } + } +} + +/*****************************************************************************/ +/* updatePortConfig is called when emulated app changes the serial port **/ +/* parameters baudrate, stopbits, number of databits, parity. **/ +/*****************************************************************************/ +void CNullModem::updatePortConfig (Bit16u divider, Bit8u lcr) { + +} + +void CNullModem::updateMSR () { + +} + +void CNullModem::transmitByte (Bit8u val, bool first) { + + // transmit it later in THR_Event + if(first) { + setEvent(SERIAL_THR_EVENT, bytetime/8); + } + else { + //if(clientsocket) clientsocket->Putchar(val); + setEvent(SERIAL_TX_EVENT, bytetime); + } + /*****************************/ + if(val==0xff) WriteChar(0xff); + + WriteChar(val); +} + +Bits CNullModem::TelnetEmulation(Bit8u data) { + Bit8u response[3]; + if(telClient.inIAC) { + if(telClient.recCommand) { + if((data != 0) && (data != 1) && (data != 3)) { + LOG_MSG("Serial%d: Unrecognized telnet option %d",COMNUMBER, data); + if(telClient.command>250) { + /* Reject anything we don't recognize */ + response[0]=0xff; + response[1]=252; + response[2]=data; /* We won't do crap! */ + if(clientsocket) clientsocket->SendArray(response, 3); + } + } + switch(telClient.command) { + case 251: /* Will */ + if(data == 0) telClient.binary[TEL_SERVER] = true; + if(data == 1) telClient.echo[TEL_SERVER] = true; + if(data == 3) telClient.supressGA[TEL_SERVER] = true; + break; + case 252: /* Won't */ + if(data == 0) telClient.binary[TEL_SERVER] = false; + if(data == 1) telClient.echo[TEL_SERVER] = false; + if(data == 3) telClient.supressGA[TEL_SERVER] = false; + break; + case 253: /* Do */ + if(data == 0) { + telClient.binary[TEL_CLIENT] = true; + response[0]=0xff; + response[1]=251; + response[2]=0; /* Will do binary transfer */ + if(clientsocket) clientsocket->SendArray(response, 3); + } + if(data == 1) { + telClient.echo[TEL_CLIENT] = false; + response[0]=0xff; + response[1]=252; + response[2]=1; /* Won't echo (too lazy) */ + if(clientsocket) clientsocket->SendArray(response, 3); + } + if(data == 3) { + telClient.supressGA[TEL_CLIENT] = true; + response[0]=0xff; + response[1]=251; + response[2]=3; /* Will Suppress GA */ + if(clientsocket) clientsocket->SendArray(response, 3); + } + break; + case 254: /* Don't */ + if(data == 0) { + telClient.binary[TEL_CLIENT] = false; + response[0]=0xff; + response[1]=252; + response[2]=0; /* Won't do binary transfer */ + if(clientsocket) clientsocket->SendArray(response, 3); + } + if(data == 1) { + telClient.echo[TEL_CLIENT] = false; + response[0]=0xff; + response[1]=252; + response[2]=1; /* Won't echo (fine by me) */ + if(clientsocket) clientsocket->SendArray(response, 3); + } + if(data == 3) { + telClient.supressGA[TEL_CLIENT] = true; + response[0]=0xff; + response[1]=251; + response[2]=3; /* Will Suppress GA (too lazy) */ + if(clientsocket) clientsocket->SendArray(response, 3); + } + break; + default: + LOG_MSG("MODEM: Telnet client sent IAC %d", telClient.command); + break; + } + telClient.inIAC = false; + telClient.recCommand = false; + return -1; //continue; + } else { + if(data==249) { + /* Go Ahead received */ + telClient.inIAC = false; + return -1; //continue; + } + telClient.command = data; + telClient.recCommand = true; + + if((telClient.binary[TEL_SERVER]) && (data == 0xff)) { + /* Binary data with value of 255 */ + telClient.inIAC = false; + telClient.recCommand = false; + return 0xff; + } + } + } else { + if(data == 0xff) { + telClient.inIAC = true; + return -1; + } + return data; + } + return -1; // ??? +} + + +/*****************************************************************************/ +/* setBreak(val) switches break on or off **/ +/*****************************************************************************/ + +void CNullModem::setBreak (bool value) { + CNullModem::setRTSDTR(getRTS(), getDTR()); +} + +/*****************************************************************************/ +/* updateModemControlLines(mcr) sets DTR and RTS. **/ +/*****************************************************************************/ +void CNullModem::setRTSDTR(bool xrts, bool xdtr) { + if(!transparent) { + Bit8u control[2]; + control[0]=0xff; + control[1]=0x0; + if(xrts) control[1]|=1; + if(xdtr) control[1]|=2; + if(LCR&LCR_BREAK_MASK) control[1]|=4; + if(clientsocket) clientsocket->SendArray(control, 2); + } +} +void CNullModem::setRTS(bool val) { + setRTSDTR(val, getDTR()); +} +void CNullModem::setDTR(bool val) { + setRTSDTR(getRTS(), val); +} +#endif diff --git a/src/hardware/serialport/nullmodem.h b/src/hardware/serialport/nullmodem.h new file mode 100644 index 00000000..226e1182 --- /dev/null +++ b/src/hardware/serialport/nullmodem.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2002-2007 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* $Id: nullmodem.h,v 1.1 2007-01-13 08:35:49 qbix79 Exp $ */ + +// include guard +#ifndef DOSBOX_NULLMODEM_WIN32_H +#define DOSBOX_NULLMODEM_WIN32_H + +#include "dosbox.h" + +#if C_MODEM + +#include "misc_util.h" +#include "serialport.h" + +#define SERIAL_SERVER_POLLING_EVENT SERIAL_BASE_EVENT_COUNT+1 +#define SERIAL_TX_REDUCTION SERIAL_BASE_EVENT_COUNT+2 +#define SERIAL_NULLMODEM_DTR_EVENT SERIAL_BASE_EVENT_COUNT+3 +#define SERIAL_NULLMODEM_EVENT_COUNT SERIAL_BASE_EVENT_COUNT+ 3 + +class CNullModem : public CSerial { +public: + TCPServerSocket* serversocket; + TCPClientSocket* clientsocket; + + CNullModem(Bitu id, CommandLine* cmd); + ~CNullModem(); + bool receiveblock; // It's not a block of data it rather blocks + Bit16u serverport; // we are a server if this is nonzero + Bit16u clientport; + + Bit8u hostnamebuffer[128]; // the name passed to us by the user + + void updatePortConfig(Bit16u divider, Bit8u lcr); + void updateMSR(); + void transmitByte(Bit8u val, bool first); + void setBreak(bool value); + + void setRTSDTR(bool rts, bool dtr); + void setRTS(bool val); + void setDTR(bool val); + void handleUpperEvent(Bit16u type); + + void ClientConnect(); + void Disconnect(); + Bits readChar(); + void WriteChar(Bit8u data); + + bool tx_block; // true while the SERIAL_TX_REDUCTION event + // is pending + + Bitu rx_retry; // counter of retries + + Bitu rx_retry_max; // how many POLL_EVENTS to wait before causing + // a overrun error. + + Bitu tx_gather; // how long to gather tx data before + // sending all of them [milliseconds] + + + bool dtrrespect; // dtr behavior - only send data to the serial + // port when DTR is on + + bool transparent; // if true, don't send 0xff 0xXX to toggle + // DSR/CTS. + + bool telnet; // Do Telnet parsing. + + // Telnet's brain +#define TEL_CLIENT 0 +#define TEL_SERVER 1 + + Bits TelnetEmulation(Bit8u data); + + // Telnet's memory + struct { + bool binary[2]; + bool echo[2]; + bool supressGA[2]; + bool timingMark[2]; + + bool inIAC; + bool recCommand; + Bit8u command; + } telClient; +}; + +#endif // C_MODEM +#endif // include guard diff --git a/src/hardware/serialport/serialdummy.cpp b/src/hardware/serialport/serialdummy.cpp index 64065974..df8c6d88 100644 --- a/src/hardware/serialport/serialdummy.cpp +++ b/src/hardware/serialport/serialdummy.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: serialdummy.cpp,v 1.3 2007-01-08 19:45:41 qbix79 Exp $ */ +/* $Id: serialdummy.cpp,v 1.4 2007-01-13 08:35:49 qbix79 Exp $ */ #include "dosbox.h" @@ -24,46 +24,54 @@ #include "serialdummy.h" #include "serialport.h" - -CSerialDummy::CSerialDummy( - IO_ReadHandler* rh, - IO_WriteHandler* wh, - TIMER_TickHandler th, - Bit16u baseAddr, - Bit8u initIrq, - Bit32u initBps, - Bit8u bytesize, - const char* parity, - Bit8u stopbits - ) : CSerial( - rh, wh, th, - baseAddr,initIrq,initBps,bytesize,parity,stopbits) - { - CSerial::Init_Registers(initBps,bytesize,parity,stopbits); - } - -CSerialDummy::~CSerialDummy() { +CSerialDummy::CSerialDummy(Bitu id, CommandLine* cmd):CSerial(id, cmd) { + CSerial::Init_Registers(); + setRI(false); + setDSR(false); + setCD(false); + setCTS(false); + InstallationSuccessful=true; } -void CSerialDummy::RXBufferEmpty() { -// no external buffer, not used here +CSerialDummy::~CSerialDummy() { + // clear events + removeEvent(SERIAL_TX_EVENT); +} + +void CSerialDummy::handleUpperEvent(Bit16u type) { + if(type==SERIAL_TX_EVENT) { + //LOG_MSG("SERIAL_TX_EVENT"); +#ifdef CHECKIT_TESTPLUG + receiveByte(loopbackdata); +#endif + ByteTransmitted(); // tx timeout + } + else if(type==SERIAL_THR_EVENT){ + //LOG_MSG("SERIAL_THR_EVENT"); + ByteTransmitting(); + setEvent(SERIAL_TX_EVENT,bytetime); + } + } /*****************************************************************************/ /* updatePortConfig is called when emulated app changes the serial port **/ /* parameters baudrate, stopbits, number of databits, parity. **/ /*****************************************************************************/ -void CSerialDummy::updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr) { +void CSerialDummy::updatePortConfig(Bit16u divider, Bit8u lcr) { //LOG_MSG("Serial port at 0x%x: Port params changed: %d Baud", base,dcb.BaudRate); } void CSerialDummy::updateMSR() { - changeMSR(0); } +void CSerialDummy::transmitByte(Bit8u val, bool first) { -void CSerialDummy::transmitByte(Bit8u val) { - ByteTransmitted(); - //LOG_MSG("UART 0x%x: TX 0x%x", base,val); + if(first) setEvent(SERIAL_THR_EVENT, bytetime/10); + else setEvent(SERIAL_TX_EVENT, bytetime); + +#ifdef CHECKIT_TESTPLUG + loopbackdata=val; +#endif } /*****************************************************************************/ @@ -75,11 +83,21 @@ void CSerialDummy::setBreak(bool value) { } /*****************************************************************************/ -/* updateModemControlLines(mcr) sets DTR and RTS. **/ +/* setRTSDTR sets the modem control lines **/ /*****************************************************************************/ -void CSerialDummy::updateModemControlLines(/*Bit8u mcr*/) { +void CSerialDummy::setRTSDTR(bool rts, bool dtr) { + setRTS(rts); + setDTR(dtr); +} +void CSerialDummy::setRTS(bool val) { +#ifdef CHECKIT_TESTPLUG + setCTS(val); +#endif +} +void CSerialDummy::setDTR(bool val) { +#ifdef CHECKIT_TESTPLUG + setDSR(val); + setRI(val); + setCD(val); +#endif } - -void CSerialDummy::Timer2(void) { -} - diff --git a/src/hardware/serialport/serialdummy.h b/src/hardware/serialport/serialdummy.h index e339d1b9..95b9d412 100644 --- a/src/hardware/serialport/serialdummy.h +++ b/src/hardware/serialport/serialdummy.h @@ -16,39 +16,34 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: serialdummy.h,v 1.3 2007-01-08 19:45:41 qbix79 Exp $ */ +/* $Id: serialdummy.h,v 1.4 2007-01-13 08:35:49 qbix79 Exp $ */ #ifndef INCLUDEGUARD_SERIALDUMMY_H #define INCLUDEGUARD_SERIALDUMMY_H #include "serialport.h" +//#define CHECKIT_TESTPLUG + class CSerialDummy : public CSerial { public: - - CSerialDummy( - IO_ReadHandler* rh, - IO_WriteHandler* wh, - TIMER_TickHandler th, - Bit16u baseAddr, - Bit8u initIrq, - Bit32u initBps, - Bit8u bytesize, - const char* parity, - Bit8u stopbits - ); - - + CSerialDummy(Bitu id, CommandLine* cmd); ~CSerialDummy(); - bool CanRecv(void); - bool CanSend(void); - void RXBufferEmpty(); - void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr); + + void setRTSDTR(bool rts, bool dtr); + void setRTS(bool val); + void setDTR(bool val); + + void updatePortConfig(Bit16u, Bit8u lcr); void updateMSR(); - void transmitByte(Bit8u val); + void transmitByte(Bit8u val, bool first); void setBreak(bool value); - void updateModemControlLines(/*Bit8u mcr*/); - void Timer2(void); + void handleUpperEvent(Bit16u type); + +#ifdef CHECKIT_TESTPLUG + Bit8u loopbackdata; +#endif + }; #endif // INCLUDEGUARD diff --git a/src/hardware/serialport/serialport.cpp b/src/hardware/serialport/serialport.cpp index 87bb7f3d..1f5b27df 100644 --- a/src/hardware/serialport/serialport.cpp +++ b/src/hardware/serialport/serialport.cpp @@ -16,270 +16,312 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: serialport.cpp,v 1.5 2007-01-08 19:45:41 qbix79 Exp $ */ - +/* $Id: serialport.cpp,v 1.6 2007-01-13 08:35:49 qbix79 Exp $ */ #include #include #include "dosbox.h" -#include "support.h" #include "inout.h" #include "pic.h" #include "setup.h" -#include "timer.h" +#include "bios.h" // SetComPorts(..) +#include "callback.h" // CALLBACK_Idle #include "serialport.h" #include "directserial_win32.h" +#include "directserial_posix.h" #include "directserial_os2.h" #include "serialdummy.h" #include "softmodem.h" +#include "nullmodem.h" + +#include "cpu.h" + + +bool device_COM::Read(Bit8u * data,Bit16u * size) { + // DTR + RTS on + sclass->Write_MCR(0x03); + for (Bit16u i=0; i<*size; i++) + { + if(!(sclass->Getchar(&data[i],true,1000))) { + *size=i; + return true; + } + } + return true; +} + + +bool device_COM::Write(Bit8u * data,Bit16u * size) { + // DTR + RTS on + sclass->Write_MCR(0x03); + for (Bit16u i=0; i<*size; i++) + { + if(!(sclass->Putchar(data[i],true,true,1000))) { + *size=i; + sclass->Write_MCR(0x01); + return false; + } + } + // RTS off + sclass->Write_MCR(0x01); + return true; +} + +bool device_COM::Seek(Bit32u * pos,Bit32u type) { + *pos = 0; + return true; +} + +bool device_COM::Close() { + return false; +} + +Bit16u device_COM::GetInformation(void) { + return 0x80A0; +}; + +device_COM::device_COM(class CSerial* sc) { + sclass = sc; + SetName(serial_comname[sclass->idnumber]); +} + +device_COM::~device_COM() { +} -#define LOG_UART LOG_MSG // COM1 - COM4 objects -static CSerial *serial1 = 0; -static CSerial *serial2 = 0; -static CSerial *serial3 = 0; -static CSerial *serial4 = 0; -//static CSerial** serialPortObjects[] = {NULL, &serial1,&serial2,&serial3,&serial4}; +CSerial* serialports[4] ={0,0,0,0}; -Bit8u serialGetComnumberByBaseAddress (Bit16u base) { - if (base == COM1_BASE) - return 1; - else if (base == COM2_BASE) - return 2; - else if (base == COM3_BASE) - return 3; - else if (base == COM4_BASE) - return 4; - else - return 255; // send thispointer to nirwana ;) -} +static Bitu SERIAL_Read (Bitu port, Bitu iolen) { + for(Bitu i = 0; i < 4; i++) { + if(serial_baseaddr[i]==(port&0xfff8) && (serialports[i]!=0)) { + Bitu retval=0xff; + switch (port & 0x7) { + case RHR_OFFSET: + retval = serialports[i]->Read_RHR(); + break; + case IER_OFFSET: + retval = serialports[i]->Read_IER(); + break; + case ISR_OFFSET: + retval = serialports[i]->Read_ISR(); + break; + case LCR_OFFSET: + retval = serialports[i]->Read_LCR(); + break; + case MCR_OFFSET: + retval = serialports[i]->Read_MCR(); + break; + case LSR_OFFSET: + retval = serialports[i]->Read_LSR(); + break; + case MSR_OFFSET: + retval = serialports[i]->Read_MSR(); + break; + case SPR_OFFSET: + retval = serialports[i]->Read_SPR(); + break; + } -// Usage of FastDelegates (http://www.codeproject.com/cpp/FastDelegate.asp) -// as I/O-Array would make many of these unneccessary. (member function pointer) - -//Some defines for repeated functions - -#define SERIAL_UPDATE(number) \ -void SERIAL##number##_Update(void) { \ - serial##number->Timer (); \ -} - -#define SERIAL_WRITE_TREE(number) \ -static void SERIAL##number##_Write (Bitu port, Bitu val, Bitu iolen) { \ - switch (port & 0x7) { \ - case THR_OFFSET: \ - serial##number ->Write_THR (val); \ - return; \ - case IER_OFFSET: \ - serial##number ->Write_IER (val); \ - return; \ - case LCR_OFFSET: \ - serial##number ->Write_LCR (val); \ - return; \ - case MCR_OFFSET: \ - serial##number ->Write_MCR (val); \ - return; \ - case MSR_OFFSET: \ - serial##number ->Write_MSR (val); \ - return; \ - case SPR_OFFSET: \ - serial##number ->Write_SPR (val); \ - return; \ - default: \ - serial##number ->Write_reserved (val, port & 0x7); \ - } \ -} - -#define SERIAL_READ_TREE(number) \ -static Bitu SERIAL##number##_Read (Bitu port, Bitu iolen) { \ - switch (port & 0x7) { \ - case RHR_OFFSET: \ - return serial##number ->Read_RHR (); \ - case IER_OFFSET: \ - return serial##number ->Read_IER (); \ - case ISR_OFFSET: \ - return serial##number ->Read_ISR (); \ - case LCR_OFFSET: \ - return serial##number ->Read_LCR (); \ - case MCR_OFFSET: \ - return serial##number ->Read_MCR (); \ - case LSR_OFFSET: \ - return serial##number ->Read_LSR (); \ - case MSR_OFFSET: \ - return serial##number ->Read_MSR (); \ - case SPR_OFFSET: \ - return serial##number ->Read_SPR (); \ - } \ - return 0; \ -} - -//The Functions - -SERIAL_UPDATE(1); -SERIAL_UPDATE(2); -SERIAL_UPDATE(3); -SERIAL_UPDATE(4); - -SERIAL_WRITE_TREE(1); -SERIAL_WRITE_TREE(2); -SERIAL_WRITE_TREE(3); -SERIAL_WRITE_TREE(4); - -SERIAL_READ_TREE(1); -SERIAL_READ_TREE(2); -SERIAL_READ_TREE(3); -SERIAL_READ_TREE(4); - -#undef SERIAL_UPDATE -#undef SERIAL_WRITE_TREE -#undef SERIAL_READ_TREE - -void CSerial::Timer (void) { - //LOG_UART("Serial port at %x: Timer", base); - if (loopback_pending) { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Loopback sent back", base); +#if SERIAL_DEBUG + const char* const dbgtext[]= + {"RHR","IER","ISR","LCR","MCR","LSR","MSR","SPR"}; + if(serialports[i]->dbg_register) + fprintf(serialports[i]->debugfp,"%12.3f read 0x%x from %s.\r\n", + PIC_FullIndex(),retval,dbgtext[port&0x7]); #endif - loopback_pending = false; - receiveByte (loopback_data); - ByteTransmitted (); + + return retval; + } + } + return 0xff; +} +static void SERIAL_Write (Bitu port, Bitu val, Bitu) { + + for(Bitu i = 0; i < 4; i++) { + if(serial_baseaddr[i]==(port&0xfff8) && serialports[i]) { + +#if SERIAL_DEBUG + const char* const dbgtext[]={"THR","IER","FCR","LCR","MCR","!LSR","MSR","SPR"}; + if(serialports[i]->dbg_register) + fprintf(serialports[i]->debugfp,"%12.3f write 0x%x to %s.\r\n", + PIC_FullIndex(),val,dbgtext[port&0x7]); +#endif + + switch (port & 0x7) { + case THR_OFFSET: + serialports[i]->Write_THR (val); + return; + case IER_OFFSET: + serialports[i]->Write_IER (val); + return; + case FCR_OFFSET: + serialports[i]->Write_FCR (val); + return; + case LCR_OFFSET: + serialports[i]->Write_LCR (val); + return; + case MCR_OFFSET: + serialports[i]->Write_MCR (val); + return; + case MSR_OFFSET: + serialports[i]->Write_MSR (val); + return; + case SPR_OFFSET: + serialports[i]->Write_SPR (val); + return; + default: + serialports[i]->Write_reserved (val, port & 0x7); + } + } + } +} + +void CSerial::changeLineProperties() { + // update the event wait time + + float bitlen = (1000.0f/115200.0f)*(float)baud_divider; + bytetime=bitlen*(float)(1+5+1); // startbit + minimum length + stopbit + bytetime+= bitlen*(float)(LCR&0x3); // databits + if(LCR&0x4) bytetime+=bitlen; // stopbit + updatePortConfig (baud_divider, LCR); +} + +static void Serial_EventHandler(Bitu val) { + Bitu serclassid=val&0x3; + if(serialports[serclassid]!=0) + serialports[serclassid]->handleEvent(val>>2); +} + +void CSerial::setEvent(Bit16u type, float duration) { + PIC_AddEvent(Serial_EventHandler,duration,(type<<2)|idnumber); +} + +void CSerial::removeEvent(Bit16u type) { + // TODO + PIC_RemoveSpecificEvents(Serial_EventHandler,(type<<2)|idnumber); +} + +void CSerial::handleEvent(Bit16u type) { + switch(type) { + case SERIAL_TX_LOOPBACK_EVENT: { + +#if SERIAL_DEBUG + if(dbg_serialtraffic) + fprintf(debugfp,loopback_data<0x10? "%12.3f tx 0x%02x (%u) (loopback)\r\n": + "%12.3f tx 0x%02x (%c) (loopback)\r\n", + PIC_FullIndex(),loopback_data, + loopback_data); +#endif + + receiveByte (loopback_data); + ByteTransmitted (); + break; + } + case SERIAL_THR_LOOPBACK_EVENT: { + ByteTransmitting(); + loopback_data=THR; + setEvent(SERIAL_TX_LOOPBACK_EVENT,bytetime); + break; + } + case SERIAL_ERRMSG_EVENT: { + LOG_MSG("Serial%d: Errors occured: "\ + "Framing %d, Parity %d, Overrun %d (IF0:%d), Break %d", COMNUMBER, + framingErrors, parityErrors, overrunErrors, overrunIF0, breakErrors); + errormsg_pending=false; + framingErrors=0; + parityErrors=0; + overrunErrors=0; + overrunIF0=0; + breakErrors=0; + break; + } + default: handleUpperEvent(type); } - Timer2 (); } /*****************************************************************************/ /* Interrupt control routines **/ /*****************************************************************************/ void CSerial::rise (Bit8u priority) { - //LOG_UART("Serial port at %x: Rise priority 0x%x", base, priority); +#if SERIAL_DEBUG + if(dbg_interrupt) + { + if(priority&TX_PRIORITY && !(waiting_interrupts&TX_PRIORITY)) + fprintf(debugfp,"%12.3f tx interrupt on.\r\n",PIC_FullIndex()); + + if(priority&RX_PRIORITY && !(waiting_interrupts&RX_PRIORITY)) + fprintf(debugfp,"%12.3f rx interrupt on.\r\n",PIC_FullIndex()); + + if(priority&MSR_PRIORITY && !(waiting_interrupts&MSR_PRIORITY)) + fprintf(debugfp,"%12.3f msr interrupt on.\r\n",PIC_FullIndex()); + + if(priority&ERROR_PRIORITY && !(waiting_interrupts&ERROR_PRIORITY)) + fprintf(debugfp,"%12.3f error interrupt on.\r\n",PIC_FullIndex()); + } +#endif + waiting_interrupts |= priority; - WriteRealIER (IER); + ComputeInterrupts(); } // clears the pending interrupt, triggers other waiting interrupt void CSerial::clear (Bit8u priority) { - //LOG_UART("Serial port at %x: cleared priority 0x%x", base, priority); + +#if SERIAL_DEBUG + if(dbg_interrupt) + { + if(priority&TX_PRIORITY && (waiting_interrupts&TX_PRIORITY)) + fprintf(debugfp,"%12.3f tx interrupt off.\r\n",PIC_FullIndex()); + + if(priority&RX_PRIORITY && (waiting_interrupts&RX_PRIORITY)) + fprintf(debugfp,"%12.3f rx interrupt off.\r\n",PIC_FullIndex()); + + if(priority&MSR_PRIORITY && (waiting_interrupts&MSR_PRIORITY)) + fprintf(debugfp,"%12.3f msr interrupt off.\r\n",PIC_FullIndex()); + + if(priority&ERROR_PRIORITY && (waiting_interrupts&ERROR_PRIORITY)) + fprintf(debugfp,"%12.3f error interrupt off.\r\n",PIC_FullIndex()); + } +#endif + + waiting_interrupts &= (~priority); - WriteRealIER (IER); + ComputeInterrupts(); } -void CSerial::WriteRealIER (Bit8u data) { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: write IER, value 0x%x", base, data); +void CSerial::ComputeInterrupts () { + + Bitu val = IER & waiting_interrupts; + + if (val & ERROR_PRIORITY) ISR = ISR_ERROR_VAL; + else if (val & RX_PRIORITY) ISR = ISR_RX_VAL; + else if (val & TX_PRIORITY) ISR = ISR_TX_VAL; + else if (val & MSR_PRIORITY) ISR = ISR_MSR_VAL; + else ISR = ISR_CLEAR_VAL; + + if(val && !irq_active) { + irq_active=true; + PIC_ActivateIRQ(irq); + +#if SERIAL_DEBUG + if(dbg_interrupt) + fprintf(debugfp,"%12.3f IRQ%d on.\r\n",PIC_FullIndex(),irq); #endif - data = data & 0xF; // THE UPPER ONES ALWAYS READ 0! NOTHIN' ELSE! - Bit8u old_pending_interrupts = pending_interrupts; - Bit8u difference_pending_interrupts; + } - // rise TX AGAIN when present and is being enabled - if ((data & TX_PRIORITY) && (!(IER & TX_PRIORITY))) - if (LSR & LSR_TX_HOLDING_EMPTY_MASK) - waiting_interrupts |= TX_PRIORITY; + if(!val && irq_active) { + irq_active=false; + PIC_DeActivateIRQ(irq); - pending_interrupts = waiting_interrupts & data; - if ((difference_pending_interrupts = (pending_interrupts ^ old_pending_interrupts))) { // something in pending interrupts has changed - if (difference_pending_interrupts & pending_interrupts) { // some new bits were set - if (!current_priority) { // activate interrupt.. - if (pending_interrupts & ERROR_PRIORITY) { - current_priority = ERROR_PRIORITY; - ISR = ISR_ERROR_VAL; - } else if (pending_interrupts & RX_PRIORITY) { - current_priority = RX_PRIORITY; - ISR = ISR_RX_VAL; - } else if (pending_interrupts & TX_PRIORITY) { - current_priority = TX_PRIORITY; - ISR = ISR_TX_VAL; - } else if (pending_interrupts & MSR_PRIORITY) { - current_priority = MSR_PRIORITY; - ISR = ISR_MSR_VAL; - } -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Interrupt activated: priority %d", base, current_priority); +#if SERIAL_DEBUG + if(dbg_interrupt) + fprintf(debugfp,"%12.3f IRQ%d off.\r\n",PIC_FullIndex(),irq); #endif - PIC_ActivateIRQ (irq); - }// else the new interrupts were already - // written into pending_interrupts - }// else no new bits were set - - if (difference_pending_interrupts & (~pending_interrupts)) { // some bits were reset - if (pending_interrupts) { // some more are waiting - if (!(current_priority & pending_interrupts)) { // the current interrupt has been cleared - // choose the next one - if (pending_interrupts & ERROR_PRIORITY) { - current_priority = ERROR_PRIORITY; - ISR = ISR_ERROR_VAL; - } else if (pending_interrupts & RX_PRIORITY) { - current_priority = RX_PRIORITY; - ISR = ISR_RX_VAL; - } else if (pending_interrupts & TX_PRIORITY) { - current_priority = TX_PRIORITY; - ISR = ISR_TX_VAL; - } else if (pending_interrupts & MSR_PRIORITY) { - current_priority = MSR_PRIORITY; - ISR = ISR_MSR_VAL; - } - } - } else { - current_priority = NONE_PRIORITY; - ISR = ISR_CLEAR_VAL; - PIC_DeActivateIRQ (irq); -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Interrupt deactivated.", base); -#endif - } - } } - IER = data; -} - - - -/*****************************************************************************/ -/* Internal register modification **/ -/*****************************************************************************/ -void CSerial::changeMSR (Bit8u data) { - if (!(MCR & MCR_LOOPBACK_Enable_MASK)) { - // see if something changed - if ((MSR & MSR_LINE_MASK) != data) { - Bit8u change = (MSR & MSR_LINE_MASK) ^ data; - // set new deltas - MSR |= (change >> 4); - // set new line states - MSR &= MSR_delta_MASK; - MSR |= data; - rise (MSR_PRIORITY); - } - } -} - -void CSerial::changeMSR_Loopback (Bit8u data) { - // see if something changed - if ((MSR & MSR_LINE_MASK) != data) { - Bit8u change = (MSR & MSR_LINE_MASK) ^ data; - // set new deltas - MSR |= (change >> 4); - // set new line states - MSR &= MSR_delta_MASK; - MSR |= data; - rise (MSR_PRIORITY); - } - -/* - Bit8u temp=MSR; - // look for signal changes - if(temp|=((data&MSR_LINE_MASK)^(MSR&MSR_LINE_MASK))>>4) - { // some signals changed - temp&=MSR_delta_MASK;// clear line states - temp|=(data&MSR_LINE_MASK); // set new line states - - MSR=temp; - rise(MSR_PRIORITY); - } - // else nothing happened*/ } /*****************************************************************************/ @@ -293,12 +335,29 @@ bool CSerial::CanReceiveByte() { /* A byte was received **/ /*****************************************************************************/ void CSerial::receiveByte (Bit8u data) { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: byte received: %d", base, data); + +#if SERIAL_DEBUG + if(dbg_serialtraffic) + fprintf(debugfp,loopback_data<0x10? "%12.3f rx 0x%02x (%u)\r\n": + "%12.3f rx 0x%02x (%c)\r\n", + PIC_FullIndex(), data, data); #endif if (LSR & LSR_RX_DATA_READY_MASK) { // Overrun error ;o - LOG_UART ("Serial port at %x: RX Overrun!", base, data); + if(!errormsg_pending) { + errormsg_pending=true; + setEvent(SERIAL_ERRMSG_EVENT,1000); + } + overrunErrors++; + Bitu iflag= GETFLAG(IF); + if(!iflag)overrunIF0++; + +#if SERIAL_DEBUG + if(dbg_serialtraffic) + fprintf(debugfp, "%12.3f rx overrun (IF=%d)\r\n", + PIC_FullIndex(), iflag); +#endif + LSR |= LSR_OVERRUN_ERROR_MASK; rise (ERROR_PRIORITY); } else { @@ -312,67 +371,166 @@ void CSerial::receiveByte (Bit8u data) { /* A line error was received **/ /*****************************************************************************/ void CSerial::receiveError (Bit8u errorword) { + + if(!errormsg_pending) { + errormsg_pending=true; + setEvent(SERIAL_ERRMSG_EVENT,1000); + } + if(errorword&LSR_PARITY_ERROR_MASK) { + parityErrors++; + +#if SERIAL_DEBUG + if(dbg_serialtraffic) + fprintf(debugfp, "%12.3f parity error\r\n", + PIC_FullIndex()); +#endif + + } + if(errorword&LSR_FRAMING_ERROR_MASK) { + framingErrors++; + +#if SERIAL_DEBUG + if(dbg_serialtraffic) + fprintf(debugfp, "%12.3f framing error\r\n", + PIC_FullIndex()); +#endif + + } + if(errorword&LSR_RX_BREAK_MASK) { + breakErrors++; + +#if SERIAL_DEBUG + if(dbg_serialtraffic) + fprintf(debugfp, "%12.3f break received\r\n", + PIC_FullIndex()); +#endif + + } LSR |= errorword; rise (ERROR_PRIORITY); } +/*****************************************************************************/ +/* ByteTransmitting: Byte has made it from THR to TX. **/ +/*****************************************************************************/ +void CSerial::ByteTransmitting() { + switch(LSR&(LSR_TX_HOLDING_EMPTY_MASK|LSR_TX_EMPTY_MASK)) + { + case LSR_TX_HOLDING_EMPTY_MASK|LSR_TX_EMPTY_MASK: + // bad case there must have been one + case LSR_TX_HOLDING_EMPTY_MASK: + case LSR_TX_EMPTY_MASK: // holding full but workreg empty impossible + LOG_MSG("Internal error in serial port(1)(0x%x).",LSR); + break; + case 0: // THR is empty now. + LSR |= LSR_TX_HOLDING_EMPTY_MASK; + + // trigger interrupt + rise (TX_PRIORITY); + break; + } +} + + /*****************************************************************************/ /* ByteTransmitted: When a byte was sent, notify here. **/ /*****************************************************************************/ void CSerial::ByteTransmitted () { - if (LSR & LSR_TX_HOLDING_EMPTY_MASK) { // one space was empty - // now both are - LSR |= LSR_TX_EMPTY_MASK; - } else { // both were full, now 1 is empty - if (MCR & MCR_LOOPBACK_Enable_MASK) { // loopback mode - transmitLoopbackByte (THR); - } else { // direct "real" mode - transmitByte (THR); - } - LSR |= LSR_TX_HOLDING_EMPTY_MASK; + switch(LSR&(LSR_TX_HOLDING_EMPTY_MASK|LSR_TX_EMPTY_MASK)) + { + case LSR_TX_HOLDING_EMPTY_MASK|LSR_TX_EMPTY_MASK: + // bad case there must have been one + case LSR_TX_EMPTY_MASK: // holding full but workreg empty impossible + LOG_MSG("Internal error in serial port(2)."); + break; + + case LSR_TX_HOLDING_EMPTY_MASK: // now both are empty + LSR |= LSR_TX_EMPTY_MASK; + break; + + case 0: // now one is empty, send the other one + LSR |= LSR_TX_HOLDING_EMPTY_MASK; + if (loopback) { + loopback_data=THR; + setEvent(SERIAL_TX_LOOPBACK_EVENT, bytetime); + } + else { + + #if SERIAL_DEBUG + if(dbg_serialtraffic) + fprintf(debugfp,THR<0x10? "%12.3f tx 0x%02x (%u) (from THR)\r\n": + "%12.3f tx 0x%02x (%c) (from THR)\r\n", + PIC_FullIndex(),THR, + THR); + #endif + + transmitByte(THR,false); + } + // It's ok here. + rise (TX_PRIORITY); + break; } - rise (TX_PRIORITY); } /*****************************************************************************/ /* Transmit Holding Register, also LSB of Divisor Latch (r/w) **/ /*****************************************************************************/ void CSerial::Write_THR (Bit8u data) { + // 0-7 transmit data + if ((LCR & LCR_DIVISOR_Enable_MASK)) { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Write to DLL value 0x%x", base, data); -#endif // write to DLL - DLL = data; - updatePortConfig (DLL, DLM, LCR); + baud_divider&=0xFF00; + baud_divider |= data; + changeLineProperties(); } else { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Write to THR value 0x%x", base, data); -#endif - // write to THR clear (TX_PRIORITY); - if (LSR & LSR_TX_HOLDING_EMPTY_MASK) { // one is empty - if (LSR & LSR_TX_EMPTY_MASK) { // both are empty - LSR &= (~LSR_TX_EMPTY_MASK); - - if (MCR & MCR_LOOPBACK_Enable_MASK) { // loopback mode - transmitLoopbackByte (data); - } else { // direct "real" mode - transmitByte (data); - } - rise (TX_PRIORITY); - } else { // only THR is empty; add byte and clear holding bit + switch(LSR&(LSR_TX_HOLDING_EMPTY_MASK|LSR_TX_EMPTY_MASK)) + { + case 0: // both full - overflow +#if SERIAL_DEBUG + if(dbg_serialtraffic) fprintf(debugfp, "%12.3f tx overflow\r\n", + PIC_FullIndex()); +#endif + // overwrite THR + THR = data; + break; + + case LSR_TX_EMPTY_MASK: // holding full but workreg empty impossible + LOG_MSG("Internal error in serial port(3)."); + break; + + case LSR_TX_HOLDING_EMPTY_MASK: // now both are full LSR &= (~LSR_TX_HOLDING_EMPTY_MASK); THR = data; - } - } else { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: TX Overflow!", base); -#endif - // TX overflow; how does real hardware respond to that... I do nothing + break; + + case LSR_TX_HOLDING_EMPTY_MASK|LSR_TX_EMPTY_MASK: + // both are full until the first delay has passed + THR=data; + LSR &= (~LSR_TX_EMPTY_MASK); + LSR &= (~LSR_TX_HOLDING_EMPTY_MASK); + if(loopback) setEvent(SERIAL_THR_LOOPBACK_EVENT, bytetime/10); + else { + +#if SERIAL_DEBUG + if(dbg_serialtraffic) + fprintf(debugfp,data<0x10? "%12.3f tx 0x%02x (%u)\r\n": + "%12.3f tx 0x%02x (%c)\r\n", + PIC_FullIndex(),data, + data); +#endif + + transmitByte (data,true); + } + + // triggered + // when holding gets empty + // rise (TX_PRIORITY); + break; } } } @@ -382,23 +540,12 @@ void CSerial::Write_THR (Bit8u data) { /* Receive Holding Register, also LSB of Divisor Latch (r/w) **/ /*****************************************************************************/ Bitu CSerial::Read_RHR () { - Bit8u retval; - if ((LCR & LCR_DIVISOR_Enable_MASK)) { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from DLL value 0x%x", base, DLL); -#endif - return DLL; - } else { + // 0-7 received data + if ((LCR & LCR_DIVISOR_Enable_MASK)) return baud_divider&0xff; + else { clear (RX_PRIORITY); LSR &= (~LSR_RX_DATA_READY_MASK); - retval = RHR; - RXBufferEmpty (); // <--- this one changes RHR - -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from RHR value 0x%x", base, retval); -#endif - - return retval; + return RHR; } } @@ -408,32 +555,28 @@ Bitu CSerial::Read_RHR () { // Modified by: // - writing to it. Bitu CSerial::Read_IER () { - if ((LCR & LCR_DIVISOR_Enable_MASK)) { // IER or MSB? -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from DLM value 0x%x", base, DLM); -#endif - return DLM; - } else { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from IER value 0x%x", base, IER); -#endif - return IER; - } + // 0 receive holding register (byte received) + // 1 transmit holding register (byte sent) + // 2 receive line status (overrun, parity error, frame error, break) + // 3 modem status + // 4-7 0 + + if (LCR & LCR_DIVISOR_Enable_MASK) return baud_divider>>8; + else return IER; } void CSerial::Write_IER (Bit8u data) { if ((LCR & LCR_DIVISOR_Enable_MASK)) { // write to DLM -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Write to DLM value 0x%x", base, data); -#endif - - DLM = data; - updatePortConfig (DLL, DLM, LCR); + baud_divider&=0xff; + baud_divider |= ((Bit16u)data)<<8; + changeLineProperties(); } else { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Write to IER value 0x%x", base, data); -#endif - WriteRealIER (data); + + IER = data&0xF; + if ((LSR & LSR_TX_HOLDING_EMPTY_MASK) && (IER&TX_PRIORITY)) + waiting_interrupts |= TX_PRIORITY; + + ComputeInterrupts(); } } @@ -444,16 +587,29 @@ void CSerial::Write_IER (Bit8u data) { // - incoming interrupts // - loopback mode Bitu CSerial::Read_ISR () { + // 0 0:interrupt pending 1: no interrupt + // 1-3 identification + // 011 LSR + // 010 RXRDY + // 001 TXRDY + // 000 MSR + // 4-7 0 + + if(IER&Modem_Status_INT_Enable_MASK) updateMSR(); Bit8u retval = ISR; // clear changes ISR!! mean.. - clear (TX_PRIORITY); -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from ISR value 0x%x", base, retval); -#endif + if(ISR==ISR_TX_VAL) clear (TX_PRIORITY); return retval; } +void CSerial::Write_FCR (Bit8u data) { + if((!fifo_warn) && (data&0x1)) { + fifo_warn=true; + LOG_MSG("Serial%d: Warning: Tried to activate FIFO.",COMNUMBER); + } +} + /*****************************************************************************/ /* Line Control Register (r/w) **/ /*****************************************************************************/ @@ -464,39 +620,31 @@ Bitu CSerial::Read_ISR () { // Modified by: // - writing to it. Bitu CSerial::Read_LCR () { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from LCR value 0x%x", base, LCR); -#endif + // 0-1 word length + // 2 stop bits + // 3 parity enable + // 4-5 parity type + // 6 set break + // 7 divisor latch enable return LCR; } void CSerial::Write_LCR (Bit8u data) { Bit8u lcr_old = LCR; LCR = data; -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Write to LCR value 0x%x", base, data); - - // for debug output - if (((data ^ lcr_old) & LCR_DIVISOR_Enable_MASK) != 0) { - if ((data & LCR_DIVISOR_Enable_MASK) != 0) - LOG_UART ("Serial port at %x: Divisor-mode entered", base); - - else - LOG_UART ("Serial port at %x: Divisor-mode exited", base); - } -#endif if (((data ^ lcr_old) & LCR_PORTCONFIG_MASK) != 0) { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: comm parameters changed", base); -#endif - updatePortConfig (DLL, DLM, LCR); + changeLineProperties(); } if (((data ^ lcr_old) & LCR_BREAK_MASK) != 0) { - setBreak ((LCR & LCR_BREAK_MASK) != 0); - -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: break toggled: %d", base, - (LCR & LCR_BREAK_MASK) != 0); + if(!loopback) setBreak ((LCR & LCR_BREAK_MASK)!=0); + else { + // TODO: set loopback break event to reveiveError after + } +#if SERIAL_DEBUG + if(dbg_serialtraffic) + fprintf(debugfp,((LCR & LCR_BREAK_MASK)!=0)? + "%12.3f break on.\r\n": + "%12.3f break off.\r\n", PIC_FullIndex()); #endif } } @@ -508,66 +656,99 @@ void CSerial::Write_LCR (Bit8u data) { // Modified by: // - writing to it. Bitu CSerial::Read_MCR () { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from MCR value 0x%x", base, MCR); -#endif - return MCR; + // 0 -DTR + // 1 -RTS + // 2 -OP1 + // 3 -OP2 + // 4 loopback enable + // 5-7 0 + Bit8u retval=0; + if(dtr) retval|=MCR_DTR_MASK; + if(rts) retval|=MCR_RTS_MASK; + if(op1) retval|=MCR_OP1_MASK; + if(op2) retval|=MCR_OP2_MASK; + if(loopback) retval|=MCR_LOOPBACK_Enable_MASK; + return retval; } void CSerial::Write_MCR (Bit8u data) { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Write to MCR value 0x%x", base, data); -#endif - data &= 0x1F; // UPPER BITS ALWAYS 0!!! + // WARNING: At the time setRTSDTR is called rts and dsr members are still wrong. - if (MCR & MCR_LOOPBACK_Enable_MASK) - if (data & MCR_LOOPBACK_Enable_MASK) { // was on, is now on - Bit8u param = 0; + bool temp_dtr = data & MCR_DTR_MASK? true:false; + bool temp_rts = data & MCR_RTS_MASK? true:false; + bool temp_op1 = data & MCR_OP1_MASK? true:false; + bool temp_op2 = data & MCR_OP2_MASK? true:false; + bool temp_loopback = data & MCR_LOOPBACK_Enable_MASK? true:false; + if(loopback!=temp_loopback) { + if(temp_loopback) setRTSDTR(false,false); + else setRTSDTR(temp_rts,temp_dtr); + } - // RTS->CD - // DTR->RI - // OP1->DSR - // OP2->CTS - - if (data & MCR_RTS_MASK) - param |= MSR_CD_MASK; - if (data & MCR_DTR_MASK) - param |= MSR_RI_MASK; - if (data & MCR_OP1_MASK) - param |= MSR_DSR_MASK; - if (data & MCR_OP2_MASK) - param |= MSR_CTS_MASK; - - changeMSR_Loopback (param); - - } else { - MCR = data; - updateModemControlLines (); - // is switched off now + if (temp_loopback) { // is on: + // DTR->DSR + // RTS->CTS + // OP1->RI + // OP2->CD + if(temp_dtr!=dtr && !d_dsr) { + d_dsr=true; + rise (MSR_PRIORITY); + } + if(temp_rts!=rts && !d_cts) { + d_cts=true; + rise (MSR_PRIORITY); + } + if(temp_op1!=op1 && !d_ri) { + // interrupt only at trailing edge + if(!temp_op1) { + d_ri=true; + rise (MSR_PRIORITY); + } + } + if(temp_op2!=op2 && !d_cd) { + d_cd=true; + rise (MSR_PRIORITY); + } } else { - if (data & MCR_LOOPBACK_Enable_MASK) { // is switched on: - Bit8u par = 0; - if (data & MCR_RTS_MASK) - par |= MSR_CD_MASK; - if (data & MCR_DTR_MASK) - par |= MSR_RI_MASK; - if (data & MCR_OP1_MASK) - par |= MSR_DSR_MASK; - if (data & MCR_OP2_MASK) - par |= MSR_CTS_MASK; + // loopback is off + if(temp_rts!=rts) { + // RTS difference + if(temp_dtr!=dtr) { + // both difference - changeMSR_Loopback (par); +#if SERIAL_DEBUG + if(dbg_modemcontrol) + { + fprintf(debugfp,temp_rts?"%12.3f RTS on.\r\n": + "%12.3f RTS off.\r\n", PIC_FullIndex()); + fprintf(debugfp,temp_dtr?"%12.3f DTR on.\r\n": + "%12.3f DTR off.\r\n", PIC_FullIndex()); + } +#endif + setRTSDTR(temp_rts, temp_dtr); + } else { + // only RTS - // go back to empty state - LSR &= (LSR_TX_EMPTY_MASK | LSR_TX_HOLDING_EMPTY_MASK); +#if SERIAL_DEBUG + if(dbg_modemcontrol) + fprintf(debugfp,temp_rts?"%12.3f RTS on.\r\n":"%12.3f RTS off.\r\n", PIC_FullIndex()); +#endif - } else { - MCR = data; - updateModemControlLines (); - // loopback is off + setRTS(temp_rts); + } + } else if(temp_dtr!=dtr) { + // only DTR +#if SERIAL_DEBUG + if(dbg_modemcontrol) + fprintf(debugfp,temp_dtr?"%12.3f DTR on.\r\n":"%12.3f DTR off.\r\n", PIC_FullIndex()); +#endif + setDTR(temp_dtr); } } - MCR = data; + dtr=temp_dtr; + rts=temp_rts; + op1=temp_op1; + op2=temp_op2; + loopback=temp_loopback; } /*****************************************************************************/ @@ -581,19 +762,14 @@ Bitu CSerial::Read_LSR () { Bitu retval = LSR; LSR &= (~LSR_ERROR_MASK); // clear error bits on read clear (ERROR_PRIORITY); - -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from LSR value 0x%x", base, retval); -#endif return retval; } void CSerial::Write_MSR (Bit8u val) { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Write to MSR, value 0x%x", base, val); -#endif - MSR &= MSR_LINE_MASK; - MSR |= val & MSR_delta_MASK; + d_cts = (val&MSR_dCTS_MASK)?true:false; + d_dsr = (val&MSR_dDSR_MASK)?true:false; + d_cd = (val&MSR_dCD_MASK)?true:false; + d_ri = (val&MSR_dRI_MASK)?true:false; } /*****************************************************************************/ @@ -605,17 +781,36 @@ void CSerial::Write_MSR (Bit8u val) { // - real values // - write operation to MCR in loopback mode Bitu CSerial::Read_MSR () { - Bit8u retval; - if (!(MCR & MCR_LOOPBACK_Enable_MASK)) { - updateMSR (); + Bit8u retval=0; + + if (loopback) { + + if (rts) retval |= MSR_CTS_MASK; + if (dtr) retval |= MSR_DSR_MASK; + if (op1) retval |= MSR_RI_MASK; + if (op2) retval |= MSR_CD_MASK; + + } else { + + updateMSR(); + if (cd) retval |= MSR_CD_MASK; + if (ri) retval |= MSR_RI_MASK; + if (dsr) retval |= MSR_DSR_MASK; + if (cts) retval |= MSR_CTS_MASK; + } - retval = MSR; + // new delta flags + if(d_cd) retval|=MSR_dCD_MASK; + if(d_ri) retval|=MSR_dRI_MASK; + if(d_cts) retval|=MSR_dCTS_MASK; + if(d_dsr) retval|=MSR_dDSR_MASK; + + d_cd = false; + d_ri = false; + d_cts = false; + d_dsr = false; + clear (MSR_PRIORITY); - // clear deltas - MSR &= MSR_LINE_MASK; -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from MSR value 0x%x", base, retval); -#endif return retval; } @@ -624,16 +819,10 @@ Bitu CSerial::Read_MSR () { /*****************************************************************************/ // Just a memory register. Not much to do here. Bitu CSerial::Read_SPR () { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Read from SPR value 0x%x", base, SPR); -#endif return SPR; } void CSerial::Write_SPR (Bit8u data) { -#ifdef SERIALPORT_DEBUGMSG - LOG_UART ("Serial port at %x: Write to SPR value 0x%x", base, data); -#endif SPR = data; } @@ -641,96 +830,100 @@ void CSerial::Write_SPR (Bit8u data) { /* Write_reserved **/ /*****************************************************************************/ void CSerial::Write_reserved (Bit8u data, Bit8u address) { - LOG_UART("Serial port at %x: Write to reserved register, value 0x%x, register %x", base, data, address); -} - -/*****************************************************************************/ -/* Loopback: add byte to loopback buffer; it is received next time Timer **/ -/* function is invoked; (time needed is not emulated correctly, but I don't **/ -/* think this is sooooo important....) **/ -/*****************************************************************************/ -void CSerial::transmitLoopbackByte (Bit8u val) { - //LOG_MSG("Serial port at %x: Loopback requested", base); - loopback_pending = true; - loopback_data = val; + /*LOG_UART("Serial%d: Write to reserved register, value 0x%x, register %x", + COMNUMBER, data, address);*/ } /*****************************************************************************/ /* MCR Access: returns cirquit state as boolean. **/ /*****************************************************************************/ bool CSerial::getDTR () { - return (MCR & MCR_DTR_MASK) != 0; + if(loopback) return false; + else return dtr; } bool CSerial::getRTS () { - return (MCR & MCR_RTS_MASK) != 0; + if(loopback) return false; + else return rts; } /*****************************************************************************/ /* MSR Access **/ /*****************************************************************************/ bool CSerial::getRI () { - return (MSR & MSR_RI_MASK) != 0; + return ri; } bool CSerial::getCD () { - return (MSR & MSR_CD_MASK) != 0; + return cd; } bool CSerial::getDSR () { - return (MSR & MSR_DSR_MASK) != 0; + return dsr; } bool CSerial::getCTS () { - return (MSR & MSR_CTS_MASK) != 0; + return cts; } -// these give errors if invoked while loopback mode... but who cares. void CSerial::setRI (bool value) { - bool compare = ((MSR & MSR_RI_MASK) != 0); - if (value != compare) { - if (value) - MSR |= MSR_RI_MASK; - else - MSR &= (~MSR_RI_MASK); - MSR |= MSR_dRI_MASK; - rise (MSR_PRIORITY); + if (value != ri) { + +#if SERIAL_DEBUG + if(dbg_modemcontrol) + fprintf(debugfp,value?"%12.3f RI on.\r\n":"%12.3f RI off.\r\n", PIC_FullIndex()); +#endif + // don't change delta when in loopback mode + ri=value; + if(!loopback) { + if(value==false) d_ri=true; + rise (MSR_PRIORITY); + } } //else no change } void CSerial::setDSR (bool value) { - bool compare = ((MSR & MSR_DSR_MASK) != 0); - if (value != compare) { - if (value) - MSR |= MSR_DSR_MASK; - else - MSR &= (~MSR_DSR_MASK); - MSR |= MSR_dDSR_MASK; - rise (MSR_PRIORITY); + if (value != dsr) { +#if SERIAL_DEBUG + if(dbg_modemcontrol) + fprintf(debugfp,value?"%12.3f DSR on.\r\n":"%12.3f DSR off.\r\n", PIC_FullIndex()); +#endif + // don't change delta when in loopback mode + dsr=value; + if(!loopback) { + d_dsr=true; + rise (MSR_PRIORITY); + } } //else no change } void CSerial::setCD (bool value) { - bool compare = ((MSR & MSR_CD_MASK) != 0); - if (value != compare) { - if (value) - MSR |= MSR_CD_MASK; - else - MSR &= (~MSR_CD_MASK); - MSR |= MSR_dCD_MASK; - rise (MSR_PRIORITY); + if (value != cd) { +#if SERIAL_DEBUG + if(dbg_modemcontrol) + fprintf(debugfp,value?"%12.3f CD on.\r\n":"%12.3f CD off.\r\n", PIC_FullIndex()); +#endif + // don't change delta when in loopback mode + cd=value; + if(!loopback) { + d_cd=true; + rise (MSR_PRIORITY); + } } //else no change } void CSerial::setCTS (bool value) { - bool compare = ((MSR & MSR_CTS_MASK) != 0); - if (value != compare) { - if (value) - MSR |= MSR_CTS_MASK; - else - MSR &= (~MSR_CTS_MASK); - MSR |= MSR_dCTS_MASK; - rise (MSR_PRIORITY); + if (value != cts) { +#if SERIAL_DEBUG + if(dbg_modemcontrol) + fprintf(debugfp,value?"%12.3f CTS on.\r\n":"%12.3f CTS off.\r\n", PIC_FullIndex()); +#endif + // don't change delta when in loopback mode + cts=value; + if(!loopback) { + d_cts=true; + rise (MSR_PRIORITY); + } } //else no change } @@ -738,8 +931,16 @@ void CSerial::setCTS (bool value) { /*****************************************************************************/ /* Initialisation **/ /*****************************************************************************/ -void CSerial::Init_Registers (Bit32u initbps, Bit8u bytesize, - const char *parity, Bit8u stopbits) { +void CSerial::Init_Registers () { + // The "power on" settings + irq_active=false; + waiting_interrupts = 0x0; + + Bit32u initbps = 9600; + Bit8u bytesize = 8; + char parity = 'N'; + Bit8u stopbits = 1; + Bit8u lcrresult = 0; Bit16u baudresult = 0; @@ -748,20 +949,27 @@ void CSerial::Init_Registers (Bit32u initbps, Bit8u bytesize, IER = 0; ISR = 0x1; LCR = 0; - MCR = 0; + //MCR = 0xff; + loopback; + dtr=true; + rts=true; + op1=true; + op2=true; + + LSR = 0x60; - MSR = 0; + d_cts = true; + d_dsr = true; + d_ri = true; + d_cd = true; + cts = true; + dsr = true; + ri = true; + cd = true; SPR = 0xFF; - DLL = 0x0; - DLM = 0x0; - - pending_interrupts = 0x0; - current_priority = 0x0; - waiting_interrupts = 0x0; - loopback_pending = false; - + baud_divider=0x0; // make lcr: byte size, parity, stopbits, baudrate @@ -774,313 +982,259 @@ void CSerial::Init_Registers (Bit32u initbps, Bit8u bytesize, else lcrresult |= LCR_DATABITS_8; - if (parity[0] == 'O' || parity[0] == 'o') - lcrresult |= LCR_PARITY_ODD; - else if (parity[0] == 'E' || parity[0] == 'e') - lcrresult |= LCR_PARITY_EVEN; - else if (parity[0] == 'M' || parity[0] == 'm') - lcrresult |= LCR_PARITY_MARK; - else if (parity[0] == 'S' || parity[0] == 's') - lcrresult |= LCR_PARITY_SPACE; - else + switch(parity) + { + case 'N': + case 'n': lcrresult |= LCR_PARITY_NONE; - - if (stopbits == 2) - lcrresult |= LCR_STOPBITS_MORE_THAN_1; - else - lcrresult |= LCR_STOPBITS_1; + break; + case 'O': + case 'o': + lcrresult |= LCR_PARITY_ODD; + break; + case 'E': + case 'e': + lcrresult |= LCR_PARITY_EVEN; + break; + case 'M': + case 'm': + lcrresult |= LCR_PARITY_MARK; + break; + case 'S': + case 's': + lcrresult |= LCR_PARITY_SPACE; + break; + } // baudrate - if (initbps > 0) baudresult = (Bit16u) (115200 / initbps); else baudresult = 12; // = 9600 baud + Write_MCR (0); Write_LCR (LCR_DIVISOR_Enable_MASK); Write_THR ((Bit8u) baudresult & 0xff); Write_IER ((Bit8u) (baudresult >> 8)); Write_LCR (lcrresult); + updateMSR(); + Read_MSR(); } -CSerial::CSerial(IO_ReadHandler * rh, IO_WriteHandler * wh, TIMER_TickHandler, - Bit16u initbase, Bit8u initirq, Bit32u initbps, Bit8u bytesize, - const char *parity, Bit8u stopbits) { - base = initbase; - irq = initirq; - TimerHnd = NULL; - //TimerHnd = th; // for destructor - //TIMER_AddTickHandler(TimerHnd); +CSerial::CSerial(Bitu id, CommandLine* cmd) { + +#if SERIAL_DEBUG + dbg_serialtraffic = cmd->FindExist("dbgtr", false); + dbg_modemcontrol = cmd->FindExist("dbgmd", false); + dbg_register = cmd->FindExist("dbgreg", false); + dbg_interrupt = cmd->FindExist("dbgirq", false); + dbg_aux = cmd->FindExist("dbgaux", false); + + + if(dbg_serialtraffic|dbg_modemcontrol|dbg_register|dbg_interrupt|dbg_aux) + debugfp=OpenCaptureFile("serlog",".serlog.txt"); + else debugfp=0; + +#endif + + + + idnumber=id; + mydosdevice=new device_COM(this); + DOS_AddDevice(mydosdevice); + Bit16u base = serial_baseaddr[id]; + fifo_warn=false; + + errormsg_pending=false; + framingErrors=0; + parityErrors=0; + overrunErrors=0; + overrunIF0=0; + breakErrors=0; + + // find the IRQ + irq = serial_defaultirq[id]; + getBituSubstring("irq:",&irq, cmd); + if (irq < 2 || irq > 15) irq = serial_defaultirq[id]; + for (Bitu i = 0; i <= 7; i++) { - WriteHandler[i].Install (i + base, wh, IO_MB); - ReadHandler[i].Install (i + base, rh, IO_MB); + WriteHandler[i].Install (i + base, SERIAL_Write, IO_MB); + ReadHandler[i].Install (i + base, SERIAL_Read, IO_MB); } + +#if SERIAL_DEBUG + if(debugfp) fprintf(debugfp,"COM%d: BASE %3x, IRQ %d\r\n\r\n", + COMNUMBER,base,irq); +#endif }; +bool CSerial::getBituSubstring(const char* name,Bitu* data, CommandLine* cmd) { + std::string tmpstring; + if(!(cmd->FindStringBegin(name,tmpstring,false))) return false; + const char* tmpchar=tmpstring.c_str(); + if(sscanf(tmpchar,"%u",data)!=1) return false; + return true; +} + CSerial::~CSerial(void) { - - if(TimerHnd) TIMER_DelTickHandler(TimerHnd); + DOS_DelDevice(mydosdevice); + for(Bitu i = 0; i <= SERIAL_BASE_EVENT_COUNT; i++) + removeEvent(i); }; - -void CSerial::InstallTimerHandler(TIMER_TickHandler th) -{ - if(TimerHnd==NULL) { - TimerHnd=th; - TIMER_AddTickHandler(th); - } -} - -bool getParameter(char *input, char *buffer, const char *parametername, - Bitu buffersize) { - Bitu outputPos = 0; - Bitu currentState = 0; // 0 = before '=' 1 = after '=' 2 = in word - Bitu startInputPos; - Bitu inputPos; - char *res1 = strstr(input, parametername); - if (res1 == 0) - return false; - inputPos = res1 - input; - inputPos += strlen (parametername); - startInputPos = inputPos; - while (input[inputPos] != 0 && outputPos + 2 < buffersize) { - if (currentState == 0) { - if (input[inputPos] == ' ') - inputPos++; - else if (input[inputPos] == ':') { - currentState = 1; - inputPos++; - } else - return false; - } else if (currentState == 1) { - if (input[inputPos] == ' ') - inputPos++; - else { - currentState = 2; - buffer[outputPos] = input[inputPos]; - outputPos++; - inputPos++; - } - - } else { - if (input[inputPos] == ' ') { - buffer[outputPos] = 0; - return true; - } else { - buffer[outputPos] = input[inputPos]; - outputPos++; - inputPos++; - } - } - } - buffer[outputPos] = 0; - if (inputPos == startInputPos) - return false; - return true; -} - -// functions for parsing the config line -bool scanNumber (char *scan, Bitu * retval) { - *retval = 0; - - while (char c = *scan) { - if (c >= '0' && c <= '9') { - *retval *= 10; - *retval += c - '0'; - scan++; - } else +bool CSerial::Getchar(Bit8u* data, bool wait_dsr, Bitu timeout) { + + double starttime=PIC_FullIndex(); + // wait for it to become empty + // wait for DSR on + if(wait_dsr) { + while((!(Read_MSR()&0x20))&&(starttime>PIC_FullIndex()-timeout)) + CALLBACK_Idle(); + if(!(starttime>PIC_FullIndex()-timeout)) { + #if SERIAL_DEBUG +if(dbg_aux) + fprintf(debugfp,"%12.3f API read timeout: MSR 0x%x\r\n", PIC_FullIndex(),Read_MSR()); +#endif return false; + } } + // wait for a byte to arrive + while((!(Read_LSR()&0x1))&&(starttime>PIC_FullIndex()-timeout)) + CALLBACK_Idle(); + + if(!(starttime>PIC_FullIndex()-timeout)) { + #if SERIAL_DEBUG +if(dbg_aux) + fprintf(debugfp,"%12.3f API read timeout: MSR 0x%x\r\n", PIC_FullIndex(),Read_MSR()); +#endif + return false; + } + + + *data=Read_RHR(); + +#if SERIAL_DEBUG + if(dbg_aux) + fprintf(debugfp,"%12.3f API read success: 0x%x\r\n", PIC_FullIndex(),*data); +#endif + return true; } -bool getFirstWord (char *input, char *buffer, Bitu buffersize) { - Bitu outputPointer = 0; - Bitu currentState = 0; // 0 = scanning spaces 1 = in word - Bitu currentPos = 0; - while (input[currentPos] != 0 && outputPointer + 2 < buffersize) { - if (currentState == 0) { - if (input[currentPos] != ' ') { - currentState = 1; - buffer[outputPointer] = input[currentPos]; - outputPointer++; - } - } else { - if (input[currentPos] == ' ') { - buffer[outputPointer] = 0; - return true; - } else { - buffer[outputPointer] = input[currentPos]; - outputPointer++; - } - } - currentPos++; - } - buffer[outputPointer] = 0; // end of string - if (currentState == 0) - return false; - else - return true; -} -void BIOS_SetComPorts (Bit16u baseaddr[]); +bool CSerial::Putchar(Bit8u data, bool wait_dsr, bool wait_cts, Bitu timeout) { + + double starttime=PIC_FullIndex(); + //Bit16u starttime= + // wait for it to become empty + while(!(LSR&0x20)) { + CALLBACK_Idle(); + } + // wait for DSR+CTS on + if(wait_dsr||wait_cts) { + if(wait_dsr||wait_cts) { + while(((Read_MSR()&0x30)!=0x30)&&(starttime>PIC_FullIndex()-timeout)) + CALLBACK_Idle(); + } else if(wait_dsr) { + while(!(Read_MSR()&0x20)&&(starttime>PIC_FullIndex()-timeout)) + CALLBACK_Idle(); + } else if(wait_cts) { + while(!(Read_MSR()&0x10)&&(starttime>PIC_FullIndex()-timeout)) + CALLBACK_Idle(); + + } + if(!(starttime>PIC_FullIndex()-timeout)) { +#if SERIAL_DEBUG + if(dbg_aux) + fprintf(debugfp,"%12.3f API write timeout: MSR 0x%x\r\n", + PIC_FullIndex(),Read_MSR()); +#endif + return false; + } + } + Write_THR(data); + +#if SERIAL_DEBUG + if(dbg_aux) + fprintf(debugfp,"%12.3f API write success: 0x%x\r\n", PIC_FullIndex(),data); +#endif + + return true; +} class SERIALPORTS:public Module_base { public: - CSerial ** serialPortObjects[4]; SERIALPORTS (Section * configuration):Module_base (configuration) { - // put handlers into arrays - IO_ReadHandler *serialReadHandlers[] = { - &SERIAL1_Read, &SERIAL2_Read, &SERIAL3_Read, &SERIAL4_Read - }; - IO_WriteHandler *serialWriteHandlers[] = { - &SERIAL1_Write, &SERIAL2_Write, &SERIAL3_Write, &SERIAL4_Write - }; - TIMER_TickHandler serialTimerHandlers[] = { - &SERIAL1_Update, &SERIAL2_Update, &SERIAL3_Update, &SERIAL4_Update - }; - - // default ports & interrupts - Bit16u addresses[] = { COM1_BASE, COM2_BASE, COM3_BASE, COM4_BASE }; - Bit8u defaultirq[] = { 4, 3, 4, 3 }; + Bit16u biosParameter[4] = { 0, 0, 0, 0 }; Section_prop *section = static_cast (configuration); - char tmpbuffer[15]; - const char *configstringsconst[4] = { + const char *configstrings[4] = { section->Get_string ("serial1"), section->Get_string ("serial2"), section->Get_string ("serial3"), section->Get_string ("serial4") }; - /* Create copies so they can be modified */ - char *configstrings[4] = { 0 }; - for(Bitu i = 0;i < 4;i++) { - size_t len = strlen(configstringsconst[i]); - configstrings[i] = new char[len+1]; - strcpy(configstrings[i],configstringsconst[i]); - configstrings[i] = upcase(configstrings[i]); - } - - serialPortObjects[0] = &serial1; - serialPortObjects[1] = &serial2; - serialPortObjects[2] = &serial3; - serialPortObjects[3] = &serial4; - // iterate through all 4 com ports for (Bitu i = 0; i < 4; i++) { - Bit16u baseAddress = addresses[i]; - Bitu irq = defaultirq[i]; - Bitu bps = 9600; - Bitu bytesize = 8; - Bitu stopbits = 1; - char parity = 'N'; - biosParameter[i] = baseAddress; + biosParameter[i] = serial_baseaddr[i]; - // parameter: irq - if (getParameter(configstrings[i], tmpbuffer, "IRQ", sizeof (tmpbuffer))) { - if (scanNumber (tmpbuffer, &irq)) { - if (irq < 0 || irq == 2 || irq > 15) - irq = defaultirq[i]; - } else - irq = defaultirq[i]; + CommandLine* cmd; + std::string str; + cmd=new CommandLine(0,configstrings[i]); + cmd->FindCommand(1,str); + + if(!str.compare("dummy")) { + serialports[i] = new CSerialDummy (i, cmd); } - // parameter: bps - if (getParameter(configstrings[i], tmpbuffer, "STARTBPS", sizeof (tmpbuffer))) { - if (scanNumber (tmpbuffer, &bps)) { - if (bps <= 0) - bps = 9600; - } else - bps = 9600; - } - // parameter: bytesize - if (getParameter(configstrings[i], tmpbuffer, "BYTESIZE", sizeof (tmpbuffer))) { - if (scanNumber (tmpbuffer, &bytesize)) { - if (bytesize < 5 || bytesize > 8) - bytesize = 8; - } else - bytesize = 8; - } - // parameter: stopbits - if (getParameter(configstrings[i], tmpbuffer, "STOPBITS", sizeof (tmpbuffer))) { - if (scanNumber (tmpbuffer, &stopbits)) { - if (stopbits < 1 || stopbits > 2) - stopbits = 1; - } else - stopbits = 1; - } - // parameter: parity - if (getParameter(configstrings[i], tmpbuffer, "PARITY", sizeof (tmpbuffer))) { - if (!(tmpbuffer[0] == 'N' || tmpbuffer[0] == 'O' || tmpbuffer[0] == 'E' - || tmpbuffer[0] == 'M' || tmpbuffer[0] == 'S')) - tmpbuffer[0] = 'N'; - parity = tmpbuffer[0]; - } - //LOG_MSG("COM%d: %s",i+1,configstrings[i]); - if (getFirstWord (configstrings[i], tmpbuffer, sizeof (tmpbuffer))) { - //LOG_MSG("COM%d: %s",i+1,tmpbuffer); - if (!strcmp (tmpbuffer, "DUMMY")) { - *serialPortObjects[i] = new CSerialDummy (serialReadHandlers[i], serialWriteHandlers[i],serialTimerHandlers[i], baseAddress, irq, bps,bytesize, &parity, stopbits); - } #ifdef DIRECTSERIAL_AVAILIBLE - else if (!strcmp (tmpbuffer, "DIRECTSERIAL")) { - // parameter: realport - if (getParameter (configstrings[i], tmpbuffer, "REALPORT", sizeof (tmpbuffer))) { // realport is required - CDirectSerial *cdstemp = new CDirectSerial (serialReadHandlers[i], serialWriteHandlers[i], serialTimerHandlers[i], baseAddress, irq, bps,bytesize, &parity,stopbits,tmpbuffer); - - if (cdstemp->InstallationSuccessful) { - *serialPortObjects[i] = cdstemp; - } else { // serial port name was wrong or already in use - delete cdstemp; - *serialPortObjects[i] = NULL; - biosParameter[i] = 0; - } - - } else { - *serialPortObjects[i] = NULL; - biosParameter[i] = 0; - } + else if(!str.compare("directserial")) { + serialports[i] = new CDirectSerial (i, cmd); + if (!serialports[i]->InstallationSuccessful) { + // serial port name was wrong or already in use + delete serialports[i]; + serialports[i] = NULL; + biosParameter[i] = 0; } + } #endif + #if C_MODEM - else if (!strcmp (tmpbuffer, "MODEM")) { - Bitu listenport = 23; - // parameter: listenport - if (getParameter(configstrings[i], tmpbuffer, "LISTENPORT", sizeof (tmpbuffer))) { - if (scanNumber (tmpbuffer, &listenport)) { - if (listenport <= 0 || listenport > 65535) - listenport = 23; - } else - listenport = 23; - } - - *serialPortObjects[i] = new CSerialModem (serialReadHandlers[i], serialWriteHandlers[i], serialTimerHandlers[i], baseAddress, irq, bps,bytesize, &parity, stopbits, 0, listenport); + else if(!str.compare("modem")) { + serialports[i] = new CSerialModem (i, cmd); + if (!serialports[i]->InstallationSuccessful) { + delete serialports[i]; + serialports[i] = NULL; + biosParameter[i] = 0; } + } + else if(!str.compare("nullmodem")) { + serialports[i] = new CNullModem (i, cmd); + if (!serialports[i]->InstallationSuccessful) { + delete serialports[i]; + serialports[i] = NULL; + biosParameter[i] = 0; + } + } #endif - else if (!strcmp (tmpbuffer, "DISABLED")) { - *serialPortObjects[i] = NULL; - biosParameter[i] = 0; - } else { - LOG_MSG ("Invalid type for COM%d.", i + 1); - *serialPortObjects[i] = NULL; - biosParameter[i] = 0; - } + else if(!str.compare("disabled")) { + serialports[i] = NULL; + biosParameter[i] = 0; } else { - *serialPortObjects[i] = NULL; + LOG_MSG ("Invalid type for COM%d.", i + 1); + serialports[i] = NULL; biosParameter[i] = 0; } - } - - delete [] configstrings[0];delete [] configstrings[1]; - delete [] configstrings[2];delete [] configstrings[3]; + delete cmd; + } // for BIOS_SetComPorts (biosParameter); } ~SERIALPORTS () { for (Bitu i = 0; i < 4; i++) - if (*serialPortObjects[i]) { - delete *(serialPortObjects[i]); - *(serialPortObjects[i]) = 0; + if (serialports[i]) { + delete serialports[i]; + serialports[i] = 0; } } }; diff --git a/src/hardware/serialport/softmodem.cpp b/src/hardware/serialport/softmodem.cpp index 541ed436..112a0cfe 100644 --- a/src/hardware/serialport/softmodem.cpp +++ b/src/hardware/serialport/softmodem.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: softmodem.cpp,v 1.6 2007-01-08 19:45:41 qbix79 Exp $ */ +/* $Id: softmodem.cpp,v 1.7 2007-01-13 08:35:49 qbix79 Exp $ */ #include "dosbox.h" @@ -26,90 +26,88 @@ #include #include -#include "SDL_net.h" - #include "support.h" -#include "timer.h" #include "serialport.h" #include "softmodem.h" +#include "misc_util.h" //#include "mixer.h" -CSerialModem::CSerialModem( - IO_ReadHandler* rh, - IO_WriteHandler* wh, - TIMER_TickHandler th, - Bit16u baseAddr, - Bit8u initIrq, - Bit32u initBps, - Bit8u bytesize, - const char* parity, - Bit8u stopbits, - const char *remotestr, - Bit16u lport) - : CSerial(rh, wh, th, - baseAddr, initIrq, initBps, bytesize, parity, stopbits) { - socket=0; - incomingsocket=0; - InstallTimerHandler(th); +CSerialModem::CSerialModem(Bitu id, CommandLine* cmd):CSerial(id, cmd) { + InstallationSuccessful=false; + connected=false; - if(!SDLNetInited) { - if(SDLNet_Init()==-1) { - LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError()); - return; - } - SDLNetInited = true; - } rqueue=new CFifo(MODEM_BUFFER_QUEUE_SIZE); tqueue=new CFifo(MODEM_BUFFER_QUEUE_SIZE); - /* Default to direct null modem connection. Telnet mode interprets IAC codes */ + // Default to direct null modem connection. Telnet mode interprets IAC codes telnetmode = false; - /* Initialize the sockets and setup the listening port */ - socketset = SDLNet_AllocSocketSet(1); - listensocketset = SDLNet_AllocSocketSet(1); - if (!socketset || !listensocketset) { - LOG_MSG("MODEM:Can't open socketset:%s",SDLNet_GetError()); - //TODO Should probably just exit - return; - } - socket=0; - listenport=lport; - if (listenport) { - IPaddress listen_ip; - SDLNet_ResolveHost(&listen_ip, NULL, listenport); - listensocket=SDLNet_TCP_Open(&listen_ip); - if (!listensocket) LOG_MSG("MODEM:Can't open listen port: %s",SDLNet_GetError()); - - else LOG_MSG("MODEM: Port listener installed at port %d",listenport); - - } - else listensocket=0; - - // TODO: Fix dialtones if requested - //mhd.chan=MIXER_AddChannel((MIXER_MixHandler)this->MODEM_CallBack,8000,"MODEM"); - //MIXER_Enable(mhd.chan,false); - //MIXER_SetMode(mhd.chan,MIXER_16MONO); + // Initialize the sockets and setup the listening port + listenport = 23; + waitingclientsocket=0; + clientsocket = 0; + serversocket = 0; + getBituSubstring("listenport:", &listenport, cmd); + + // TODO: Fix dialtones if requested + //mhd.chan=MIXER_AddChannel((MIXER_MixHandler)this->MODEM_CallBack,8000,"MODEM"); + //MIXER_Enable(mhd.chan,false); + //MIXER_SetMode(mhd.chan,MIXER_16MONO); - Reset(); - //EnterIdleState(); - CSerial::Init_Registers(initBps,bytesize,parity,stopbits); - } + CSerial::Init_Registers(); + Reset(); // reset calls EnterIdleState + + setEvent(SERIAL_POLLING_EVENT,1); + InstallationSuccessful=true; +} - CSerialModem::~CSerialModem() { - if(socket) { - SDLNet_TCP_DelSocket(socketset,socket); - SDLNet_TCP_Close(socket); +CSerialModem::~CSerialModem() { + if(serversocket) delete serversocket; + if(clientsocket) delete clientsocket; + if(waitingclientsocket) delete waitingclientsocket; + + delete rqueue; + delete tqueue; + + // remove events + for(Bitu i = SERIAL_BASE_EVENT_COUNT+1; i <= SERIAL_MODEM_EVENT_COUNT; i++) + removeEvent(i); +} + +void CSerialModem::handleUpperEvent(Bit16u type) { + switch(type) + { + case SERIAL_RX_EVENT: + { + break; + } + case MODEM_TX_EVENT: + { + if(tqueue->left()) { + tqueue->addb(waiting_tx_character); + if(tqueue->left() < 2) { + CSerial::setCTS(false); + } + } else LOG_MSG("MODEM: TX Buffer overflow!"); + ByteTransmitted(); + + break; + } + case SERIAL_POLLING_EVENT: + { + Timer2(); + setEvent(SERIAL_POLLING_EVENT,1); + break; } - if(listensocket) SDLNet_TCP_Close(listensocket); - if(socketset) SDLNet_FreeSocketSet(socketset); - - delete rqueue; - delete tqueue; + case MODEM_RING_EVENT: + { + break; + } } +} void CSerialModem::SendLine(const char *line) { rqueue->addb(0xd); @@ -164,18 +162,9 @@ void CSerialModem::SendRes(ResTypes response) { } } -void CSerialModem::openConnection(void) { - if (socket) { - LOG_MSG("Huh? already connected"); - SDLNet_TCP_DelSocket(socketset,socket); - SDLNet_TCP_Close(socket); - } - socket = SDLNet_TCP_Open(&openip); -} - bool CSerialModem::Dial(char * host) { - /* Scan host for port */ + // Scan host for port Bit16u port; char * hasport=strrchr(host,':'); if (hasport) { @@ -183,26 +172,30 @@ bool CSerialModem::Dial(char * host) { port=(Bit16u)atoi(hasport); } else port=MODEM_DEFAULT_PORT; - /* Resolve host we're gonna dial */ + // Resolve host we're gonna dial LOG_MSG("Connecting to host %s port %d",host,port); - if (!SDLNet_ResolveHost(&openip,host,port)) { - openConnection(); - EnterConnectedState(); - return true; - } else { - LOG_MSG("Failed to resolve host %s: %s",host,SDLNet_GetError()); - SendRes(ResNODIALTONE); + clientsocket = new TCPClientSocket(host, port); + if(!clientsocket->isopen) { + delete clientsocket; + clientsocket=0; + LOG_MSG("Failed to connect."); + SendRes(ResNOCARRIER); EnterIdleState(); return false; + } else { + EnterConnectedState(); + return true; } } void CSerialModem::AcceptIncomingCall(void) { -// assert(!socket); - socket=incomingsocket; - SDLNet_TCP_AddSocket(socketset,socket); - incomingsocket = 0; - EnterConnectedState(); + if(waitingclientsocket) { + clientsocket=waitingclientsocket; + waitingclientsocket=0; + EnterConnectedState(); + } else { + EnterIdleState(); + } } Bitu CSerialModem::ScanNumber(char * & scan) { @@ -224,7 +217,10 @@ void CSerialModem::Reset(){ oldDTRstate = getDTR(); flowcontrol = 0; plusinc = 0; - incomingsocket = 0; + if(clientsocket) { + delete clientsocket; + clientsocket=0; + } memset(®,0,sizeof(reg)); reg[MREG_AUTOANSWER_COUNT]=0; // no autoanswer reg[MREG_RING_COUNT] = 1; @@ -246,23 +242,29 @@ void CSerialModem::EnterIdleState(void){ connected=false; ringing=false; - if(socket) { // clear current socket - SDLNet_TCP_DelSocket(socketset,socket); - SDLNet_TCP_Close(socket); - socket=0; + if(clientsocket) { + delete clientsocket; + clientsocket=0; } - if(incomingsocket) { // clear current incoming socket - SDLNet_TCP_DelSocket(socketset,incomingsocket); - SDLNet_TCP_Close(incomingsocket); + + if(waitingclientsocket) { // clear current incoming socket + delete waitingclientsocket; + waitingclientsocket=0; } // get rid of everything - if(listensocket) { - while(incomingsocket=SDLNet_TCP_Accept(listensocket)) { - SDLNet_TCP_DelSocket(socketset,incomingsocket); - SDLNet_TCP_Close(incomingsocket); - } + if(serversocket) { + while(waitingclientsocket=serversocket->Accept()) + delete waitingclientsocket; + } else if (listenport) { + + serversocket=new TCPServerSocket(listenport); + if(!serversocket->isopen) { + LOG_MSG("Serial%d: Modem could not open TCP port %d.",COMNUMBER,listenport); + delete serversocket; + serversocket=0; + } else LOG_MSG("Serial%d: Modem listening on port %d...",COMNUMBER,listenport); } - incomingsocket=0; + waitingclientsocket=0; commandmode = true; CSerial::setCD(false); @@ -273,19 +275,18 @@ void CSerialModem::EnterIdleState(void){ } void CSerialModem::EnterConnectedState(void) { - if(socket) { - SDLNet_TCP_AddSocket(socketset,socket); - SendRes(ResCONNECT); - commandmode = false; - memset(&telClient, 0, sizeof(telClient)); - connected = true; - ringing = false; - CSerial::setCD(true); - CSerial::setRI(false); - } else { - SendRes(ResNOCARRIER); - EnterIdleState(); + if(serversocket) { + // we don't accept further calls + delete serversocket; + serversocket=0; } + SendRes(ResCONNECT); + commandmode = false; + memset(&telClient, 0, sizeof(telClient)); + connected = true; + ringing = false; + CSerial::setCD(true); + CSerial::setRI(false); } void CSerialModem::DoCommand() { @@ -309,7 +310,7 @@ void CSerialModem::DoCommand() { if ((cmdbuf[0] != 'A') || (cmdbuf[1] != 'T')) { SendRes(ResERROR); - return;//goto ret_error; + return; } if (strstr(cmdbuf,"NET0")) { @@ -329,7 +330,7 @@ void CSerialModem::DoCommand() { if (*foundstr=='T' || *foundstr=='P') foundstr++; /* Small protection against empty line */ if (!foundstr[0]) { - SendRes(ResERROR);//goto ret_error; + SendRes(ResERROR); return; } char* helper; @@ -397,7 +398,7 @@ void CSerialModem::DoCommand() { if (connected) { SendRes(ResNOCARRIER); EnterIdleState(); - return;//goto ret_none; + return; } //Else return ok };break; @@ -405,12 +406,12 @@ void CSerialModem::DoCommand() { switch (num=ScanNumber(scanbuf)) { case 0: - if (socket) { + if (clientsocket) { commandmode = false; - return;//goto ret_none; + return; } else { SendRes(ResERROR); - return;//goto ret_none; + return; } };break; case 'T': //Tone Dial @@ -421,18 +422,18 @@ void CSerialModem::DoCommand() { ScanNumber(scanbuf); break; case 'A': //Answer call - if (incomingsocket) { + if (waitingclientsocket) { AcceptIncomingCall(); } else { SendRes(ResERROR); - return;//goto ret_none; + return; } - return;//goto ret_none; + return; case 'Z': //Reset and load profiles { // scan the number away, if any ScanNumber(scanbuf); - if (socket) SendRes(ResNOCARRIER); + if (clientsocket/*socket*/) SendRes(ResNOCARRIER); Reset(); break; } @@ -525,107 +526,103 @@ void CSerialModem::TelnetEmulation(Bit8u * data, Bitu size) { LOG_MSG("MODEM: Unrecognized option %d", c); if(telClient.command>250) { /* Reject anything we don't recognize */ + tqueue->addb(0xff); + tqueue->addb(252); + tqueue->addb(c); /* We won't do crap! */ + } + } + switch(telClient.command) { + case 251: /* Will */ + if(c == 0) telClient.binary[TEL_SERVER] = true; + if(c == 1) telClient.echo[TEL_SERVER] = true; + if(c == 3) telClient.supressGA[TEL_SERVER] = true; + break; + case 252: /* Won't */ + if(c == 0) telClient.binary[TEL_SERVER] = false; + if(c == 1) telClient.echo[TEL_SERVER] = false; + if(c == 3) telClient.supressGA[TEL_SERVER] = false; + break; + case 253: /* Do */ + if(c == 0) { + telClient.binary[TEL_CLIENT] = true; + tqueue->addb(0xff); + tqueue->addb(251); + tqueue->addb(0); /* Will do binary transfer */ + } + if(c == 1) { + telClient.echo[TEL_CLIENT] = false; tqueue->addb(0xff); tqueue->addb(252); - tqueue->addb(c); /* We won't do crap! */ + tqueue->addb(1); /* Won't echo (too lazy) */ } - } - switch(telClient.command) { - case 251: /* Will */ - if(c == 0) telClient.binary[TEL_SERVER] = true; - if(c == 1) telClient.echo[TEL_SERVER] = true; - if(c == 3) telClient.supressGA[TEL_SERVER] = true; - break; - case 252: /* Won't */ - if(c == 0) telClient.binary[TEL_SERVER] = false; - if(c == 1) telClient.echo[TEL_SERVER] = false; - if(c == 3) telClient.supressGA[TEL_SERVER] = false; - break; - case 253: /* Do */ - if(c == 0) { - telClient.binary[TEL_CLIENT] = true; - tqueue->addb(0xff); - tqueue->addb(251); - tqueue->addb(0); /* Will do binary transfer */ - } - if(c == 1) { - telClient.echo[TEL_CLIENT] = false; - tqueue->addb(0xff); - tqueue->addb(252); - tqueue->addb(1); /* Won't echo (too lazy) */ - } - if(c == 3) { - telClient.supressGA[TEL_CLIENT] = true; - tqueue->addb(0xff); - tqueue->addb(251); - tqueue->addb(3); /* Will Suppress GA */ - } - break; - case 254: /* Don't */ - if(c == 0) { - telClient.binary[TEL_CLIENT] = false; - tqueue->addb(0xff); - tqueue->addb(252); - tqueue->addb(0); /* Won't do binary transfer */ - } - if(c == 1) { - telClient.echo[TEL_CLIENT] = false; - tqueue->addb(0xff); - tqueue->addb(252); - tqueue->addb(1); /* Won't echo (fine by me) */ - } - if(c == 3) { - telClient.supressGA[TEL_CLIENT] = true; - tqueue->addb(0xff); - tqueue->addb(251); - tqueue->addb(3); /* Will Suppress GA (too lazy) */ - } - break; - default: - LOG_MSG("MODEM: Telnet client sent IAC %d", telClient.command); - break; - } - + if(c == 3) { + telClient.supressGA[TEL_CLIENT] = true; + tqueue->addb(0xff); + tqueue->addb(251); + tqueue->addb(3); /* Will Suppress GA */ + } + break; + case 254: /* Don't */ + if(c == 0) { + telClient.binary[TEL_CLIENT] = false; + tqueue->addb(0xff); + tqueue->addb(252); + tqueue->addb(0); /* Won't do binary transfer */ + } + if(c == 1) { + telClient.echo[TEL_CLIENT] = false; + tqueue->addb(0xff); + tqueue->addb(252); + tqueue->addb(1); /* Won't echo (fine by me) */ + } + if(c == 3) { + telClient.supressGA[TEL_CLIENT] = true; + tqueue->addb(0xff); + tqueue->addb(251); + tqueue->addb(3); /* Will Suppress GA (too lazy) */ + } + break; + default: + LOG_MSG("MODEM: Telnet client sent IAC %d", telClient.command); + break; + } + telClient.inIAC = false; + telClient.recCommand = false; + continue; + } else { + if(c==249) { + /* Go Ahead received */ + telClient.inIAC = false; + continue; + } + telClient.command = c; + telClient.recCommand = true; + + if((telClient.binary[TEL_SERVER]) && (c == 0xff)) { + /* Binary data with value of 255 */ telClient.inIAC = false; telClient.recCommand = false; + rqueue->addb(0xff); continue; - - } else { - if(c==249) { - /* Go Ahead received */ - telClient.inIAC = false; - continue; - } - telClient.command = c; - telClient.recCommand = true; - - if((telClient.binary[TEL_SERVER]) && (c == 0xff)) { - /* Binary data with value of 255 */ - telClient.inIAC = false; - telClient.recCommand = false; - rqueue->addb(0xff); - continue; - } - - } - } else { - if(c == 0xff) { - telClient.inIAC = true; - continue; - } - rqueue->addb(c); } } + } else { + if(c == 0xff) { + telClient.inIAC = true; + continue; + } + rqueue->addb(c); + } } +} void CSerialModem::Timer2(void) { - int result =0; + unsigned long args = 1; bool sendbyte = true; Bitu usesize; Bit8u txval; Bitu txbuffersize =0; - Bitu testres = 0; // check for bytes to be sent to port if(CSerial::CanReceiveByte()) @@ -634,13 +631,11 @@ void CSerialModem::Timer2(void) { //LOG_MSG("Modem: sending byte %2x back to UART3",rbyte); CSerial::receiveByte(rbyte); } - /* Check for eventual break command */ + // Check for eventual break command if (!commandmode) cmdpause++; - /* Handle incoming data from serial port, read as much as available */ - //Bitu tx_size=tqueue->inuse(); - //Bitu tx_first = tx_size; // TODO:comment out + // Handle incoming data from serial port, read as much as available CSerial::setCTS(true); // buffer will get 'emptier', new data can be received - while (/*tx_size--*/tqueue->inuse()) { + while (tqueue->inuse()) { txval = tqueue->getb(); if (commandmode) { if (echo) { @@ -658,9 +653,9 @@ void CSerialModem::Timer2(void) { } } else {// + character - /* 1000 ticks have passed, can check for pause command */ + // 1000 ticks have passed, can check for pause command if (cmdpause > 1000) { - if(txval ==/* '+')*/reg[MREG_ESCAPE_CHAR]) + if(txval ==reg[MREG_ESCAPE_CHAR]) // + { plusinc++; if(plusinc>=3) { @@ -673,46 +668,37 @@ void CSerialModem::Timer2(void) { } else { plusinc=0; } - //If not a special pause command, should go for bigger blocks to send + // If not a special pause command, should go for bigger blocks to send } tmpbuf[txbuffersize] = txval; txbuffersize++; } } // while loop - if (socket && sendbyte && txbuffersize) { + if (clientsocket && sendbyte && txbuffersize) { // down here it saves a lot of network traffic - SDLNet_TCP_Send(socket, tmpbuf,txbuffersize); + clientsocket->SendArray(tmpbuf,txbuffersize); //TODO error testing } - SDLNet_CheckSockets(socketset,0); - /* Handle incoming to the serial port */ - if(!commandmode && socket) { - if(rqueue->left() && SDLNet_SocketReady(socket) /*&& CSerial::getRTS()*/) - { - usesize = rqueue->left(); - if (usesize>16) usesize=16; - result = SDLNet_TCP_Recv(socket, tmpbuf, usesize); - if (result>0) { - if(telnetmode) { - /* Filter telnet commands */ - TelnetEmulation(tmpbuf, result); - } else { - rqueue->adds(tmpbuf,result); - } - cmdpause = 0; - } else { - SendRes(ResNOCARRIER); - EnterIdleState(); - } - } + // Handle incoming to the serial port + if(!commandmode && clientsocket && rqueue->left()) { + usesize = rqueue->left(); + if (usesize>16) usesize=16; + if(!clientsocket->ReceiveArray(tmpbuf, &usesize)) { + SendRes(ResNOCARRIER); + EnterIdleState(); + } else if(usesize) { + // LOG_MSG("rcv:%d", result); + // Filter telnet commands + if(telnetmode) TelnetEmulation(tmpbuf, usesize); + else rqueue->adds(tmpbuf,usesize); + cmdpause = 0; + } } - /* Check for incoming calls */ - if (!connected && !incomingsocket && listensocket) { - incomingsocket = SDLNet_TCP_Accept(listensocket); - if (incomingsocket) { - SDLNet_TCP_AddSocket(listensocketset, incomingsocket); - + // Check for incoming calls + if (!connected && !waitingclientsocket && serversocket) { + waitingclientsocket=serversocket->Accept(); + if(waitingclientsocket) { if(!CSerial::getDTR()) { // accept no calls with DTR off; TODO: AT &Dn EnterIdleState(); @@ -754,18 +740,14 @@ void CSerialModem::RXBufferEmpty() { } } -void CSerialModem::transmitByte(Bit8u val) { +void CSerialModem::transmitByte(Bit8u val, bool first) { + waiting_tx_character=val; + setEvent(MODEM_TX_EVENT, bytetime); // TX event + if(first) ByteTransmitting(); //LOG_MSG("MODEM: Byte %x to be transmitted",val); - if(tqueue->left()) { - tqueue->addb(val); - if(tqueue->left() < 2) { - CSerial::setCTS(false); - } - } else LOG_MSG("MODEM: TX Buffer overflow!"); - CSerial::ByteTransmitted(); } -void CSerialModem::updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr) { +void CSerialModem::updatePortConfig(Bit16u, Bit8u lcr) { // nothing to do here right? } @@ -777,6 +759,21 @@ void CSerialModem::setBreak(bool) { // TODO: handle this } +void CSerialModem::setRTSDTR(bool rts, bool dtr) { + setDTR(dtr); +} +void CSerialModem::setRTS(bool val) { + +} +void CSerialModem::setDTR(bool val) { + if(!val && connected) { + // If DTR goes low, hang up. + SendRes(ResNOCARRIER); + EnterIdleState(); + LOG_MSG("Modem: Hang up due to dropped DTR."); + } +} +/* void CSerialModem::updateModemControlLines() { //bool txrdy=tqueue->left(); //if(CSerial::getRTS() && txrdy) CSerial::setCTS(true); @@ -793,7 +790,7 @@ void CSerialModem::updateModemControlLines() { oldDTRstate = getDTR(); } - +*/ #endif diff --git a/src/hardware/serialport/softmodem.h b/src/hardware/serialport/softmodem.h index 7cbf65b4..c51d915e 100644 --- a/src/hardware/serialport/softmodem.h +++ b/src/hardware/serialport/softmodem.h @@ -16,16 +16,17 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: softmodem.h,v 1.6 2007-01-08 19:45:41 qbix79 Exp $ */ +/* $Id: softmodem.h,v 1.7 2007-01-13 08:35:49 qbix79 Exp $ */ #ifndef DOSBOX_SERIALMODEM_H #define DOSBOX_SERIALMODEM_H #include "dosbox.h" #if C_MODEM -#include "SDL_net.h" #include "serialport.h" +#include "misc_util.h" + #define MODEMSPD 57600 #define SREGS 100 @@ -34,6 +35,12 @@ #define MODEM_DEFAULT_PORT 23 +#define MODEM_TX_EVENT SERIAL_BASE_EVENT_COUNT + 1 +#define MODEM_RX_POLLING SERIAL_BASE_EVENT_COUNT + 2 +#define MODEM_RING_EVENT SERIAL_BASE_EVENT_COUNT + 3 +#define SERIAL_MODEM_EVENT_COUNT SERIAL_BASE_EVENT_COUNT+3 + + enum ResTypes { ResNONE, ResOK,ResERROR, @@ -118,9 +125,6 @@ public: private: Bit8u * data; Bitu size,pos,used; - //Bit8u tmpbuf[MODEM_BUFFER_QUEUE_SIZE]; - - }; #define MREG_AUTOANSWER_COUNT 0 #define MREG_RING_COUNT 1 @@ -136,20 +140,7 @@ public: CFifo *rqueue; CFifo *tqueue; - CSerialModem( - IO_ReadHandler* rh, - IO_WriteHandler* wh, - TIMER_TickHandler th, - Bit16u baseAddr, - Bit8u initIrq, - Bit32u initBps, - Bit8u bytesize, - const char* parity, - Bit8u stopbits, - - const char *remotestr = NULL, - Bit16u lport = 23); - + CSerialModem(Bitu id, CommandLine* cmd); ~CSerialModem(); void Reset(); @@ -172,17 +163,21 @@ public: void TelnetEmulation(Bit8u * data, Bitu size); + //TODO void Timer2(void); + void handleUpperEvent(Bit16u type); void RXBufferEmpty(); - void transmitByte(Bit8u val); - void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr); + void transmitByte(Bit8u val, bool first); + void updatePortConfig(Bit16u divider, Bit8u lcr); void updateMSR(); void setBreak(bool); - void updateModemControlLines(/*Bit8u mcr*/); + void setRTSDTR(bool rts, bool dtr); + void setRTS(bool val); + void setDTR(bool val); protected: char cmdbuf[255]; @@ -199,7 +194,7 @@ protected: bool connected; Bitu doresponse; - + Bit8u waiting_tx_character; Bitu cmdpause; Bits ringtimer; @@ -208,18 +203,15 @@ protected: Bitu cmdpos; Bitu flowcontrol; - //Bit8u mctrl; Bit8u tmpbuf[MODEM_BUFFER_QUEUE_SIZE]; Bitu listenport; Bit8u reg[SREGS]; - IPaddress openip; - TCPsocket incomingsocket; - TCPsocket socket; - TCPsocket listensocket; - SDLNet_SocketSet socketset; - SDLNet_SocketSet listensocketset; + + TCPServerSocket* serversocket; + TCPClientSocket* clientsocket; + TCPClientSocket* waitingclientsocket; struct { bool binary[2]; diff --git a/src/ints/bios.cpp b/src/ints/bios.cpp index ce2cb729..1865691f 100644 --- a/src/ints/bios.cpp +++ b/src/ints/bios.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: bios.cpp,v 1.64 2007-01-08 19:45:41 qbix79 Exp $ */ +/* $Id: bios.cpp,v 1.65 2007-01-13 08:35:49 qbix79 Exp $ */ #include "dosbox.h" #include "mem.h" @@ -29,6 +29,7 @@ #include "joystick.h" #include "mouse.h" #include "setup.h" +#include "serialport.h" /* if mem_systems 0 then size_extended is reported as the real size else @@ -361,10 +362,15 @@ static Bitu INT17_Handler(void) { static Bitu INT14_Handler(void) { + if(reg_ah > 0x3 || reg_dx > 0x3) { // 0-3 serial port functions + // and no more than 4 serial ports + LOG_MSG("BIOS INT14: Unhandled call AH=%2X DX=%4x",reg_dx); + return CBRET_NONE; + } + Bit16u port = real_readw(0x40,reg_dx*2); // DX is always port number - if(reg_dx > 0x3 || port==0) // no more than 4 serial ports - { - LOG_MSG("BIOS INT14: port %d does not exist.",reg_dx); + if(port==0) { + LOG(LOG_BIOS,LOG_NORMAL)("BIOS INT14: port %d does not exist.",reg_dx); return CBRET_NONE; } switch (reg_ah) @@ -402,12 +408,11 @@ static Bitu INT14_Handler(void) // disable interrupts IO_WriteB(port+1, 0); IO_ReadB(port+2); - // put RTS and DTR on - IO_WriteB(port+4,0x3); // get result reg_ah=IO_ReadB(port+5); reg_al=IO_ReadB(port+6); + CALLBACK_SCF(false); } break; case 0x01: /* Write character */ @@ -417,126 +422,49 @@ static Bitu INT14_Handler(void) // AH: line status // AL: modem status { - if((IO_ReadB(port+5)&&0x20)==0) - { - // TODO: should wait until they become empty->timeout - LOG_MSG("BIOS INT14: port %d: transmit register not empty.",reg_dx); - reg_ah = IO_ReadB(port+5)|0x80; - return CBRET_NONE; + if(serialports[reg_dx]) { + bool timeout; + // switch modem lines on + IO_WriteB(port+4,0x3); + timeout = !serialports[reg_dx]->Putchar(reg_al,true,true, + mem_readb(BIOS_COM1_TIMEOUT+reg_dx)*1000); + // get result + reg_ah=IO_ReadB(port+5); + if(timeout) reg_ah |= 0x80; + reg_al=IO_ReadB(port+6); } - // transmit it - IO_WriteB(port,reg_al); - - if((IO_ReadB(port+5)&&0x60)==0) - { - // TODO: should wait until they become empty->timeout - LOG_MSG("BIOS INT14: port %d: transmit register not empty after write.",reg_dx); - reg_ah = IO_ReadB(port+5)|0x80; - return CBRET_NONE; - } - - // get result - reg_ah=IO_ReadB(port+5); - reg_al=IO_ReadB(port+6); + CALLBACK_SCF(false); } break; case 0x02: /* Read character */ { - if((IO_ReadB(port+5)&0x1)!=0) - { - reg_al=IO_ReadB(port); + if(serialports[reg_dx]) { + bool timeout; + Bit8u buffer; + // switch modem lines on + IO_WriteB(port+4,0x3); + // wait for something + timeout = !serialports[reg_dx]->Getchar(&buffer,true, + mem_readb(BIOS_COM1_TIMEOUT+reg_dx)*1000); + + // RTS off + IO_WriteB(port+4,0x1); + // get result + reg_ah=IO_ReadB(port+5); + if(timeout) reg_ah |= 0x80; + else reg_al=buffer; } - else - { - // TODO: should wait until timeout - LOG_MSG("BIOS INT14: port %d: nothing received.",reg_dx); - reg_ah = IO_ReadB(port+5)|0x80; - return CBRET_NONE; - } - reg_ah=IO_ReadB(port+5); + CALLBACK_SCF(false); + break; } - break; case 0x03: // get status { reg_ah=IO_ReadB(port+5); - //LOG_MSG("status reg_ah: %x",reg_ah); reg_al=IO_ReadB(port+6); + CALLBACK_SCF(false); } - break; - case 0x04: // extended initialisation - // Parameters: - // AL: break - // BH: parity - // BL: stopbit - // CH: word length - // CL: baudrate - { - Bit8u lcr = 0; - - // baud rate - Bitu baudrate = 9600; - Bit16u baudresult; - Bitu rawbaud=reg_cl; - - if(rawbaud==0){ baudrate=110;} - else if (rawbaud==1){ baudrate=150;} - else if (rawbaud==2){ baudrate=300;} - else if (rawbaud==3){ baudrate=600;} - else if (rawbaud==4){ baudrate=1200;} - else if (rawbaud==5){ baudrate=2400;} - else if (rawbaud==6){ baudrate=4800;} - else if (rawbaud==7){ baudrate=9600;} - else if (rawbaud==8){ baudrate=19200;} - - baudresult = (Bit16u)(115200 / baudrate); - - IO_WriteB(port+3, 0x80); // enable divider access - IO_WriteB(port,(Bit8u)baudresult&0xff); - IO_WriteB(port+1,(Bit8u)(baudresult>>8)); - - // line configuration - // break - if(reg_al!=0) lcr=0x40; - // parity - if(reg_bh!=0) - { - if(reg_bh==1)lcr|=0x8;// odd - else if(reg_bh==2)lcr|=0x18;// even - else if(reg_bh==3)lcr|=0x28;// mark - else if(reg_bh==4)lcr|=0x38;// mark - } - // stopbit - if(reg_bl!=0) - { - lcr|=0x4; - } - // data length - lcr|=(reg_ch&0x3); - IO_WriteB(port+3,lcr); - - reg_ah=IO_ReadB(port+5); - reg_al=IO_ReadB(port+6); - } - break; - case 0x05: // modem control - { - if(reg_al==0) // read MCR - { - reg_bl=IO_ReadB(port+4); - } - else if(reg_al==1) // write MCR - { - IO_WriteB(port+4,reg_bl); - } - else LOG_MSG("BIOS INT14: port %d, function 5: invalid subfunction.",reg_dx); - reg_ah=IO_ReadB(port+5); - reg_al=IO_ReadB(port+6); - } - break; - default: - LOG_MSG("Unhandled INT 14 call %2X",reg_ah); - + break; } return CBRET_NONE; } @@ -848,7 +776,7 @@ public: BIOS_SetupDisks(); /* INT 14 Serial Ports */ - callback[3].Install(&INT14_Handler,CB_IRET,"Int 14 COM-port"); + callback[3].Install(&INT14_Handler,CB_IRET_STI,"Int 14 COM-port"); callback[3].Set_RealVec(0x14); /* INT 15 Misc Calls */ @@ -922,46 +850,49 @@ public: } /* Setup some stuff in 0x40 bios segment */ + + // port timeouts + // always 1 second even if the port does not exist + mem_writeb(BIOS_LPT1_TIMEOUT,1); + mem_writeb(BIOS_LPT2_TIMEOUT,1); + mem_writeb(BIOS_LPT3_TIMEOUT,1); + mem_writeb(BIOS_COM1_TIMEOUT,1); + mem_writeb(BIOS_COM2_TIMEOUT,1); + mem_writeb(BIOS_COM3_TIMEOUT,1); + mem_writeb(BIOS_COM4_TIMEOUT,1); + /* detect parallel ports */ - Bit8u DEFAULTPORTTIMEOUT = 10; // 10 whatevers Bitu ppindex=0; // number of lpt ports if ((IO_Read(0x378)!=0xff)|(IO_Read(0x379)!=0xff)) { // this is our LPT1 mem_writew(BIOS_ADDRESS_LPT1,0x378); - mem_writeb(BIOS_LPT1_TIMEOUT,DEFAULTPORTTIMEOUT); ppindex++; if((IO_Read(0x278)!=0xff)|(IO_Read(0x279)!=0xff)) { // this is our LPT2 mem_writew(BIOS_ADDRESS_LPT2,0x278); - mem_writeb(BIOS_LPT2_TIMEOUT,DEFAULTPORTTIMEOUT); ppindex++; if((IO_Read(0x3bc)!=0xff)|(IO_Read(0x3be)!=0xff)) { // this is our LPT3 mem_writew(BIOS_ADDRESS_LPT3,0x3bc); - mem_writeb(BIOS_LPT3_TIMEOUT,DEFAULTPORTTIMEOUT); ppindex++; } } else if((IO_Read(0x3bc)!=0xff)|(IO_Read(0x3be)!=0xff)) { // this is our LPT2 mem_writew(BIOS_ADDRESS_LPT2,0x3bc); - mem_writeb(BIOS_LPT2_TIMEOUT,DEFAULTPORTTIMEOUT); ppindex++; } } else if((IO_Read(0x3bc)!=0xff)|(IO_Read(0x3be)!=0xff)) { // this is our LPT1 mem_writew(BIOS_ADDRESS_LPT1,0x3bc); - mem_writeb(BIOS_LPT1_TIMEOUT,DEFAULTPORTTIMEOUT); ppindex++; if((IO_Read(0x278)!=0xff)|(IO_Read(0x279)!=0xff)) { // this is our LPT2 mem_writew(BIOS_ADDRESS_LPT2,0x278); - mem_writeb(BIOS_LPT2_TIMEOUT,DEFAULTPORTTIMEOUT); ppindex++; } } else if((IO_Read(0x278)!=0xff)|(IO_Read(0x279)!=0xff)) { // this is our LPT1 mem_writew(BIOS_ADDRESS_LPT1,0x278); - mem_writeb(BIOS_LPT1_TIMEOUT,DEFAULTPORTTIMEOUT); ppindex++; } @@ -1035,24 +966,14 @@ public: // set com port data in bios data area // parameter: array of 4 com port base addresses, 0 = none void BIOS_SetComPorts(Bit16u baseaddr[]) { - Bit8u DEFAULTPORTTIMEOUT = 10; // 10 whatevers Bit16u portcount=0; Bit16u equipmentword; for(Bitu i = 0; i < 4; i++) { if(baseaddr[i]!=0) portcount++; - if(i==0) { // com1 - mem_writew(BIOS_BASE_ADDRESS_COM1,baseaddr[i]); - mem_writeb(BIOS_COM1_TIMEOUT,DEFAULTPORTTIMEOUT); - } else if(i==1) { - mem_writew(BIOS_BASE_ADDRESS_COM2,baseaddr[i]); - mem_writeb(BIOS_COM2_TIMEOUT,DEFAULTPORTTIMEOUT); - } else if(i==2) { - mem_writew(BIOS_BASE_ADDRESS_COM3,baseaddr[i]); - mem_writeb(BIOS_COM3_TIMEOUT,DEFAULTPORTTIMEOUT); - } else { - mem_writew(BIOS_BASE_ADDRESS_COM4,baseaddr[i]); - mem_writeb(BIOS_COM4_TIMEOUT,DEFAULTPORTTIMEOUT); - } + if(i==0) mem_writew(BIOS_BASE_ADDRESS_COM1,baseaddr[i]); + else if(i==1) mem_writew(BIOS_BASE_ADDRESS_COM2,baseaddr[i]); + else if(i==2) mem_writew(BIOS_BASE_ADDRESS_COM3,baseaddr[i]); + else mem_writew(BIOS_BASE_ADDRESS_COM4,baseaddr[i]); } // set equipment word equipmentword = mem_readw(BIOS_CONFIGURATION); diff --git a/visualc_net/dosbox.vcproj b/visualc_net/dosbox.vcproj index 26e93462..5512e72f 100644 --- a/visualc_net/dosbox.vcproj +++ b/visualc_net/dosbox.vcproj @@ -113,7 +113,7 @@ Name="VCCustomBuildTool"/> + + + + + + + + + + + + + + + + + + +