The Crypt

Here is a small collection of programs that I wrote back in the day. It isn't at all comprehensive; it is simply what just happened to survive. It is likely that this of no interest to anybody but me, but if nothing else, it has been a fun exercise in nostalgia to put together.

The high school years, 1978-1982:

I attended the University of Illinois from 1982-1985, but I have saved nothing from that period. My first job out of college in 1985 was for a minicomputer manufacturer that was in financial trouble. I did all the work that was asked of me, but often there wasn't a lot to do, plus I essentially spent every waking hour at work, tinkering on personal projects.

That first job lasted only a year, and I moved to a maker of OCR equipment. Palantir/Calera Recognition Systems was my first in-depth exposure to C and the unix mindset.

1978-1980: Wang 2200 BASIC Immersion

Warning: this section has nothing remarkable or interesting. It is simply the story of my earliest programming recollections.

Back in junior high, I was quite interested in electronics, but had little money and mostly dreamed about making cool stuff. I'd go to the library and read each issue of Popular Electronics every month, or check out back issues to take home. During 8th grade, I attempted to design a tic-tac-toe machine using TTL logic. However, being untutored, I took the brute-force approach, and simply built combinational logic for every possibility that I could see, and didn't take advantage of symmetries of the board to simplify things. Realizing that even if my design was correct, it would take more than $100 to buy all the TTL parts it required, so it was never built.

In the mid 70s, there were more and more articles about affordable computers. Then the Radio Shack started selling their TRS-80 Model 1, and I could see one with my own eyes. A computer store, The Byte Shop, opened up down the street from the library, so I would always stop by there on my way home and look at the things in the store front for a while.

In the fall of 1978 I was a freshman in high school. There was an announcement a couple weeks into the school year that anybody interested in learning about computers should come by room at 3:10 pm. So I went.

A math teacher, Mr. Cermak, would drive over after school from the other campus to supervise us underclassmen (and we were all boys, never any girls). It was always a bit of a crap-shoot. He usually came, but not always, and we'd never know in advance if he was coming until after the bus home had left. If he didn't show, we'd have to kill an hour until the late bus came to do its rounds.

That room contained two Wang 2200 microcomputers. Each had 8 KB of RAM and used a digital cassette drive for program and data storage. One machine was on casters allowing it to be rolled into math classrooms for demonstrations. The other was tied to the line printer and the card reader.

That first day after school, the sophomores went about using the computers while Mr. Cermak sat down the two or three freshman that showed up that day. Before we could use the computers, we had to read a few pages from an "intro to BASIC" guide and then answer some questions, both to orient us and to make sure we were interested. I went home, read it, and it made complete sense. I went back to some electronics magazine from a few months before that had an introduction to BASIC programming. I read the article again and the program was easy to follow, something very much like this:

    10 LET R=0
    20 FOR T=1 TO 10
    30 LET A=INT(RND(1)*10)
    40 LET B=INT(RND(1)*10)
    50 PRINT "WHAT IS "; A; " + "; B;
    60 INPUT S
    70 IF S=A+B THEN 100
    80 PRINT "I'M SORRY BUT THE CORRECT ANSWER IS "; A+B
    90 GOTO 120
    100 PRINT "CORRECT!"
    110 LET R=R+1
    120 NEXT T
    130 PRINT "YOU GOT "; R; " OUT OF 10 RIGHT"
    140 PRINT "YOU GOT "; 10-R; " OUT OF 10 WRONG"
    150 END

I rewrote the program to be more general: it would ask how many questions you wanted, and it would randomly ask an addition, subtraction, multiplication, or division problem. The next day I returned, passed Mr. Cermak's readiness quiz, and got my 15 minutes on the computer. Although the lab was open for around 50 minutes, there usually were more people there than computers. That 15 minutes was just enough time to enter my program and see it work. But I didn't have a cassette tape, so when my time was up, the next guy simply typed "CLEAR" to wipe out my program. I went home, asked my mom for $8 to buy a Verbatim data cassette tape, and was ready for the next time.

Even though I only had 15 minutes or so per day to program, I would spend the rest of the time looking over the shoulder of the more experienced guys there picking up bits of syntax, or I would leaf through the Wang BASIC syntax manual that was out on the table. Wang BASIC has some pretty idiosyncratic commands. On some days when Mr. Cermak didn't show up, I'd go to the library and enter my latest opus on punch cards, which I could then rapidly read into the machine the next time I had access.

Immediately after conquering the math quiz program, I set about writing an program for a problem which interested me: tic-tac-toe. Finally I would be able to build my tic-tac-toe game without having to buy lots of TTL. I set about it the same way I set about the digital design: no FOR loops, not using board symmetries, just a long linear list of IF-THEN statements checking for all the possibilities. The program was never finished. After seeing some of the things other students were doing, I was inspired to try something new.

Because of the limited time on the machine, my approach was this. I had a spiral-bound lined notebook where I would print out my programs, using only every other line so I could squeeze edits between the lines after simulating the program flow in my head and finding problems. I always used pen because of some hangup about hating the smell of #2 pencils, so there was a lot of scratching out and arrows to reorder lines. One notebook filled, up, then another, then another. I probably wrote and mentally debugged five programs for every one that I actually keyed into the computer.

Because I had no money, that one Verbatim cassette tape had to hold all of my programs. When a new program was saved to tape, I was also sure to save a number of blank blocks after it to allow it room to grow without clobbering the subsequent program on the tape.

The tape drive on the Wang was much more reliable and sophisticated than the home cassette recorders that hobby computers, like the TRS-80, used. In addition to running faster and having digital read/write electronics instead of audio grade circuits, the tape would run something like 3x faster than a normal tape to allow accessing more bytes per second. Files were written in 256 byte blocks; each block was written twice for redundancy, so that if there was an error in one block the other was often readable and no data loss occurred. However it did sometimes happen, and everyone there learned to dread the sound the drive would make as the relays would clack on and off causing the tape drive to read over the bad data block ten times hoping one would result in a good read. Tension would mount with each retry until either the tape would resume normal operation (phew!) or it would throw an error about having a bad block (@#$*!) and come to rest.

Someone had written a program that would solve mazes. The maze pattern itself had to be edited into the program as a bunch of DATA statements. It was fun to watch the cursor march its way around the screen until it hit the exit. Inspired by this, I tried to write a maze builder. It would start with a fully occupied maze, and as it marched around, it would knock down walls, but it was constrained to not knock down a wall if it would break through to already plowed ground. The resulting mazes were mostly trivial to solve.

One of my ambitious but never completed projects was to write a BASIC-to-FORTRAN translator. That seemed much more tractable than the reverse direction, plus restricting the permitted BASIC syntax to a small but useful subset helped. Even so, it was way too much to ask to fit into an 8K computer, even after resorting to multiple passes with chained programs to do each pass.

Because memory was limited, one tended to develop a rather terse, dense, and unreadable coding style. Putting two statements on one line had an overhead of just one byte, for the ":" character. Splitting them over two lines had an overhead of four bytes, so packing saved three bytes. Single letter variable names were used whenever possible, as using a two character variable name (eg, "A3" or "B0") cost one extra byte for each use. This pressure was so great that it was very common for "A" to hold one thing on certain lines, mean something else on a different group of lines, etc. Each space cost a byte, so there was no use for using whitespace for clarity. Likewise, REM statements were eschewed, both for using up bytes needlessly, and because they slowed down program interpretation. Code tended to look like this snippet:

    150PRINT HEX(010C0C):SELECT P4,PRINT 205:PRINTUSING 40,A,N,V:SELECT P,PRINT 005:PRINT HEX(01);
    160R=1+RND(1)*3.6:I=INT(8*RND(1))+1
    170I=I+1:FOR T=1TO 13:IF I=TTHEN 180:IF H<>TTHEN 230:PRINT TAB(W);"!";:GOTO 230
    180IF I<>HTHEN 210:IF INT(I*R)+3<=WTHEN 190:IF INT(I*R)>WTHEN 200:T=13:NEXT T:GOTO 310
    190PRINT TAB(I*R);">R>";TAB(W);"!";:GOTO 230
    200PRINT TAB(W);"!";TAB(I*R);">R>";:GOTO 230
    210IF H<>I+1THEN 220:IF INT(I*R)+3<=WTHEN 220:IF INT(I*R)>WTHEN 220:T=13:NEXT T:GOTO 310
    220PRINT TAB(I*R);">R>";
    230PRINT TAB(63):KEYIN A$,240,260:GOTO 270
    240IF A$<"1"THEN 270:IF A$>"3"THEN 270:CONVERT A$TO B:IF R$(B)<"1"THEN 270:W=15*B:H=13:V=V-1:ADD(R$(B),FF):IF R$(B)>"0"THEN 250:R$(B)=" "
    250PRINT HEX(0D010C0C0C):PRINTUSING 30,R$(1),R$(2),R$(3):PRINTUSING 40,A,N,V;:PRINT HEX(0D01);:FOR B=1TO T:PRINT :NEXT B:GOTO 270
    260IF A$>HEX(01)THEN 270:W=W-1+2*VAL(A$)
    270NEXT T:PRINT HEX(01);:H=SGN(INT(30*RND(1)))*(H-SGN(H)):IF I<7+SGN(INT(20*RND(1)))*7THEN 170:N=N+1:IF I=14THEN 280:PRINT HEX(01);"VERSAGER...":GOTO 120
    280R=INT(I*R):FOR I=1TO 3:IF ABS(R-I*15+1)<=1THEN 290:NEXT I:IF ABS(R-57)<3THEN 330:GOTO 120
    290IF R$(I)<"1"THEN 300:V=V-VAL(R$(I))+48:R$(I)="%":PRINT HEX(01);"SILO";I;"ZERSTOERT...";HEX(0C0C0C0C0C0C):SELECT P6:PRINT TAB(15*I-2);HEX(5C0A5C0A250C2F0C2F08080821080A21)
    300I=3:NEXT I:GOTO 120

The only reason there are spaces at all is that Wang BASIC tokenized the keywords into a single byte and some keywords had a space automatically appended after the keyword when listed.

Unquestionably, my most accomplished game was my own version of TREK, called STARTREK. I had seen listings and printouts of this game in some book or other, but I had no desire to type it in, nor even to play it. The fun was writing my own. So without studying the code or game logic at all, just the printouts showing sector maps and such, I created my own. It was in constant evolution and other kids would grab a copy to play; no two every played the same version. I stole a smaller real-time shoot-em up that another kid wrote based on the Star Wars tie-fighter scene and modified it for my game. If the player got to the last sector that had Klingons to destroy, the program would chain to the tie fighter program for the final shootout. I'm hoping against hope that one day one of these kids will contact me saying they still have their tape in a shoe box somewhere.

Those early experiences certainly didn't install any sense of structured programming, and perhaps in some way hindered my development as a programmer. But the sheer excitement of writing those early programs will never be topped. Back then writing a twenty line program could be thrilling and deeply rewarding; now, it seems like I need to write 2000 lines before the program even starts to do something interesting. The ante is simply a lot higher now.

As a junior in high school, I was attending that other campus, and the old computer lab was two miles away. The North campus did have one Wang microcomputer, but they also had two TRS-80 Model 1 Level IIs. The TRS-80s seemed cheaply made and unreliable, but they had a few advantages as well. They had 32 KB of RAM; they had disk drives; they could do block-level graphics; they could be programmed in assembly language. Over time I used the 2200 less and less, and the TRS-80s more. My senior year, the school purchased thirty brand spanking new TRS-80 Model IIIs. These were better built than the Model I, were much more reliable, and there was never a wait to use one. One chapter closed; another opened.

1981: Maze Game for TRS-80 BASIC

maze game screenshot

(click on image to watch a short youtube clip of gameplay)

MAZE is a simple game, and it really isn't a maze. Using the cursor keys, navigate your "*" starting in the upper left to the X's around the "FINISH" box in the bottom right of the screen. As time passes, more and more obstacles get in the way as they randomly appear, ultimately blocking any hope of success. So be fast. One charm of this is that the program is very simple.

To run it under an emulator, download the zip file, extract the virtual disk image (JimBattle.dsk), "insert" it into the emulator's virtual floppy drive 0, and reboot. After filling in the date and time, you should get a TRSDOS 1.3 command line. Type:

    BASIC MAZE/BAS

You can also play it right now in your browser.

1981: Z80 Disassembler for TRS-80 BASIC

DISASSEM.BAS was a Z80 disassembler I wrote in TRS-80 Level II BASIC. Although it isn't very fast, it only had to be faster than the printer. It was used to dump the entire Model III ROM to a stack of green lined, fan-fold paper. The goal was to decode it and understand how the BASIC interpreter worked and perhaps find some useful routines. Hours were spent pouring over it and writing comments in the margin about what it was doing. Eventually, after a couple of years, it all got tossed. In the end it was mostly about the puzzle of figuring out the intent of the code, rather than being of any practical use.

1982: Pong for the TRS-80 Model 3

pong game screenshot

(click on image to watch a short youtube clip of gameplay)

This was a fun little version of Pong with a twist. I believe it was my first Z80 assembly language program. I added some code to give it sound effects, but of course to hear them on a real TRS-80 you'd have to plug a small amplifier into the cassette port. Fortunately, the popular TRS-80 emulators support this feature.

One issue might be the paddle control. The key choice made sense on the Model III, but what keys they map to on a PC keyboard depends on the emulator. The original used the up and down arrows on the left side of the keyboard to control the left paddle, and the right arrow/clear keys for the right player paddle.

Here is the source code.

To run it under an emulator, download the zip file, extract the virtual disk image (JimBattle.dsk), "insert" it into the emulator's virtual floppy drive 0, and reboot. After filling in the date and time, you should get a TRSDOS 1.3 command line. Type:

    PONG

You can also play it right now in your browser.

1982: Symbolic Differentiator for the TRS-80 Model 3

In senior year of high school, for the Calculus class, one required project was to write a paper, e.g., the story behind some important figure in math or the story of the development of some key idea. The idea was to show students that writing skills could be used outside of the English classroom. I successfully lobbied to instead write a program that could do differentiation. That part is fairly mechanistic, and the heuristic part comes when trying to reduce the results.

This is what I came up with -- not great, but not terrible. There was limited time to develop it and I was happy with it. First, the equation which is entered is differentiated, using straight-forward algorithmic means. Then some simple heuristics are used to simplify things a bit, but nothing at all heroic is attempted. X*0 is changed to 0, X+0 is changed to X, X+X is turned into 2*X, etc.

Initially I started writing this in BASIC, but it was so terribly slow and clumsy that I scrapped that and wrote it in Z80 assembly language. The source code was too big to fit into memory at once, so I split it up into two pieces: DIFFV3.SRC and REDUCE.SRC. After each was assembled, I had to somehow had to load them into memory separately, then save the combined memory image to get the final program.

My math teacher encouraged me to enter some programming exhibit for high schoolers. They let me borrow a Model III so I went. I won third place, and the prize was a TI-57 programmable calculator, which I never really used.

To run it under an emulator, download the zip file, extract the virtual disk image (JimBattle.dsk), "insert" it into the emulator's virtual floppy drive 0, and reboot. After filling in the date and time, you should get a TRSDOS 1.3 command line. Type:

    DIFF

and you are ready to start using it.

You can also use it right now in your browser.

Use:

From the TRSDOS command line, enter one of these two program names
    DIFFV3    (differentiator without expression reduction)
    DIFF      (differentiator with    expression reduction)

Variables and constants are in lower case.
Functions are upper case.
Commands begin with the '$' escape.

">" is the input prompt

$Q<cr>                    quits

$S<cr>                    forces output to the screen

$C<'a'..'z'><cr>          declare one or more symbols to be constants
   eg: $C a                  declares 'a' to be a constant
       $C a,b,c              adds 'b' and 'c' to the list of constants
       $C def                adds 'd', 'e', and 'f' as well

$C<cr>                    lists any defined constants

$V<'a'..'z'><cr>          declare one or more symbols to be variables
   eg: $V r,s,t              declares 'r', 's', 't' to be variables
       $V xyz                declares 'x', 'y', 'z' to be variables
       $V a                  'a' is a variable, and is removed from
                                 the constant list if it was on it

$V<cr>                    lists any defined variables

Example use:

    >x
    With respect to what variable?  x
    1

    >a*x
    With respect to what variable?  x
    a+x*da

    >$C a
    >a*x
    With respect to what variable?  x
    a

    >x+x
    With respect to what variable?  x
    2

(note on TRS32, Matthew Reed's emulator, the exponentiation operator is
 entered by hitting the up arrow, and it appears on the screen as '[')

    >x^2
    With respect to what variable?  x
    2*x

Function names are all upper case:

    >x*COS(2*x)
    With respect to what variable?  x
    COS(2*x)-SIN(2*X)*2*x

The pattern matching is not very smart at all and misses many obvious ways
to simplify the result:

    >x/x
    With respect to what variable?  x
    (x-x)/(x^2)

    >x*(x+3)
    With respect to what variable?  x
    x+x+3

    >COS(X)+COS(X)
    With respect to what variable?  x
    -(SIN(x)+SIN(x))

A buggy parser:

    >x
    With respect to what variable?  x
    1

    >-x
    With respect to what variable?  x
    -1

    >--x
    With respect to what variable?  x
    -1

    >-(-x)
    With respect to what variable?  x
    1


Predefined constants:
    PI  3.14159...
    E   2.71828

Functions that it knows about:
    SIN(...)
    COS(...)
    TAN(...)
    CSC(...)
    SEC(...)
    COT(...)
    SIN'(...)
    COS'(...)
    TAN'(...)
    COT'(...)
    SEC'(...)
    CSC'(...)
    SINH(...)
    COSH(...)
    TANH(...)
    CSCH(...)
    SECH(...)
    COTH(...)
    SINH'(...)
    COSH'(...)
    TANH'(...)
    COTH'(...)
    CSCH'(...)
    SECH'(...)
    LN(...)

1985: A 68000 Microcomputer

When I first started working at my first job out of college in 1985, I spent all my time at work, and there wasn't a lot to do, so I decided to build myself a computer. I already owned Steve Ciarcia's Build Your Own Z80 Computer. Although I didn't use his design, I was inspired by it and designed my own Z80 computer, on paper. When I noticed that Motorola 68000 microprocessors (in that huge 64-pin DIP) were selling for $28, I decided to start over and build a 68K computer.

I used a couple of S-100 wirewrap cards and an S-100 backplane, although I didn't bother following the standard; to me it was just a convenient way to route a bunch of wires between two boards. It has 128 KB of DRAM, a DUART, a timer chip, and a couple of EPROMs for the monitor. Altogether, the parts cost around $280, including 128 KB of DRAM.

Luckily, one of the designs at work (a serial communication controller) used a 68000, so I had an assembler available. I started with their monitor, which they had mostly taken from some open source (although it wasn't called that then) university project, VuBug. I added many new commands, fixed some bugs, and generalized some things. The biggest addition was adding an assembler and disassembler. I never did anything with it once it was working reliably and I had polished up the monitor program.

All the files are bunched up into this one zip file. The individual files are here:

1986: A Toy Modula Compiler

It is gone now, but I loved visiting the Computer Literacy bookstore in Sunnyvale. It was stocked with many technical books and it was a danger that I might spend my entire salary picking up interesting books. Having bailed out of a Computer Science degree in trade for an Electrical Engineering degree, I never had any advanced computer science classes. Compensating for this, I bought "the dragon book," better known as Compilers: Principles, Techniques, and Tools, by Aho, Sethi, and Ullman. Visiting family during Christmas week that year, I read a large part of the book and came back ready to put the new-found knowledge to work.

After returning to Sunnyvale and Computer Literacy, I bought Pascal Implementation: The P4 Compiler and Interpreter, by Pemberton and Daniels. Steven Pemberton has kindly made the full text of the book available online for free. I often consulted this book to see how things were done, like how error handling is performed during recursive descent parsing. I don't think I stole very much code from it, mainly ideas. It actually came as two small books: one provided the code, and the second was the commentary for each block of code, and went into far more detail than would make sense for inline comments in the code itself.

As the computer system I was on had a Pascal compiler, and succumbing to the idea that Wirth's next language, Modula, would be even better, I decided to write a Modula compiler in Pascal. I worked on it on evenings and weekends along with my other hobby projects. It compiled to a P-CODE like intermediate representation, which was then interpreted. Unlike the P-CODE system, which has a stack based model, this project used a two or three address instruction model.

I made two mistakes in this project. First, I didn't break up the compilation process into passes -- I would do a recursive descent of the syntax, generate code, and perform register allocation on the fly. Although Wirth can write a compiler like that in his sleep, for a first timer it would have been better to break it into smaller conceptual stages. Second, I didn't plan anything; I just started coding and cobbling things in as required.

At the time I stopped working on it, it could compile and run simple programs, but it was still buggy and didn't support independent compilation units, which was the whole point of Modula. When the flaws of my approach started choking progress and fixing it all would have needed a major rewrite, I put it aside for more fun projects and never came back to it.

Because the local dialect of Pascal didn't support independently compiled units, the compiler was one big program and the intermediate code interpreter was another, smaller program. The format of the intermediate code was documented as well.

Here it is all wrapped up into a zip file.

1986: A Chess Program

thumbnail of chess program

One of the other new engineers that I worked with had spent an inordinate amount of time writing a tic-tac-toe program, except he never got around to adding the logic that would allow a human to play against the machine. It only allowed games of human vs human tic-tac-toe. For childish reasons, I decided to show him up by writing a chess program in a single weekend. This is it, although I did spend a few more evenings polishing the display, adding better error messages, and supporting more terminal types. Because of the simplicity of the approach, the program is quite understandable.

Note that this is a extremely simple minded: it has no book openings, no specialized endgame routines, a scoring function based purely on the sum total of the value of the pieces (no positional scoring). The opening moves are haphazard (specifically, random), and if it somehow manages to be ahead at the end of the game, it will push pieces around aimlessly until the human makes a mistake that the computer can capitalize on. It simply plays legal chess and can look far enough ahead to probably beat a beginning player.

It is also interesting to note how much faster computers have gotten in the past 25 years. The HELP page says this about the response time for various levels of lookahead:

    Level 1:  1 second
    Level 2:  3 seconds
    Level 3: 45 seconds
    Level 4: 15 minutes

On a 2010-era PC, level 4 responds in under a second. I never played a game at level 4, and maybe played a level 3 game only once or twice for a lack of patience. It would have been trivial to support deeper lookahead, but at the time it was prohibitively slow, so it was disallowed.

original_CHESS.P is the exact source from either 1985 or 1986, recovered from a 5.25" floppy disk. It contains some idioms of the local dialect of Pascal. It has been modified to use standard pascal, and the code for various terminal support has been stripped out (if you want to put support back in for the hazeltine 1500 serial terminal, please go right ahead).

The stripped down version is named CHESS.P. It was compiled using version 2.4.2 of Free Pascal. It seems like a very professionally done piece of software, and is in active development. Although free pascal has many "units" available that I could probably use to spiff up the user interface, I have decided to remain true to my original vision (i.e., I'm being lazy).

You can download a zip file containing the sources and a precompiled executable, suitable for running on any win32 machine.

1986: My First C Program

After a few rounds of layoffs at my first employer, I left for a new job, at Palantir Corporation. There, I worked on a Wyse serial terminal connected to a Sun computer somewhere in the building. This was my first real use of a unix system and I liked it. Years before I had read about the Tiny C compiler in Dr. Dobbs, but the language seemed primitive. Once I had been indoctrinated into the unix mindset, my adjective for C changed from primitive to spunky.

The best way to learn is to do, so I decided to write an anagram generator. Type in a phrase and using /usr/dict/words, it would generate all the word combinations that could be spelled with those letters. I came up with an efficient algorithm quickly, but getting my head wrapped around the naked pointers that C provides took a bit of time.

This is also about the time that I started using awk, and frequently, back before perl existed.

1986: An Interesting Finite State Machine Design Technique

In 1986, I left my first employer and joined The Palantir Corporation, in Santa Clara, CA. Later they changed their name to Calera Recognition Systems, Inc. Calera made high end general purpose OCR equipment.

At the time I joined, Calera was shipping a $30K box consisting of a scanner and five 68000 microprocessors lashed together into a parallel processing pipeline. One of my first jobs was to help evaluate what performance benefit there would be in upgrading one or more of the processors to a 68020 design. The 68020 not only had a wider memory interface, it was capable of running at a higher speed. I designed a daughter card containing a 68020, boot a bank of DRAM, a couple PALs, and a few discrete TTL parts. A 68000 CPU of the original design would be unplugged and this daughter card would plug into that narrow socket. Surprisingly, it worked pretty well.

The cycle time was shorter and the timing constraints tighter, so I came up with an interesting approach to designing the memory controller logic. This approach allowed resolving timing to half a clock period without running a double frequency clock around. I used this trick in a few designs there.

In late 1988, my then girlfriend was applying to some graduate schools, and not knowing what I'd do without her, I applied to a couple myself, in a rather half-hearted manner as I really was happier getting paid for my work.

As part of my application, I had to supply a sample of something interesting I had worked on, and I chose to write a small exposition of my finite state machine design. The document was saved in Pagemaker 3 format, but I never again had access to this program, so the write-up has been locked away for more than twenty years. With a bit of effort, I was able to convert it to PDF.

Feedback

If you want to contact me for whatever reason, try me at jim@thebattles.net.