Initial version of modem and serial port
Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@1084
This commit is contained in:
parent
c2f035784b
commit
2b61bd2759
3 changed files with 1211 additions and 0 deletions
135
include/serialport.h
Normal file
135
include/serialport.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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.
|
||||
*/
|
||||
|
||||
#if !defined __SERIALPORT_H
|
||||
#define __SERIALPORT_H
|
||||
|
||||
#include <dosbox.h>
|
||||
|
||||
//If it's too high you overflow terminal clients buffers i think
|
||||
#define FIFO_SIZE (1024)
|
||||
|
||||
// Serial port interface //
|
||||
|
||||
#define M_CTS 0x01
|
||||
#define M_DSR 0x02
|
||||
#define M_RI 0x04
|
||||
#define M_DCD 0x08
|
||||
|
||||
enum INT_TYPES {
|
||||
INT_MS,
|
||||
INT_TX,
|
||||
INT_RX,
|
||||
INT_RX_FIFO,
|
||||
INT_LS,
|
||||
INT_NONE,
|
||||
};
|
||||
|
||||
typedef void MControl_Handler(Bitu mc);
|
||||
|
||||
class CSerial {
|
||||
public:
|
||||
|
||||
// Constructor takes base port (0x3f0, 0x2f0, 0x2e0, etc.), IRQ, and initial bps //
|
||||
CSerial (Bit16u initbase, Bit8u initirq, Bit32u initbps);
|
||||
virtual ~CSerial();
|
||||
|
||||
// External port functions for IOHandlers //
|
||||
void write_port(Bit32u port, Bit8u val);
|
||||
Bit8u read_port(Bit32u port);
|
||||
|
||||
static void write_serial(Bit32u port,Bit8u val);
|
||||
static Bit8u read_serial(Bit32u port);
|
||||
|
||||
void SetMCHandler(MControl_Handler * mcontrol);
|
||||
|
||||
/* Allow the modem to change the modem status bits */
|
||||
void setmodemstatus(Bit8u status);
|
||||
Bit8u getmodemstatus(void);
|
||||
Bit8u getlinestatus(void);
|
||||
|
||||
void checkint(void);
|
||||
void raiseint(INT_TYPES type);
|
||||
void lowerint(INT_TYPES type);
|
||||
|
||||
/* Access to the receive fifo */
|
||||
Bitu rx_free();
|
||||
void rx_addb(Bit8u byte);
|
||||
void rx_adds(Bit8u * data,Bitu size);
|
||||
Bitu rx_size();
|
||||
Bit8u rx_readb(void);
|
||||
|
||||
/* Access to the transmit fifo */
|
||||
Bitu tx_free();
|
||||
void tx_addb(Bit8u byte);
|
||||
Bitu tx_size();
|
||||
Bit8u tx_readb(void);
|
||||
|
||||
|
||||
// These variables maintain the status of the serial port
|
||||
Bit16u base;
|
||||
Bit16u irq;
|
||||
Bit32u bps;
|
||||
|
||||
bool FIFOenabled;
|
||||
Bit16u FIFOsize;
|
||||
|
||||
private:
|
||||
|
||||
void setdivisor(Bit8u dmsb, Bit8u dlsb);
|
||||
void checkforirq(void);
|
||||
struct {
|
||||
Bitu used;
|
||||
Bitu pos;
|
||||
Bit8u data[FIFO_SIZE];
|
||||
} rx_fifo,tx_fifo;
|
||||
struct {
|
||||
Bitu requested;
|
||||
Bitu enabled;
|
||||
INT_TYPES active;
|
||||
} ints;
|
||||
|
||||
Bitu rx_lastread;
|
||||
Bit8u irq_pending;
|
||||
|
||||
Bit8u scratch;
|
||||
Bit8u dlab;
|
||||
Bit8u divisor_lsb;
|
||||
Bit8u divisor_msb;
|
||||
Bit8u wordlen;
|
||||
Bit8u dtr;
|
||||
Bit8u rts;
|
||||
Bit8u out1;
|
||||
Bit8u out2;
|
||||
Bit8u local_loopback;
|
||||
Bit8u linectrl;
|
||||
Bit8u intid;
|
||||
Bit8u ierval;
|
||||
Bit8u mstatus;
|
||||
|
||||
Bit8u txval;
|
||||
Bit8u timeout;
|
||||
|
||||
MControl_Handler * mc_handler;
|
||||
char remotestr[4096];
|
||||
};
|
||||
|
||||
// This function returns the CSerial objects for ports 1-4 //
|
||||
CSerial *getComport(Bitu portnum);
|
||||
|
||||
#endif
|
441
src/hardware/serialport.cpp
Normal file
441
src/hardware/serialport.cpp
Normal file
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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 "sdl_syswm.h"
|
||||
|
||||
#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 SERIALPORT_COUNT 2
|
||||
|
||||
#define LOG_UART LOG_MSG
|
||||
CSerial *serialports[SERIALPORT_COUNT];
|
||||
|
||||
|
||||
void CSerial::setdivisor(Bit8u dmsb, Bit8u dlsb) {
|
||||
Bitu divsize=(dmsb << 8) | dlsb;
|
||||
if (divsize!=0) {
|
||||
bps = SERIALBASERATE / divsize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSerial::checkint(void) {
|
||||
/* Find lowest priority interrupt to activate */
|
||||
Bitu i;
|
||||
for (i=0;i<INT_NONE;i++) {
|
||||
if (ints.requested & ints.enabled & (1 << i)) {
|
||||
PIC_ActivateIRQ(irq);
|
||||
ints.active=(INT_TYPES)i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Not a single interrupt schedulded, lower IRQ */
|
||||
PIC_DeActivateIRQ(irq);
|
||||
ints.active=INT_NONE;
|
||||
}
|
||||
|
||||
void CSerial::raiseint(INT_TYPES type) {
|
||||
// LOG_MSG("Raising int %X rx size %d tx size %d",type,rx_fifo.used,tx_fifo.used);
|
||||
ints.requested|=1 << type;
|
||||
checkint();
|
||||
}
|
||||
|
||||
void CSerial::lowerint(INT_TYPES type){
|
||||
ints.requested&=~(1 << type);
|
||||
checkint();
|
||||
}
|
||||
|
||||
|
||||
void CSerial::write_port(Bit32u port, Bit8u val) {
|
||||
|
||||
port-=base;
|
||||
// LOG_UART("Serial write %X val %x %c",port,val,val);
|
||||
switch(port) {
|
||||
case 0x8: // Transmit holding buffer + Divisor LSB
|
||||
if (dlab) {
|
||||
divisor_lsb = val;
|
||||
setdivisor(divisor_msb, divisor_lsb);
|
||||
return;
|
||||
}
|
||||
if (local_loopback) {
|
||||
rx_addb(val);
|
||||
} else tx_addb(val);
|
||||
break;
|
||||
case 0x9: // Interrupt enable register + Divisor MSB
|
||||
if (dlab) {
|
||||
divisor_msb = val;
|
||||
setdivisor(divisor_msb, divisor_lsb);
|
||||
return;
|
||||
}
|
||||
/* Only enable the FIFO interrupt by default */
|
||||
ints.enabled=1 << INT_RX_FIFO;
|
||||
if (val & 0x1) ints.enabled|=1 << INT_RX;
|
||||
if (val & 0x2) ints.enabled|=1 << INT_TX;
|
||||
if (val & 0x4) ints.enabled|=1 << INT_LS;
|
||||
if (val & 0x8) ints.enabled|=1 << INT_MS;
|
||||
ierval = val;
|
||||
checkint();
|
||||
break;
|
||||
case 0xa: // FIFO Control register
|
||||
FIFOenabled = false;
|
||||
if (val & 0x1) {
|
||||
// FIFOenabled = true;
|
||||
timeout = 0;
|
||||
}
|
||||
if (val & 0x2) { //Clear receiver FIFO
|
||||
rx_fifo.used=0;
|
||||
rx_fifo.pos=0;
|
||||
}
|
||||
if (val & 0x4) { //Clear transmit FIFO
|
||||
tx_fifo.used=0;
|
||||
tx_fifo.pos=0;
|
||||
}
|
||||
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;
|
||||
wordlen = (val & 0x3);
|
||||
dlab = (val & 0x80) > 0;
|
||||
break;
|
||||
case 0xc: // Modem control register
|
||||
dtr = val & 0x01;
|
||||
rts = (val & 0x02) > 0 ;
|
||||
out1 = (val & 0x04) > 0;
|
||||
out2 = (val & 0x08) > 0;
|
||||
if (mc_handler) (*mc_handler)(val & 0xf);
|
||||
local_loopback = (val & 0x10) > 0;
|
||||
break;
|
||||
case 0xf: // Scratch register
|
||||
scratch = val;
|
||||
break;
|
||||
default:
|
||||
LOG_UART("Modem: Write to 0x%x, with 0x%x '%c'\n", port,val,val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CSerial::write_serial(Bit32u port, Bit8u val) {
|
||||
int i;
|
||||
|
||||
for(i=0;i<SERIALPORT_COUNT;i++){
|
||||
if( (port>=serialports[i]->base+0x8) && (port<=(serialports[i]->base+0xf)) ) {
|
||||
serialports[i]->write_port(port,val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bit8u CSerial::read_port(Bit32u port) {
|
||||
Bit8u outval = 0;
|
||||
|
||||
port-=base;
|
||||
// LOG_MSG("Serial read form %X",port);
|
||||
switch(port) {
|
||||
case 0x8: // Receive buffer + Divisor LSB
|
||||
if (dlab) {
|
||||
return divisor_lsb ;
|
||||
} else {
|
||||
outval = rx_readb();
|
||||
// LOG_UART("Read from %X %X %c remain %d",port,outval,outval,rx_fifo.used);
|
||||
return outval;
|
||||
}
|
||||
case 0x9: // Interrupt enable register + Divisor MSB
|
||||
if (dlab) {
|
||||
return divisor_msb ;
|
||||
} else {
|
||||
// LOG_UART("Read from %X %X",port,ierval);
|
||||
return ierval;
|
||||
}
|
||||
case 0xa: // Interrupt identification register
|
||||
switch (ints.active) {
|
||||
case INT_MS:
|
||||
outval = 0x0;
|
||||
break;
|
||||
case INT_TX:
|
||||
outval = 0x2;
|
||||
lowerint(INT_TX);
|
||||
goto skipreset;
|
||||
case INT_RX:
|
||||
outval = 0x4;
|
||||
break;
|
||||
case INT_RX_FIFO:
|
||||
lowerint(INT_RX_FIFO);
|
||||
outval = 0xc;
|
||||
goto skipreset;
|
||||
case INT_LS:
|
||||
outval = 0x6;
|
||||
break;
|
||||
case INT_NONE:
|
||||
outval = 0x1;
|
||||
break;
|
||||
}
|
||||
ints.active=INT_NONE;
|
||||
skipreset:
|
||||
if (FIFOenabled) outval |= 3 << 6;
|
||||
// LOG_UART("Read from %X %X",port,outval);
|
||||
return outval;
|
||||
case 0xb: // Line control register
|
||||
LOG_UART("Read from %X %X",port,outval);
|
||||
return linectrl;
|
||||
case 0xC: // Modem control register
|
||||
outval = dtr | (rts << 1) | (out1 << 2) | (out2 << 3) | (local_loopback << 4);
|
||||
// LOG_UART("Read from %X %X",port,outval);
|
||||
return outval;
|
||||
case 0xD: // Line status register
|
||||
lowerint(INT_LS);
|
||||
outval = 0x40;
|
||||
if (FIFOenabled) {
|
||||
if (!tx_fifo.used) outval|=0x20;
|
||||
} else if (tx_fifo.used<FIFO_SIZE) outval|=0x20;
|
||||
|
||||
if (rx_fifo.used) outval|= 1;
|
||||
// LOG_UART("Read from %X %X",port,outval);
|
||||
return outval;
|
||||
break;
|
||||
case 0xE: // modem status register
|
||||
lowerint(INT_MS);
|
||||
LOG_UART("Read from %X %X",port,outval);
|
||||
outval=mstatus;
|
||||
mstatus&=0xf0;
|
||||
return outval;
|
||||
case 0xF: // Scratch register
|
||||
return scratch;
|
||||
default:
|
||||
//LOG_DEBUG("Modem: Read from 0x%x\n", port);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Bit8u CSerial::read_serial(Bit32u port)
|
||||
{
|
||||
int i;
|
||||
for(i=0;i<SERIALPORT_COUNT;i++){
|
||||
if( (port>=serialports[i]->base+0x8) && (port<=(serialports[i]->base+0xf)) ) {
|
||||
return serialports[i]->read_port(port);
|
||||
}
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
Bitu CSerial::rx_free() {
|
||||
return FIFO_SIZE-rx_fifo.used;
|
||||
}
|
||||
|
||||
Bitu CSerial::tx_free() {
|
||||
return FIFO_SIZE-tx_fifo.used;
|
||||
}
|
||||
|
||||
Bitu CSerial::tx_size() {
|
||||
if (FIFOenabled && rx_fifo.used && (rx_lastread < (PIC_Ticks-2))) {
|
||||
raiseint(INT_RX_FIFO);
|
||||
}
|
||||
return tx_fifo.used;
|
||||
}
|
||||
|
||||
Bitu CSerial::rx_size() {
|
||||
return rx_fifo.used;
|
||||
}
|
||||
|
||||
void CSerial::rx_addb(Bit8u data) {
|
||||
LOG_UART("RX add %c",data);
|
||||
if (rx_fifo.used<FIFO_SIZE) {
|
||||
Bitu where=rx_fifo.pos+rx_fifo.used;
|
||||
if (where>=FIFO_SIZE) where-=FIFO_SIZE;
|
||||
rx_fifo.data[where]=data;
|
||||
rx_fifo.used++;
|
||||
if (FIFOenabled && (rx_fifo.used < FIFOsize)) return;
|
||||
/* Raise rx irq if possible */
|
||||
if (ints.active != INT_RX) raiseint(INT_RX);
|
||||
}
|
||||
}
|
||||
|
||||
void CSerial::rx_adds(Bit8u * data,Bitu size) {
|
||||
if ((rx_fifo.used+size)<=FIFO_SIZE) {
|
||||
Bitu where=rx_fifo.pos+rx_fifo.used;
|
||||
rx_fifo.used+=size;
|
||||
while (size--) {
|
||||
if (where>=FIFO_SIZE) where-=FIFO_SIZE;
|
||||
rx_fifo.data[where++]=*data++;
|
||||
}
|
||||
if (FIFOenabled && (rx_fifo.used < FIFOsize)) return;
|
||||
if (ints.active != INT_RX) raiseint(INT_RX);
|
||||
} else LOG_MSG("WTF");
|
||||
}
|
||||
|
||||
void CSerial::tx_addb(Bit8u data) {
|
||||
LOG_UART("TX add %c",data);
|
||||
if (tx_fifo.used<FIFO_SIZE) {
|
||||
Bitu where=tx_fifo.pos+tx_fifo.used;
|
||||
if (where>=FIFO_SIZE) where-=FIFO_SIZE;
|
||||
tx_fifo.data[where]=data;
|
||||
tx_fifo.used++;
|
||||
if (tx_fifo.used<(FIFO_SIZE-16)) {
|
||||
/* Only generate FIFO irq's every 16 bytes */
|
||||
if (FIFOenabled && (tx_fifo.used & 0xf)) return;
|
||||
raiseint(INT_TX);
|
||||
}
|
||||
} else {
|
||||
LOG_MSG("tx addb");
|
||||
}
|
||||
}
|
||||
|
||||
Bit8u CSerial::rx_readb() {
|
||||
if (rx_fifo.used) {
|
||||
rx_lastread=PIC_Ticks;
|
||||
Bit8u val=rx_fifo.data[rx_fifo.pos];
|
||||
rx_fifo.pos++;
|
||||
if (rx_fifo.pos>=FIFO_SIZE) rx_fifo.pos-=FIFO_SIZE;
|
||||
rx_fifo.used--;
|
||||
//Don't care for FIFO Size
|
||||
if (FIFOenabled || !rx_fifo.used) lowerint(INT_RX);
|
||||
else raiseint(INT_RX);
|
||||
return val;
|
||||
} else {
|
||||
LOG_MSG("WTF rx readb");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Bit8u CSerial::tx_readb() {
|
||||
if (tx_fifo.used) {
|
||||
Bit8u val=tx_fifo.data[tx_fifo.pos];
|
||||
tx_fifo.pos++;
|
||||
if (tx_fifo.pos>=FIFO_SIZE) tx_fifo.pos-=FIFO_SIZE;
|
||||
tx_fifo.used--;
|
||||
if (FIFOenabled && !tx_fifo.used) raiseint(INT_TX);
|
||||
return val;
|
||||
} else {
|
||||
LOG_MSG("WTF tx readb");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSerial::setmodemstatus(Bit8u status) {
|
||||
Bitu oldstatus=mstatus >> 4;
|
||||
if(oldstatus ^ status ) {
|
||||
mstatus=status << 4;
|
||||
mstatus|=(oldstatus ^ status);
|
||||
raiseint(INT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
Bit8u CSerial::getmodemstatus() {
|
||||
return (mstatus >> 4);
|
||||
}
|
||||
|
||||
Bit8u CSerial::getlinestatus() {
|
||||
return read_port(0xd);
|
||||
}
|
||||
|
||||
|
||||
void CSerial::SetMCHandler(MControl_Handler * mcontrol) {
|
||||
mc_handler=mcontrol;
|
||||
}
|
||||
|
||||
CSerial::CSerial (Bit16u initbase, Bit8u initirq, Bit32u initbps) {
|
||||
|
||||
int i;
|
||||
Bit16u initdiv;
|
||||
|
||||
base=initbase;
|
||||
irq=initirq;
|
||||
bps=initbps;
|
||||
|
||||
mc_handler = 0;
|
||||
tx_fifo.used = tx_fifo.pos = 0;
|
||||
rx_fifo.used = rx_fifo.pos = 0;
|
||||
|
||||
rx_lastread = PIC_Ticks;
|
||||
linectrl = dtr = rts = out1 = out2 = 0;
|
||||
local_loopback = 0;
|
||||
ierval = 0;
|
||||
ints.enabled=1 << INT_RX_FIFO;
|
||||
ints.active=INT_NONE;
|
||||
ints.requested=0;
|
||||
|
||||
FIFOenabled = false;
|
||||
FIFOsize = 1;
|
||||
timeout = 0;
|
||||
dlab = 0;
|
||||
ierval = 0;
|
||||
|
||||
initdiv = SERIALBASERATE / bps;
|
||||
setdivisor(initdiv >> 8, initdiv & 0x0f);
|
||||
|
||||
for (i=8;i<=0xf;i++) {
|
||||
|
||||
IO_RegisterWriteHandler(initbase+i,write_serial,"Serial Port");
|
||||
IO_RegisterReadHandler(initbase+i,read_serial,"Serial Port");
|
||||
}
|
||||
|
||||
PIC_RegisterIRQ(irq,0,"SERIAL");
|
||||
|
||||
|
||||
};
|
||||
|
||||
CSerial::~CSerial(void)
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
CSerial *getComport(Bitu portnum)
|
||||
{
|
||||
return serialports[portnum-1];
|
||||
}
|
||||
|
||||
void SERIAL_Init(Section* sec) {
|
||||
|
||||
unsigned long args = 1;
|
||||
Section_prop * section=static_cast<Section_prop *>(sec);
|
||||
|
||||
// if(!section->Get_bool("enabled")) return;
|
||||
|
||||
serialports[0] = new CSerial(0x3f0,4,SERIALBASERATE);
|
||||
serialports[1] = new CSerial(0x2f0,3,SERIALBASERATE);
|
||||
}
|
635
src/hardware/softmodem.cpp
Normal file
635
src/hardware/softmodem.cpp
Normal file
|
@ -0,0 +1,635 @@
|
|||
/*
|
||||
* Copyright (C) 2002 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 Library 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 "sdl_net.h"
|
||||
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "inout.h"
|
||||
#include "mixer.h"
|
||||
#include "dma.h"
|
||||
#include "pic.h"
|
||||
#include "hardware.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 CONNECTED (M_CTS | M_DSR | M_DCD )
|
||||
#define DISCONNECTED (M_CTS | M_DSR )
|
||||
|
||||
|
||||
/* DTMF tone generator */
|
||||
float col[] = { 1209.0, 1336.0, 1477.0, 1633.0 };
|
||||
float row[] = { 697.0, 770.0, 852.0, 941.0 };
|
||||
char positions[] = "123A456B789C*0#D";
|
||||
#define duration 1000
|
||||
#define pause 400
|
||||
|
||||
static Bit8u tmpbuf[FIFO_SIZE+1];
|
||||
|
||||
struct ModemHd {
|
||||
char cmdbuf[FIFO_SIZE];
|
||||
bool commandmode;
|
||||
bool cantrans;
|
||||
bool incomingcall;
|
||||
bool autoanswer;
|
||||
bool echo;
|
||||
Bitu cmdpause;
|
||||
Bits ringcounter;
|
||||
Bit16u plusinc;
|
||||
Bit16u cmdpos;
|
||||
|
||||
|
||||
TCPsocket socket;
|
||||
TCPsocket listensocket;
|
||||
SDLNet_SocketSet socketset;
|
||||
|
||||
IPaddress openip;
|
||||
|
||||
Bitu comport;
|
||||
Bitu listenport;
|
||||
|
||||
char remotestr[4096];
|
||||
|
||||
bool dialing;
|
||||
float f1, f2;
|
||||
Bitu diallen;
|
||||
Bitu dialpos;
|
||||
char dialstr[256];
|
||||
MIXER_Channel * chan;
|
||||
};
|
||||
|
||||
static CSerial * mdm;
|
||||
static ModemHd mhd;
|
||||
|
||||
static void sendStr(const char *usestr) {
|
||||
if (!mhd.echo) return;
|
||||
Bitu i=0;
|
||||
while (*usestr != 0) {
|
||||
if (*usestr == 10) {
|
||||
mdm->rx_addb(0xd);
|
||||
mdm->rx_addb(0xa);
|
||||
} else {
|
||||
mdm->rx_addb((Bit8u)*usestr);
|
||||
}
|
||||
usestr++;
|
||||
}
|
||||
}
|
||||
|
||||
static void sendOK() {
|
||||
sendStr("\nOK\n");
|
||||
}
|
||||
|
||||
static void sendError() {
|
||||
sendStr("\nERROR\n");
|
||||
}
|
||||
|
||||
static void toUpcase(char *buffer) {
|
||||
Bitu i=0;
|
||||
while (buffer[i] != 0) {
|
||||
buffer[i] = toupper(buffer[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void openConnection() {
|
||||
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(&mhd.openip);
|
||||
if (mhd.socket) {
|
||||
SDLNet_TCP_AddSocket(mhd.socketset,mhd.socket);
|
||||
sendStr("\nCONNECT 57600\n");
|
||||
mhd.commandmode = false;
|
||||
mdm->setmodemstatus(CONNECTED);
|
||||
} else {
|
||||
sendStr("\nNO DIALTONE\n");
|
||||
}
|
||||
}
|
||||
|
||||
static 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(&mhd.openip,host,port)) {
|
||||
/* Prepare string for dial sound generator */
|
||||
int c;
|
||||
char *addrptr=host;
|
||||
mhd.dialstr[0] = 'd';
|
||||
mhd.dialstr[1] = 'd';
|
||||
mhd.dialstr[2] = 'd';
|
||||
mhd.dialstr[3] = 'd';
|
||||
mhd.dialstr[4] = 'd';
|
||||
mhd.dialstr[5] = 'p';
|
||||
c=6;
|
||||
while(*addrptr != 0x00) {
|
||||
if (strchr(positions, *addrptr)) {
|
||||
mhd.dialstr[c] = *addrptr;
|
||||
c++;
|
||||
}
|
||||
addrptr++;
|
||||
}
|
||||
mhd.dialstr[c] = 0x00;
|
||||
|
||||
mhd.diallen = strlen(mhd.dialstr) * (Bit32u)(duration + pause);
|
||||
mhd.dialpos = 0;
|
||||
mhd.f1 = 0;
|
||||
mhd.f2 = 0;
|
||||
mhd.dialing = true;
|
||||
MIXER_Enable(mhd.chan,true);
|
||||
return true;
|
||||
} else {
|
||||
LOG_MSG("Failed to resolve host %s:%s",host,SDLNet_GetError());
|
||||
sendStr("\nNO CARRIER\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DoCommand() {
|
||||
bool found = false;
|
||||
bool foundat = false;
|
||||
char *foundstr;
|
||||
bool connResult = false;
|
||||
char msgbuf[4096];
|
||||
|
||||
Bitu result;
|
||||
mhd.cmdbuf[mhd.cmdpos] = 0;
|
||||
toUpcase(mhd.cmdbuf);
|
||||
LOG_MSG("Modem Sent Command: %s\n", mhd.cmdbuf);
|
||||
mhd.cmdpos = 0;
|
||||
|
||||
result = 0;
|
||||
|
||||
|
||||
/* Just for kicks */
|
||||
if ((mhd.cmdbuf[0] == 'A') && (mhd.cmdbuf[1] == 'T')) foundat = true;
|
||||
if (foundat) {
|
||||
if (strstr(mhd.cmdbuf,"I3")) {
|
||||
sendStr("\nDosBox Emulated Modem Firmware V1.00\n");
|
||||
result = 1;
|
||||
}
|
||||
if (strstr(mhd.cmdbuf,"I4")) {
|
||||
sprintf(msgbuf, "\nModem compiled for DosBox version %s\n", VERSION);
|
||||
sendStr(msgbuf);
|
||||
result = 1;
|
||||
}
|
||||
if (strstr(mhd.cmdbuf,"S0=1")) {
|
||||
mhd.autoanswer = true;
|
||||
}
|
||||
if (strstr(mhd.cmdbuf,"S0=0")) {
|
||||
mhd.autoanswer = false;
|
||||
}
|
||||
|
||||
if (strstr(mhd.cmdbuf,"E0")) {
|
||||
mhd.echo = false;
|
||||
}
|
||||
if (strstr(mhd.cmdbuf,"E1")) {
|
||||
mhd.echo = true;
|
||||
}
|
||||
|
||||
if (strstr(mhd.cmdbuf,"ATH")) {
|
||||
/* Check if we're actually connected */
|
||||
if (mhd.socket) {
|
||||
sendStr("\nNO CARRIER\n");
|
||||
SDLNet_TCP_DelSocket(mhd.socketset,mhd.socket);
|
||||
SDLNet_TCP_Close(mhd.socket);
|
||||
mhd.socket=0;
|
||||
mdm->setmodemstatus(DISCONNECTED);
|
||||
mhd.commandmode = true;
|
||||
result = 3;
|
||||
} else result = 2;
|
||||
}
|
||||
if(strstr(mhd.cmdbuf,"ATO")) {
|
||||
/* Check for connection before switching to data mode */
|
||||
if (mhd.socket) {
|
||||
mhd.commandmode = false;
|
||||
result=3;
|
||||
} else {
|
||||
result=2;
|
||||
}
|
||||
}
|
||||
if(strstr(mhd.cmdbuf,"ATDT")) {
|
||||
foundstr = strstr(mhd.cmdbuf,"ATDT");
|
||||
foundstr+=4;
|
||||
/* Small protection against empty line */
|
||||
if (!foundstr[0]) {
|
||||
result=2;
|
||||
} else {
|
||||
connResult = Dial(foundstr);
|
||||
result=3;
|
||||
}
|
||||
}
|
||||
if(strstr(mhd.cmdbuf,"ATA")) {
|
||||
if (mhd.incomingcall) {
|
||||
sendStr("\nCONNECT 57600\n");
|
||||
LOG_MSG("Connected!\n");
|
||||
MIXER_Enable(mhd.chan,false);
|
||||
mdm->setmodemstatus(CONNECTED);
|
||||
mhd.incomingcall = false;
|
||||
mhd.commandmode = false;
|
||||
SDLNet_TCP_AddSocket(mhd.socketset,mhd.socket);
|
||||
result = 3;
|
||||
} else {
|
||||
mhd.autoanswer = true;
|
||||
result = 3;
|
||||
}
|
||||
}
|
||||
if (result==0) result = 1;
|
||||
} else result=2;
|
||||
|
||||
if (strlen(mhd.cmdbuf)<2) {
|
||||
if(!mhd.dialing) {
|
||||
result = 0;
|
||||
mhd.autoanswer = false;
|
||||
} else {
|
||||
MIXER_Enable(mhd.chan,false);
|
||||
mhd.dialing = false;
|
||||
sendStr("\nNO CARRIER\n");
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case 1:
|
||||
sendOK();
|
||||
break;
|
||||
case 2:
|
||||
sendError();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void MC_Changed(Bitu new_mc) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void MODEM_Hardware(Bitu ticks) {
|
||||
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=mdm->tx_size();
|
||||
while (tx_size--) {
|
||||
txval = mdm->tx_readb();
|
||||
if (mhd.commandmode) {
|
||||
if(txval != 0xd) {
|
||||
if(txval == 0x8) {
|
||||
if (mhd.cmdpos > 0) {
|
||||
--mhd.cmdpos;
|
||||
}
|
||||
} else {
|
||||
if (txval != '+') {
|
||||
if(mhd.cmdpos<FIFO_SIZE) {
|
||||
mhd.cmdbuf[mhd.cmdpos] = txval;
|
||||
mhd.cmdpos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(txval != 0x10) {
|
||||
if (mhd.echo) mdm->rx_addb(txval);
|
||||
} else if (mhd.echo) {
|
||||
mdm->rx_addb(10);
|
||||
mdm->rx_addb(13);
|
||||
}
|
||||
} else {
|
||||
DoCommand();
|
||||
}
|
||||
} else {
|
||||
/* 1000 ticks have passed, can check for pause command */
|
||||
if (mhd.cmdpause > 1000) {
|
||||
if(txval == '+') {
|
||||
mhd.plusinc++;
|
||||
if(mhd.plusinc>=3) {
|
||||
mhd.commandmode = true;
|
||||
sendStr("\nOK\n");
|
||||
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 outgoing to the serial port */
|
||||
if(!mhd.commandmode && mhd.socket && mdm->rx_free() && SDLNet_SocketReady(mhd.socket)) {
|
||||
usesize = mdm->rx_free();
|
||||
result = SDLNet_TCP_Recv(mhd.socket, tmpbuf, usesize);
|
||||
if (result>0) {
|
||||
mdm->rx_adds(tmpbuf,result);
|
||||
mhd.cmdpause = 0;
|
||||
} else {
|
||||
/* Error close the socket and disconnect */
|
||||
mdm->setmodemstatus(DISCONNECTED);
|
||||
mhd.commandmode = true;
|
||||
sendStr("\nNO CARRIER\n");
|
||||
SDLNet_TCP_DelSocket(mhd.socketset,mhd.socket);
|
||||
SDLNet_TCP_Close(mhd.socket);
|
||||
mhd.socket=0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for incoming calls */
|
||||
if (!mhd.socket && !mhd.incomingcall && mhd.listensocket) {
|
||||
mhd.socket = SDLNet_TCP_Accept(mhd.listensocket);
|
||||
if (mhd.socket) {
|
||||
mhd.dialpos = 0;
|
||||
mhd.incomingcall = true;
|
||||
mhd.diallen = 12000;
|
||||
mhd.dialpos = 0;
|
||||
//TODO Set ring in Modemstatus?
|
||||
sendStr("\nRING\n");
|
||||
MIXER_Enable(mhd.chan,true);
|
||||
mhd.ringcounter = 24000;
|
||||
}
|
||||
}
|
||||
|
||||
if (mhd.incomingcall) {
|
||||
if (mhd.ringcounter <= 0) {
|
||||
if (mhd.autoanswer) {
|
||||
mhd.incomingcall = false;
|
||||
sendStr("\nCONNECT 57600\n");
|
||||
MIXER_Enable(mhd.chan,false);
|
||||
mdm->setmodemstatus(CONNECTED);
|
||||
mhd.incomingcall = false;
|
||||
mhd.commandmode = false;
|
||||
SDLNet_TCP_AddSocket(mhd.socketset,mhd.socket);
|
||||
return;
|
||||
}
|
||||
sendStr("\nRING\n");
|
||||
mhd.diallen = 12000;
|
||||
mhd.dialpos = 0;
|
||||
|
||||
MIXER_Enable(mhd.chan,true);
|
||||
|
||||
mhd.ringcounter = 3000; /* Ring every three seconds for accurate emulation */
|
||||
|
||||
}
|
||||
if (mhd.ringcounter > 0) --mhd.ringcounter;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
03F8 -W serial port, transmitter holding register (THR), which contains the
|
||||
character to be sent. Bit 0 is sent first.
|
||||
bit 7-0 data bits when DLAB=0 (Divisor Latch Access Bit)
|
||||
03F8 R- receiver buffer register (RBR), which contains the received
|
||||
character. Bit 0 is received first
|
||||
bit 7-0 data bits when DLAB=0 (Divisor Latch Access Bit)
|
||||
03F8 RW divisor latch low byte (DLL) when DLAB=1 (see #P0876)
|
||||
03F9 RW divisor latch high byte (DLM) when DLAB=1 (see #P0876)
|
||||
03F9 RW interrupt enable register (IER) when DLAB=0 (see #P0877)
|
||||
03FA R- interrupt identification register (see #P0878)
|
||||
Information about a pending interrupt is stored here. When the ID
|
||||
register is addressed, thehighest priority interrupt is held, and
|
||||
no other interrupts are acknowledged until the CPU services that
|
||||
interrupt.
|
||||
03FA -W 16650 FIFO Control Register (FCR) (see #P0879)
|
||||
03FB RW line control register (LCR) (see #P0880)
|
||||
03FC RW modem control register (see #P0881)
|
||||
03FD R- line status register (LSR) (see #P0882)
|
||||
03FE R- modem status register (MSR) (see #P0883)
|
||||
03FF RW scratch register (SCR)
|
||||
(not used for serial I/O; available to any application using 16450,
|
||||
16550) (not present on original 8250)
|
||||
*/
|
||||
|
||||
static void MODEM_CallBack(Bit8u * stream,Bit32u len) {
|
||||
char *cp;
|
||||
float ci,ri;
|
||||
Bit32u innum, splitnum, quad, eighth, sixth, amp;
|
||||
Bit8u curchar;
|
||||
Bit32s buflen = (Bit32s)len;
|
||||
if(mhd.incomingcall) {
|
||||
|
||||
if(mhd.dialpos>=mhd.diallen) {
|
||||
MIXER_Enable(mhd.chan,false);
|
||||
return;
|
||||
} else {
|
||||
quad = (mhd.diallen/14);
|
||||
eighth = quad / 2;
|
||||
sixth = eighth /2;
|
||||
|
||||
while ((buflen>0) && (mhd.dialpos<mhd.diallen)) {
|
||||
innum = mhd.dialpos % quad;
|
||||
splitnum = innum % eighth;
|
||||
ci = 650;
|
||||
ri = 950;
|
||||
|
||||
while (splitnum < eighth) {
|
||||
amp = (sixth - abs(sixth - splitnum)) * (0x4000/sixth);
|
||||
|
||||
|
||||
*(Bit16s*)(stream) = (Bit16s)(sin(mhd.f1)*amp + (sin(mhd.f2)*amp));
|
||||
mhd.f1 += 6.28/8000.0*ri;
|
||||
mhd.f2 += 6.28/8000.0*ci;
|
||||
--buflen;
|
||||
innum++;
|
||||
splitnum++;
|
||||
mhd.dialpos++;
|
||||
stream+=2;
|
||||
if(buflen<=0) return;
|
||||
}
|
||||
while (splitnum < quad) {
|
||||
*(Bit16s*)(stream) = 0;
|
||||
mhd.f1 = 0;
|
||||
mhd.f2 = 0;
|
||||
--buflen;
|
||||
innum++;
|
||||
splitnum++;
|
||||
mhd.dialpos++;
|
||||
stream+=2;
|
||||
if(buflen<=0) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(mhd.dialing) {
|
||||
if(mhd.dialpos>=mhd.diallen) {
|
||||
while(len-->0) {
|
||||
*(Bit16s*)(stream) = 0;
|
||||
stream+=2;
|
||||
}
|
||||
MIXER_Enable(mhd.chan,false);
|
||||
mhd.dialing = false;
|
||||
openConnection();
|
||||
return;
|
||||
} else {
|
||||
|
||||
while ((buflen>0) && (mhd.dialpos<mhd.diallen)) {
|
||||
curchar = (Bit8u)(mhd.dialpos / (duration + pause));
|
||||
innum = mhd.dialpos % (duration + pause);
|
||||
|
||||
switch(mhd.dialstr[curchar]) {
|
||||
case 'p':
|
||||
while(innum<pause) {
|
||||
*(Bit16s*)(stream) = 0;
|
||||
mhd.f1 = 0;
|
||||
mhd.f2 = 0;
|
||||
--buflen;
|
||||
innum++;
|
||||
mhd.dialpos++;
|
||||
stream+=2;
|
||||
if(buflen<=0) return;
|
||||
}
|
||||
mhd.dialpos+=duration;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
/* Dialtone */
|
||||
ci = 350;
|
||||
ri = 440;
|
||||
while(innum<(pause+duration)) {
|
||||
*(Bit16s*)(stream) = (Bit16s)((sin(mhd.f1)*0x1fff) + (sin(mhd.f2)*0x1fff));
|
||||
mhd.f1 += 6.28/8000.0*ri;
|
||||
mhd.f2 += 6.28/8000.0*ci;
|
||||
--buflen;
|
||||
innum++;
|
||||
mhd.dialpos++;
|
||||
stream+=2;
|
||||
if(buflen<=0) return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
cp = strchr(positions, mhd.dialstr[curchar]);
|
||||
|
||||
ci = col[(cp - positions) % 4];
|
||||
ri = row[(cp - positions) / 4];
|
||||
while(innum<duration) {
|
||||
*(Bit16s*)(stream) = (Bit16s)((sin(mhd.f1)*0x3fff) + (sin(mhd.f2)*0x3fff));
|
||||
mhd.f1 += 6.28/8000.0*ri;
|
||||
mhd.f2 += 6.28/8000.0*ci;
|
||||
--buflen;
|
||||
innum++;
|
||||
mhd.dialpos++;
|
||||
stream+=2;
|
||||
if(buflen<=0) return;
|
||||
}
|
||||
while(innum<(pause+duration)) {
|
||||
mhd.f1 = 0;
|
||||
mhd.f2 = 0;
|
||||
*(Bit16s*)(stream) = 0;
|
||||
--buflen;
|
||||
innum++;
|
||||
mhd.dialpos++;
|
||||
stream+=2;
|
||||
if(buflen<=0) return;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MODEM_Init(Section* sec) {
|
||||
|
||||
unsigned long args = 1;
|
||||
Section_prop * section=static_cast<Section_prop *>(sec);
|
||||
|
||||
if(!section->Get_bool("enabled")) return;
|
||||
|
||||
if(SDLNet_Init()==-1) {
|
||||
LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
mhd.cmdpos = 0;
|
||||
mhd.commandmode = true;
|
||||
mhd.plusinc = 0;
|
||||
mhd.cantrans = false;
|
||||
mhd.incomingcall = false;
|
||||
mhd.autoanswer = false;
|
||||
mhd.cmdpause = 0;
|
||||
mhd.echo = true;
|
||||
|
||||
/* Bind the modem to the correct serial port */
|
||||
mhd.comport=section->Get_int("comport");
|
||||
strcpy(mhd.remotestr, section->Get_string("remote"));
|
||||
mdm = getComport(mhd.comport);
|
||||
mdm->setmodemstatus(DISCONNECTED);
|
||||
mdm->SetMCHandler(&MC_Changed);
|
||||
|
||||
TIMER_RegisterTickHandler(&MODEM_Hardware);
|
||||
|
||||
/* 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=section->Get_int("listenport");
|
||||
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;
|
||||
|
||||
mhd.chan=MIXER_AddChannel(&MODEM_CallBack,8000,"MODEM");
|
||||
MIXER_Enable(mhd.chan,false);
|
||||
MIXER_SetMode(mhd.chan,MIXER_16MONO);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue