Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C Programming

Source Code Accompanies This Article. Download It Now.


JUL95: C PROGRAMMING

CD-ROMs, Classes, and GTIs (Graphically Tattooed Individuals)

Once upon a time, magazine columnists never had to buy blank diskettes. Compiler and tool vendors regularly send us betas and review copies of their products. A typical Windows compiler takes more than a dozen diskettes, and there are always new versions and betas coming out, so my scratch diskette drawer was always full. I've sent many book chapters, articles, and other stuff to publishers, readers, and associates on diskettes that were paid for by Microsoft, Borland, Symantec, and other generous suppliers.

Microsoft must have gotten wise to what I was doing. They started popping out the little sliders that write-protect the diskette to thwart my reuse of their media, but that was only a minor inconvenience. The drawer still holds a lot of those adhesive tabs from the days of the 5.25-inch diskettes, and they plug up that write-protect hole just fine. I figured when I ran out of the tabs, I'd use chewing gum.

Then came the CD-ROM. A wonderful medium to be sure, but the free diskette supply quickly dried up when vendors started shipping their goods on the shiny, read-only discs. I can see the bottom of that diskette drawer now. Guess I'll have to break down and buy some diskettes. Shows how things have gone to hell.

Now I have a different problem. A big stack of outdated CD-ROMs languishes in another drawer. Beta releases. Superseded versions. Junk free software that came with this or that piece of hardware or shrink- wrapped with some overpriced magazine. One thousand clip-art samples. Five hundred crummy DOS games. NT 3.5 in every language on the face of the earth. Version 1 of the Dr. Dobb's Journal|CD.

What to do with all those useless silver discs? They last forever, are not reusable, and can't be recycled. The environmentalist in me won't let me contribute them to the nonbiodegradable rubbish at the county landfill. Judy says it's because I'm a pack rat and can't throw away anything that might still work.

If I had a dog, the discs would make good Frisbees for playing fetch, but I have no dog, and the cat won't play Frisbee with me.

A mobile hangs in my office made of Borland C++ CD-ROMs. There weren't enough of them to balance the mobile, and I didn't want to spoil the set with compilers from those other guys, so I filled the mobile out with Philippe's saxophone CDs.

I don't need any more mobiles, though, and there are still plenty more obsolete CD-ROMs to go.

Chicago Beta 1 CD-ROM is a pretty disc, with gold lettering on a navy blue background and a pattern of black steel girders to suggest something under construction. It's a copy of the wallpaper that beta 1 starts up with. Quality artwork, so Chicago Beta 1 CD-ROM is a nice clock on my office wall now. For six bucks the local craft shop offers a matching quartz clockwork with gold hands (battery not included). Later Windows 95 (née Chicago) betas are not as pretty as that first one. I wouldn't hang one of them on my wall. Too cheap looking. Besides, they don't have the heirloom appeal of old number one.

Judy wants wind chimes on the patio. I tried a dangling farrago of CD-ROMs, but the dumb plastic discs have no musical timbre. They just rattle drearily in the wind. No tinkle to speak of.

Someone else suggested skeet shooting. Pull! Kaboom! Another TrueType font collection blown to smithereens.

Teaching C and C++

The Contract On America promises to downsize government. There are repercussions in my home town. The Kennedy Space Center (KSC) employs a lot of people, both contractors and government employees. The contractors do the technical work, and the feds manage the projects.

Contractors are accustomed to job changes and relocations when the government alters how work is done and who does it. But the federal workers' jobs have always been relatively secure. There were always projects of one kind or another, which meant that there was always a need for project managers.

That security is evaporating. Some government jobs will be eliminated, which means that the government employees who are doing those jobs will have to find something else to do. The lucky ones will find reassignment within the government. Many others will be cut from the payroll, either through early retirement or, in the case of younger staff, that government euphemism for getting canned, the Reduction In Force (RIF).

Downsizing also demands more efficient spending, which, on a bottom line somewhere, means fewer contractors. The feds who survive the RIF have to start doing more of the technical stuff. Many of them have technical aptitude, but their skills have been dulled by a career spent counting beans and administering contracts. To sharpen their skills, NASA has begun to offer classes to government employees who want to improve their chances to avoid the RIF and, if that doesn't work out, to enhance their employability elsewhere.

As part of this retraining program, I was recently hired to teach two classes, an introduction to C and an introduction to C++. The classes were contracted to a local training company. They found me by asking around, I guess.

The teaching environment was unique in my experience. Each class was for five days, six hours each day, with 15 students per class. Each student sat at a PC, and I used a laptop with a projection system. The training company provided the PCs and whatever software was to be used in the class, and the government provided the classroom at KSC.

I was permitted to choose the teaching materials I wanted, so I used the two books that I wrote as tutorials on those subjects. PowerPoint was invaluable for making teaching slides. Importing the Word outlines for the book chapters created instant slides, which needed only some formatting and the addition of some figures.

The first day of class had a bummer of a downside. Actually, we did not learn about the problem until the second morning. NASA had told us the first day that we could not lock the classroom, because they did not have the combination to the cipher lock. They assured us that the building itself would be locked, however, and our stuff would be safe. We dutifully left the door unlocked, and that night someone stole two brand new Gateway 200 4DX2-66V computers from our classroom. Guess what? NASA told us that the loss was our responsibility because we failed to lock the door.

When they get around to choosing RIF candidates, I hope they call me for an opinion.

The C-class outline assumed that the students understood programming, which they did, although none of them were actively working as programmers at the time. The C++-class outline assumed that the students understood C well enough to keep up, which most of them did. There were no dropouts, and all the C students finished satisfactorily. One of the C++ students had no C experience at all, and he was soon lost, but I gave him a copy of the C book and let him teach himself at his own pace. It wasn't his fault. The organizers did not properly advertise the prerequisites.

During each class, I ran examples from the book and had the students follow along on their PCs. Whenever we reached a critical point, I had the students write programs that used what they had learned so far. They were allowed to work together, keep the book open, and ask questions, a learning environment that I believe in. A classroom should provide students with the same information available to them in the office.

While they worked on their programs, I went from student to student and made sure that everyone had a running program before we went to the next lesson. This, I learned, is an important technique. If students are forced, due to schedule demands, to go to the next plateau without having successfully completed the current one, they lose confidence and, consequently, interest. Fortunately, the exercises were easy enough that no one's individual pace slowed the class down.

Given the level of experience of the students, I couldn't cover either language completely in one week by using the methods I chose. If the C students were active programmers in other languages, and if the C++ students were active C programmers, we could have covered more ground, but I doubt that we would have exhausted either subject.

It is possible, I am sure, to teach all of C or C++ in a single week, but only if you restrict the lessons to lectures and examples demonstrated by the instructor from the podium. There would be no time for lab work or much class participation. When asked, the students enthusiastically agreed that by having the PCs and writing their own programs in class they learned much more than they would have from a nonstop lecture, even if more material could have been covered. For one thing, the class was more interesting because they had something to do besides sit and take notes for hours on end. For another, at each step their learning was reinforced by immediate experience.

Serial Port and Modem Classes

Last month I described IMail, an application that calls an Internet host and collects and sends e-mail. That project is now complete and available for download. Recently I've been working on a project with PC games in C++, and one of its features supports multiuser games across a serial port and optionally through a modem. I adapted the two classes from IMail that encapsulate the serial port and the modem for the game library, and they ported with only a few changes. After congratulating myself on having built such reusable software components, I decided to use the two classes as the code-related part of this month's column.

There are many applications for connecting computers with the serial port both directly and through a modem. These two classes saw first light several years ago as the serial port and modem function libraries for a C project called SMALLCOM. Now they are C++ classes.

The CommPort Class

Listings One and Two, are serial.h and serial.cpp, the source files that implement the CommPort class. Serial.h begins by defining a simple Timer class to track timeouts during port input/output. There are a number of constant variables (I hate that--how can a variable be constant, or vice versa?) that specify some port addresses and control values, a buffer size for serial input, buffer fill thresholds for sending XOF during reception, and a bit-field structure that defines the serial port's initialization byte.

A structure contains the usual initialization parameters to be used when constructing an object of the CommPort class: port number, parity, number of stop and data bits, and baud rate.

The CommPort class includes data members for the initialization byte, pointers to the input buffer, a switch to indicate whether Xon/Xoff protocols are enabled, indicators to control the protocol, an instance of the parameter structure, a timer object, and a set of inline functions that compute port, IRQ, and interrupt-vector addresses based on the port chosen in the parameters. The public interface for the CommPort class provides for object construction from a copy of the parameter structure, a function to initialize the port, and functions to read and write characters from the port, clear the input queue, test for carrier detect, set the timeout value, and enable and disable Xon/Xoff protocols.

When a program constructs a CommPort object, the data members are initialized based on the parameter structure passed. An input buffer is allocated from the heap. Then the port is initialized, which includes conditioning the port to send and receive characters in the specified format at the specified baud rate and intercepting the serial-input and system-timer interrupts.

When the CommPort object goes out of scope, the destructor deletes the serial-input buffer and restores the timer and serial input-interrupt vectors.

The timer's interrupt-service routine counts down the timer if it is running.

The serial-input interrupt service routine (ISR) maintains a circular buffer of input characters into which it reads serial characters--one for each interrupt. This ISR also manages the Xon/Xoff protocols. If the input-buffer count has reached the fill threshold, the ISR transmits the XOF character to the other computer and sets a flag that says that it is waiting to send the XON character. The protocol assumes that the other computer will receive the XOF character and will not transmit any further data characters until it receives the XON character. If the serial-input ISR receives the XOF character, it sets a flag telling the local CommPort object to wait for an XON before sending any further data characters. If the serial input ISR receives the XON character, it clears that flag.

The input_char_ready function returns a true value if there are data bytes in the input buffer. The readcomm function waits for that condition to be true and fetches the next character from the input buffer to be returned to the caller. The function also tests to see if the object is waiting to send the XON protocol byte and if the fetch operation reduced the number of bytes in the input buffer to below the fill threshold. If so, the function sends the XON byte and turns off the flag that says that it is waiting to do so.

The writecomm function is called to send a byte to the other computer. First it goes into what looks like a dead loop while the flag is set that says the object is waiting for an XON character. Since that flag is cleared by the ISR, the loop is not really dead. Then the function sets a timer and waits for the port to be available to transmit a byte or for a timeout to occur. If the port becomes available, the function transmits the character.

The Modem Class

Listings Three and Four are modem.h and modem.cpp, the source code that implements the Modem class. A program that makes a direct connection instantiates an object of the CommPort class. If you are going to use a modem, you can instantiate an object of only the Modem class, since it instantiates its own CommPort object. The Modem class has a pointer to the CommPort class with which to instantiate the CommPort object during construction. It also has a flag to tell if the connection has been made and references to serial port and modem initialization parameters.

Modem parameters consist of the strings that reset and initialize the modem, dial a number, put the modem in answer mode, and go on hook (hang up the phone).

You instantiate a Modem object by providing communications parameters and modem parameters. A program usually gets these values from a configuration file that the user can modify. When the program is ready to make a call, it calls the Dial function, waits for the connection to be made or to fail and returns true or false accordingly. The HangUp function breaks the connection. The program can call the InputCharReady function to see if there is a character to be read from the modem. The ReadChar and CharOut functions read and write characters between the local computer and the computer at the other end of the line. TestCarrier returns True as long as the connection is made. This allows a program to test to see if either end broke the connection.

The Answer function puts the modem in answer mode. The program can call this function and then wait until the TestCarrier function returns a true value to process a call that has been received.

The IMail version of modem.cpp changes the baud rate and resets the serial port if the connection indicates that the other end answered with a lower baud rate than the one that was used to place the call. This version does not do that because it is meant to be used when the two users agree in advance about how they are placing the call. That version also supports the XModem file transfer. You can get the IMail source code if you want to add that feature to this version of the serial and modem classes.

Listing Five is comm.cpp, a program that uses the Modem and Serial classes to connect two computers for a chat. The caller specifies the port number (1 or 2) and a phone number on the command line. The answerer specifies only the port number.

Source Code

The source code files for IMail and the D-Flat libraries are free. You can download them from the DDJ Forum on CompuServe, the Internet by anonymous ftp, as well as other sources; see "Availability," page 3.

If you cannot get to one of the online sources, send a 3.5-inch diskette and an addressed, stamped mailer to me at Dr. Dobb's Journal, 411 Borel Avenue, San Mateo, CA 94402, and I'll send you the source code. Make sure that you include a note that says which project you want. The code is free, but if you care to support my Careware charity, include a dollar for the Brevard County Food Bank.

Petzold's Permanent Pixelderm Patch

At Software Development 95 in San Francisco, I was sitting in the corner at a vendor's party and feeling morose. Christi Westphal of Sax Software had just broken the heart of every red-blooded man in the room by announcing her impending nuptials. I needed something to cheer me up. Then I saw a peculiar thing. Across the room Windows programming guru Charles Petzold was facing down a colleague and taking off his shirt. "A fight!" I thought. Great. Just the thing to brighten up a dull evening. Maybe Charles was talking to a Macintosh programmer and, emotions running high as they will, words were exchanged, and things were about to get out of hand. But, alas, no fight ensued. She must not have been a Mac programmer. As it turned out, Charles was showing off the Windows logo prominently tattooed on his bicep. I read about that tattoo in Dvorak's column. For once Dvorak was right about something.

By now we all know what you must do to be permitted to display that logo on your packaging. We may, therefore, conclude that Charles has been carefully examined by Microsoft and that their impartial panel of judges certifies that Charles is made of 32-bits, drags and drops properly, uses long names, runs under NT, is mail-enabled, and, when pressed on his right mouse button, blurts out a context menu.

Listing One

//  serial.h
#ifndef SERIAL_H
#define SERIAL_H
#include <dos.h>
#undef disable
typedef int bool;
const int true = 1;
const int false = 0;
const int systimer = 8;
class Timer {

    int timer;
public:
    Timer()
        { timer = -1; }
    bool timed_out()
        { return timer == 0; }
    void set(int secs)
        { timer=secs*182/10+1; }
    void disable()
        { timer = -1; }
    bool running()
        { return timer > 0; }
    void countdown()
        { timer; }
    bool disabled()
        { return timer == -1; }
};
const int xon  = 17;
const int xoff = 19;
const int PIC01 = 0x21; // 8259 Programmable Interrupt Controller
const int PIC00 = 0x20; //                                  
const int EOI   = 0x20; // End of Interrupt command
// - line status register values
const int XmitDataReady = 0x20;
//  modem control register values
const int DTR = 1;
const int RTS = 2;
const int OUT2 = 8;
//  modem status register values
const int RLSD = 0x80;
const int DSR = 0x20;
const int CTS = 0x10;
// - interrupt enable register signals
const int DataReady = 1;
// - serial input interrupt buffer
const int BufSize = 1024;
const int SafetyLevel = (BufSize/4);
const int Threshold = (SafetyLevel*3);
// - com port initialization parameter byte
union portinit   {
    struct {
        unsigned wordlen  : 2;
        unsigned stopbits : 1;
        unsigned parity   : 3;
        unsigned brk      : 1;
        unsigned divlatch : 1;
    } initbits;
    char initchar;
};
//  parameters to initialize the com port
struct CommParameters    {
    int port;
    int parity;
    int stopbits;
    int databits;
    int baud;
};
//  CommPort class
class CommPort {
    friend class Modem;
    portinit initcom;
    char *mp_recvbuff;
    bool xonxoff_enabled;
    char *mp_nextin, *mp_nextout;
    int buffer_count;
    CommParameters commparms;
    bool waiting_for_xon;
    bool waiting_to_send_xon;
    static CommPort *mp_CommPort;
    int timeout;
    static Timer serialtimer;
    int BasePort()
        { return (0x3f8-((commparms.port-1)<<8)); }
    int TxData()
        { return BasePort(); }
    int RxData()
        { return BasePort(); }
    int DivLSB()
        { return BasePort(); }
    int DivMSB()
        { return BasePort()+1; }
    int IntEnable()
        { return BasePort()+1; }
    int IntIdent()
        { return BasePort()+2; }
    int LineCtl()
        { return BasePort()+3; }
    int ModemCtl()
        { return BasePort()+4; }
    int LineStatus()
        { return BasePort()+5; }
    int ModemStatus()
        { return BasePort()+6; }
    int irq()
        { return 4-(commparms.port-1); }
    int vector()
        { return 12-(commparms.port-1); }
    int ComIRQ()
        { return ~(1 << irq()); }
    void CommInterrupt();
    friend void interrupt newcomint(...);
    friend void interrupt newtimer(...);
public:
    CommPort(const CommParameters& cp);
    ~CommPort();
    void Initialize();
    int readcomm();
    bool writecomm(int ch);
    void clear_serial_queue();
    bool carrier()
        { return (inp(ModemStatus()) & RLSD) != 0; }
    bool input_char_ready()
        { return mp_nextin != mp_nextout; }
    void SetTimeout(int to)
        { timeout = to; }
    const CommParameters& CommParms()
        { return commparms; }
    void EnableXonXoff()
        { xonxoff_enabled = true; }
    void DisableXonXoff()
        { xonxoff_enabled = false; }
};
#endif

Listing Two

// - serial.cpp
#include serial.h
static void interrupt (*oldtimer)(...);
static void interrupt (*oldcomint)(...);
Timer CommPort::serialtimer;
CommPort *CommPort::mp_CommPort;
//  ISRs to count timer ticks
void interrupt newtimer(...)
{
    (*oldtimer)();
    if (CommPort::serialtimer.running())
        CommPort::serialtimer.countdown();
}
//  serial input interrupt service routine
void interrupt newcomint(...)
{
    CommPort::mp_CommPort->CommInterrupt();
}
void CommPort::CommInterrupt()
{
    int c;
    outp(PIC00,EOI);
    if (mp_nextin == mp_recvbuff+BufSize)
        mp_nextin = mp_recvbuff;     // circular buffer
    c = inp(RxData());               // read the input
    if (xonxoff_enabled)
        if (c == xoff)               // test XON
            waiting_for_xon = true;
        else if (c == xon)           // test XOFF
            waiting_for_xon = false;
    if (!xonxoff_enabled || (c != xon && c != xoff))    {
        *mp_nextin++ = (char) c;     // put char in buff
        buffer_count++;
    }
    if (xonxoff_enabled && !waiting_to_send_xon &&
            buffer_count > Threshold)    {
        while ((inp(LineStatus()) & XmitDataReady) == 0)
            ;
        outp(TxData(), xoff);        // send XOFF
        waiting_to_send_xon = true;
    }
}
CommPort::CommPort(const CommParameters& cp) : commparms(cp)
{
    mp_CommPort = this;
    mp_recvbuff = new char[BufSize];
    mp_nextin = mp_nextout = mp_recvbuff;
    xonxoff_enabled = true;
    buffer_count = 0;
    waiting_for_xon = false;
    waiting_to_send_xon = false;
    oldtimer = 0;
    oldcomint = 0;
    timeout = 10;
    Initialize();
}
CommPort::~CommPort()
{
    delete mp_recvbuff;
    if (oldcomint)    {
        setvect(vector(), oldcomint);
        oldcomint = 0;
    }
    if (oldtimer)    {
        setvect(systimer, oldtimer);
        oldtimer = 0;
    }
}
//  initialize the com port
void CommPort::Initialize()
{
    initcom.initbits.parity   =
        commparms.parity == 2 ? 3 : commparms.parity;
    initcom.initbits.stopbits = commparms.stopbits-1;
    initcom.initbits.wordlen  = commparms.databits-5;
    initcom.initbits.brk      = 0;
    initcom.initbits.divlatch = 1;
    outp(LineCtl(), initcom.initchar);
    outp(DivLSB(), (char) ((115200L/commparms.baud) & 255));
    outp(DivMSB(), (char) ((115200L/commparms.baud) >> 8));
    initcom.initbits.divlatch = 0;
    outp(LineCtl(), initcom.initchar);
    //  intercept the timer interrupt vector
    if (oldtimer == 0)    {
        oldtimer = getvect(systimer);
        setvect(systimer, &newtimer);
    }
    //  hook serial interrupt vector
    if (oldcomint == 0)    {
        oldcomint = getvect(vector());
        setvect(vector(), &newcomint);
    }
    outp(ModemCtl(), (inp(ModemCtl()) | DTR | RTS | OUT2));
    outp(PIC01, (inp(PIC01) & ComIRQ()));
    outp(IntEnable(), DataReady);
    outp(PIC00, EOI);
    // - flush any old interrupts
    inp(RxData());
    inp(IntIdent());
    inp(LineStatus());
    inp(ModemStatus());
}
int CommPort::readcomm()
{
    CommPort::serialtimer.set(timeout);
    while (!input_char_ready())
        if (CommPort::serialtimer.timed_out())
            return 0;
    if (mp_nextout == mp_recvbuff+BufSize)
        mp_nextout = mp_recvbuff;
    buffer_count;
    if (waiting_to_send_xon && buffer_count < SafetyLevel)  {
        waiting_to_send_xon = false;
        writecomm(xon);
    }
    return *mp_nextout++;
}
bool CommPort::writecomm(int ch)
{
    while (waiting_for_xon)
        ;
    CommPort::serialtimer.set(timeout);
    while ((inp(LineStatus()) & XmitDataReady) == 0)
        if (CommPort::serialtimer.timed_out())
            return false;
    outp(TxData(), ch);
    return true;
}
void CommPort::clear_serial_queue()
{
    mp_nextin = mp_nextout = mp_recvbuff;
    buffer_count = 0;
}

Listing Three

//  modem.h
#ifndef MODEM_H
#define MODEM_H
#include <fstream.h>
#include serial.h
//  default modem control strings
#define RESETMODEM ~+++~ATZ
#define INITMODEM  AT&C1E0M1S7=35V1X4S0=0
#define DIAL       ATDT
#define ANSWER     ATS0=1
#define HANGUP     ~+++~ATH0S0=0
struct ModemParameters    {
    char resetmodem[20]; // reset string
    char initmodem[50];  // initialize string
    char dial[10];       // dial command
    char answer[10];     // answer command
    char hangup[50];     // hangup string
};
const int MaxStrings = 15;
class Modem {
    CommPort *mp_comport;
    bool connected;
    const CommParameters& commparms;
    const ModemParameters& modemparms;
    bool WaitForConnect();
public:
    Modem(const CommParameters& cp, const ModemParameters& mp);
    ~Modem();
    bool Dial(const char *phoneno);
    void HangUp();
    void Answer()
        { CommandOut(ANSWER); }
    int WaitForStrings(char *tbl[]);
    void CommandOut(const char *s, int sendcr = true);
    void StringOut(const char *s);
    void FlushInput();
    bool CharOut(int c);
    bool InputCharReady()
        { return mp_comport->input_char_ready(); }
    int ReadChar()
        { return mp_comport->readcomm(); }
    bool TestCarrier()
        { return mp_comport->carrier(); }
    CommPort *Port() const
        { return mp_comport; }
};
#endif

Listing Four

//  modem.cpp
#include modem.h
Modem::Modem(const CommParameters& cp,
                      const ModemParameters& mp) :
                      commparms(cp), modemparms(mp)
{
    connected = false;
    mp_comport = new CommPort(commparms);
    CommandOut(modemparms.resetmodem);
    sleep(1);
    CommandOut(modemparms.initmodem);
    sleep(1);
    mp_comport->clear_serial_queue();
}
Modem::~Modem()
{
    HangUp();
    sleep(1);
    CommandOut(modemparms.resetmodem);
    sleep(1);
    delete mp_comport;
}
// - write a character to the modem
bool Modem::CharOut(int c)
{
    if (mp_comport->writecomm(c))    {
        if (mp_comport->input_char_ready())
            mp_comport->readcomm();
        return true;
    }
    return false;
}
//  flush the input buffer
void Modem::FlushInput()
{
    while (mp_comport->input_char_ready())
        mp_comport->readcomm();
}
// - write a string to the modem
void Modem::StringOut(const char *s)
{
    while(*s && CharOut(*s))
        s++;
    FlushInput();
}
// - write a command string to the modem
void Modem::CommandOut(const char *s, int sendcr)
{
    while(*s)    {
        if (*s == ~)
            sleep(1);
        else if (!CharOut(*s))
            break;
        s++;
    }
    if (sendcr)
        CharOut(\r);
}
// - place a call
bool Modem::Dial(const char *phoneno)
{
    CommandOut(modemparms.dial, false);
    CommandOut(phoneno);
    connected = WaitForConnect();
    return connected;
}
//  hang up
void Modem::HangUp()
{
       CommandOut(modemparms.hangup);
    connected = false;
}
//  modem result codes
static char *results[] = {
    CONNECT\r\n,
    CONNECT 1200,
    CONNECT 2400,
    CONNECT 9600,
    CONNECT 14400,
    OK,
    RING,
    NO CARRIER,
    ERROR,
    BUSY,
    NO DIAL TONE,
    NO ANSWER,
};
// - wait for the modem to connect to the remote
bool Modem::WaitForConnect()
{
    int lbaud = 0;
    int rtn = false;
    while (lbaud == 0)    {
        rtn = WaitForStrings(results);
        switch (rtn)    {
            case 0:    lbaud = 300;   break; // CONNECT       
            case 1:    lbaud = 1200;  break; // CONNECT 1200  
            case 2:    lbaud = 2400;  break; // CONNECT 2400  
            case 3:    lbaud = 9600;  break; // CONNECT 9600  
            case 4:    lbaud = 19200; break; // CONNECT 14400 
            case 5:                          // OK            
            case 6:    break;                // RING          
            case 7:                          // NO CARRIER    
            case 8:                          // ERROR         
            case 9:                          // BUSY          
            case 10:                         // NO ANSWER     
            case 11:                         // NO ANSWER     
            case -1:   lbaud = -1;    break; // time-out      
            default:   break;                // anything else 
        }
    }
    return (lbaud != -1);
}
// - wait for one of a table of strings as input
int Modem::WaitForStrings(char *tbl[])
{
    int c;
    int done = false;
    char *sr[MaxStrings];
    for (int i = 0; tbl[i] != NULL; i++)
        sr[i] = tbl[i];
    while (!done)   {
        CommPort::serialtimer.set(60);
        while (!mp_comport->input_char_ready())
            if (CommPort::serialtimer.timed_out())
                return -1;
        c = mp_comport->readcomm();
        for (i = 0; tbl[i] != 0; i++)    {
            if (c == *(sr[i]))   {
                if (*(++(sr[i])) == \0)   {
                    done = true;
                    break;
                }
            }
            else
                sr[i] = tbl[i];
        }
    }
    return i;
}

Listing Five

#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#include modem.h
CommParameters cp = { 1, 0, 1, 8, 2400 };
ModemParameters mp = {
    RESETMODEM,
    INITMODEM,
    DIAL,
    ANSWER,
    HANGUP
};
void dispchar(int ch)
{
    putch(ch);
    if (ch == \r)
        putch(\n);
}
int getkey()
{
    int ch = 0;
    if (kbhit())
        ch = getch();
    return ch;
}
int main(int argc, char *argv[])
{
    int ch = 0;
    cp.port = atoi(argv[1]);
    Modem *modem = new Modem(cp, mp);
    if (argc > 2)    {
        cout << Dialing  << argv[2] << \n;
        if (modem->Dial(argv[2]))
            cout << Connected\n;
        else
            cout << Call aborted\n;
    }
    else    {
        cout << Waiting for call\n;
        modem->Answer();
        while (!modem->TestCarrier())
            if ((ch = getkey()) == 27)
                break;
        if (ch != 27)
            cout << Call received\n;
        else
            cout << Aborted\n;
    }
    while (ch != 27 && modem->TestCarrier())    {
        for (;;)    {
            if ((ch = getkey()) == 27)
                break;
            if (ch)    {
                modem->CharOut(ch);
                dispchar(ch);
            }
            if (modem->InputCharReady())    {
                int ch = modem->ReadChar();
                dispchar(ch);
            }
        }
    }
    cout << \nHanging up\n;
    delete modem;
    return 0;
}
DDJ

Copyright © 1995, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.