The first example reads the port and displays the status of the joysticks.
/* JOYBITS.C ---- Polls joystick status bits. * by Gary Neal, Jr. -- garyneal@geocities.com */ #include <conio.h> /* Console input/output */ /* Joystick Pots */ #define Bit0 0x01 /* Joystick A X-axis */ #define Bit1 0x02 /* Joystick A Y-axis */ #define Bit2 0x04 /* Joystick B X-axis */ #define Bit3 0x08 /* Joystick B Y-axis */ /* Joystick Buttons */ #define Bit4 0x10 /* Joystick A Button 1 */ #define Bit5 0x20 /* Joystick A Button 2 */ #define Bit6 0x40 /* Joystick B Button 1 */ #define Bit7 0x80 /* Joystick B Button 2 */ int main(void) { int joyState; /* Port value */ joyState = inp(0x201); /* Read joystick port */ /* Check joystick pot status */ cprintf("Capacitor for Joystick A's X-axis is %s charged.\r\n", ((joyState & Bit0) ? "not" : "fully")); cprintf("Capacitor for Joystick A's Y-axis is %s charged.\r\n", ((joyState & Bit1) ? "not" : "fully")); cprintf("Capacitor for Joystick B's X-axis is %s charged.\r\n", ((joyState & Bit2) ? "not" : "fully")); cprintf("Capacitor for Joystick B's Y-axis is %s charged.\r\n", ((joyState & Bit3) ? "not" : "fully")); /* Check joystick buttons status */ cprintf("\r\nButton 1 on Joystick A is %spressed.\r\n", ((joyState & Bit4) ? "not " : "")); cprintf("Button 2 on Joystick A is %spressed.\r\n", ((joyState & Bit5) ? "not " : "")); cprintf("Button 1 on Joystick B is %spressed.\r\n", ((joyState & Bit6) ? "not " : "")); cprintf("Button 2 on Joystick B is %spressed.\r\n", ((joyState & Bit7) ? "not " : "")); /* Finish up */ return 0; }
Compile and run this program and it'll display the status of the joysticks by reading Port 201h and individually checking its bits. Each time you run the program, it displays the current status of the joysticks. After running it a few times and pressing one or more of the joystick buttons you can see that it is very useful for checking their status. Please note that when you're checking the port for the buttons' status, the bits returned are high if the joystick button is not pressed and low it it is. To see what I mean, compile and run the next example.
/* JOYBITS2.C ---- Polls and displays bits of port 201h. * by Gary Neal, Jr. -- garyneal@geocities.com */ #include <conio.h> /* Console specific I/O */ int main(void) { int joyState, bitMask; clrscr(); /* Blank the screen */ _setcursortype(_NOCURSOR); /* Specific to Turbo C, okay to delete */ gotoxy(10, 7); /* Set screen position */ cputs("Port 201h status bits:"); gotoxy(15, 9); /* Set screen position */ cputs("7 6 5 4 3 2 1 0"); gotoxy(15, 13); /* Set screen position */ cputs("^ ^ ^ ^ ^ ^ ^ ^"); gotoxy(15, 14); /* Set screen position */ cputs("| | | | | | | +--- Capacitor state for Joystick A X-axis"); gotoxy(15, 15); /* Set screen position */ cputs("| | | | | | +----- Capacitor state for Joystick A Y-axis"); gotoxy(15, 16); /* Set screen position */ cputs("| | | | | +------- Capacitor state for Joystick B X-axis"); gotoxy(15, 17); /* Set screen position */ cputs("| | | | +--------- Capacitor state for Joystick B Y-axis"); gotoxy(15, 18); /* Set screen position */ cputs("| | | +----------- Switch state for Joystick A Button 1"); gotoxy(15, 19); /* Set screen position */ cputs("| | +------------- Switch state for Joystick A Button 2"); gotoxy(15, 20); /* Set screen position */ cputs("| +--------------- Switch state for Joystick B Button 1"); gotoxy(15, 21); /* Set screen position */ cputs("+----------------- Switch state for Joystick B Button 2"); while (kbhit()) getch(); /* Clear the keyboard */ while (!kbhit()) { joyState = inp(0x201); /* Read the joystick port */ gotoxy(15, 11); /* Screen position for bits */ for (bitMask = 0x80; bitMask; bitMask >>= 1) { if (joyState & bitMask) /* Mask bit and display it */ putch('1'); else putch('0'); putch(' '); } } while (kbhit()) getch(); /* Clear the keyboard */ _setcursortype(_NORMALCURSOR); /* Restore the cursor */ return 0; }
As you can see, as long as the joystick button is not pressed, its corresponding bit is high (logic 1). It goes low (becomes zero) when the button is pressed. This must be taken into account when functions are written that employ their use. Now let's look at the last four bits (the ones under the 3, 2, 1, and 0 columns). These bits tell us if the joystick caps are fully charged or not. If they are fully charged, the corresponding bit is low. If the capacitor is still charging, or is otherwise uncharged, the bit will be high. Currently, all bits should be either a one or a zero. If they remain a one, chances are good that no joystick is plugged in for that port. The capacitors in this case cannot charge because there is no joystick pot in series with that cap. (Cap, by the way, is a lazy way Electronics and Electrical people refer to capacitors.)
Of course, this still tells us very little about the position of the joystick. It's may be nice to know whether or not the caps themselves are charge or not, but we really want to know the resistance of the joystick pots in series with those caps. By gauging their resistances, we can measure the position on the X and Y axes and use that information to determine where the joystick is being deflected. So, what we need to do is discharge those joystick caps and time how long it takes for them to recharge. You see, because the joystick pots themselves vary their resistance according to the position in which the stick is being held (as illustrated earlier), the time in which the capacitors take to charge will also vary accordingly. If you happen to be studying (or have studied) basic electronics, you would've immediately recognized the circuit as a basic RC circuit. Of course, the recording of the charge times on the capacitors is done in software so here's some code that illustrates how this can be done.
/* JOYPOTS.C -- Samples joystick pots and retreives their values. * by Gary Neal, Jr. -- garyneal@geocities.com */ #include <conio.h> /* Console specific I/O */ int main(void) { unsigned int joyState; /* Bit status of Port 201h */ unsigned short joyPotVal[4], limit; /* Loop counters & counting limit */ clrscr(); /* Clear the screen */ gotoxy(10, 9); /* Set screen position */ cputs("Joystick potentiometer values:"); /* Initialize loop counters */ joyPotVal[0] = joyPotVal[1] = joyPotVal[2] = joyPotVal[3] = limit = 0; outp(0x201, 0); /* Discharge capacitors */ do { joyState = inp(0x201); /* Get capacitor status bits */ /* Increase pot value count until cap fully charges */ joyPotVal[0] += ((joyState & 0x01) != 0); joyPotVal[1] += ((joyState & 0x02) != 0); joyPotVal[2] += ((joyState & 0x04) != 0); joyPotVal[3] += ((joyState & 0x08) != 0); /* Increase loop limit count */ limit += 1; /* Continue until all caps are fully charged or limit is reached */ } while ((joyState & 0x0F) && limit); /* Write joystick pot values on screen */ gotoxy(15, 11); cprintf("Joystick A's X-axis = %u", joyPotVal[0]); gotoxy(15, 12); cprintf("Joystick A's Y-axis = %u", joyPotVal[1]); gotoxy(15, 13); cprintf("Joystick B's X-axis = %u", joyPotVal[2]); gotoxy(15, 14); cprintf("Joystick B's Y-axis = %u", joyPotVal[3]); return 0; }
This is basically how joystick enabling software works. If we examined the lines of code, we can see that after initializing our counters to zero, we discharge the capacitors. After that, we then increment all the counters while the corresponding caps are charging. When all the caps are fully charged (joyState & 0xF will equal zero) OR when the limit has been reached (limit equals zero again, timing out the loop) the counter values are then displayed as joystick pot values. It is that simple...
One thing I forgot to mention earlier. To discharge the joystick capacitors, just write to port address 201h. This program writes a zero to this address like so:
outp(0x201, 0);
However, any value can be written, the hardware discharges the joystick caps when something is written to this port address, regardless of what value is actually being written.