[Back to JOYSTICK SWAG index]  [Back to Main SWAG index]  [Original]

{
The basic approach to reading a joystick is to monitor port 201h.  The eight
bits at that port correspond to:

01h - Joystick A, "X" position
02h - Joystick A, "Y" position
04h - Joystick B, "X" position
08h - Joystick B, "Y" position
10h - Joystick A, button 1
20h - Joystick A, button 2
40h - Joystick B, button 1
80h - Joystick B, button 2

The buttons are easy: a bit of "0" means "pressed" and "1" means "not
pressed".  But a single bit to read a joystick position?!?  Here's what
you do:

1)  Write a value -- any value -- to port 201h.  The four lowest bits
    will then all assume a value of "1".

2)  Start a counter, and see how many iterations it takes for your
    desired bit to go to zero.  The number of iterations = the joystick
    position, with lower values corresponding to "left" or "up" and
    higher values corresponding to "right" or "down".

Like any joystick code, thess routines return the button statuses and the
joystick positions.  They also return boolean indicators of whether the
stick is left or right, up or down, based on a sensitivity you define.

The routines you call are:

procedure calibrate(r: real) -- Call this at the beginning of your program.
                                Determines presence of joysticks and how
                                far the stick has to be moved to constitute
                                L/R/U/D.  "r" is a real value from 0 to 1;
                                a value of 0.25 means that the stick has to
                                move 25% from center to count as L/R/U/D.
procedure readsticks         -- Reads sticks and buttons.  Call this once
                                every round of play or whatever.

THE CODE:
{--------------------------------------------------------------------------}
unit joystick;

interface

var jax, jay, jbx, jby: word;                 { Joystick positions }
    ja1, ja2, jaleft, jaright, jaup, jadown,  { JA1, JA2, JB1, JB2 = buttons }
    jb1, jb2, jbleft, jbright, jbup, jbdown,  { GotJoystickA/B record which }
    gotjoysticka, gotjoystickb: boolean;      {   joysticks are present }

   { lefts, rights, ups, downs are determined when the joysticks are read:
       if the stick is sufficiently off-center, L, R, U, and/or D will be
       flagged appropriately }

procedure readsticks;                         { reads joysticks }
procedure calibrate(offcenterthresh: real);   { determines what stick values
                                                  constitute L/R/U/D }
{--------------------------------------------------------------------------}
implementation

var jal, jar, jau, jad, jbl, jbr, jbu, jbd: word;   { thresholds for L/R/U/D }
{--------------------------------------------------------------------------}
procedure calibrate(offcenterthresh: real);   { get base figures for sticks }
begin
  gotjoysticka := true;                       { initially assume both sticks }
  gotjoystickb := true;                       {   are present }
  readsticks;                                 { get stick positions }
  gotjoysticka := (jax > 0) or (jay > 0);     { if joystick reads as position }
  gotjoystickb := (jbx > 0) or (jby > 0);     {   (0,0), it doesn't exist }
  jal := round(jax*(1 - offcenterthresh));    { OFFCENTERTHRESH is a real }
  jar := round(jax*(1 + offcenterthresh));    {   from 0 to 1 that tells the }
  jau := round(jay*(1 - offcenterthresh));    {   system how far off-center }
  jad := round(jay*(1 + offcenterthresh));    {   the stick has to be to get }
  jbl := round(jbx*(1 - offcenterthresh));    {   counted as L/R/U/D.  For }
  jbr := round(jbx*(1 + offcenterthresh));    {   example, a value of "0.25" }
  jbu := round(jby*(1 - offcenterthresh));    {   means the stick has to be }
  jbd := round(jby*(1 + offcenterthresh));    {   25% below base to be L / U, }
  end;                                        {   or 25% above to be R / D. }

procedure readsticks;                       { Reads sticks & buttons. }
var gotax, gotay, gotbx, gotby: boolean;    { whether we have a stick value }
    cnter: word;                            { just a counter }
begin
  if gotjoysticka or gotjoystickb then begin  { if no sticks, skip reading them }
    ja1 := (port[$201] and $10) = 0;          { read the buttons }
    ja2 := (port[$201] and $20) = 0;
    jb1 := (port[$201] and $40) = 0;
    jb2 := (port[$201] and $80) = 0;
    gotax := not gotjoysticka;              { Flags: do we have values yet? }
    gotay := not gotjoysticka;              { Set to "true" for nonexistent }
    gotbx := not gotjoystickb;              {   stick -- no need to give    }
    gotby := not gotjoystickb;              {   them a second thought }
    jax := 0;                               { set actual stick positions to }
    jay := 0;                               {   zero -- on "existing" sticks }
    jbx := 0;                               {   the number will increase }
    jby := 0;
    asm
      mov  cx, 0000h   { set counter to zero }
      mov  al, 0fh     { AL contains "new" port value (initialized to 0fh) }
      mov  ah, al      { AH contains "old" port value (initialized to 0fh) }
      mov  dx, 0201h   { load up joystick port }
      out  dx, al      { "prime" joystick port by writing 0fh to it }
      @beginloop:      { the stick-reading loop }

      in   al, dx      { read joystick port }
      and  al, 0fh     { "and" it with 0fh to "eliminate" the button bits }
      cmp  al, ah      { compare to the "old" port value }
      je   @endloop    { if no change, skip past position checking }

      @chkax:          { checking "X" value on joystick "A" }
      cmp  gotax, 01h  { see if "gotax" equals 1: if so, we've already got }
      je   @chkay      {   a reading on it, and skip to "ay" readings }
      test al, 01h     { if first bit of BL is a 1: if so, we don't have a }
      jnz  @chkay      {   value for "ax", so jump over to "ay" }
      mov  gotax, 01h  { set boolean "gotax" to "true" }
      mov  jax, cx     { record counter value }

      @chkay:          { checking "Y" value on joystick "A" }
      cmp  gotay, 01h
      je   @chkbx
      test al, 02h
      jnz  @chkbx
      mov  gotay, 01h
      mov  jay, cx

      @chkbx:          { checking "X" value on joystick "B" }
      cmp  gotbx, 01h
      je   @chkby
      test al, 04h
      jnz  @chkby
      mov  gotbx, 01h
      mov  jbx, cx

      @chkby:          { checking "Y" value on joystick "B" }
      cmp  gotby, 01h
      je   @endloop
      test al, 08h
      jnz  @endloop
      mov  gotby, 01h
      mov  jby, cx

      @endloop:        { counter increments and data-evaluating }
      mov  ah, al      { store "new" port value as "old" value for next pass }
      inc  cx          { increment counter }
      cmp  cx, 65535   { compare counter to 65535 }
      je   @ending     { if counter = 65535, get out of loop }
      cmp  gotax, 01h  { see if we have a value for "ax"; }
      jne  @beginloop  {   if not, jump to top of loop for another pass }
      cmp  gotay, 01h  { check "ay" }
      jne  @beginloop
      cmp  gotbx, 01h  { check "bx" }
      jne  @beginloop
      cmp  gotby, 01h  { check "by" }
      jne  @beginloop
      @ending:         { we're past the end of the loop }
      mov  cnter,cx    { store counter value into "Pascal" variable }
      end;
    end;
  jaleft := (jax < jal);  jaright := (jax > jar);  { determine L/R/U/D }
  jaup   := (jay < jau);  jadown  := (jay > jad);
  jbleft := (jbx < jbl);  jbright := (jbx > jbr);
  jbup   := (jby < jbu);  jbdown  := (jby > jbd);
  end;
{--------------------------------------------------------------------------}
  end.

[Back to JOYSTICK SWAG index]  [Back to Main SWAG index]  [Original]