Investigating the GCA, and making adapter boards for the ISA bus
I never paid much attention to the Game Control Adapter, or game port, although it appeared with the first IBM PC. However, now that I am searching for new things to study, it happens that the game port can teach some good lessons. It is very simple, and shows how to make an interface card for the I/O Bus, or 8-bit ISA bus, as it is now known. For many purposes, a fast I/O bus is not desirable, though all the development has been towards speedier communications. The ISA bus that will be used here is the one on my 25 MHz 80386 system, which operates at 8 MHz. An I/O read or write cycle takes 5 clocks, or 625 ns, which allows the use of a wide variety of devices on the bus.
The game port is input only, four bits and four curious timer channels controlled by varying an external resistance. These controls are usually contained in a "joystick" that connects to the DB15S game port. The address used on the first IBM game adapter was 201 (hex). The "1" was required by the simple address decoding using two 74LS138's--the last pin available was an active-high enable. An output to this port address triggered, or "fired," the four timers in an NE558 quad timer chip. An input from this port address read the four "button" bits and the four outputs of the timers. The processor read the port repeatedly to find out when the timers timed out, the interval depending on the external resistance. This circuitry was intended to support two game controllers, each with two potentiometers (for x,y) and two buttons. For all I know, the game port is still used, since it keeps appearing on all kinds of I/O adapters. If you have one, it may be interesting to become acquainted with it.
The game port connector is a 15-pin DB-15S, with pins in two rows. Don't confuse it with the 15-pin video connector, which has three rows of pins. I did not even have a DB-15P connector when I decided to play with the game port, so I made one from a DB-25P. The connector consists of front and back metal guards, two plastic headers, and 25 pins. Drill out the eyelet-type join at the screw holes, and the whole thing comes apart. Remove the 10 unwanted pins, and assemble the connector again, using two 4-40 screws and nuts to hold it together. This expedient mates with the DB-15S well enough. More tedious is soldering a 16-wire flat cable with DIP plug to the connector, but it can be done.
It is pretty safe fooling around with the game port, except for one thing. Ground and +5 are brought out, and if you manage to connect them, there will be trouble. I fried a 100k pot when mistakenly putting a wire in the wrong hole in the breadboard, so I learned this by experience. Fortunately, there was no other casualty than the pot. Also, the computer manual had its own way of numbering the pins (different from me and IBM) that gave me some confusion until I figured it out. The pinout of the IBM game port is standard, and is shown at the right. The B's are the button inputs, pulled up to +5 with a 1k resistor internally. They are grounded when the button is pressed. The P's are the trigger inputs, and are connected to +5 through a resistance, usually a potentiometer. There is an internal 2.2k resistor in series, so there is no problem with excess current. The +5's are, of course, +5 for connecting the timing resistors, and the G's are grounds for the B inputs. On some interfaces, pin 5 is a no-connection, and only the grounds at pins 4 and 12 are used, one for each game controller.
You can experiment with the port well enough with DEBUG, especially exercising the buttons, which are very easy to get to work. If you connect no external resistors, writing to port 201 (-o201 00) will trigger all four timers, which will probably not time out, so you will read FF from 201 (assuming all the buttons are up). If you put in timing resistors or potentiometers, then you will read F0, since the timers will all time out while you are using DEBUG. To see the timers actually work, a program must be written that triggers them, then reads them repeatedly to determine when they time out. I wrote a program in C to do this. Actually, this calls for assembly language, since high-level languages like C are too cumbersome when doing loops. However, it worked well enough for demonstration, and perhaps for some practical uses, with C.
The circuit for one of the units in an NE558 Quad Timer is shown at the left. It works just like the familiar 555, except that the threshold and discharge are connected together internally to economize on pins. This makes astable oscillators difficult, but the monostable function is exactly the same. If you do not have a 558, a 555 will do as well for experiments. The reference voltage is set by internal resistors, and is brought out to the control pin, where it may be bypassed to ground with a .01 μF capacitor to suppress noise. The trigger pulse triggers the flip-flop as it goes low, turning off both of the transistors, so that the output goes positive, while C charges toward +5. The timers are specified to time out at a time T = RC, where R and C are the timing resistor and the timing capacitor (this is different from the 555). On the IBM interface, C = 0.01 μF, so considering the 2.2k internal resistor, T = 22 + 0.01R μs, where R is in ohms. By actual test, I found that a 1 MΩ timing resistor gave a delay of almost 100 counts (that is, iterations of the loop testing the outputs), and that the count was proportional to the resistance. For any accuracy, each channel would have to be separately calibrated. From this, I gathered that the processor took about 100 μs to complete the loop. When you write the program, the case when a timer will not time out in any reasonable time because it has no timing resistor has to be considered. I allowed for a maximum of 200 counts. When the timer times out, both transistors are again turned on, so C discharges and the output is pulled low, its normal state.
The C program will give about 1% precision with a 1 MΩ potentiometer. An assembly routine for timing the timers is shown at the right. This routine can be entered into DEBUG and executed. Borland C++ makes it very easy to put assembly routines in C programs by using "in-line assembly" right in the IDE. My routine had problems incrementing array variables, so simple variables had to be used, and opcodes could not follow a label on the same line (these quirks are not mentioned in the manual). Nevertheless, this is an extremely valuable and easy-to-use feature. When using this routine, make certain that resistors are installed for each timer, since the routine waits until all four timers have timed out. This routine is 4-5 times faster than the C routine, and is even yet not the tightest possible. Incrementing the memory locations storing the count is expensive in time. A faster routine would increment BX, CX, SI and DI instead, then load the memory locations at the end. The assembly routine was actually easier and quicker to write than the C program, since the processor's capabilities could be used directly. The idea of the routine is to load the timer input and shift it out into carry bit by bit, then increment the counter if the carry is set. When all bits are read as zero, then the routine terminates. The game port is ignored in every DOS Programmer's Manual I have seen, and it is not supported in the original ROM BIOS.
The original Game Control Adapter (GCA) was an "adapter" or "plug-in" board for the I/O Channel, now the ISA bus. The I/O Channel accommodated 20 address bits and 8 data bits, using a 62-pin card edge connector with .100" spacing. When a second, smaller connector was added to provide space for 8 more data bits and more interrupts, it became the ISA bus. This bus gives access to the processor, for memory as well as for I/O operations. The processor puts the address on the address lines, puts data on the data lines for a write or listens on the data lines for a read, then issues active-low strobes /MEMR, /MEMW, /IOR, /IOW for memory read, memory write, I/O read and I/O write cycles, respectively. The bus is very tolerant of timing, and easy to use. Another signal, AEN, must be low for these cycles (when it is high, the DMA controller is in charge, not the processor).
I/O operations are handled with only the two instructions IN and OUT, and for them only 16 bits of the address are valid. In the PC, only 10 address lines are considered for these cycles; higher bits are ignored. The GCA answers only to the address 201, but the block 200-20F is reserved for it. This means you have a wealth of possible addresses for simple adapter cards of this type, 16 in all, of which only one, 201, is normally used. Making a GPA adapter card for the 8-bit ISA bus is an excellent exercise for learning how to interface with the processor.
A circuit for a GCA or similar card is shown at the left. This is the same as the original IBM GCA, except that the address can be selected to any of the 16 addresses in the range 200-20F. The original GCA used two 74LS138's for address decoding, and the address was fixed at 201. The pullup resistors are all 4.7k or 5.1k. R and C can be chosen as you wish for each timer. Many modifications are possible. For example, the timer outputs could be read instead of the button switches. A 74LS374 latch could be clocked by /FIRE to write an output byte to the adapter. I have a multi-I/O card that uses port addresses 200-207--this waste of addresses is a trick by the designer to use fewer address lines by ignoring A0-A2. This would eliminate the need for the LS138 in the design.
Every plug-in card has the same common functional components: the address decode, the strobe generator, and the data latches or buffers. The address decode takes AEN and A0-A19 as inputs, and produces chip selects, which are normally active low. The strobe generator accepts the chip selects and the bus strobes (/IOR. /IOW, /MEMR, /MEMW) and makes the necessary individual strobes, which again are normally active low. The data lines, D0-D7, pass through the data latches and buffers and are managed by the strobes, which normally latch write data from the processor, and put read data to the processor on the data bus at the proper time. The strobes can be made to do work by themselves, without involving data, such as setting and resetting flip-flops, or triggering timers, as in the GCA.
This card does not load any of the signal lines excessively, since the load is only one LSTTL input, so no buffering is required. Two LSTTL loads is specified as the maximum (assuming all slots full). The load on the +5 supply should also be kept as low as possible, but a simple board like this one uses only a relatively small amount of power. Memory chips and such, not LS devices, are usually power hogs. The system power should not be used to power external devices. When you short the bus, the system power supply shuts down, but doing this is not recommended.
The Global Specialties PB-88 was a solderless breadboard plug-in card. The bus signals were brought to connections at the top, signals from a DB-25P connector were brought to connections at the end, and a solderless breadboard with 63 rows of 5-5 connections and two busses on each side occupied the center of the card. The GCA could very easily be prototyped with this board.
It is possible to construct a permanent board with plug-in prototyping boards and wire-wrap. The JDR PR-1 is a 4 x 13 long board ($27.85), while the Jameco JE417 or 21531 is 4 x 6 ($22.95). Either will do well. These boards seem to be very expensive, but then again the market is probably a bit thin. These boards have a place for a DB connector for external connections. The 62-pin connector is standard, so there ought to be boards out there that will fit in it other than the expensive ones specially intended for computer use. DIP sockets, headers and wire-wrap pins are about all that is required. A manual wire-wrap tool is the only special tool needed, and there is a bit of soldering involved, mainly to tack things in.
The board should be tested before first plugging it into a computer, to ensure that there are no shorts or other surprises. I have a tester with a circuit breaker on the power that can apply any addresses and read or write any data, as well as issue the strobes with debounced pushbuttons, for 8-bit ISA boards, that does just this job. The thought of making one for a 16-bit bus, or the PCI bus, is daunting, not to mention expensive. However, its capability for static testing is very convenient and time-saving. If a board works on the tester, it usually works in the computer.
If you are experimenting with plug-in boards, it is a good idea to save the connector on the motherboard by using a connector saver that stays in the system connector while you change cards. Even better is a connector extender that brings the connector above the level of the other cards so the board can be worked on conveniently. I use an XT with the cover removed for testing boards and for other experiments, and it works very well.
Composed by J. B. Calvert
Created 16 August 2002
Last revised 18 August 2002