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:
parent
4b6fd3c481
commit
a5252330f7
19 changed files with 3329 additions and 1484 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
7
src/hardware/serialport/Makefile.am
Normal file
7
src/hardware/serialport/Makefile.am
Normal 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
|
377
src/hardware/serialport/directserial_win32.cpp
Normal file
377
src/hardware/serialport/directserial_win32.cpp
Normal 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
|
84
src/hardware/serialport/directserial_win32.h
Normal file
84
src/hardware/serialport/directserial_win32.h
Normal 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
|
85
src/hardware/serialport/serialdummy.cpp
Normal file
85
src/hardware/serialport/serialdummy.cpp
Normal 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) {
|
||||
}
|
||||
|
54
src/hardware/serialport/serialdummy.h
Normal file
54
src/hardware/serialport/serialdummy.h
Normal 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
|
1089
src/hardware/serialport/serialport.cpp
Normal file
1089
src/hardware/serialport/serialport.cpp
Normal file
File diff suppressed because it is too large
Load diff
826
src/hardware/serialport/softmodem.cpp
Normal file
826
src/hardware/serialport/softmodem.cpp
Normal 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(®,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<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
|
||||
|
243
src/hardware/serialport/softmodem.h
Normal file
243
src/hardware/serialport/softmodem.h
Normal 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
|
|
@ -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
|
|
@ -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){
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue