1
0
Fork 0

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:
Sjoerd van der Berg 2003-07-06 13:07:16 +00:00
parent c2f035784b
commit 2b61bd2759
3 changed files with 1211 additions and 0 deletions

135
include/serialport.h Normal file
View 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
View 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
View 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);
}