I was introduced to the javax.comm package of classes when I discovered they were used in the development kit for the Java Ring. (For details on javax.comm, see Rinaldo Di Giorgio's Java Developer column in the May issue of JavaWorld: "Java gets serial support with the new javax.comm package.") During my mad rush at JavaOne to get a program into my ring, I ran into a variety of problems, not the least of which was communicating with the ring. I downloaded the distribution from the Java Developer Connection and tried unsuccessfully to use it to talk to the Java Ring. Later, I discovered the problem with my ring: I didn't have Dallas Semiconductor's legacy APIs installed correctly. With the ring working, I basically forgot about the communications package. That is, until one weekend about a month ago, which is the starting point for this story.
For many different reasons (mostly having to do with highly interactive simulated environments -- for example, games), the primary computer in my "lab" runs Windows 95. However, on this particular weekend I was more concerned with another computer that, in many ways, was about as powerful as the Java Ring: a Digital Equipment Corporation PDP-8/e.
The PDP-8 was arguably the first true personal computer. Designed in the late 1960s and produced in relatively high quantities in the 70s, the PDP-8 could be lifted by a single individual, was powered by 120-volt line current, and cost less than 0,000. Most of these computers shipped with a single peripheral: a Teletype Model ASR-33 terminal -- the original "TTY" in computer lingo.
The ASR-33 teletype was a printing terminal that came with a paper-tape reader and punch. Yep, it was paper tape, 1"-wide paper with holes punched in it, that was the primary storage medium for programs on the PDP-8.
The PDP-8 was the first computer I ever programmed and therefore it has a special place in my heart. Further, due to some fortuitous circumstances, I was in the right place at the right time and managed to save a PDP-8 that was going to be scrapped as junk. A photograph of my prize is shown below.
On this special weekend not too long ago, I decided to bring the PDP-8 back to life, if only to relive those precious early memories and to show my daughter just how good she has it with her "measley old 133-MHz Pentium."
Reviving one classic by simulating another
To begin my revival effort, I had to get a program into the PDP-8. On the PDP-8, this is achieved by following a three-step process:
Using the front-panel switches, the user "keys" a short program into the magnetic core memory. This program is called the RIM Loader, and its purpose is to load another program from paper tape that is in Read-in-Mode, or RIM, format.
RIM Loader loads the paper tape in RIM format. This tape contains a program called a BIN Loader, which can load programs from paper tape in binary (BIN) format.
- Finally, you run BIN Loader to load the program you really want, which is on a paper tape in BIN format. Whew!
After going through these three steps, the program you want to run is stored in core memory. All the user needs to do then is set the starting address and tell the machine to "go."
In my effort to revive the machine, Step 1 was no problem, but Step 2 involved the use of the paper-tape reader in the Teletype -- and I didn't have a Teletype. Of course, I did have my desktop computer, so the logical step was to simulate a paper tape reader on my desktop.
From a logical and programming standpoint, simulating a paper-tape reader is trivial. You simply read a file that contains the data from the "tape," send it out to a serial port at 110 baud (yes, only 10 characters per second), until you have exhausted the file. I could write a program in C on my Solaris system or my FreeBSD system in about 10 minutes that could do this -- but, remember, I was on a Windows 95 system, not a Unix system.
From bad to ugly and back again
I knew I could easily write this program in C, so that was my language of choice. Bad choice. I brought up my copy of Visual C++ 5.0 and whipped out a simple program called sendtape.c that called open()
on the communications port. I tried to set it into RAW mode (the mode in Unix where the operating system doesn't try to interpret anything on the serial port as user input) and then tried to compile it. Whoops, no ioctl()
function or tty
functions -- nada, zip, zilch!
No problemo, I thought to myself, "I've got the whole Microsoft Software Developer's Network library on CD with my C compiler; I will do a quick search on the keywords 'COM port'."
The search turned up many references to the Microsoft Component Object Model (also called COM), and also references to MSComm. MSComm is a C++ class that Microsoft supplies to talk to the serial ports. I looked at the examples and was appalled at how much code it would take to do such a simple thing as write bytes to the serial port at 110 baud. All I wanted to do was open the darned serial port, set its baud rate, and stuff a few bytes down it -- not create a new class of serial communications-enhanced applications!
Sitting in front of my monitor was the Blue Dot receptor for my Java Ring, and I thought to myself, "Aha! The folks at Dallas Semiconductor have figured out how to talk to a serial port on the PC. Let's see what they do." After looking through the company's source code for Win32, it was clear that talking to serial ports was not going to be a simple task.
Java to the rescue
At this point in my weekend, I was thinking perhaps I'd drag one of my Unix machines to the lab in order to code the program on it instead of using what I already had. Then I remembered my experience with the Java Ring and the java.comm package from Sun. I decided to pursue that avenue instead.
What does java.comm provide?
The Java Communications API -- or java.comm -- provides a platform-independent method for accessing serial and parallel ports from Java. As with other Java APIs such as JFC, JDBC, and Java 3D, a certain level of indirection is forced on the programmer to isolate the platform's idea of "what a serial port is" from the programming model. In the case of the javax.comm design, items like device names, which vary from platform to platform, are never used directly. The three interfaces of the API provide platform-independent access to serial and parallel ports. These interfaces provide method calls to list the available communication ports, control shared and exclusive access to ports, and control specific port features such as baud rate, parity generation, and flow control.
When I saw the example SimpleWrite.java in the documentation, and compared its 40 lines of code to the 150 to 200 lines of code I was looking at writing in C, I knew the solution was at hand.
The high-level abstraction for this package is the class javax.comm.CommPort
. The CommPort
class defines the kinds of things you would typically do with a port, which includes getting InputStream
and OutputStream
objects that are the I/O channels for the port. The CommPort
class also includes methods for controlling buffer sizes and adjusting how input is handled. Since I knew these classes were supporting the Dallas Semiconductor One-Wire protocol (a protocol that involved dynamic changes in baud rate, and complete transparency to the bytes being transferred), I knew the javax.comm API had to be flexible. What came as a pleasant suprise was how tight the classes were: They had just enough flexibility to get the job done and no more. There was little to no unnecessary bloatware in the form of "convenience methods" or support of modem protocols like Kermit or xmodem.
A companion class to CommPort
is the javax.comm.CommPortIdentifier
class. This class abstracts the relationship between how a port is named on a particular system (that is, "/dev/ttya" on Unix systems, and "COM1" on Windows systems) and how ports are discovered. The static method getCommPortIdentifiers
will list all known communication ports on the system; furthermore, you can add your own port names for pseudo communication ports using the addPortName
method.
The CommPort
class is actually abstract, and what you get back from an invocation of openPort
in the CommPortIdentifier
is a subclass of CommPort
that is either ParallelPort
or SerialPort
. These two subclasses each have additional methods that let you control the port itself.
The power of Java
You can argue about the reality of "write once, run anywhere" all you want, but I will tell you from experience that for single- threaded or even simple multithreaded non-GUI applications, Java is there. Specifically, if you want to write a program that runs on Unix systems, Win32, and Mac systems, and can access the serial port, then Java is the only solution today.
The benefit here is that fewer resources are required to maintain code that runs on a large number of platforms -- and this reduces cost.
A number of applications share a requirement to have pretty low-level access to the serial port. The term low-level in this context means that a program has access to interfaces that allow it to change modes on-the-fly and directly sample and change the states of the hardware flow-control pins. Besides my PDP-8 project, Dallas Semiconductor needed to use its Blue Dot interfaces on serial ports to talk to the iButton with Java. In addition, the makers of microprocessors have evaluation boards that use a serial port for communications and program loading. All of these applications can now be completely, and portably, written in Java -- a pretty powerful statement.
All of this power to control the parallel and serial ports of the host machine comes from the javax.comm library. Giving Java programmers access to the ports opens up an entirely new set of applications that target embedded systems. In my case, it gave me the ability to write my TTY paper-tape reader emulator completely in Java.
How do you get to play with this stuff?
To get a copy of the latest javax.comm distribution, first you need to sign up as a developer on the Java Developer Connection (JDC) if you haven't done so already. (See Resources.) JDC is free, and as a member you will get early access to Java classes that will eventually be part of the final product.
Go to the Java Communications API section and download the latest javax.comm archive file. Unpack the file and install the shared libraries (yes, the Java virtual machine needs native code to talk to the ports -- fortunately for you, you don't have to write it), and install the comm.jar file. Finally, add the comm.jar file to your CLASSPATH
variable.
Once the comm.jar file is stored in the lib directory of your Java installation, and the win32comm.dll is stored in the bin directory of your Java installation, you can compile and run all the examples that come with the download. I encourage you to look them over as there is lots of good information nestled in with the source code.
Where does this leave the PDP-8?
So, what's happened with the PDP-8? I thought you'd never ask! After reading the README document that came with the javax.comm distribution, then scanning the JavaDocs for the javax.comm package, I put together an application class called SendTape
. This class simulates a paper-tape reader by opening the serial port and stuffing bytes over it at 110 baud. The code for this class is shown here:
import javax.comm.*; import java.io.*; public class SendTape { static final int LEADER = 0; static final int COLLECT_ADDR = 1; static final int COLLECT_DATA = 2; static final int COLLECT_DATA2 = 3; /* This array holds a copy of the BIN format loader */ static byte binloader[] = { (byte) 0x80,(byte) 0x80,(byte) 0x80,(byte) 0x80, ... (byte) 0x80,(byte) 0x80, };
The code fragment above is the first part of the SendTape
class. This class begins by implicitly importing all classes in the javax.comm package and the java.io packages. The SendTape
class then defines some constants and pre-initializes a byte array to contain the BIN Loader program I mentioned earlier. I included the BIN Loader because it is always needed when initializing the memory of the PDP-8 and I kept losing track of where I had last stored the file containing its image in RIM format. With this crucial paper tape image embedded in the class in this way, I always have the ability to load it with this class.
/** * This method runs a mini-state machine that gives * a useful human readable output of what is happening * with the download. */ static int newState(int oldState, byte b) { ... }
Following the initialization, you have the code for the method newState
, shown above, that tracks the contents of the paper tape (whether it is address information or programming information). The method above also prints out a message for each location of memory on the PDP-8 that is initialized.
Next you have the main
method, which is shown below; it opens the file and reads it in. Then the code opens the serial port and sets its communication parameters.