Channels ▼

Embedded Systems

You've Come a Long Way, Baby

Source Code Accompanies This Article. Download It Now.

Jan01: C Programming

Al is a DDJ contributing editor. He can be contacted at [email protected]

Having made it this far through the magazine, you can't help but know that this issue celebrates Dr. Dobb's Journal's 25th anniversary. To get an idea about how far we've come, let's look at the very first issue from January, 1976. It had a total of six articles. Five were about TinyBasic and one of those was a reprint from People's Computer Company (PCC), an earlier publication by the publishers of DDJ. The sixth article consisted of letters to the editor. Because this was DDJ's first issue, the letters were to the editors of PCC. One of the letters included a schematic for a scientific calculator. In 1976, I suppose, people built their own calculators. Establishing the DDJ tradition, the issue contains several source-code listings, mostly assembly language. The listing for TinyBasic Extended, however, is just over two pages of 8080 machine language in octal notation. If you wanted the program, you toggled the machine-language code in by using the front panel switches of your Altair 8800 computer. Some of the listings are in fonts so minuscule you can hardly read the code. If you don't have a copy of that first issue, take a look at any of the listings in this one. Some things never change.

This retrospective got me thinking about what I was doing 25 years ago. Using Dr. Dobb's Journal Volume 1, Number 1 as a guide, you might conclude that no substantial tools existed for microcomputer programmers then, and that people had to toggle in machine-language programs whenever they wanted to run them; but that was not the case. It's just that there were no really cheap tools available, and cheapness was an essential ingredient to the survival of home computing and programming as a hobby.

During the year prior to DDJ's birth, I wrote firmware for an embedded system that my brother Fred designed. I've mentioned the project here before — a telephone call accounting system that logged telephone usage and activity by sensing the telephony events of a PBX. To do 8080 assembly-language firmware development, Fred and I borrowed an Intel MDS system, an 8080-based microcomputer with 8-inch floppy disks, an editor, assembler and debugger, a teletype console device, an EPROM programmer, and a real disk operating system, a variety of CP/M, which was an 8-bit ancestor of MS-DOS.

Hobbyist programmers were DDJ's main constituency then, and most hobbyists could not afford to buy Intel MDS microcomputers. But fundamental development tools did exist for professionals who had project development budgets (and for resourceful freelancers such as Fred and me who had the chutzpah to beg, borrow, and steal time from those with deeper pockets).

DDJ gets around to mentioning CP/M in the April 1976 issue and suggests that readers "very seriously consider obtaining a floppy disc subsystem — hardware and software — for your home computer." Good advice.

At about the same time, I was working a day job at the Pentagon. The spooks in khaki bought a bunch of dual screen, so-called "intelligent" terminals for their intelligence analysts to use. The Univac 1652 terminal had no disks or printer, just two screens, a light pen, a keyboard, some function keys, a serial port, 8 KB of RAM, and an 8080 microprocessor. By eliminating electromechanical devices, the device minimized RFI emissions, and presumably thwarted the nefarious electronic interception of sensitive data by the other side. That was before the wall came down, you see. There was no assembler or debugger. There was a bootstrap loader in ROM that would load and execute a program from the serial port. We hooked those terminals to DEC PDP-11 minicomputers, which did have a macro assembler, but for a different assembly language. Somewhere among the literature of the day, I found a library of PDP-11 macros that translated 8080 assembly-language statements into the binary codes associated with 8080 machine language, and that became our cross-assembler. I wrote a simple debugger to load into the terminal ahead of the applications program, and development was underway.

Those early microcomputers ran at the blazing speed of 2 MHz and had a maximum memory address space of 64 KB. That's right, KB, as in kilobytes, as in 1024, as in not very many. 16 bits wide. No segmented architecture. The spooky intelligent intelligence terminal had only 8 KB. The blue suits begrudgingly upgraded it from 4 KB after we civilians whined that we couldn't cram the text-processing software they wanted into 4 KB.

We've come a long way. The venerable 2-MHz 8-bitters were slow but reliable. Rugged, even. Maybe because we had to put them together and test them ourselves. Last Tuesday I went to Sam's (a WalMart discount outlet) and bought an appliance Compaq 1000-MHz Athlon desktop with DVD, CD/RW, 128 MB, 19-inch monitor, Sound Blaster PCIII, powered speakers, 56K fax/modem, five USB ports, a 60-GB hard drive, 10BaseT LAN card, and Windows 98. Wow! Blazingly fast, high capacity stuff. On Wednesday I returned this 21st century wonder to the store for a cheerful refund. It was the flakiest computer I have ever seen. It kept me up all night. It wouldn't even install the copy of Word 2000 bundled with it. The drawer on the DVD drive wouldn't open manually. The CD/RW drive wouldn't read most of my CDs. Lots of mysterious crashes — one that left the computer in an unbootable condition. In their haste to pump up those numbers for the performance-hungry cable modem Napster generation, computer manufacturers are pushing them out the door faster than they can test them. Maybe we haven't come that far, after all.

Quincy Gets a New Compiler

The folks at Inprise released their Borland 5.5 C/C++ compiler and Turbo Debugger as free downloadable software. The compiler is the one that underpins their commercial C++Builder package. I mentioned in an earlier column that I would like to port this compiler to Quincy 2000. For new readers, Quincy 2000 is an IDE that integrates a programmer's editor, compiler tools, and debugger. You can get it from DDJ (see "Resource Center", page 5) or from my web site at The compiler has always been one of the Win32 ports of the GNU C/C++ compiler suite, most recently the gnu-mingw32 port, which is open source and free, which is why I chose it. Mingw32 is slow to comply with Standard C++, however, and when Inprise announced its free compiler, which is more compliant and which I will call BCC55 for brevity, I decided to integrate it with Quincy 2000.

In recent months I discussed some modifications to Quincy that revealed to me what a sloppy programmer I can be. Integrating an improved editor was a particularly humbling experience because it slammed me square between the eyes with the paucity of encapsulation in the original design. Integrating BCC55 did it again. Quincy launches command-line compiler tools by redirecting stdin, stderr, and stdout, and running the command-line programs from within an independent Win32 thread. The main thread uses an idle loop to see if the user has done anything that might affect the compilation, such as click the Stop button, for example, and to watch to see when the compile and link process is complete.

After the editor fiasco, imagine my horror when I realized I had implemented the entire compile and link process in the CWinApp-derived application class. This wasn't even good structured programming, much less good object-oriented programming. This is how I wrote Cobol programs 30-plus years ago. Oh, the shame of it all.

In my defense, you could argue that the Quincy application integrates a compiler with other things, and that compiling could be thought of as being integral to the application class, and that further encapsulation is unnecessary. One could, but one would be wrong. Fortunately, I did separate the compile functions into their own source-code module, something that Developer Studio does not particularly encourage, so the pain of adding another compiler was lessened somewhat. But the real pain is not in the work it takes to make a major modification; the real pain comes with the realization that I did not design for such a likelihood, that I did not do a proper job as a programmer. It makes me wonder how much real-world software development similarly fails to provide for the future with extensible designs. Oh, I've read most of the books, articles, and essays about planning and design — I've even written some of them — but I wonder how often programmers do what I did out of convenience, slam the code into place, get it working, and move on.

I will not detail the differences between Quincy before and after the integration of BCC55, except to say there is now an abstract Compiler class from which classes GCCCompiler and BCCCompiler derive. Listing One is compiler.h, which defines the three classes. You can compare this approach with the earlier, shameful code by downloading the source code for Quincy 2000 and Quincy 99 ( and comparing compiler.cpp from both systems.

Quincy's Debugger

Quincy 2000 has its own debugger and does not use the gdb command-line debugger that accompanies gnu-mingw32. As I write this column, Quincy's debugger does not debug programs compiled with BCC55. This is because the two compilers use different formats for encoding debug information associated with an executable program. Gnu-mingw32 embeds debug information in the .exe file in a format called "stabs." Using documentation that describes stabs, I wrote code to extract and interpret that information when I originally wrote Quincy 96 four years ago. As C++ has grown and the compiler has matured, I've had to modify this code periodically to keep it current.

BCC55 encodes debugging information in a separate file with a .tds extension, and it uses a proprietary Borland format. Inprise released a DLL with functions that extract the debugging information, and I am now working with that DLL to integrate debugging into Quincy 2000 under the Borland compiler mode of development. In the meantime, you can launch the free Turbo Debugger from within Quincy 2000. TD is a DOS application that uses text-mode characters to simulate a windowing environment. Oldtimers will remember TurboVision and D-Flat, libraries that enabled this kind of development under DOS. Although TD uses the old user interface with text mode, it is really a Win32 console application.

There might be hope for me as a programmer yet. When I reviewed Quincy's debugging code to see what I'd have to do to integrate Borland debugging, I was delighted to find a good measure of encapsulation. There is a Debugger class that implements debugging activity. The Debugger class uses an embedded CStab class to extract debugging information from the executable. It's hard to imagine that the same programmer who did that nice piece of design also built the klutzy editor and compiler code for the original Quincy. When you download the code, take a look at debugger.cpp and debugger.h so that I might feel exonerated.

I beat myself up a lot when I consider how I've done certain things in this business, but during this particular project it occurred to me that Quincy 2000 might be the most complex piece of software I ever developed all by myself. Quincy has taken four elapsed years getting to where it is now, and maybe one labor year of effort, given that I've done other things during the same time. On the surface it doesn't look that complicated — less than 20,000 lines of code — but it comprises a lot of complex logic to implement an editor, debugger, and project manager, and integrate a Win32 GUI application with two different suites of command-line tools. Then there were all the goofy things I had to learn just to make Quincy work with the Win32 operating environment and MFC, much of which I've described in this column over the years. Fortunately, I had a lot of help from the folks who built the compiler suites, from other programmers who have built similar projects, and from programmers who download Quincy and promptly let me know when something needs fixing.

Autumn Leaves

I'm writing this from my Florida office in the middle of October. Tomorrow, Judy and I leave in the DobbsMobile (our DDJ logo-emblazoned motorhome) heading north to enjoy the changing of the seasons in Pennsylvania and Virginia and to attend Software Development East in Washington, DC, my home town. I make it a point every year at this time to be somewhere pleasant.

For, you see, this is the anniversary of the scariest two weeks of my life — the time in October 1962, when our young president and the premier of the USSR played chicken in the air and on the high seas over some Russian nuclear missiles sticking out of the ground in Cuba. I was a programmer at the CIA, and as the media remind us each year at this time, we harbored serious concerns about how many more sunrises, how many more moons, how many more changes of season we would see. It was indeed that close. It was particularly scary for me not just because Armageddon was near and inevitable, not just because I was virtually sequestered on the job, but because I had a new baby daughter at home about a week old. Looking into Sharon Ruth's crib as I prepared for the siege at work, watching her gurgle and burp and giggle and sleep, wondering about her future and doubting that she even had one was a solemn and frightening experience for a young father.

Last week, Sharon celebrated her 38th birthday on Columbus Day, one day before Friday the 13th, during a full moon. All those converging, interesting coincidences, and the prospect of another Autumn in the east, and the knowledge that my children and their children are healthy, happy, safe, and secure in a world that my generation damn near destroyed gives me much reason to be thankful. Happy birthday, kid.


Listing One

// ----- compiler.h
#ifndef COMPILER_H
#define COMPILER_H

#include "Quincy.h"

class Compiler  {
    enum CompileStatus { idle, preprocessing, compiling, assembling, 
        linking, buildinglibrary, finished, aborting, aborted };
    enum CompileAction { none, execute, step, turbodebugger };
    PROCESS_INFORMATION m_CompileInformation;   // for compiling
    bool m_bLinkCpp;            // true if linking c++
    bool m_bCpp;                // true if compiling c++
    bool m_bFoundDef;           // true if def is in project

    CompileStatus m_CompileStatus;
    CompileStatus m_OldCompileStatus;
    CompileAction m_CompileAction;
    CStringArray m_SourceFiles;     // source files to compile
    CStringArray m_ObjectFiles;     // object files to link
    CStringArray m_LibraryFiles;    // library files to link
    CStringArray m_ResourceFiles;   // resource files to bind

    CString m_strTargetName;    // path of compiler file to build &/or execute
    CString m_strFilename;      // source code file name without path
    CString m_strOldFilename;   // previous name compiled
    CString m_strDefname;       // .def file name
    CString m_strErrorFile;     // error stdout file from compiler

    CString m_strOFile;         // object file path and name
    CString m_strOPath;         // object file path
    CString m_strObjFiles;      // object file list
    CString m_strLibFiles;      // library file list
    bool m_bErrorsOpen;
    DWORD exitcode;
    DWORD ExitCode;
    CStdioFile m_ErrorCStdioFile;
    CErrorLogDialog* m_pdlgErrors;  // error log dialog box
    bool m_bErrorCreated;           // true if error log list has been created
    int m_nErrorLogWidth;           // width of widest message in error log
    // ---- error log
    struct el ErrorLog[maxerrors];
    UINT m_nErrorCount;             // # entries in error/warning log
    static UINT BldProg(void*);
    void CollectErrorMessages();
    void OpenErrorMessagesFile();
    void CloseErrorMessagesFile();
    void CompileStep();
    CString Enquote(const CString& str) const;
    bool RunCompilerProgram(CString& strCmd, bool bIsRC = false);
    virtual void BuildStdFiles() = 0;
    virtual void BuildResourceScriptCommand(CString& strCmd, 
                                    const CString& strResFile) = 0;
    virtual void BuildResFileCommand(CString& strCmd) = 0;
    virtual void BuildCompilerCommand(CString& strCmd, 
                                                 const CString& strFile) = 0;
    virtual void BuildLibCommand(CString& strCmd) = 0;
    virtual void BuildDLLCommand(CString& strCmd) = 0;
    virtual void BuildDefCommand(CString& strCmd, const CString& strFile) = 0;
    virtual void BuildLinkerCommand(CString& strCmd) = 0;
    virtual void ProcessErrorMessage(char* buf) = 0;
    virtual void CompileOneSource(const CString& strFile);
    bool OnIdle();
    bool CompileRunning() const
        { return m_CompileStatus != idle; }
    void SetAborting()
        { m_CompileStatus = aborting; }
    void SetBuildingCPP(bool bSet)
        { m_bLinkCpp = bSet; }
    void ClearArrays();
    void AddSourceFile(const CString& rstrSrc);
    void AddObjectFile(const CString& rstrObj);
    void AddResourceFile(const CString& rstrRc);
    virtual void BuildTarget();
    virtual void CompileAllSources();
    virtual int  GatherObjects();
    virtual void BuildLibraryTarget();
    virtual void BuildExeTarget();
    virtual void BuildDLLTarget();
    virtual void GatherLibraries();
    virtual CString MakeObjectFileName(const CString& str) const = 0;
    virtual void AddLibraryFile(const CString& rstrLib) = 0;
    void BuildTarget(const CString& strTargetName, 
                    CompileAction action = none, bool bUseTD = false);
    void ShowErrorLog();
    void ClearErrorLog();
    const CString& GetTargetName() const
        { return m_strTargetName; }
    const CString& GetErrorFile() const
        { return m_strErrorFile; }
class GCCCompiler : public Compiler {
    CString m_strPrevLine;      // previous error message line
    CString MakeObjectFileName(const CString& str) const
        { return str + ".o"; }
    void CompileAllSources();
    void CompileOneSource(const CString& strFile);
    void AddLibraryFile(const CString& rstrLib);
    void BuildStdFiles();
    void BuildResourceScriptCommand(CString& strCmd,
                                           const CString& strResFile);
    void BuildResFileCommand(CString& strCmd);
    void BuildCompilerCommand(CString& strCmd, const CString& strFile);
    void BuildLibCommand(CString& strCmd);
    void BuildDLLCommand(CString& strFile);
    void BuildDefCommand(CString& strCmd, const CString& strFile);
    void BuildLinkerCommand(CString& strCmd);
    void ProcessErrorMessage(char* buf);
    void GetLibraryPaths(CString& strCmd);
    void GetGUILibraries(CString& strCmd);
class BCCCompiler : public Compiler {
    CString MakeObjectFileName(const CString& str) const
        { return str + ".obj"; }
    void AddLibraryFile(const CString& rstrLib);
    void BuildStdFiles();
    void BuildExeTarget();
    void BuildResourceScriptCommand(CString& strCmd, 
                                           const CString& strResFile);
    void BuildResFileCommand(CString& strCmd);
    void BuildCompilerCommand(CString& strCmd, const CString& strFile);
    void BuildLibCommand(CString& strCmd);
    void BuildDLLCommand(CString& strCmd);
    void BuildDefCommand(CString& strCmd, const CString& strFile);
    void GetLibraryPaths(CString& strCmd);
    void BuildLinkerCommand(CString& strCmd);
    void ProcessErrorMessage(char* buf);


Back to Article

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.