1
0
Fork 0

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
This commit is contained in:
Peter Veenstra 2005-07-30 14:41:31 +00:00
parent 4b6fd3c481
commit a5252330f7
19 changed files with 3329 additions and 1484 deletions

View file

@ -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

View file

@ -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

View file

@ -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 <assert.h>
// 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);
Bitu where=pos+used;
if (where>=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 <list>
typedef std::list<CSerial *> CSerialList;
typedef std::list<CSerial *>::iterator CSerial_it;
extern CSerialList seriallist;
#define COM1_BASE 0x3f8
#define COM2_BASE 0x2f8
#define COM3_BASE 0x3e8
#define COM4_BASE 0x2e8
#endif

View file

@ -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

View file

@ -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 <stdlib.h>
#include <stdarg.h>
@ -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"

View file

@ -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

View file

@ -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 <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "setup.h"
#include "serialport.h"
// Win32 related headers
#include <windows.h>
/* 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<Section_prop *>(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

View file

@ -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 <string.h>
#include <list>
#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);
}

View file

@ -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

View file

@ -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 <windows.h>
/* 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

View file

@ -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 <windows.h>
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

View file

@ -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) {
}

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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 <string.h>
#include <stdlib.h>
#include <ctype.h>
#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(&reg,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(&reg,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<strlen(foundstr); i++)
if (foundstr[i] < '0' || foundstr[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; i<strlen(foundstr); i++) {
buffer[j++] = foundstr[i];
// Add a dot after the third, sixth and ninth number
if (i == 2 || i == 5 || i == 8)
buffer[j++] = '.';
// If the string is longer than 12 digits, interpret the rest as port
if (i == 11 && strlen(foundstr)>12)
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;i<size;i++) {
c = data[i];
if(telClient.inIAC) {
if(telClient.recCommand) {
if((c != 0) && (c != 1) && (c != 3)) {
LOG_MSG("MODEM: Unrecognized option %d", c);
if(telClient.command>250) {
/* Reject anything we don't recognize */
tqueue->addb(0xff);
tqueue->addb(252);
tqueue->addb(c); /* We won't do crap! */
}
}
switch(telClient.command) {
case 251: /* Will */
if(c == 0) telClient.binary[TEL_SERVER] = true;
if(c == 1) telClient.echo[TEL_SERVER] = true;
if(c == 3) telClient.supressGA[TEL_SERVER] = true;
break;
case 252: /* Won't */
if(c == 0) telClient.binary[TEL_SERVER] = false;
if(c == 1) telClient.echo[TEL_SERVER] = false;
if(c == 3) telClient.supressGA[TEL_SERVER] = false;
break;
case 253: /* Do */
if(c == 0) {
telClient.binary[TEL_CLIENT] = true;
tqueue->addb(0xff);
tqueue->addb(251);
tqueue->addb(0); /* Will do binary transfer */
}
if(c == 1) {
telClient.echo[TEL_CLIENT] = false;
tqueue->addb(0xff);
tqueue->addb(252);
tqueue->addb(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

View file

@ -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);
Bitu where=pos+used;
if (where>=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

View file

@ -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 <stdarg.h>
#include <string.h>
#include <ctype.h>
#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<strlen(foundstr); i++)
if (foundstr[i] < '0' || foundstr[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; i<strlen(foundstr); i++) {
buffer[j++] = foundstr[i];
// Add a dot after the third, sixth and ninth number
if (i == 2 || i == 5 || i == 8)
buffer[j++] = '.';
// If the string is longer than 12 digits, interpret the rest as port
if (i == 11 && strlen(foundstr)>12)
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;i<size;i++) {
c = data[i];
if(telClient.inIAC) {
if(telClient.recCommand) {
if((c != 0) && (c != 1) && (c != 3)) {
LOG_MSG("MODEM: Unrecognized option %d", c);
if(telClient.command>250) {
/* Reject anything we don't recognize */
tqueue->addb(0xff);
tqueue->addb(252);
tqueue->addb(c); /* We won't do crap! */
}
}
switch(telClient.command) {
case 251: /* Will */
if(c == 0) telClient.binary[TEL_SERVER] = true;
if(c == 1) telClient.echo[TEL_SERVER] = true;
if(c == 3) telClient.supressGA[TEL_SERVER] = true;
break;
case 252: /* Won't */
if(c == 0) telClient.binary[TEL_SERVER] = false;
if(c == 1) telClient.echo[TEL_SERVER] = false;
if(c == 3) telClient.supressGA[TEL_SERVER] = false;
break;
case 253: /* Do */
if(c == 0) {
telClient.binary[TEL_CLIENT] = true;
tqueue->addb(0xff);
tqueue->addb(251);
tqueue->addb(0); /* Will do binary transfer */
}
if(c == 1) {
telClient.echo[TEL_CLIENT] = false;
tqueue->addb(0xff);
tqueue->addb(252);
tqueue->addb(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<QUEUE_SIZE) {
mhd.cmdbuf[mhd.cmdpos] = txval;
mhd.cmdpos++;
}
}
} else {
/* 1000 ticks have passed, can check for pause command */
if (mhd.cmdpause > 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<Section_prop *>(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

View file

@ -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){

View file

@ -404,9 +404,6 @@
<File
RelativePath="..\src\hardware\cmos.cpp">
</File>
<File
RelativePath="..\src\hardware\directserial_win32.cpp">
</File>
<File
RelativePath="..\src\hardware\dma.cpp">
</File>
@ -440,12 +437,6 @@
<File
RelativePath="..\src\hardware\pic.cpp">
</File>
<File
RelativePath="..\src\hardware\serialport.cpp">
</File>
<File
RelativePath="..\src\hardware\softmodem.cpp">
</File>
<File
RelativePath="..\src\hardware\timer.cpp">
</File>
@ -514,6 +505,31 @@
RelativePath="..\src\hardware\tandy_sound.cpp">
</File>
</Filter>
<Filter
Name="serialport"
Filter="">
<File
RelativePath="..\src\hardware\serialport\directserial_win32.cpp">
</File>
<File
RelativePath="..\src\hardware\serialport\directserial_win32.h">
</File>
<File
RelativePath="..\src\hardware\serialport\serialdummy.cpp">
</File>
<File
RelativePath="..\src\hardware\serialport\serialdummy.h">
</File>
<File
RelativePath="..\src\hardware\serialport\serialport.cpp">
</File>
<File
RelativePath="..\src\hardware\serialport\softmodem.cpp">
</File>
<File
RelativePath="..\src\hardware\serialport\softmodem.h">
</File>
</Filter>
</Filter>
<Filter
Name="gui"