796 lines
18 KiB
C++
796 lines
18 KiB
C++
/*
|
|
* Copyright (C) 2002-2004 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;
|
|
};
|
|
|
|
|
|
static Bitu call_int14;
|
|
|
|
#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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
void MODEM_Init(Section* sec) {
|
|
|
|
unsigned long args = 1;
|
|
Section_prop * section=static_cast<Section_prop *>(sec);
|
|
|
|
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");
|
|
|
|
csm = NULL;
|
|
|
|
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)
|
|
call_int14=CALLBACK_Allocate();
|
|
CALLBACK_Setup(call_int14,&INT14_FOSSIL,CB_IRET);
|
|
RealSetVec(0x14,CALLBACK_RealPointer(call_int14));
|
|
}
|
|
|
|
|
|
#endif
|
|
|