The first part describes in detail how the games port hardware works, how the Intel 8253/8254 Timer chip works, and how standard BIOS or custom software works with these hardware components. It is followed by outline instructions showing how to build an inexpensive temperature probe and a humidity probe.
The pinouts of the joystick port are shown below, a DB15P (male) plug is at the card end (the joystick uses a DB15S, a female socket). The table also shows the hardware port bits associated with each pin and the BASIC commands that can access data from each of the pins (Note: most languages can be used to access this hardware):
G a m e P o r t P i n U s a g e Pin Usage Port Bit BASIC Command ------------------------------------------------------ 1 +5 Volts - 2 Switch A1 4 STRIG(0/1) 3 A (x) axis 0 STICK(0) 4 Ground - 5 Ground - 6 A (y) axis 1 STICK(1) 7 Switch A2 5 STRIG(2/3) 8 +5 Volts - 9 +5 Volts - 10 Switch B1 6 STRIG(4/5) 11 B (x) axis 2 STICK(2) 12 Ground - 13 B (y) axis 4 STICK(3) 14 Switch B2 7 STRIG(6/7) 15 +5 Volts -The IBM joystick is called an analogue device because some other joysticks (eg. the Atari) use a set of 4 switches to indicate the up/down and left/right positions whereas the IBM device reports all position between the X and Y limits by sensing the value of the X and Y resistors.
The rest of the sensing is done in software. The processor times how long it takes for the capacitor to charge by reading the time at the start of the charging process and again when the sense circuit reaches the threshold. The difference in times gives a measure of the resistance and hence the position of the joystick.
The threshold sensing and discharging of the capacitors at the start of a new read cycle is handled for each of the 4 axis inputs by just one chip. An NE558 (made by Signetics) was used by IBM on the original games board. This is decoded to the I/O address of 201 Hex (decimal 513). Whenever 201H is accessed with an out instruction (the data is not decoded) the NE558 is triggered and the capacitors are discharged. Inputting from address 201H establishes the state of the 4 threshold sense outputs as well as the open closed/closed status of the 4 buttons. The bits of the byte read from address 201H are shown in the above table under the "bit" column.
Position sense resistors are connected from the +5Volt rail to each threshold sense input. The switch inputs however are connected to ground to CLOSE the circuit and either left open or connected to +5Volts to OPEN the circuit. Switches are normally open which means they read as a 1 (high) bit when unconnected. The schematic of a standard joystick is shown in fig 1, the schematic of the original PC's "Game Control Adaptor" can be found in Ref 1.
8 2 5 3 T i m e r F u n c t i o n s Counter 0 generates the 18.2 Hz system clock tick Counter 1 generates Dynamic RAM refresh signals Counter 2 generates sound pulses for internal speakerFor more information on the 8253/8254 can be found here.
Counter 0 is the one we're interested in as it allows us to time intervals as short as 0.84 uSec. The input to this counter comes from a 1.19318 MHz signal generated by dividing the 4.77 MHz processor clock by 4. Interestingly, the PC master clock frequency was selected to be 4 times the NTSC (US colour TV system) colour burst frequency of approximately 3.58 MHz, presumably because it saved a dollar or two in the original PC's design. This results in the following derived frequency and timing periods:
C o u n t e r 0 T i m e r P e r i o d C a l c u l a t i o n s Master crystal locked Clock frequency 14.31818 MHz (NTS Colour Burst frequency) Processor Clock frequency 4.77273 MHz (Master Clock/3) 8253 Timer Clock input frequency 1.19318 MHz (Processor Clock/4) 8353 Timer Clock period 0.83810 uSec (period = 1 / frequency) PC Software clock period 54.92549 mSec (65,536 x Clock period) PC Software clock frequency 18.2064 Hz (about 18 times per second)The important factor in the above figures is that counter 0 decrements every 0.83810 micro Seconds (let's call this 0.84 uSec). If we read the 16 bit counter when we first trigger the NE558 and then again when the threshold is reached, we can calculate how many multiples of 0.84 uSecs have elapsed. Obviously this gives us an interval that is totally independent of the speed of the processor doing the timing.
Counter 0 continually counts down and on reaching 0 its next count is 0FFFF Hex. The calculation of the count interval must take this wrap-around into account and after this is done an interval of up to 54.9 mSecs (= 0.838 x 65,536) may be reliably measured in increments of 0.84 uSec. Obviously longer intervals may be measured as multiples of the 54.9 mSec period but this is not as simple as it first seems (see ref 2).
The IBM Rom BIOS manual by Ray Duncan (Ref 3) describes Int 15H, Function 84h as the Read Joystick function (available on AT and PS/2). This has two subfunctions; to read the joystick switch settings (with AH=84H, DX=00, return AL bits 4..7 = switches A1, A2, B1, B2); and to read the resistive inputs (call with AH=84H, DX=01, return AX=A(x), BX=A(y), CX=B(x), DX=B(y)). You can call these functions (at least on an AT) from any language that allows you to perform a software interrupt.
stick() function in C #include <dos.h> // Use real INT instuctions, not a call unsigned int stick (unsigned int n) { if (n > 3) return -1; // Invalid n return _AH = 0x84; // Function 84H _DX = 01; // Subfunction 1 - Read resistive input geninterrupt (0x15); // Int 15H if (n == 0) return (_AX); // for n = 0 read A(x) if (n == 1) return (_BX); // for n = 1 read A(y) if (n == 2) return (_CX); // for n = 2 read B(x) if (n == 3) return (_DX); // for n = 3 read B(y) }From GW and QBASIC you can use the STICK(n) and STRIG(m) functions. STICK(n) behaves just like the C function above. The value returned by STRIG(m) is either 0 or -1 depending on whether the trigger is pressed (-1) or not (0). The STRIG function has another form, STRIG ON and STRIG OFF. STRIG ON tells BASIC to record, before each statement is executed, the status of the switches, the intention being to catch a button being pressed before a call to STRIG(n) is executed. Refer to the Q-BASIC on-line documentation (with DOS 5) or the GW-BASIC manual for more information.
But what exactly are the values returned by int 15H, function 84H, subfunction 01. Ray Duncan (Ref 3) states that the values returned will be between 0 and 416 if a 250k* joystick is used, but more information can be had from a BIOS listing (ref 4). This listing shows that the time taken for the capacitor to charge is calculated as described above but the count of 0.84*Secs periods is rounded and truncated as follows (using C syntax):
count = (count & 0x1FF0) >> 4;This can be expressed in assembler code as follows (the count is in CX):
AND CX,01FF0H ;remove bits 13..15 SHR CX,1 ;divide by 2 SHR CX,1 ; by 4 SHR CX,1 ; by 8 SHR CX,1 ; by 16What this is saying mathematically is that the count is divided by 16 and truncated to 9 binary bits or a maximum decimal value of 512. A value of 1 represents a time interval of between 13.41 and 26.82 * Secs and the function measures a maximum value of 6.86 mSecs. In fact, if the time interval is greater than 6.86 mSecs then the integer returned will represent the time modulo 6.86 mSecs. This is due to the AND function removing bits 13, 14 and 15 from the raw count.
However, we are not limited to using the BIOS functions to measure the resistors. We can write our own function in assembler to give us back a 0.84*Sec resolution. This also allows us to write code that is sure to run on an XT as well as an AT as long as we have the games port hardware. Assuming we have done this and using the fact that the capacitor is nominally 0.01*F and knowing the threshold of the NE558, we can arrive at a mathematical formula relating the measured count to the resistance. Ref 5, a reasonable text on some of the PC's internal hardware, gives the relationship between the time and resistance as:
T = 24.2 + (11.0 x R) (1) time in *Sec and resistance in K*.We can relate this to our measured count as 1 count equals 0.84*Sec:
C = (24.2 + (11.0 x R)) / 0.84 (2) calculated count for Resistance RThis formula says that if we have a 250k* resistance we'll get a 3310 count of 0.84*Sec periods and our STICK(n) function (either BASIC or C routine) will return 3310/16 or 207. The astute reader will notice that Ref 3 indicates the value will be 416 or twice as much for 250K*. The only way to resolve this is to actually measure the count for a real value of resistance. Well, I did this with a 270k* resistor on the A(x) input and measured a GW-BASIC STICK(0) value of between 207 and 213. I measured the same value from my C STICK(n) function. I can only conclude that Mr Ray Duncan is wrong, perhaps he was thinking of a 500k* resistor ?
It is worth pointing out that formula (1) and (2) above are only approximate because of the tolerance of the analogue components used. In particular, the capacitors probably have a manufacturing tolerance of * 5% (at worst they may be *20%). This will swamp out other smaller variations such differences in the threshold sensing circuit within the NE558 etc. This tolerance will mean that a given resistance will produce a variety of STICK(n) values for a given games port and for different games boards. Fortunately, most people are smart enough to write software that can be calibrated. This involves including a variable software factor that we can adjust with a known resistance.
NTC Thermistor, 100k* nominal @ 25 C, -5.2k*/ C, -55 C to +200 C.This says that it is manufactured to have a resistance of about 100k*, will change resistance at the rate of 5.2k* per Celsius degree, and is rated to do this over the -55 C to +200 C temperature range. The NTC (Negative Temperature Coefficient) indicates that resistance decreases with increase in temperature.
All that we need to do is attach this thermistor to one of the resistance measuring inputs of the games port, write some software we can calibrate, and Bob's your uncle. By the time this article is in 16 bits I should have completed some software to run under DOS (XT and AT) and also WINDOWS to actually display the temperature. If you're interested then give me a call on (06) 251 5519.
Range 10% to 90%, 122pF *15% at 25*C, 43%RH and 100kHz, typical sensitivity 0.4pF/%RH.The main specification to note is that it will vary by about 0.4pF per % of Relative Humidity (%RH) and has a nominal value of 122pF (pica Farad) at 43%RH and 25*C. As you can see, this device seems a bit more complicated than the thermistor as the capacitance may also have a small thermal effect (although the specs don't mention this explicitly). The capacitance will vary from about 109pF to about 141pF over the range 10% to 90%RH. This is a variation of about 30% and should allow us a reasonable indication of humidity with a simple circuit.
The trick to using the humidity sensor is to be able to convert the capacitance into a time varying signal. An RC (Resistance-Capacitance) oscillator can be made very cheaply (about $2.00 worth of components) and this signal can be fed into one of the switch inputs (not the resistance inputs) of the games port. The idea is that the frequency of the oscillator will change as the humidity changes. Using our time interval measuring techniques described above (but not using the NE558) we can measure the period (equal to 1/frequency) of the oscillator. By again using a calibration technique, we can write some software to display relative humidity. If enough interest is generated I'll publish this simple circuit in the next issue. In the mean time, contact me if you want more details.