From a5252330f7381b4497e7964fa5531ae95b9d5d04 Mon Sep 17 00:00:00 2001 From: Peter Veenstra Date: Sat, 30 Jul 2005 14:41:31 +0000 Subject: [PATCH] Add patch 1151453 from h-a-l-9000. Improves softmodem and directserial supports. Adds a dummy serial class as well and makes the serial ports runtime changable Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@2257 --- configure.in | 1 + include/bios.h | 4 +- include/serialport.h | 369 ++++-- src/Makefile.am | 2 +- src/dosbox.cpp | 67 +- src/hardware/Makefile.am | 7 +- src/hardware/directserial_win32.cpp | 198 --- src/hardware/serialport.cpp | 276 ----- src/hardware/serialport/Makefile.am | 7 + .../serialport/directserial_win32.cpp | 377 ++++++ src/hardware/serialport/directserial_win32.h | 84 ++ src/hardware/serialport/serialdummy.cpp | 85 ++ src/hardware/serialport/serialdummy.h | 54 + src/hardware/serialport/serialport.cpp | 1089 +++++++++++++++++ src/hardware/serialport/softmodem.cpp | 826 +++++++++++++ src/hardware/serialport/softmodem.h | 243 ++++ src/hardware/softmodem.cpp | 820 ------------- src/ints/bios.cpp | 270 +++- visualc_net/dosbox.vcproj | 34 +- 19 files changed, 3329 insertions(+), 1484 deletions(-) delete mode 100644 src/hardware/directserial_win32.cpp delete mode 100644 src/hardware/serialport.cpp create mode 100644 src/hardware/serialport/Makefile.am create mode 100644 src/hardware/serialport/directserial_win32.cpp create mode 100644 src/hardware/serialport/directserial_win32.h create mode 100644 src/hardware/serialport/serialdummy.cpp create mode 100644 src/hardware/serialport/serialdummy.h create mode 100644 src/hardware/serialport/serialport.cpp create mode 100644 src/hardware/serialport/softmodem.cpp create mode 100644 src/hardware/serialport/softmodem.h delete mode 100644 src/hardware/softmodem.cpp diff --git a/configure.in b/configure.in index 0a4878bf..fa36b356 100644 --- a/configure.in +++ b/configure.in @@ -287,6 +287,7 @@ src/dos/Makefile src/fpu/Makefile src/gui/Makefile src/hardware/Makefile +src/hardware/serialport/Makefile src/ints/Makefile src/misc/Makefile src/shell/Makefile diff --git a/include/bios.h b/include/bios.h index 4002dc94..ccb4d9ee 100644 --- a/include/bios.h +++ b/include/bios.h @@ -72,7 +72,9 @@ /* 0x47b is reserved */ #define BIOS_COM1_TIMEOUT 0x47c #define BIOS_COM2_TIMEOUT 0x47d -/* 0x47e is reserved */ +#define BIOS_COM3_TIMEOUT 0x47e +#define BIOS_COM4_TIMEOUT 0x47f +/* 0x47e is reserved */ //<- why that? /* 0x47f-0x4ff is unknow for me */ #define BIOS_KEYBOARD_BUFFER_START 0x480 #define BIOS_KEYBOARD_BUFFER_END 0x482 diff --git a/include/serialport.h b/include/serialport.h index 1515ebaa..b64b4de6 100644 --- a/include/serialport.h +++ b/include/serialport.h @@ -16,146 +16,283 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* $Id: serialport.h,v 1.9 2005-07-30 14:41:30 qbix79 Exp $ */ + #ifndef DOSBOX_SERIALPORT_H #define DOSBOX_SERIALPORT_H -#include +// Uncomment this for a lot of debug messages: +//#define SERIALPORT_DEBUGMSG #ifndef DOSBOX_DOSBOX_H -#include "dosbox.h" +#include "dosbox.h" #endif #ifndef DOSBOX_INOUT_H -#include "inout.h" +#include "inout.h" +#endif +#ifndef DOSBOX_TIMER_H +#include "timer.h" #endif -//If it's too high you overflow terminal clients buffers i think -#define QUEUE_SIZE 1024 // Serial port interface // -#define MS_CTS 0x01 -#define MS_DSR 0x02 -#define MS_RI 0x04 -#define MS_DCD 0x08 - -#define MC_DTR 0x1 -#define MC_RTS 0x2 - - -class CFifo { -public: - CFifo(Bitu _size) { - size=_size; - pos=used=0; - data=new Bit8u[size]; - } - ~CFifo() { - delete[] data; - } - INLINE Bitu left(void) { - return size-used; - } - INLINE Bitu inuse(void) { - return used; - } - void clear(void) { - used=pos=0; - } - bool isFull() { - return (used >= size); - } - void addb(Bit8u _val) { - assert(used=size) where-=size; - data[where]=_val; - used++; - } - void adds(Bit8u * _str,Bitu _len) { - assert((used+_len)<=size); - Bitu where=pos+used; - used+=_len; - while (_len--) { - if (where>=size) where-=size; - data[where++]=*_str++; - } - } - Bit8u getb(void) { - if (!used) return data[pos]; - Bitu where=pos; - if (++pos>=size) pos-=size; - used--; - return data[where]; - } - void gets(Bit8u * _str,Bitu _len) { - assert(used>=_len); - used-=_len; - while (_len--) { - *_str++=data[pos]; - if (++pos>=size) pos-=size; - } - } -private: - Bit8u * data; - Bitu size,pos,used; -}; - class CSerial { public: - CSerial() { - - } - // Constructor takes base port (0x3f0, 0x2f0, 0x2e0, etc.), IRQ, and initial bps // - CSerial (Bit16u initbase, Bit8u initirq, Bit32u initbps); + // 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); + + TIMER_TickHandler TimerHnd; virtual ~CSerial(); + + IO_ReadHandleObject ReadHandler[8]; + IO_WriteHandleObject WriteHandler[8]; - void write_reg(Bitu reg, Bitu val); - Bitu read_reg(Bitu reg); - - void SetModemStatus(Bit8u status); - virtual bool CanRecv(void)=0; - virtual bool CanSend(void)=0; - virtual void Send(Bit8u val)=0; - virtual Bit8u Recv(Bit8u val)=0; - virtual void Timer(void); - - void checkint(void); - + void Timer(void); + virtual void Timer2(void)=0; + Bitu base; Bitu irq; - Bitu bps; - Bit8u mctrl; + + bool CSerial::getDTR(); + bool CSerial::getRTS(); - CFifo *rqueue; - CFifo *tqueue; + bool CSerial::getRI(); + bool CSerial::getCD(); + bool CSerial::getDSR(); + bool CSerial::getCTS(); + + void CSerial::setRI(bool value); + void CSerial::setDSR(bool value); + void CSerial::setCD(bool value); + void CSerial::setCTS(bool value); + + void CSerial::Write_THR(Bit8u data); + Bitu CSerial::Read_RHR(); + Bitu CSerial::Read_IER(); + void CSerial::Write_IER(Bit8u data); + Bitu CSerial::Read_ISR(); + Bitu CSerial::Read_LCR(); + void CSerial::Write_LCR(Bit8u data); + Bitu CSerial::Read_MCR(); + void CSerial::Write_MCR(Bit8u data); + Bitu CSerial::Read_LSR(); + + // Really old hardware seems to have the delta part of this register writable + void CSerial::Write_MSR(Bit8u data); + + Bitu CSerial::Read_MSR(); + Bitu CSerial::Read_SPR(); + void CSerial::Write_SPR(Bit8u data); + void CSerial::Write_reserved(Bit8u data, Bit8u address); + + // If a byte comes from wherever(loopback or real port or maybe + // that softmodem thingy), put it in here. + void CSerial::receiveByte(Bit8u data); + + // If an error was received, put it here (in LSR register format) + void CSerial::receiveError(Bit8u errorword); + + // connected device checks, if port can receive data: + bool CSerial::CanReceiveByte(); + + // When done sending, notify here + void CSerial::ByteTransmitted(); + + // Virtual app has read the received data + virtual void RXBufferEmpty()=0; + + // real transmit + virtual void transmitByte(Bit8u val)=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; + + // CSerial requests an update of the input lines + virtual void updateMSR()=0; + + // after update request, or some "real" changes, + // modify MSR here + void CSerial::changeMSR(Bit8u data); // make public + + void CSerial::Init_Registers(Bit32u initbps, + Bit8u bytesize, const char* parity, Bit8u stopbits); private: - void UpdateBaudrate(void); - bool FIFOenabled; - Bit8u FIFOsize; - bool dotxint; - - Bit8u scratch; - Bit8u dlab; - Bit8u divisor_lsb; - Bit8u divisor_msb; - Bit8u local_loopback; - Bit8u iir; - Bit8u ier; - Bit8u mstatus; - Bit8u linectrl; - Bit8u errors; - IO_ReadHandleObject ReadHandler[9]; - IO_WriteHandleObject WriteHandler[9]; + // I used this spec: http://www.exar.com/products/st16c450v420.pdf + + void CSerial::changeMSR_Loopback(Bit8u data); + + void CSerial::WriteRealIER(Bit8u data); + // reason for an interrupt has occured - functions triggers interrupt + // if it is enabled and no higher-priority irq pending + void CSerial::rise(Bit8u priority); + + // clears the pending interrupt + void CSerial::clear(Bit8u priority); + + #define ERROR_PRIORITY 4 // overrun, parity error, frame error, break + #define RX_PRIORITY 1 // a byte has been received + #define TX_PRIORITY 2 // tx buffer has become empty + #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 + + Bit8u RHR; // r Receive Holding Register, also LSB of Divisor Latch (r/w) + #define RHR_OFFSET 0 + // Data: whole byte + + Bit8u THR; // w Transmit Holding Register + #define THR_OFFSET 0 + // Data: whole byte + + Bit8u IER; // r/w Interrupt Enable Register, also MSB of Divisor Latch (r/w) + #define IER_OFFSET 1 + // Data: + // bit0 receive holding register + // bit1 transmit holding register + // bit2 receive line status interrupt + // bit3 modem status interrupt + + #define RHR_INT_Enable_MASK 0x1 + #define THR_INT_Enable_MASK 0x2 + #define Receive_Line_INT_Enable_MASK 0x4 + #define Modem_Status_INT_Enable_MASK 0x8 + + Bit8u ISR; // r Interrupt Status Register + #define ISR_OFFSET 2 + + #define ISR_CLEAR_VAL 0x1 + #define ISR_ERROR_VAL 0x6 + #define ISR_RX_VAL 0x4 + #define ISR_TX_VAL 0x2 + #define ISR_MSR_VAL 0x0 +public: + Bit8u LCR; // r/w Line Control Register +private: + #define LCR_OFFSET 3 + // bit0: word length bit0 + // bit1: word length bit1 + // bit2: stop bits + // bit3: parity enable + // bit4: even parity + // bit5: set parity + // bit6: set break + // bit7: divisor latch enable + + + #define LCR_BREAK_MASK 0x40 + #define LCR_DIVISOR_Enable_MASK 0x80 + #define LCR_PORTCONFIG_MASK 0x3F + + #define LCR_PARITY_NONE 0x0 + #define LCR_PARITY_ODD 0x8 + #define LCR_PARITY_EVEN 0x18 + #define LCR_PARITY_MARK 0x28 + #define LCR_PARITY_SPACE 0x38 + + #define LCR_DATABITS_5 0x0 + #define LCR_DATABITS_6 0x1 + #define LCR_DATABITS_7 0x2 + #define LCR_DATABITS_8 0x3 + + #define LCR_STOPBITS_1 0x0 + #define LCR_STOPBITS_MORE_THAN_1 0x4 + + Bit8u MCR; // r/w Modem Control Register + #define MCR_OFFSET 4 + // bit0: DTR + // bit1: RTS + // bit2: OP1 + // bit3: OP2 + // 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 + + Bit8u LSR; // r Line Status Register + #define LSR_OFFSET 5 + + #define LSR_RX_DATA_READY_MASK 0x1 + #define LSR_OVERRUN_ERROR_MASK 0x2 + #define LSR_PARITY_ERROR_MASK 0x4 + #define LSR_FRAMING_ERROR_MASK 0x8 + #define LSR_RX_BREAK_MASK 0x10 + #define LSR_TX_HOLDING_EMPTY_MASK 0x20 + #define LSR_TX_EMPTY_MASK 0x40 + + #define LSR_ERROR_MASK 0x1e + + + Bit8u MSR; // r Modem Status Register + #define MSR_OFFSET 6 + // bit0: deltaCTS + // bit1: deltaDSR + // bit2: deltaRI + // bit3: deltaCD + // bit4: CTS + // bit5: DSR + // bit6: RI + // bit7: CD + + #define MSR_delta_MASK 0xf + #define MSR_LINE_MASK 0xf0 + + #define MSR_dCTS_MASK 0x1 + #define MSR_dDSR_MASK 0x2 + #define MSR_dRI_MASK 0x4 + #define MSR_dCD_MASK 0x8 + #define MSR_CTS_MASK 0x10 + #define MSR_DSR_MASK 0x20 + #define MSR_RI_MASK 0x40 + #define MSR_CD_MASK 0x80 + + Bit8u SPR; // r/w Scratchpad Register + #define SPR_OFFSET 7 + + + // For loopback purposes... + bool loopback_pending; + Bit8u loopback_data; + void transmitLoopbackByte(Bit8u val); + + // 16C550 (FIFO) + // TODO + //Bit8u FCR; // FIFO Control Register + }; -#include - -typedef std::list CSerialList; -typedef std::list::iterator CSerial_it; - -extern CSerialList seriallist; +#define COM1_BASE 0x3f8 +#define COM2_BASE 0x2f8 +#define COM3_BASE 0x3e8 +#define COM4_BASE 0x2e8 #endif + diff --git a/src/Makefile.am b/src/Makefile.am index 24279b30..c58e2e4f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,5 +6,5 @@ bin_PROGRAMS = dosbox dosbox_SOURCES = dosbox.cpp dosbox_LDADD = cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware/libhardware.a gui/libgui.a \ - ints/libints.a misc/libmisc.a shell/libshell.a + ints/libints.a misc/libmisc.a shell/libshell.a hardware/serialport/libserial.a diff --git a/src/dosbox.cpp b/src/dosbox.cpp index 8d865c43..4cc4c2d1 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.85 2005-06-13 14:48:00 qbix79 Exp $ */ +/* $Id: dosbox.cpp,v 1.86 2005-07-30 14:41:31 qbix79 Exp $ */ #include #include @@ -80,17 +80,11 @@ void TANDYSOUND_Init(Section*); void DISNEY_Init(Section*); void SERIAL_Init(Section*); -#if C_MODEM -void MODEM_Init(Section*); -#endif #if C_IPX void IPX_Init(Section*); #endif -#if C_DIRECTSERIAL -void DIRECTSERIAL_Init(Section* sec); -#endif void SID_Init(Section* sec); void PIC_Init(Section*); @@ -218,7 +212,6 @@ void DOSBOX_Init(void) { secprop->AddInitFunction(&PROGRAMS_Init); secprop->AddInitFunction(&TIMER_Init);//done secprop->AddInitFunction(&CMOS_Init);//done - secprop->AddInitFunction(&SERIAL_Init); //done MSG_Add("DOSBOX_CONFIGFILE_HELP", "language -- Select another language file.\n" @@ -351,10 +344,6 @@ void DOSBOX_Init(void) { "disney -- Enable Disney Sound Source emulation.\n" ); secprop=control->AddSection_prop("bios",&BIOS_Init,false);//done - secprop->AddInitFunction(&INT10_Init); - secprop->AddInitFunction(&MOUSE_Init); //Must be after int10 as it uses CurMode - secprop->AddInitFunction(&JOYSTICK_Init); - secprop->Add_string("joysticktype","2axis"); MSG_Add("BIOS_CONFIGFILE_HELP", "joysticktype -- Type of joystick to emulate: none, 2axis, 4axis,\n" " fcs (Thrustmaster) ,ch (CH Flightstick).\n" @@ -362,6 +351,27 @@ void DOSBOX_Init(void) { " 2axis is the default and supports two joysticks.\n" ); + secprop->AddInitFunction(&INT10_Init); + secprop->AddInitFunction(&MOUSE_Init); //Must be after int10 as it uses CurMode + secprop->AddInitFunction(&JOYSTICK_Init); + secprop->Add_string("joysticktype","2axis"); + + // had to rename these to serial due to conflicts in config + secprop=control->AddSection_prop("serial",&SERIAL_Init,true); + secprop->Add_string("serial1","dummy"); + secprop->Add_string("serial2","dummy"); + secprop->Add_string("serial3","disabled"); + 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" + " 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" + " for modem: listenport (optional).\n" + ); + /* All the DOS Related stuff, which will eventually start up in the shell */ //TODO Maybe combine most of the dos stuff in one section like ems,xms secprop=control->AddSection_prop("dos",&DOS_Init,false);//done @@ -375,39 +385,8 @@ void DOSBOX_Init(void) { ); // Mscdex secprop->AddInitFunction(&MSCDEX_Init); -#if C_MODEM - secprop=control->AddSection_prop("modem",&MODEM_Init,true);//done - secprop->Add_bool("modem",false); - secprop->Add_hex("comport",2); - secprop->Add_int("listenport",23); - - MSG_Add("MODEM_CONFIGFILE_HELP", - "modem -- Enable virtual modem emulation.\n" - "comport -- COM Port modem is connected to.\n" - "listenport -- TCP Port the modem listens on for incoming connections.\n" - ); -#endif -#if C_DIRECTSERIAL - secprop=control->AddSection_prop("directserial",&DIRECTSERIAL_Init); - secprop->Add_bool("directserial", false); - secprop->Add_hex("comport",1); - secprop->Add_string("realport", "COM1"); - secprop->Add_int("defaultbps", 1200); - secprop->Add_string("parity", "N"); // Could be N, E, O - secprop->Add_int("bytesize", 8); // Could be 5 to 8 - secprop->Add_int("stopbit", 1); // Could be 1 or 2 - MSG_Add("DIRECTSERIAL_CONFIGFILE_HELP", - "directserial -- Enable serial passthrough support.\n" - "comport -- COM Port inside DOSBox.\n" - "realport -- COM Port on the Host.\n" - "defaultbps -- Default BPS.\n" - "parity -- Parity of the packets. This can be N, E or O.\n" - "bytesize -- Size of each packet. This can be 5 or 8.\n" - "stopbit -- The number of stopbits. This can be 1 or 2.\n" - ); -#endif #if C_IPX - secprop=control->AddSection_prop("ipx",&IPX_Init); + secprop=control->AddSection_prop("ipx",&IPX_Init,true); secprop->Add_bool("ipx", false); MSG_Add("IPX_CONFIGFILE_HELP", "ipx -- Enable ipx over UDP/IP emulation.\n" diff --git a/src/hardware/Makefile.am b/src/hardware/Makefile.am index f51dc2a0..60657aaa 100644 --- a/src/hardware/Makefile.am +++ b/src/hardware/Makefile.am @@ -1,5 +1,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include +SUBDIRS = serialport + EXTRA_DIST = fmopl.c fmopl.h ymf262.h ymf262.c noinst_LIBRARIES = libhardware.a @@ -8,9 +10,6 @@ libhardware_a_SOURCES = adlib.cpp dma.cpp gameblaster.cpp hardware.cpp iohandler memory.cpp mixer.cpp pcspeaker.cpp pic.cpp sblaster.cpp tandy_sound.cpp timer.cpp \ vga.cpp vga_attr.cpp vga_crtc.cpp vga_dac.cpp vga_draw.cpp vga_gfx.cpp vga_other.cpp \ vga_memory.cpp vga_misc.cpp vga_seq.cpp vga_xga.cpp cmos.cpp disney.cpp \ - gus.cpp mpu401.cpp serialport.cpp softmodem.cpp ipx.cpp ipxserver.cpp \ - directserial_win32.cpp - - + gus.cpp mpu401.cpp ipx.cpp ipxserver.cpp diff --git a/src/hardware/directserial_win32.cpp b/src/hardware/directserial_win32.cpp deleted file mode 100644 index a04c0ef1..00000000 --- a/src/hardware/directserial_win32.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2002-2005 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_win32.cpp,v 1.5 2005-02-10 10:21:08 qbix79 Exp $ */ - -#include "dosbox.h" - -#if C_DIRECTSERIAL - -/* Windows version */ -#if defined (WIN32) - -#include -#include -#include - -#include "setup.h" -#include "serialport.h" - -// Win32 related headers -#include - -/* This is a serial passthrough class. Its amazingly simple to */ -/* write now that the serial ports themselves were abstracted out */ - - -class CDirectSerial : public CSerial { -public: - HANDLE hCom; - DCB dcb; - BOOL fSuccess; - - CDirectSerial(char * realPort, Bit16u baseAddr, Bit8u initIrq, Bit32u initBps, Bit16u bytesize, char *parity, Bit16u stopbits ) : CSerial(baseAddr, initIrq, initBps) { - LOG_MSG("Opening Windows serial port"); - hCom = CreateFile(realPort, 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 - NULL // hTemplate must be NULL for comm devices - ); - - if (hCom == INVALID_HANDLE_VALUE) - { - LOG_MSG("CreateFile failed with error %d.\n", GetLastError()); - hCom = 0; - return; - } - - fSuccess = GetCommState(hCom, &dcb); - - if (!fSuccess) - { - // Handle the error. - LOG_MSG("GetCommState failed with error %d.\n", GetLastError()); - return; - } - - - dcb.BaudRate = initBps; // set the baud rate - dcb.ByteSize = bytesize; // data size, xmit, and rcv - if(parity[0] == 'N') - dcb.Parity = NOPARITY; // no parity bit - if(parity[1] == 'E') - dcb.Parity = EVENPARITY; // even parity bit - if(parity[2] == 'O') - dcb.Parity = ODDPARITY; // odd parity bit - - - if(stopbits == 1) - dcb.StopBits = ONESTOPBIT; // one stop bit - if(stopbits == 2) - dcb.StopBits = TWOSTOPBITS; // two stop bits - - fSuccess = SetCommState(hCom, &dcb); - - // Configure timeouts to effectively use polling - COMMTIMEOUTS ct; - ct.ReadIntervalTimeout = MAXDWORD; - ct.ReadTotalTimeoutConstant = 0; - ct.ReadTotalTimeoutMultiplier = 0; - ct.WriteTotalTimeoutConstant = 0; - ct.WriteTotalTimeoutMultiplier = 0; - SetCommTimeouts(hCom, &ct); - - } - - ~CDirectSerial() { - if(hCom != INVALID_HANDLE_VALUE) CloseHandle(hCom); - } - - bool CanRecv(void) { return true; } - bool CanSend(void) { return true; } - - void Send(Bit8u val) { tqueue->addb(val); } - - Bit8u Recv(Bit8u val) { return rqueue->getb(); } - - void updatestatus(void) { - Bit8u ms=0; - DWORD stat = 0; - GetCommModemStatus(hCom, &stat); - - //Check for data carrier - if(stat & MS_RLSD_ON) ms|=MS_DCD; - if (stat & MS_RING_ON) ms|=MS_RI; - if (stat & MS_DSR_ON) ms|=MS_DSR; - if (stat & MS_CTS_ON) ms|=MS_CTS; - SetModemStatus(ms); - } - - void Timer(void) { - DWORD dwRead; - Bit8u chRead; - - if (ReadFile(hCom, &chRead, 1, &dwRead, NULL)) { - if(dwRead != 0) { - if(!rqueue->isFull()) rqueue->addb(chRead); - } - } - - updatestatus(); - - Bit8u txval; - - Bitu tx_size=tqueue->inuse(); - while (tx_size--) { - txval = tqueue->getb(); - DWORD bytesWritten; - BOOL result; - result = WriteFile(hCom, &txval, 1, &bytesWritten, NULL); - if (!result) - { - // Handle the error. - LOG_MSG("WriteFile failed with error %d.\n", GetLastError()); - return; - } - - } - } - -}; - -CDirectSerial *cds; - -void DIRECTSERIAL_Init(Section* sec) { - - unsigned long args = 1; - Section_prop * section=static_cast(sec); - - - if(!section->Get_bool("directserial")) return; - - Bit16u comport = section->Get_int("comport"); - Bit32u bps = section->Get_int("defaultbps"); - switch (comport) { - case 1: - cds = new CDirectSerial((char *)section->Get_string("realport"), 0x3f0, 4, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit")); - break; - case 2: - cds = new CDirectSerial((char *)section->Get_string("realport"), 0x2f0, 3, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit")); - break; - case 3: - cds = new CDirectSerial((char *)section->Get_string("realport"), 0x3e0, 4, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit")); - break; - case 4: - cds = new CDirectSerial((char *)section->Get_string("realport"), 0x2e0, 3, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit")); - break; - default: - cds = new CDirectSerial((char *)section->Get_string("realport"), 0x3f0, 4, bps, section->Get_int("bytesize"), (char *)section->Get_string("parity"), section->Get_int("stopbit")); - break; - - } - - - - seriallist.push_back(cds); -} -#else /*linux and others oneday maybe */ - -#endif -#endif - diff --git a/src/hardware/serialport.cpp b/src/hardware/serialport.cpp deleted file mode 100644 index 5fce2f6e..00000000 --- a/src/hardware/serialport.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2002-2005 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 -#include - -#include "dosbox.h" -#include "inout.h" -#include "mixer.h" -#include "pic.h" -#include "setup.h" -#include "timer.h" -#include "math.h" -#include "regs.h" -#include "serialport.h" - -#define SERIALBASERATE 115200 -#define LOG_UART LOG_MSG - -CSerialList seriallist; - -void CSerial::UpdateBaudrate(void) { - Bitu divsize=(divisor_msb << 8) | divisor_lsb; - if (divsize) { - bps = SERIALBASERATE / divsize; - } -} - - -void CSerial::Timer(void) { - dotxint=true; - checkint(); -} - -void CSerial::checkint(void) { - /* Check which irq to activate */ - //TODO Check for line status changes, but we can't get errors anyway - //Since we only check once every millisecond, we just ignore the fifosize parameter - if ((ier & 0x1) && rqueue->inuse()) { -// LOG_MSG("RX IRQ with %d",rqueue->inuse()); - iir=0x4; - } else if ((ier & 0x2) && !tqueue->inuse() && dotxint) { - iir=0x2; - } else if ((ier & 0x8) && (mstatus & 0x0f)) { - iir=0x0; - } else { - iir=0x1; - PIC_DeActivateIRQ(irq); - return; - } - if (mctrl & 0x8) PIC_ActivateIRQ(irq); - else PIC_DeActivateIRQ(irq); -} - -void CSerial::write_reg(Bitu reg, Bitu val) { -// if (port!=9 && port!=8) LOG_MSG("Serial write %X val %x %c",port,val,val); - switch(reg) { - case 0x8: // Transmit holding buffer + Divisor LSB - if (dlab) { - divisor_lsb = val; - UpdateBaudrate(); - return; - } - if (local_loopback) rqueue->addb(val); - else tqueue->addb(val); - break; - case 0x9: // Interrupt enable register + Divisor MSB - if (dlab) { - divisor_msb = val; - UpdateBaudrate(); - return; - } else { - ier = val; - dotxint=true; - } - break; - case 0xa: // FIFO Control register - FIFOenabled = (val & 0x1) > 0; - if (val & 0x2) rqueue->clear(); //Clear receiver FIFO - if (val & 0x4) tqueue->clear(); //Clear transmit FIFO - if (val & 0x8) LOG(LOG_MISC,LOG_WARN)("UART:Enabled DMA mode"); - switch (val >> 6) { - case 0:FIFOsize = 1;break; - case 1:FIFOsize = 4;break; - case 2:FIFOsize = 8;break; - case 3:FIFOsize = 14;break; - } - break; - case 0xb: // Line control register - linectrl = val; - dlab = (val & 0x80); - break; - case 0xc: // Modem control register - mctrl=val; - local_loopback = (val & 0x10); - break; - case 0xf: // Scratch register - scratch = val; - break; - default: - LOG_UART("Modem: Write to 0x%x, with 0x%x '%c'\n", reg,val,val); - break; - } -} - -static void WriteSerial(Bitu port,Bitu val,Bitu iolen) { - Bitu check=port&~0xf; - for (CSerial_it it=seriallist.begin();it!=seriallist.end();it++){ - CSerial * serial=(*it); - if (check==serial->base) { - serial->write_reg(port&0xf,val); - return; - } - } -} - -static Bitu ReadSerial(Bitu port,Bitu iolen) { - Bitu check=port&~0xf; - - for (CSerial_it it=seriallist.begin();it!=seriallist.end();it++){ - CSerial * serial=(*it); - if (check==serial->base) { - - return serial->read_reg(port&0xf); - } - } - return 0; -} - -void SERIAL_Update(void) { - for (CSerial_it it=seriallist.begin();it!=seriallist.end();it++){ - CSerial * serial=(*it); - serial->Timer(); - } -} - - - -Bitu CSerial::read_reg(Bitu reg) { - Bitu retval; -// if (port!=0xd && port!=0xa) LOG_MSG("REad from port %x",reg); - switch(reg) { - case 0x8: // Receive buffer + Divisor LSB - if (dlab) { - return divisor_lsb ; - } else { - retval=rqueue->getb(); - //LOG_MSG("Received char %x %c",retval,retval); - checkint(); - return retval; - } - case 0x9: // Interrupt enable register + Divisor MSB - if (dlab) { - return divisor_msb ; - } else { - return ier; - } - case 0xa: // Interrupt identification register - retval=iir; - if (iir==2) { - dotxint=false; - iir=1; - } -// LOG_MSG("Read iir %d after %d",retval,iir); - return retval | ((FIFOenabled) ? (3 << 6) : 0); - case 0xb: // Line control register - return linectrl; - case 0xC: // Modem control register - return mctrl; - case 0xD: // Line status register - retval = 0x40; - if (!tqueue->inuse()) retval|=0x20; - if (rqueue->inuse()) retval|= 1; -// LOG_MSG("Read from line status %x",retval); - return retval; - case 0xE: // modem status register - retval=mstatus; - mstatus&=0xf0; - checkint(); - return retval; - case 0xF: // Scratch register - return scratch; - default: - //LOG_DEBUG("Modem: Read from 0x%x\n", port); - break; - } - return 0x00; -} - - -void CSerial::SetModemStatus(Bit8u status) { - status&=0xf; - Bit8u oldstatus=mstatus >> 4; - if (oldstatus ^ status ) { - mstatus=(mstatus & 0xf) | status << 4; - mstatus|=(oldstatus ^ status) & ((status & 0x4) | (0xf-0x4)); - } -} - - -CSerial::CSerial (Bit16u initbase, Bit8u initirq, Bit32u initbps) { - - Bitu i; - Bit16u initdiv; - - base=initbase; - irq=initirq; - bps=initbps; - - local_loopback = 0; - ier = 0; - iir = 1; - - FIFOenabled = false; - FIFOsize = 1; - dlab = 0; - mstatus = 0; - - initdiv = SERIALBASERATE / bps; - UpdateBaudrate(); - - for (i=0;i<=8;i++) { - WriteHandler[i].Install(base+i+8,WriteSerial,IO_MB); - ReadHandler[i].Install(base+i+8,ReadSerial,IO_MB); - } - - rqueue=new CFifo(QUEUE_SIZE); - tqueue=new CFifo(QUEUE_SIZE); -}; - -CSerial::~CSerial(void) -{ - //Remove pointer in list if present - for (CSerial_it it=seriallist.begin();it!=seriallist.end();) - if(this==*it) it=seriallist.erase(it); else it++; - - delete rqueue; - delete tqueue; - -}; - -class SERIALPORTS:public Module_base{ -public: - SERIALPORTS(Section* configuration):Module_base(configuration){ - TIMER_AddTickHandler(&SERIAL_Update); - } - ~SERIALPORTS(){ - TIMER_DelTickHandler(&SERIAL_Update); - } -}; - -static SERIALPORTS* test; - -void SERIAL_Destroy(Section* sec){ - delete test; -} - -void SERIAL_Init(Section* sec) { - test = new SERIALPORTS(sec); - sec->AddDestroyFunction(&SERIAL_Destroy,false); -} diff --git a/src/hardware/serialport/Makefile.am b/src/hardware/serialport/Makefile.am new file mode 100644 index 00000000..dd3633d7 --- /dev/null +++ b/src/hardware/serialport/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_LIBRARIES = libserial.a + +libserial_a_SOURCES = directserial_win32.cpp directserial_win32.h \ + serialdummy.cpp serialdummy.h serialport.cpp \ + softmodem.cpp softmodem.h diff --git a/src/hardware/serialport/directserial_win32.cpp b/src/hardware/serialport/directserial_win32.cpp new file mode 100644 index 00000000..8bf84e9b --- /dev/null +++ b/src/hardware/serialport/directserial_win32.cpp @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2002-2005 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_win32.cpp,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */ + +#include "dosbox.h" + +#if C_DIRECTSERIAL + +/* Windows version */ +#if defined (WIN32) + +#include "serialport.h" +#include "directserial_win32.h" + +// Win32 related headers +#include + +/* 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) { + InstallationSuccessful = false; + 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 + NULL, // no security attributes + OPEN_EXISTING, // must use OPEN_EXISTING + 0, // non overlapped I/O + NULL // hTemplate must be NULL for comm devices + ); + + if (hCom == INVALID_HANDLE_VALUE) { + int error = GetLastError (); + LOG_MSG ("Serial port \"%s\" could not be opened.", realPort); + if (error == 2) { + LOG_MSG ("The specified port does not exist."); + } else if (error == 5) { + LOG_MSG ("The specified port is already in use."); + } else { + LOG_MSG ("Windows error %d occurred.", error); + } + + hCom = 0; + return; + } + + fSuccess = GetCommState (hCom, &dcb); + + if (!fSuccess) { + // Handle the error. + LOG_MSG ("GetCommState failed with error %d.\n", GetLastError ()); + hCom = 0; + return; + } + // Configure timeouts to effectively use polling + COMMTIMEOUTS ct; + ct.ReadIntervalTimeout = MAXDWORD; + ct.ReadTotalTimeoutConstant = 0; + ct.ReadTotalTimeoutMultiplier = 0; + ct.WriteTotalTimeoutConstant = 0; + ct.WriteTotalTimeoutMultiplier = 0; + SetCommTimeouts (hCom, &ct); + + CSerial::Init_Registers (initBps, bytesize, parity, stopbits); + InstallationSuccessful = true; + //LOG_MSG("InstSuccess"); +} + +CDirectSerial::~CDirectSerial () { + if (hCom != INVALID_HANDLE_VALUE) + CloseHandle (hCom); +} + +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); + } + } + } + // 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); + } +} + +/*****************************************************************************/ +/* 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) { + Bit8u parity = 0; + Bit8u bytelength = 0; + Bit16u baudrate = 0; + + // baud + baudrate = dlm; + baudrate = baudrate << 8; + baudrate |= dll; + if (baudrate <= 0x1) + dcb.BaudRate = CBR_115200; + else if (baudrate <= 0x2) + dcb.BaudRate = CBR_57600; + else if (baudrate <= 0x3) + dcb.BaudRate = CBR_38400; + else if (baudrate <= 0x6) + dcb.BaudRate = CBR_19200; + else if (baudrate <= 0xc) + dcb.BaudRate = CBR_9600; + else if (baudrate <= 0x18) + dcb.BaudRate = CBR_4800; + else if (baudrate <= 0x30) + dcb.BaudRate = CBR_2400; + else if (baudrate <= 0x60) + dcb.BaudRate = CBR_1200; + else if (baudrate <= 0xc0) + dcb.BaudRate = CBR_600; + else if (baudrate <= 0x180) + dcb.BaudRate = CBR_300; + else if (baudrate <= 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 + + // byte length + bytelength = lcr & 0x3; + bytelength += 5; + dcb.ByteSize = bytelength; + + // parity + parity = lcr & 0x38; + parity = parity >> 3; + switch (parity) { + case 0x1: + dcb.Parity = ODDPARITY; + break; + case 0x3: + dcb.Parity = EVENPARITY; + break; + case 0x5: + dcb.Parity = MARKPARITY; + break; + case 0x7: + dcb.Parity = SPACEPARITY; + break; + default: + dcb.Parity = NOPARITY; + break; + } + + // stopbits + if (lcr & 0x4) { + if (bytelength == 5) + dcb.StopBits = ONE5STOPBITS; + else + dcb.StopBits = TWOSTOPBITS; + } else { + 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); +} + +void CDirectSerial::updateMSR () { + Bit8u newmsr = 0; + DWORD dptr = 0; + + if (!GetCommModemStatus (hCom, &dptr)) { +#ifdef SERIALPORT_DEBUGMSG + LOG_MSG ("Serial port at %x: GetCommModemStatus failed!", base); +#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); +} + +void CDirectSerial::transmitByte (Bit8u val) { + // 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(); + } +} + +/*****************************************************************************/ +/* 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); +} + +/*****************************************************************************/ +/* updateModemControlLines(mcr) sets DTR and RTS. **/ +/*****************************************************************************/ +void CDirectSerial::updateModemControlLines ( /*Bit8u mcr */ ) { + bool change = false; + + /*** 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 (); +} + + +#else /*linux and others oneday maybe */ + +#endif +#endif diff --git a/src/hardware/serialport/directserial_win32.h b/src/hardware/serialport/directserial_win32.h new file mode 100644 index 00000000..e2622803 --- /dev/null +++ b/src/hardware/serialport/directserial_win32.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2002-2005 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_win32.h,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */ + +// include guard +#ifndef DOSBOX_DIRECTSERIAL_WIN32_H +#define DOSBOX_DIRECTSERIAL_WIN32_H + +#include "dosbox.h" + +#if C_DIRECTSERIAL +#ifdef WIN32 + + + +#define DIRECTSERIAL_AVAILIBLE +#include "serialport.h" +#include + +class CDirectSerial : public CSerial { +public: + HANDLE hCom; + 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 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 updateMSR(); + void transmitByte(Bit8u val); + void setBreak(bool value); + void updateModemControlLines(/*Bit8u mcr*/); + void Timer2(void); + + +}; + +#endif // WIN32 +#endif // C_DIRECTSERIAL +#endif // include guard diff --git a/src/hardware/serialport/serialdummy.cpp b/src/hardware/serialport/serialdummy.cpp new file mode 100644 index 00000000..24af9fdc --- /dev/null +++ b/src/hardware/serialport/serialdummy.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2002-2005 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: serialdummy.cpp,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */ + +#include "dosbox.h" + +#include "setup.h" +#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() { +} + +void CSerialDummy::RXBufferEmpty() { +// no external buffer, not used here +} + +/*****************************************************************************/ +/* 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) { + //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) { + ByteTransmitted(); + //LOG_MSG("UART 0x%x: TX 0x%x", base,val); +} + +/*****************************************************************************/ +/* setBreak(val) switches break on or off **/ +/*****************************************************************************/ + +void CSerialDummy::setBreak(bool value) { + //LOG_MSG("UART 0x%x: Break toggeled: %d", base, value); +} + +/*****************************************************************************/ +/* updateModemControlLines(mcr) sets DTR and RTS. **/ +/*****************************************************************************/ +void CSerialDummy::updateModemControlLines(/*Bit8u mcr*/) { +} + +void CSerialDummy::Timer2(void) { +} + diff --git a/src/hardware/serialport/serialdummy.h b/src/hardware/serialport/serialdummy.h new file mode 100644 index 00000000..0306a4ff --- /dev/null +++ b/src/hardware/serialport/serialdummy.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2002-2005 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: serialdummy.h,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */ + +#ifndef INCLUDEGUARD_SERIALDUMMY_H +#define INCLUDEGUARD_SERIALDUMMY_H + +#include "serialport.h" + +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(); + bool CanRecv(void); + bool CanSend(void); + void RXBufferEmpty(); + void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr); + void updateMSR(); + void transmitByte(Bit8u val); + void setBreak(bool value); + void updateModemControlLines(/*Bit8u mcr*/); + void Timer2(void); +}; + +#endif // INCLUDEGUARD diff --git a/src/hardware/serialport/serialport.cpp b/src/hardware/serialport/serialport.cpp new file mode 100644 index 00000000..c67d87cf --- /dev/null +++ b/src/hardware/serialport/serialport.cpp @@ -0,0 +1,1089 @@ +/* + * Copyright (C) 2002-2005 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: serialport.cpp,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */ + +#include +#include + +#include "dosbox.h" + +#include "support.h" +#include "inout.h" +#include "pic.h" +#include "setup.h" +#include "timer.h" + +#include "serialport.h" +#include "directserial_win32.h" +#include "serialdummy.h" +#include "softmodem.h" + +#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}; + +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 ;) +} + +// 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); +#endif + loopback_pending = false; + receiveByte (loopback_data); + ByteTransmitted (); + } + Timer2 (); +} + +/*****************************************************************************/ +/* Interrupt control routines **/ +/*****************************************************************************/ +void CSerial::rise (Bit8u priority) { + //LOG_UART("Serial port at %x: Rise priority 0x%x", base, priority); + waiting_interrupts |= priority; + WriteRealIER (IER); +} + +// 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); + waiting_interrupts &= (~priority); + WriteRealIER (IER); +} + +void CSerial::WriteRealIER (Bit8u data) { +#ifdef SERIALPORT_DEBUGMSG + LOG_UART ("Serial port at %x: write IER, value 0x%x", base, data); +#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; + + 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); +#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*/ +} + +/*****************************************************************************/ +/* Can a byte be received? **/ +/*****************************************************************************/ +bool CSerial::CanReceiveByte() { + return (LSR & LSR_RX_DATA_READY_MASK) == 0; +} + +/*****************************************************************************/ +/* A byte was received **/ +/*****************************************************************************/ +void CSerial::receiveByte (Bit8u data) { +#ifdef SERIALPORT_DEBUGMSG + LOG_UART ("Serial port at %x: byte received: %d", base, data); +#endif + + if (LSR & LSR_RX_DATA_READY_MASK) { // Overrun error ;o + LOG_UART ("Serial port at %x: RX Overrun!", base, data); + LSR |= LSR_OVERRUN_ERROR_MASK; + rise (ERROR_PRIORITY); + } else { + RHR = data; + LSR |= LSR_RX_DATA_READY_MASK; + rise (RX_PRIORITY); + } +} + +/*****************************************************************************/ +/* A line error was received **/ +/*****************************************************************************/ +void CSerial::receiveError (Bit8u errorword) { + LSR |= errorword; + + rise (ERROR_PRIORITY); +} + +/*****************************************************************************/ +/* 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; + } + rise (TX_PRIORITY); +} + +/*****************************************************************************/ +/* Transmit Holding Register, also LSB of Divisor Latch (r/w) **/ +/*****************************************************************************/ +void CSerial::Write_THR (Bit8u 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); + } 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 + 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 + } + } +} + + +/*****************************************************************************/ +/* 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 { + 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; + } +} + +/*****************************************************************************/ +/* Interrupt Enable Register, also MSB of Divisor Latch (r/w) **/ +/*****************************************************************************/ +// 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; + } +} + +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); + } else { +#ifdef SERIALPORT_DEBUGMSG + LOG_UART ("Serial port at %x: Write to IER value 0x%x", base, data); +#endif + WriteRealIER (data); + } +} + +/*****************************************************************************/ +/* Interrupt Status Register (r) **/ +/*****************************************************************************/ +// modified by: +// - incoming interrupts +// - loopback mode +Bitu CSerial::Read_ISR () { + 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 + return retval; +} + +/*****************************************************************************/ +/* Line Control Register (r/w) **/ +/*****************************************************************************/ +// signal decoder configuration: +// - parity, stopbits, word length +// - send break +// - switch between RHR/THR and baud rate registers +// 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 + 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); + } + 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); +#endif + } +} + +/*****************************************************************************/ +/* Modem Control Register (r/w) **/ +/*****************************************************************************/ +// Set levels of RTS and DTR, as well as loopback-mode. +// 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; +} + +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!!! + + if (MCR & MCR_LOOPBACK_Enable_MASK) + if (data & MCR_LOOPBACK_Enable_MASK) { // was on, is now on + Bit8u param = 0; + + // 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 + } 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; + + changeMSR_Loopback (par); + + // go back to empty state + LSR &= (LSR_TX_EMPTY_MASK | LSR_TX_HOLDING_EMPTY_MASK); + + } else { + MCR = data; + updateModemControlLines (); + // loopback is off + } + } + MCR = data; +} + +/*****************************************************************************/ +/* Line Status Register (r) **/ +/*****************************************************************************/ +// errors, tx registers status, rx register status +// modified by: +// - event from real serial port +// - loopback +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; +} + +/*****************************************************************************/ +/* Modem Status Register (r) **/ +/*****************************************************************************/ +// Contains status of the control input lines (CD, RI, DSR, CTS) and +// their "deltas": if level changed since last read delta = 1. +// modified by: +// - real values +// - write operation to MCR in loopback mode +Bitu CSerial::Read_MSR () { + Bit8u retval; + if (!(MCR & MCR_LOOPBACK_Enable_MASK)) { + updateMSR (); + } + retval = MSR; + 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; +} + +/*****************************************************************************/ +/* Scratchpad Register (r/w) **/ +/*****************************************************************************/ +// 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; +} + +/*****************************************************************************/ +/* 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; +} + +/*****************************************************************************/ +/* MCR Access: returns cirquit state as boolean. **/ +/*****************************************************************************/ +bool CSerial::getDTR () { + return (MCR & MCR_DTR_MASK) != 0; +} + +bool CSerial::getRTS () { + return (MCR & MCR_RTS_MASK) != 0; +} + +/*****************************************************************************/ +/* MSR Access **/ +/*****************************************************************************/ +bool CSerial::getRI () { + return (MSR & MSR_RI_MASK) != 0; +} + +bool CSerial::getCD () { + return (MSR & MSR_CD_MASK) != 0; +} + +bool CSerial::getDSR () { + return (MSR & MSR_DSR_MASK) != 0; +} + +bool CSerial::getCTS () { + return (MSR & MSR_CTS_MASK) != 0; +} + +// 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); + } + //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); + } + //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); + } + //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); + } + //else no change +} + +/*****************************************************************************/ +/* Initialisation **/ +/*****************************************************************************/ +void CSerial::Init_Registers (Bit32u initbps, Bit8u bytesize, + const char *parity, Bit8u stopbits) { + Bit8u lcrresult = 0; + Bit16u baudresult = 0; + + RHR = 0; + THR = 0; + IER = 0; + ISR = 0x1; + LCR = 0; + MCR = 0; + LSR = 0x60; + MSR = 0; + + SPR = 0xFF; + + DLL = 0x0; + DLM = 0x0; + + pending_interrupts = 0x0; + current_priority = 0x0; + waiting_interrupts = 0x0; + loopback_pending = false; + + + // make lcr: byte size, parity, stopbits, baudrate + + if (bytesize == 5) + lcrresult |= LCR_DATABITS_5; + else if (bytesize == 6) + lcrresult |= LCR_DATABITS_6; + else if (bytesize == 7) + lcrresult |= LCR_DATABITS_7; + 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 + lcrresult |= LCR_PARITY_NONE; + + if (stopbits == 2) + lcrresult |= LCR_STOPBITS_MORE_THAN_1; + else + lcrresult |= LCR_STOPBITS_1; + + // baudrate + + if (initbps > 0) + baudresult = (Bit16u) (115200 / initbps); + else + baudresult = 12; // = 9600 baud + + Write_LCR (LCR_DIVISOR_Enable_MASK); + Write_THR ((Bit8u) baudresult & 0xff); + Write_IER ((Bit8u) (baudresult >> 8)); + Write_LCR (lcrresult); +} + +CSerial::CSerial(IO_ReadHandler * rh, IO_WriteHandler * wh, TIMER_TickHandler th, + Bit16u initbase, Bit8u initirq, Bit32u initbps, Bit8u bytesize, + const char *parity, Bit8u stopbits) { + base = initbase; + irq = initirq; + TimerHnd = th; // for destructor + TIMER_AddTickHandler(TimerHnd); + + for (Bitu i = 0; i <= 7; i++) { + WriteHandler[i].Install (i + base, wh, IO_MB); + ReadHandler[i].Install (i + base, rh, IO_MB); + } +}; + +CSerial::~CSerial(void) { + TIMER_DelTickHandler(TimerHnd); +}; + +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 + return false; + } + 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[]); + +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] = { + 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; + + // 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]; + } + // 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; + } + } +#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); + } +#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 { + *serialPortObjects[i] = NULL; + biosParameter[i] = 0; + } + } + + delete [] configstrings[0];delete [] configstrings[1]; + delete [] configstrings[2];delete [] configstrings[3]; + BIOS_SetComPorts (biosParameter); + } + + ~SERIALPORTS () { + for (Bitu i = 0; i < 4; i++) + if (*serialPortObjects[i]) { + delete *(serialPortObjects[i]); + *(serialPortObjects[i]) = 0; + } + } +}; + +static SERIALPORTS *testSerialPortsBaseclass; + +void SERIAL_Destroy (Section * sec) { + delete testSerialPortsBaseclass; + testSerialPortsBaseclass = NULL; +} + +void SERIAL_Init (Section * sec) { + // should never happen + if (testSerialPortsBaseclass) delete testSerialPortsBaseclass; + testSerialPortsBaseclass = new SERIALPORTS (sec); + sec->AddDestroyFunction (&SERIAL_Destroy, true); +} diff --git a/src/hardware/serialport/softmodem.cpp b/src/hardware/serialport/softmodem.cpp new file mode 100644 index 00000000..7b9ca4b5 --- /dev/null +++ b/src/hardware/serialport/softmodem.cpp @@ -0,0 +1,826 @@ +/* + * Copyright (C) 2002-2005 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: softmodem.cpp,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */ + +#include "dosbox.h" + +#if C_MODEM + +#include +#include +#include + +#include "SDL_net.h" + +#include "support.h" +#include "timer.h" +#include "serialport.h" +#include "softmodem.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; + + 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); + + //cmdpos = 0; + + //plusinc = 0; + //incomingsocket = 0; +// answermode = false; + //memset(®,0,sizeof(reg)); + //cmdpause = 0; + //echo = true; + //doresponse = true; + //numericresponse = false; + + /* 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); + + Reset(); + //EnterIdleState(); + CSerial::Init_Registers(initBps,bytesize,parity,stopbits); + } + + CSerialModem::~CSerialModem() { + if(socket) { + SDLNet_TCP_DelSocket(socketset,socket); + SDLNet_TCP_Close(socket); + } + + if(listensocket) SDLNet_TCP_Close(listensocket); + if(socketset) SDLNet_FreeSocketSet(socketset); + + delete rqueue; + delete tqueue; + } + +void CSerialModem::SendLine(const char *line) { + rqueue->addb(0xd); + rqueue->addb(0xa); + rqueue->adds((Bit8u *)line,strlen(line)); + rqueue->addb(0xd); + rqueue->addb(0xa); +} + +// only for numbers < 1000... +void CSerialModem::SendNumber(Bitu val) { + rqueue->addb(0xd); + rqueue->addb(0xa); + + rqueue->addb(val/100+'0'); + val = val%100; + rqueue->addb(val/10+'0'); + val = val%10; + rqueue->addb(val+'0'); + + rqueue->addb(0xd); + rqueue->addb(0xa); +} + +void CSerialModem::SendRes(ResTypes response) { + char * string;Bitu /*char **/ code; + switch (response) + { + case ResNONE: return; + case ResOK: string="OK"; code=0; break; + case ResERROR: string="ERROR"; code=4; break; + case ResRING: string="RING"; code=2; break; + case ResNODIALTONE: string="NO DIALTONE"; code=6; break; + case ResNOCARRIER: string="NO CARRIER" ;code=3; break; + case ResCONNECT: string="CONNECT 57600"; code=1; break; + } + + if(doresponse!=1) { + if(doresponse==2 && (response==ResRING || + response == ResCONNECT || response==ResNOCARRIER)) return; + if(numericresponse) + //{ + SendNumber(code); + // rqueue->addb(*code+'0'); + // rqueue->addb(0xd);rqueue->addb(0xa); + //} + else + //{ + SendLine(string); + //rqueue->addb(0xd);rqueue->addb(0xa); + //rqueue->adds((Bit8u *)string,strlen(string)); + //rqueue->addb(0xd);rqueue->addb(0xa); + //} + //if(CSerial::CanReceiveByte()) // very fast response + // if(rqueue->inuse() && CSerial::getRTS()) + // { Bit8u rbyte =rqueue->getb(); + // CSerial::receiveByte(rbyte); + // LOG_MSG("Modem: sending byte %2x back to UART2",rbyte); + // } + + LOG_MSG("Modem response: %s", string); + } +} + +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); + //if (!socket) + +} + +bool CSerialModem::Dial(char * host) { + char* helper; + // scan for and remove spaces; weird bug: with leading spaces in the string, + // SDLNet_ResolveHost will return no error but not work anyway (win) + while(host[0]==' ') host++; + helper=host; + helper+=strlen(host); + while(helper[0]==' ') { + helper[0]=0; + helper--; + } + + /* Scan host for port */ + Bit16u port; + char * hasport=strrchr(host,':'); + if (hasport) { + *hasport++=0; + port=(Bit16u)atoi(hasport); + } + else port=MODEM_DEFAULT_PORT; + /* 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); + EnterIdleState(); + return false; + } +} + +void CSerialModem::AcceptIncomingCall(void) { +// assert(!socket); + socket=incomingsocket; + SDLNet_TCP_AddSocket(socketset,socket); + incomingsocket = 0; + EnterConnectedState(); +} + +Bitu CSerialModem::ScanNumber(char * & scan) { + Bitu ret=0; + while (char c=*scan) { + if (c>='0' && c<='9') { + ret*=10; + ret+=c-'0'; + scan++; + } else break; + } + return ret; +} + +void CSerialModem::Reset(){ + EnterIdleState(); + cmdpos = 0; + cmdbuf[0]=0; + oldDTRstate = getDTR(); + + plusinc = 0; + incomingsocket = 0; + //answermode = false; // no autoanswer + memset(®,0,sizeof(reg)); + reg[MREG_AUTOANSWER_COUNT]=0; // no autoanswer + reg[MREG_RING_COUNT] = 1; + reg[MREG_ESCAPE_CHAR]='+'; + reg[MREG_CR_CHAR]='\r'; + reg[MREG_LF_CHAR]='\n'; + reg[MREG_BACKSPACE_CHAR]='\b'; + + cmdpause = 0; + echo = true; + doresponse = 0; + numericresponse = false; + + /* Default to direct null modem connection. Telnet mode interprets IAC codes */ + telnetmode = false; +} + +void CSerialModem::EnterIdleState(void){ + connected=false; + ringing=false; + txbufferfull=false; + + if(socket) { // clear current socket + SDLNet_TCP_DelSocket(socketset,socket); + SDLNet_TCP_Close(socket); + socket=0; + } + if(incomingsocket) { // clear current incoming socket + SDLNet_TCP_DelSocket(socketset,incomingsocket); + SDLNet_TCP_Close(incomingsocket); + } + // get rid of everything + while(incomingsocket=SDLNet_TCP_Accept(listensocket)) { + SDLNet_TCP_DelSocket(socketset,incomingsocket); + SDLNet_TCP_Close(incomingsocket); + } + incomingsocket=0; + + commandmode = true; + CSerial::setCD(false); + CSerial::setRI(false); + tqueue->clear(); +} + +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(); + } +} + +void CSerialModem::DoCommand() { + cmdbuf[cmdpos] = 0; + cmdpos = 0; //Reset for next command + upcase(cmdbuf); + LOG_MSG("Command sent to modem: ->%s<-\n", cmdbuf); + /* Check for empty line, stops dialing and autoanswer */ + if (!cmdbuf[0]) { + reg[0]=0; // autoanswer off + //answermode = false; + return;//goto ret_none; + // } + //else { + //MIXER_Enable(mhd.chan,false); + // dialing = false; + // SendRes(ResNOCARRIER); + // goto ret_none; + // } + } + /* AT command set interpretation */ + + if ((cmdbuf[0] != 'A') || (cmdbuf[1] != 'T')) { + SendRes(ResERROR); + return;//goto ret_error; + } + + if (strstr(cmdbuf,"NET0")) { + telnetmode = false; + SendRes(ResOK); + return; + } + else if (strstr(cmdbuf,"NET1")) { + telnetmode = true; + SendRes(ResOK); + return; + } + + /* Check for dial command */ + if(strncmp(cmdbuf,"ATD3",3)==0) { + char * foundstr=&cmdbuf[3]; + if (*foundstr=='T' || *foundstr=='P') foundstr++; + /* Small protection against empty line */ + if (!foundstr[0]) { + SendRes(ResERROR);//goto ret_error; + return; + } + if (strlen(foundstr) >= 12) { + // Check if supplied parameter only consists of digits + bool isNum = true; + for (Bitu i=0; i '9') + isNum = false; + if (isNum) { + // Parameter is a number with at least 12 digits => this cannot be a valid IP/name + // Transform by adding dots + char buffer[128]; + Bitu j = 0; + for (Bitu i=0; i12) + buffer[j++] = ':'; + } + buffer[j] = 0; + foundstr = buffer; + } + } + Dial(foundstr); + return;//goto ret_none; + } + char * scanbuf; + scanbuf=&cmdbuf[2]; + char chr; + Bitu num; + while (chr=*scanbuf++) { + switch (chr) { + case 'I': //Some strings about firmware + switch (num=ScanNumber(scanbuf)) { + case 3:SendLine("DosBox Emulated Modem Firmware V1.00");break; + case 4:SendLine("Modem compiled for DosBox version " VERSION);break; + };break; + case 'E': //Echo on/off + switch (num=ScanNumber(scanbuf)) { + case 0:echo = false;break; + case 1:echo = true;break; + };break; + case 'V': + switch (num=ScanNumber(scanbuf)) { + case 0:numericresponse = true;break; + case 1:numericresponse = false;break; + };break; + case 'H': //Hang up + switch (num=ScanNumber(scanbuf)) { + case 0: + if (connected) { + SendRes(ResNOCARRIER); + EnterIdleState(); + return;//goto ret_none; + } + //Else return ok + };break; + case 'O': //Return to data mode + switch (num=ScanNumber(scanbuf)) + { + case 0: + if (socket) { + commandmode = false; + return;//goto ret_none; + } else { + SendRes(ResERROR); + return;//goto ret_none; + } + };break; + case 'T': //Tone Dial + case 'P': //Pulse Dial + break; + case 'M': //Monitor + case 'L': //Volume + ScanNumber(scanbuf); + break; + case 'A': //Answer call + if (incomingsocket) { + AcceptIncomingCall(); + } else { + SendRes(ResERROR); + return;//goto ret_none; + } + return;//goto ret_none; + case 'Z': //Reset and load profiles + { + // scan the number away, if any + ScanNumber(scanbuf); + if (socket) SendRes(ResNOCARRIER); + Reset(); + break; + } + case ' ': //Space just skip + break; + case 'Q': // Response options + { // 0 = all on, 1 = all off, + // 2 = no ring and no connect/carrier in answermode + Bitu val = ScanNumber(scanbuf); + if(!(val>2)) { + doresponse=val; + break; + } else { + SendRes(ResERROR); + return; + } + } + case 'S': //Registers + { + Bitu index=ScanNumber(scanbuf); + if(index>=SREGS) { + SendRes(ResERROR); + return; //goto ret_none; + } + + while(scanbuf[0]==' ') scanbuf++; // skip spaces + + if(scanbuf[0]=='=') { // set register + scanbuf++; + while(scanbuf[0]==' ') scanbuf++; // skip spaces + Bitu val = ScanNumber(scanbuf); + reg[index]=val; + break; + } + else if(scanbuf[0]=='?') { // get register + SendNumber(reg[index]); + scanbuf++; + break; + } + //else LOG_MSG("print reg %d with %d",index,reg[index]); + } + break; + case '&': + { + if(scanbuf[0]!=0) { + char ch = scanbuf[0]; + //switch(scanbuf[0]) Maybe You want to implement it? + scanbuf++; + LOG_MSG("Modem: Unhandled command: &%c%d",ch,ScanNumber(scanbuf)); + } else { + SendRes(ResERROR); + return; + } + } + break; + + default: + LOG_MSG("Modem: Unhandled command: %c%d",chr,ScanNumber(scanbuf)); + } + } + +/* + } + + if (strstr(mhd.cmdbuf,"NET0")) + { + telnetmode = false; + } + if (strstr(mhd.cmdbuf,"NET1")) + { + telnetmode = true; + } + #endif*/ + //ret_ok: + SendRes(ResOK); + return; + //ret_error: + //SendRes(ResERROR); + //ret_none: + // return; + + } + +void CSerialModem::TelnetEmulation(Bit8u * data, Bitu size) { + Bitu i; + Bit8u c; + for(i=0;i250) { + /* 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(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; + } + + 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 == 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()) + if(rqueue->inuse() && CSerial::getRTS()) { + Bit8u rbyte = rqueue->getb(); + //LOG_MSG("Modem: sending byte %2x back to UART3",rbyte); + CSerial::receiveByte(rbyte); + } + /* 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 + CSerial::setCTS(true); // buffer will get 'emptier', new data can be received + while (/*tx_size--*/tqueue->inuse()) { + txval = tqueue->getb(); + if (commandmode) { + if (echo) { + rqueue->addb(txval); + //LOG_MSG("Echo back to queue: %x",txval); + } + if (txval==0xa) continue; //Real modem doesn't seem to skip this? + else if (txval==0x8 && (cmdpos > 0)) --cmdpos; // backspace + else if (txval==0xd) DoCommand(); // return + else if (txval != '+') { + if(cmdpos<99) { + cmdbuf[cmdpos] = txval; + cmdpos++; + } + } + } + else {// + character + /* 1000 ticks have passed, can check for pause command */ + if (cmdpause > 1000) { + if(txval ==/* '+')*/reg[MREG_ESCAPE_CHAR]) + { + plusinc++; + if(plusinc>=3) { + LOG_MSG("Modem: Entering command mode(escape sequence)"); + commandmode = true; + SendRes(ResOK); + plusinc = 0; + } + sendbyte=false; + } else { + plusinc=0; + } + //If not a special pause command, should go for bigger blocks to send + } + tmpbuf[txbuffersize] = txval; + txbuffersize++; + } + } // while loop + + if (socket && sendbyte && txbuffersize) { + // down here it saves a lot of network traffic + SDLNet_TCP_Send(socket, 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(); + } + } + } + /* Check for incoming calls */ + if (!connected && !incomingsocket && listensocket) { + incomingsocket = SDLNet_TCP_Accept(listensocket); + if (incomingsocket) { + SDLNet_TCP_AddSocket(listensocketset, incomingsocket); + + if(!CSerial::getDTR()) { + // accept no calls with DTR off; TODO: AT &Dn + EnterIdleState(); + } else { + ringing=true; + SendRes(ResRING); + CSerial::setRI(!CSerial::getRI()); + //MIXER_Enable(mhd.chan,true); + ringtimer = 3000; + reg[1] = 0; //Reset ring counter reg + } + } + } + if (ringing) { + if (ringtimer <= 0) { + reg[1]++; + if ((reg[0]>0) && (reg[0]>=reg[1])) { + AcceptIncomingCall(); + return; + } + SendRes(ResRING); + CSerial::setRI(!CSerial::getRI()); + + //MIXER_Enable(mhd.chan,true); + ringtimer = 3000; + } + --ringtimer; + } +} + + +//TODO +void CSerialModem::RXBufferEmpty() { + // see if rqueue has some more bytes + if(rqueue->inuse() && CSerial::getRTS()){ + Bit8u rbyte = rqueue->getb(); + //LOG_MSG("Modem: sending byte %2x back to UART1",rbyte); + CSerial::receiveByte(rbyte); + + //CSerial::receiveByte(rqueue->getb()); + } +} + +void CSerialModem::transmitByte(Bit8u val) { + //LOG_MSG("MODEM: Byte %x to be transmitted",val); + if(tqueue->left()) { + tqueue->addb(val); + if(!tqueue->left()) { + CSerial::setCTS(false); + txbufferfull=true; + } + } else LOG_MSG("MODEM: TX Buffer overflow!"); + CSerial::ByteTransmitted(); +} + +void CSerialModem::updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr) { +// nothing to do here right? +} + +void CSerialModem::updateMSR() { + // think it is not needed +} + +void CSerialModem::setBreak(bool) { + // TODO: handle this +} + +void CSerialModem::updateModemControlLines(/*Bit8u mcr*/) { + //if(!txbufferfull) + //{ + // if(CSerial::getRTS()) CSerial::setCTS(true); + // else CSerial::setCTS(false); + //} + + // If DTR goes low, hang up. + if(connected) + if(oldDTRstate) + if(!getDTR()) { + SendRes(ResNOCARRIER); + EnterIdleState(); + LOG_MSG("Modem: Hang up due to dropped DTR."); + } + + oldDTRstate = getDTR(); +} + + +#endif + diff --git a/src/hardware/serialport/softmodem.h b/src/hardware/serialport/softmodem.h new file mode 100644 index 00000000..80ce6134 --- /dev/null +++ b/src/hardware/serialport/softmodem.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2002-2005 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: softmodem.h,v 1.1 2005-07-30 14:41:31 qbix79 Exp $ */ + +#ifndef DOSBOX_SERIALMODEM_H +#define DOSBOX_SERIALMODEM_H + +#include "dosbox.h" +#include "SDL_net.h" +#include "serialport.h" + +#define MODEMSPD 57600 +#define SREGS 100 + +//If it's too high you overflow terminal clients buffers i think +#define MODEM_BUFFER_QUEUE_SIZE 1024 + +#define MODEM_DEFAULT_PORT 23 + +enum ResTypes { + ResNONE, + ResOK,ResERROR, + ResCONNECT,ResRING, + ResBUSY,ResNODIALTONE,ResNOCARRIER, +}; + +#define TEL_CLIENT 0 +#define TEL_SERVER 1 + +class CFifo { +public: + CFifo(Bitu _size) { + size=_size; + pos=used=0; + data=new Bit8u[size]; + } + ~CFifo() { + delete[] data; + } + INLINE Bitu left(void) { + return size-used; + } + INLINE Bitu inuse(void) { + return used; + } + void clear(void) { + used=pos=0; + } + + void addb(Bit8u _val) { + if(used>=size) { + LOG_MSG("FIFO Overflow!"); + return; + } + //assert(used=size) where-=size; + data[where]=_val; + //LOG_MSG("+%x",_val); + used++; + } + void adds(Bit8u * _str,Bitu _len) { + if((used+_len)>size) { + LOG_MSG("FIFO Overflow!"); + return; + } + + //assert((used+_len)<=size); + Bitu where=pos+used; + used+=_len; + while (_len--) { + if (where>=size) where-=size; + //LOG_MSG("+%x",*_str); + data[where++]=*_str++; + } + } + Bit8u getb(void) { + if (!used) { + LOG_MSG("MODEM: FIFO UNDERFLOW!"); + return data[pos]; + } + Bitu where=pos; + if (++pos>=size) pos-=size; + used--; + //LOG_MSG("-%x",data[where]); + return data[where]; + } + void gets(Bit8u * _str,Bitu _len) { + if (!used) { + LOG_MSG("MODEM: FIFO UNDERFLOW!"); + return; + } + //assert(used>=_len); + used-=_len; + while (_len--) { + //LOG_MSG("-%x",data[pos]); + *_str++=data[pos]; + if (++pos>=size) pos-=size; + } + } +private: + Bit8u * data; + Bitu size,pos,used; + //Bit8u tmpbuf[MODEM_BUFFER_QUEUE_SIZE]; + + +}; +#define MREG_AUTOANSWER_COUNT 0 +#define MREG_RING_COUNT 1 +#define MREG_ESCAPE_CHAR 2 +#define MREG_CR_CHAR 3 +#define MREG_LF_CHAR 4 +#define MREG_BACKSPACE_CHAR 5 + + +class CSerialModem : public CSerial { +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(); + + void Reset(); + + void SendLine(const char *line); + void SendRes(ResTypes response); + void SendNumber(Bitu val); + + void EnterIdleState(); + void EnterConnectedState(); + + void openConnection(void); + bool Dial(char * host); + void AcceptIncomingCall(void); + Bitu ScanNumber(char * & scan); + + void DoCommand(); + + void MC_Changed(Bitu new_mc); + + void TelnetEmulation(Bit8u * data, Bitu size); + + void Timer2(void); + + void RXBufferEmpty(); + + void transmitByte(Bit8u val); + void updatePortConfig(Bit8u dll, Bit8u dlm, Bit8u lcr); + void updateMSR(); + + void setBreak(bool); + + void updateModemControlLines(/*Bit8u mcr*/); + +protected: + char cmdbuf[255]; + bool commandmode; // true: interpret input as commands + //bool answermode; + bool echo; // local echo on or off + + bool oldDTRstate; + bool ringing; + //bool response; + bool numericresponse; // true: send control response as number. + // false: send text (i.e. NO DIALTONE) + bool telnetmode; // true: process IAC commands. + + bool connected; + bool txbufferfull; + Bitu doresponse; + + + + Bitu cmdpause; + Bits ringtimer; + Bits ringcount; + Bitu plusinc; + Bitu cmdpos; + + + //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; + + struct { + bool binary[2]; + bool echo[2]; + bool supressGA[2]; + bool timingMark[2]; + + bool inIAC; + bool recCommand; + Bit8u command; + } telClient; + struct { + bool active; + double f1, f2; + Bitu len,pos; + char str[256]; + } dial; +}; + +#endif diff --git a/src/hardware/softmodem.cpp b/src/hardware/softmodem.cpp deleted file mode 100644 index 38d0486b..00000000 --- a/src/hardware/softmodem.cpp +++ /dev/null @@ -1,820 +0,0 @@ -/* - * Copyright (C) 2002-2005 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 -#include -#include -#include "SDL_net.h" - -#include "inout.h" -#include "mixer.h" -#include "pic.h" -#include "setup.h" -#include "programs.h" -#include "debug.h" -#include "timer.h" -#include "callback.h" -#include "math.h" -#include "regs.h" -#include "serialport.h" - -#define MODEMSPD 57600 -#define SREGS 100 - - -static Bit8u tmpbuf[QUEUE_SIZE]; - -struct ModemHd { - char cmdbuf[QUEUE_SIZE]; - bool commandmode; - bool answermode; - bool echo,response,numericresponse; - bool telnetmode; - Bitu cmdpause; - Bits ringtimer; - Bits ringcount; - Bitu plusinc; - Bitu cmdpos; - - Bit8u reg[SREGS]; - TCPsocket incomingsocket; - TCPsocket socket; - TCPsocket listensocket; - SDLNet_SocketSet socketset; - - IPaddress openip; - - Bitu comport; - Bitu listenport; - - char remotestr[4096]; - - bool dialing; - double f1, f2; - Bitu diallen; - Bitu dialpos; - char dialstr[256]; - // TODO: Re-enable dialtons - //MIXER_Channel * chan; -}; - -enum ResTypes { - ResNONE, - ResOK,ResERROR, - ResCONNECT,ResRING, - ResBUSY,ResNODIALTONE,ResNOCARRIER, -}; - -#define TEL_CLIENT 0 -#define TEL_SERVER 1 - -struct telnetClient { - bool binary[2]; - bool echo[2]; - bool supressGA[2]; - bool timingMark[2]; - - bool inIAC; - bool recCommand; - Bit8u command; -}; - - - -#if 1 - -static void toUpcase(char *buffer) { - Bitu i=0; - while (buffer[i] != 0) { - buffer[i] = toupper(buffer[i]); - i++; - } -} - - - -class CSerialModem : public CSerial { -public: - ModemHd mhd; - - CSerialModem(Bit16u baseAddr, Bit8u initIrq, Bit32u initBps, const char *remotestr = NULL, Bit16u lport = 27) - : CSerial(baseAddr, initIrq, initBps) - { - - - mhd.cmdpos = 0; - mhd.commandmode = true; - mhd.plusinc = 0; - mhd.incomingsocket = 0; - mhd.answermode = false; - memset(&mhd.reg,0,sizeof(mhd.reg)); - mhd.cmdpause = 0; - mhd.echo = true; - mhd.response = true; - mhd.numericresponse = false; - - /* Default to direct null modem connection. Telnet mode interprets IAC codes */ - mhd.telnetmode = false; - - /* Bind the modem to the correct serial port */ - //strcpy(mhd.remotestr, remotestr); - - /* Initialize the sockets and setup the listening port */ - mhd.socketset = SDLNet_AllocSocketSet(1); - if (!mhd.socketset) { - LOG_MSG("MODEM:Can't open socketset:%s",SDLNet_GetError()); - //TODO Should probably just exit - return; - } - mhd.socket=0; - mhd.listenport=lport; - if (mhd.listenport) { - IPaddress listen_ip; - SDLNet_ResolveHost(&listen_ip, NULL, mhd.listenport); - mhd.listensocket=SDLNet_TCP_Open(&listen_ip); - if (!mhd.listensocket) LOG_MSG("MODEM:Can't open listen port:%s",SDLNet_GetError()); - } else mhd.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); - - } - - ~CSerialModem(){ - if (mhd.socket) { - SDLNet_TCP_DelSocket(mhd.socketset,mhd.socket); - SDLNet_TCP_Close(mhd.socket); - } - if(mhd.listensocket) SDLNet_TCP_Close(mhd.listensocket); - if(mhd.socketset) SDLNet_FreeSocketSet(mhd.socketset); - } - - void SendLine(const char *line) { - rqueue->addb(0xd); - rqueue->addb(0xa); - rqueue->adds((Bit8u *)line,strlen(line)); - rqueue->addb(0xd); - rqueue->addb(0xa); - - } - - void SendRes(ResTypes response) { - char * string;char * code; - switch (response) { - case ResNONE: - return; - case ResOK:string="OK";code="0";break; - case ResERROR:string="ERROR";code="4";break; - case ResRING:string="RING";code="2"; - case ResNODIALTONE:string="NO DIALTONE";code="6";break; - case ResNOCARRIER:string="NO CARRIER";code="3";break; - case ResCONNECT:string="CONNECT 57600";code="1";break; - } - - rqueue->addb(0xd);rqueue->addb(0xa); - rqueue->adds((Bit8u *)string,strlen(string)); - rqueue->addb(0xd);rqueue->addb(0xa); - } - - void Send(Bit8u val) { - tqueue->addb(val); - } - - Bit8u Recv(Bit8u val) { - return rqueue->getb(); - - } - - void openConnection(void) { - if (mhd.socket) { - LOG_MSG("Huh? already connected"); - SDLNet_TCP_DelSocket(mhd.socketset,mhd.socket); - SDLNet_TCP_Close(mhd.socket); - } - mhd.socket = SDLNet_TCP_Open(&openip); - if (mhd.socket) { - SDLNet_TCP_AddSocket(mhd.socketset,mhd.socket); - SendRes(ResCONNECT); - mhd.commandmode = false; - memset(&telClient, 0, sizeof(telClient)); - updatemstatus(); - } else { - SendRes(ResNODIALTONE); - } - } - - void updatemstatus(void) { - Bit8u ms=0; - //Check for data carrier, a connection that is - if (mhd.incomingsocket) ms|=MS_RI; - if (mhd.socket) ms|=MS_DCD; - if (!mhd.commandmode) ms|=MS_DSR; - //Check for DTR reply with DSR - // if (cport->mctrl & MC_DTR) ms|=MS_DSR; - //Check for RTS reply with CTS - if (mctrl & MC_RTS) ms|=MS_CTS; - SetModemStatus(ms); - } - - bool Dial(char * host) { - /* Scan host for port */ - Bit16u port; - char * hasport=strrchr(host,':'); - if (hasport) { - *hasport++=0; - port=(Bit16u)atoi(hasport); - } else port=23; - /* Resolve host we're gonna dial */ - LOG_MSG("host %s port %x",host,port); - if (!SDLNet_ResolveHost(&openip,host,port)) { - openConnection(); - return true; - } else { - LOG_MSG("Failed to resolve host %s:%s",host,SDLNet_GetError()); - SendRes(ResNOCARRIER); - return false; - } - } - - void AcceptIncomingCall(void) { - assert(!mhd.socket); - mhd.socket=mhd.incomingsocket; - SDLNet_TCP_AddSocket(mhd.socketset,mhd.socket); - SendRes(ResCONNECT); - LOG_MSG("Connected!\n"); - - mhd.incomingsocket = 0; - mhd.commandmode = false; - } - - Bitu ScanNumber(char * & scan) { - Bitu ret=0; - while (char c=*scan) { - if (c>='0' && c<='9') { - ret*=10; - ret+=c-'0'; - scan++; - } else break; - } - return ret; - } - - - void HangUp(void) { - SendRes(ResNOCARRIER); - SDLNet_TCP_DelSocket(mhd.socketset,mhd.socket); - SDLNet_TCP_Close(mhd.socket); - mhd.socket=0; - mhd.commandmode = true; - updatemstatus(); - } - - void DoCommand() { - mhd.cmdbuf[mhd.cmdpos] = 0; - mhd.cmdpos = 0; //Reset for next command - toUpcase(mhd.cmdbuf); - LOG_MSG("Modem Sent Command: %s\n", mhd.cmdbuf); - /* Check for empty line, stops dialing and autoanswer */ - if (!mhd.cmdbuf[0]) { - if(!mhd.dialing) { - mhd.answermode = false; - goto ret_none; - } else { - //MIXER_Enable(mhd.chan,false); - mhd.dialing = false; - SendRes(ResNOCARRIER); - goto ret_none; - } - } - /* AT command set interpretation */ - if ((mhd.cmdbuf[0] != 'A') || (mhd.cmdbuf[1] != 'T')) goto ret_error; - /* Check for dial command */ - if(strncmp(mhd.cmdbuf,"ATD3",3)==0) { - char * foundstr=&mhd.cmdbuf[3]; - if (*foundstr=='T' || *foundstr=='P') foundstr++; - /* Small protection against empty line */ - if (!foundstr[0]) goto ret_error; - if (strlen(foundstr) >= 12){ - // Check if supplied parameter only consists of digits - bool isNum = true; - for (Bitu i=0; i '9') - isNum = false; - if (isNum) { - // Parameter is a number with at least 12 digits => this cannot be a valid IP/name - // Transform by adding dots - char buffer[128]; - Bitu j = 0; - for (Bitu i=0; i12) - buffer[j++] = ':'; - } - buffer[j] = 0; - foundstr = buffer; - } - } - Dial(foundstr); - goto ret_none; - } - char * scanbuf; - scanbuf=&mhd.cmdbuf[2];char chr;Bitu num; - while (chr=*scanbuf++) { - switch (chr) { - case 'I': //Some strings about firmware - switch (num=ScanNumber(scanbuf)) { - case 3:SendLine("DosBox Emulated Modem Firmware V1.00");break; - case 4:SendLine("Modem compiled for DosBox version " VERSION);break; - };break; - case 'E': //Echo on/off - switch (num=ScanNumber(scanbuf)) { - case 0:mhd.echo = false;break; - case 1:mhd.echo = true;break; - };break; - case 'V': - switch (num=ScanNumber(scanbuf)) { - case 0:mhd.numericresponse = true;break; - case 1:mhd.numericresponse = false;break; - };break; - case 'H': //Hang up - switch (num=ScanNumber(scanbuf)) { - case 0: - if (mhd.socket) { - HangUp(); - goto ret_none; - } - //Else return ok - };break; - case 'O': //Return to data mode - switch (num=ScanNumber(scanbuf)) { - case 0: - if (mhd.socket) { - mhd.commandmode = false; - goto ret_none; - } else { - goto ret_error; - } - };break; - case 'T': //Tone Dial - case 'P': //Pulse Dial - break; - case 'M': //Monitor - case 'L': //Volume - ScanNumber(scanbuf); - break; - case 'A': //Answer call - if (mhd.incomingsocket) { - AcceptIncomingCall(); - } else { - mhd.answermode = true; - } - goto ret_none; - case 'Z': //Reset and load profiles - num=ScanNumber(scanbuf); - break; - case ' ': //Space just skip - break; - case 'S': //Registers - { - Bitu index=ScanNumber(scanbuf); - bool hasequal=(*scanbuf == '='); - if (hasequal) scanbuf++; - Bitu val=ScanNumber(scanbuf); - if (index>=SREGS) goto ret_error; - if (hasequal) mhd.reg[index]=val; - else LOG_MSG("print reg %d with %d",index,mhd.reg[index]); - };break; - default: - LOG_MSG("Unhandled cmd %c%d",chr,ScanNumber(scanbuf)); - } - } - #if 0 - if (strstr(mhd.cmdbuf,"S0=1")) { - mhd.autoanswer = true; - } - if (strstr(mhd.cmdbuf,"S0=0")) { - mhd.autoanswer = false; - } - - if (strstr(mhd.cmdbuf,"NET0")) { - mhd.telnetmode = false; - } - if (strstr(mhd.cmdbuf,"NET1")) { - mhd.telnetmode = true; - } - #endif - LOG_MSG("Sending OK"); - SendRes(ResOK); - return; - ret_error: - LOG_MSG("Sending ERROR"); - SendRes(ResERROR); - ret_none: - return; - - } - - void MC_Changed(Bitu new_mc) { - LOG_MSG("New MC %x",new_mc ); - if (!(new_mc & 1) && mhd.socket) HangUp(); - updatemstatus(); - } - - void TelnetEmulation(Bit8u * data, Bitu size) { - Bitu i; - Bit8u c; - for(i=0;i250) { - /* 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(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; - } - - 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 == 0xff) { - telClient.inIAC = true; - continue; - } - rqueue->addb(c); - } - } - } - - void Timer(void) { - int result =0; - unsigned long args = 1; - bool sendbyte = true; - Bitu usesize; - Bit8u txval; - - - /* Check for eventual break command */ - if (!mhd.commandmode) mhd.cmdpause++; - /* Handle incoming data from serial port, read as much as available */ - Bitu tx_size=tqueue->inuse(); - while (tx_size--) { - txval = tqueue->getb(); - if (mhd.commandmode) { - if (mhd.echo) rqueue->addb(txval); - if (txval==0xa) continue; //Real modem doesn't seem to skip this? - else if (txval==0x8 && (mhd.cmdpos > 0)) --mhd.cmdpos; - else if (txval==0xd) DoCommand(); - else if (txval != '+') { - if(mhd.cmdpos 1000) { - if(txval == '+') { - mhd.plusinc++; - if(mhd.plusinc>=3) { - LOG_MSG("Entering command mode"); - mhd.commandmode = true; - SendRes(ResOK); - mhd.plusinc = 0; - } - sendbyte=false; - } else { - mhd.plusinc=0; - } - //If not a special pause command, should go for bigger blocks to send - } - - tmpbuf[0] = txval; - tmpbuf[1] = 0x0; - - if (mhd.socket && sendbyte) { - SDLNet_TCP_Send(mhd.socket, tmpbuf,1); - //TODO error testing - } - } - } - - SDLNet_CheckSockets(mhd.socketset,0); - /* Handle incoming to the serial port */ - if(!mhd.commandmode && mhd.socket) { - if(rqueue->left() && SDLNet_SocketReady(mhd.socket) && (mctrl & MC_RTS)) { - usesize = rqueue->left(); - if (usesize>16) usesize=16; - result = SDLNet_TCP_Recv(mhd.socket, tmpbuf, usesize); - if (result>0) { - if(mhd.telnetmode) { - /* Filter telnet commands */ - TelnetEmulation(tmpbuf, result); - } else { - - rqueue->adds(tmpbuf,result); - } - mhd.cmdpause = 0; - } else HangUp(); - } - } - /* Check for incoming calls */ - if (!mhd.socket && !mhd.incomingsocket && mhd.listensocket) { - mhd.incomingsocket = SDLNet_TCP_Accept(mhd.listensocket); - if (mhd.incomingsocket) { - mhd.diallen = 12000; - mhd.dialpos = 0; - SendRes(ResRING); - //MIXER_Enable(mhd.chan,true); - mhd.ringtimer = 3000; - mhd.reg[1] = 0; //Reset ring counter reg - } - } - if (mhd.incomingsocket) { - if (mhd.ringtimer <= 0) { - mhd.reg[1]++; - if (mhd.answermode || (mhd.reg[0]==mhd.reg[1])) { - AcceptIncomingCall(); - return; - } - SendRes(ResRING); - mhd.diallen = 12000; - mhd.dialpos = 0; - //MIXER_Enable(mhd.chan,true); - mhd.ringtimer = 3000; - } - --mhd.ringtimer; - } - updatemstatus(); - } - - bool CanSend(void) { - return true; - } - - bool CanRecv(void) { - if(rqueue->inuse()) { - return true; - } else { - return false; - } - } - - -protected: - char cmdbuf[QUEUE_SIZE]; - bool commandmode; - bool answermode; - bool echo; - bool telnetmode; - Bitu cmdpause; - Bits ringtimer; - Bits ringcount; - Bitu plusinc; - Bitu cmdpos; - - Bit8u reg[SREGS]; - IPaddress openip; - TCPsocket incomingsocket; - TCPsocket socket; - TCPsocket listensocket; - SDLNet_SocketSet socketset; - - struct { - bool binary[2]; - bool echo[2]; - bool supressGA[2]; - bool timingMark[2]; - - bool inIAC; - bool recCommand; - Bit8u command; - } telClient; - struct { - bool active; - double f1, f2; - Bitu len,pos; - char str[256]; - } dial; -}; - -#endif - - - -CSerialModem *csm; - - -static Bitu INT14_FOSSIL(void) { - switch (reg_ah) { - case 0x01: - // Serial - Write character to port - csm->tqueue->addb(reg_al); - reg_ah = csm->read_reg(0xd) & 0x7f; - break; - - case 0x02: - // FOSSIL - Receive character with wait - //LOG_MSG("FOSSIL: Calling get to receive character", reg_ah); - csm->mctrl |= MC_RTS; - while(!csm->rqueue->inuse()) { - // Wait for byte. Yes, I realize that this locks up DosBox, but - // it would an oldskool system as well. - csm->Timer(); - } - reg_al = csm->rqueue->getb(); - reg_ah = 0; - break; - case 0x03: - //LOG_MSG("FOSSIL: Calling get port status", reg_ah); - // Serial - Get port status - csm->mctrl |= MC_RTS; - reg_ah = csm->read_reg(0xd) ; // Line status - if(csm->rqueue->inuse()) reg_ah |= 0x1; - - reg_al = csm->read_reg(0xe); // Modem status - break; - default: - LOG_MSG("FOSSIL: Func 0x%x not handled", reg_ah); - break; - } - return CBRET_NONE; -} - - -class VIRTUAL_MODEM:public Module_base { -private: - CALLBACK_HandlerObject callback; -public: - VIRTUAL_MODEM(Section* configuration):Module_base(configuration){ - Section_prop * section=static_cast(configuration); - csm = NULL; - - if(!section->Get_bool("modem")) return; - - if(SDLNet_Init()==-1) { - LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError()); - return; - } - - if(!SDLNetInited) { - if(SDLNet_Init()==-1) { - LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError()); - return; - } - SDLNetInited = true; - } - - Bit16u comport = section->Get_int("comport"); - - - - switch (comport) { - case 1: - csm = new CSerialModem(0x3f0, 4, 57600, section->Get_string("remote"), section->Get_int("listenport")); - break; - case 2: - csm = new CSerialModem(0x2f0, 3, 57600, section->Get_string("remote"), section->Get_int("listenport")); - break; - case 3: - csm = new CSerialModem(0x3e0, 4, 57600, section->Get_string("remote"), section->Get_int("listenport")); - break; - case 4: - csm = new CSerialModem(0x2e0, 3, 57600, section->Get_string("remote"), section->Get_int("listenport")); - break; - default: - // Default to COM2 - csm = new CSerialModem(0x2f0, 3, 57600, section->Get_string("remote"), section->Get_int("listenport")); - break; - - } - - if(csm != NULL) seriallist.push_back(csm); - - //Enable FOSSIL support (GTERM, etc) - callback.Install(&INT14_FOSSIL,CB_IRET,"int14 fossil"); - callback.Set_RealVec(0x14); - } - ~VIRTUAL_MODEM(){ - if(!csm) return; - delete csm; - } -}; - -static VIRTUAL_MODEM* test; - -void MODEM_Destroy(Section* sec){ - delete test; -} - - -void MODEM_Init(Section* sec) { - test = new VIRTUAL_MODEM(sec); - sec->AddDestroyFunction(&MODEM_Destroy,true); -} - - -#endif diff --git a/src/ints/bios.cpp b/src/ints/bios.cpp index 9fdaf4da..69c32647 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.42 2005-06-21 18:47:25 qbix79 Exp $ */ +/* $Id: bios.cpp,v 1.43 2005-07-30 14:41:31 qbix79 Exp $ */ #include "dosbox.h" #include "mem.h" @@ -177,14 +177,179 @@ static Bitu INT17_Handler(void) { return CBRET_NONE; } -static Bitu INT14_Handler(void) { - switch (reg_ah) { +static Bitu INT14_Handler(void) +{ + 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); + return CBRET_NONE; + } + switch (reg_ah) + { case 0x00: /* Init port */ + // Parameters: + // AL: port parameters + // Return: + // AH: line status + // AL: modem status { - Bitu port=real_readw(0x40,reg_dx*2); + // set baud rate + Bitu baudrate = 9600; + Bit16u baudresult; + Bitu rawbaud=reg_al>>5; + + 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;} + + 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)); + + // set line parameters, disable divider access + IO_WriteB(port+3, reg_al&0x1F);//LCR + + // 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); + } + break; + case 0x01: /* Write character */ + // Parameters: + // AL: character + // Return: + // 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; + } + // 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); + } + break; + + case 0x02: /* Read character */ + { + if((IO_ReadB(port+5)&0x1)!=0) + { + reg_al=IO_ReadB(port); + } + 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); + } + 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); + } + 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); - LOG_MSG("AX %X DX %X",reg_ax,reg_dx); } break; default: @@ -429,7 +594,6 @@ private: CALLBACK_HandlerObject callback[10]; public: BIOS(Section* configuration):Module_base(configuration){ - MSG_Add("BIOS_CONFIGFILE_HELP","Nothing to setup yet!\n"); /* Clear the Bios Data Area */ for (Bit16u i=0;i<1024;i++) real_writeb(0x40,i,0); /* Setup all the interrupt handlers the bios controls */ @@ -495,15 +659,61 @@ public: callback[9].Set_RealVec(0x1); /* Setup some stuff in 0x40 bios segment */ - /* Test for parallel port */ - if (IO_Read(0x378)!=0xff) real_writew(0x40,0x08,0x378); - /* Test for serial port */ - Bitu index=0; - if (IO_Read(0x3fa)!=0xff) real_writew(0x40,(index++)*2,0x3f8); - if (IO_Read(0x2fa)!=0xff) real_writew(0x40,(index++)*2,0x2f8); - /* Setup equipment list */ - //1 Floppy, 2 serial and 1 parrallel - Bitu config=0x4400; + /* 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++; + } + + /* Setup equipment list */ + // look http://www.bioscentral.com/misc/bda.htm + + //Bitu config=0x4400; //1 Floppy, 2 serial and 1 parrallel + Bitu config = 0; + + // set number of parallel ports + // if(ppindex == 0) config |= 0x8000; // looks like 0 ports are not specified + //else if(ppindex == 1) config |= 0x0000; + if(ppindex == 2) config |= 0x4000; + else config |= 0xc000; // 3 ports #if (C_FPU) //FPU config|=0x2; @@ -534,6 +744,36 @@ 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); + } + } + // set equipment word + equipmentword = mem_readw(BIOS_CONFIGURATION); + equipmentword &= (~0x0E00); + equipmentword |= (portcount << 9); + mem_writew(BIOS_CONFIGURATION,equipmentword); +} + + static BIOS* test; void BIOS_Destroy(Section* sec){ diff --git a/visualc_net/dosbox.vcproj b/visualc_net/dosbox.vcproj index 7fec8a92..701ca121 100644 --- a/visualc_net/dosbox.vcproj +++ b/visualc_net/dosbox.vcproj @@ -404,9 +404,6 @@ - - @@ -440,12 +437,6 @@ - - - - @@ -514,6 +505,31 @@ RelativePath="..\src\hardware\tandy_sound.cpp"> + + + + + + + + + + + + + + + +