IMPLEMENTATION MODULE Glove;

FROM SYSTEM         IMPORT SHIFT, TERMPROC, LONG, ADR,
                           BYTE, ADDRESS, LONGWORD;
FROM PotgoResource  IMPORT PotgoBits, PotgoBitSet, 
                           PotgoName, PotgoBase,
                           AllocPotBits, FreePotBits, 
                           WritePotgo;
FROM DOSProcess     IMPORT Delay;
FROM RunTime        IMPORT ExecBase;
FROM ExecBase       IMPORT ExecBaseRecPtr, ExecBaseRec, AttnFlagSet,
                           AttnFlags;
FROM CIAHardware    IMPORT CIA, CIAA, CIAGamePort1;
FROM CustomHardware IMPORT custom, Custom;
FROM Resources      IMPORT OpenResource;
FROM Pause          IMPORT PauseMicros, InitPause, EndPause;
FROM Tasks          IMPORT TaskPtr, Task, AddTask, RemTask;
FROM Nodes          IMPORT NodeType;
FROM Memory         IMPORT AllocMem, FreeMem, MemReqSet, 
                           MemReqs;
FROM Interrupts     IMPORT Forbid, Permit;
FROM Events         IMPORT WriteEvent, InitEvents, 
                           EndEvents;
FROM Globals        IMPORT Rate, Enabled;
FROM Notify         IMPORT UhOh;
FROM Tasks	    IMPORT Signal, SignalSet, CurrentTask, 
                           FindTask, AllocSignal, AnySignal,
                           FreeSignal, NoSignals;

(*--------------------------------------------------------\
 |                                                        |
 |  Thanks to Carolyn Scheppner for her article in        |
 |   AmigaMail "Joystick Port Output".  The code to write |
 |   to the joystick port is translated to Modula-2       |
 |   directly from that article.                          |
 |                                                        |
 \--------------------------------------------------------*)
 
CONST
  OurBits   = PotgoBitSet{OUTRX,DATRX};
  PotRXMsk  = PotgoBitSet{OUTRX,DATRX};
  PotRXClr  = PotgoBitSet{OUTRX};
  PotRXSet  = PotgoBitSet{OUTRX,DATRX};
  StackSize = 4000;
  Pin4      = 0;
  TaskName  = "PowerMouse Background Task";

TYPE
  GloveBits = (GloveA,
               GloveB,
               GloveSelect,
               GloveStart,
               GloveUp,
               GloveDown,
               GloveLeft,
               GloveRight);
               
  GloveArray = ARRAY [MIN(GloveBits)..MAX(GloveBits)]
               OF BOOLEAN;
  
  MyTaskRec  = RECORD
                 task  : Task;
                 stack : ARRAY [0..StackSize-1] OF BYTE; 
               END;
                  
VAR
  State           : GloveArray;
  MasterProcess   : TaskPtr;
  OurPotBits      : PotgoBitSet;
  MyTask          : POINTER TO MyTaskRec;
  IsFastProcessor,
  SelectButton,
  StartButton,
  Halting,
  HaltOK          : BOOLEAN;
  EBPtr           : ExecBaseRecPtr;

(*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*)

PROCEDURE StrobeReset();

(*--------------------------------------------------------\
 |                                                        |
 |  Sends a H-L pulse to pin 5 on the joystick port.      |
 |  This resets the glove for the next set of 8 bits.     |
 |                                                        |
 \--------------------------------------------------------*)

BEGIN
  WritePotgo(PotRXSet,PotRXMsk);   (* Set Pin 5 HIGH *)
  IF IsFastProcessor THEN  
    PauseMicros(5)
  END;
  WritePotgo(PotRXClr,PotRXMsk);   (* Set Pin 5 low  *)
END StrobeReset;

(*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*)

PROCEDURE StrobeData();

(*--------------------------------------------------------\
 |                                                        |
 |  Sends a H-L-H pulse to pin 6 of the joystick port.    |
 |  This signals the glove to send the next bit in the 8  |
 |   bit data set.                                        |
 |                                                        |
 \--------------------------------------------------------*)

BEGIN
  EXCL(CIAA^.ciapra,CIAGamePort1);  (* Set Pin 6 low *)
  INCL(CIAA^.ciapra,CIAGamePort1);  (* Set Pin 6 HIGH *)
END StrobeData;

(*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*)

PROCEDURE PollGlove();

(*--------------------------------------------------------\
 |                                                        |
 |  Loop through, reading each of the data bits from the  |
 |    glove (one at a time) on pin 4 of the joystick      |
 |    port.  After 8 bits are read the data is            |
 |    interpreted and the appropriate action is taken.    |                                                      |
 |                                                        |
 \--------------------------------------------------------*)

VAR 
  i         : GloveBits;
  Prevvposr : CARDINAL;
  
BEGIN

  StrobeReset;

  PauseMicros((21 - Rate) * 2500); (* Wait a bit *)

  FOR i := MIN(GloveBits) TO MAX(GloveBits) DO
    State[i] := (Pin4 IN BITSET(custom^.joy1dat));
    StrobeData;
  END;
  
  IF Enabled THEN 
    WriteEvent(State[GloveLeft],
               State[GloveRight],
               State[GloveUp],
               State[GloveDown],
               State[GloveB],
               State[GloveA])
  END;

  IF State[GloveSelect] THEN 
    IF NOT SelectButton THEN 
      Signal(MasterProcess,SignalSet{SelectSignal});
      SelectButton := TRUE
    END
  ELSE
    SelectButton := FALSE 
  END;
  
  IF State[GloveStart] THEN 
    IF NOT StartButton THEN
      Signal(MasterProcess,SignalSet{StartSignal});
      StartButton := TRUE;
    END
  ELSE
    StartButton := FALSE
  END;

END PollGlove;

(*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*)

PROCEDURE GloveTask();
BEGIN
  IF InitEvents() AND InitPause() THEN 
    REPEAT  
      PollGlove
    UNTIL Halting;
    EndEvents;
    EndPause;
  ELSE
    Signal(MasterProcess,SignalSet{HaltSignal})
  END;
  Forbid;
    HaltOK := TRUE;
    RemTask(CurrentTask);
  Permit;
    
END GloveTask;

(*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*)

PROCEDURE StartGloveTask;

BEGIN
  
  PotgoBase := OpenResource(ADR(PotgoName));
  IF PotgoBase = NIL THEN
    UhOh("Can't open potgo resource");
    HALT;
  END;
  
  (* allocate Pin 5 for our use *)
  OurPotBits := AllocPotBits(OurBits); 
  IF OurPotBits # OurBits THEN
    UhOh("Can't allocate potbits");
    HALT;
  END;

  (* Set Pin 6 to output *)
  INCL(CIAA^.ciaddra,CIAGamePort1);  
    
  MasterProcess := FindTask(CurrentTask);
  SelectSignal := AllocSignal(AnySignal);
  StartSignal  := AllocSignal(AnySignal);
  HaltSignal   := AllocSignal(AnySignal);
  IF (SelectSignal = CARDINAL(NoSignals)) OR
     (StartSignal  = CARDINAL(NoSignals)) OR
     (HaltSignal   = CARDINAL(NoSignals)) THEN
     UhOh("Could not get signals");
     HALT
  END;

  MyTask := AllocMem(SIZE(MyTaskRec),
                     MemReqSet{MemClear,MemPublic});
  IF MyTask = NIL THEN
    UhOh("Could not start up background task");
    HALT
  END;
  HaltOK := FALSE;
  WITH MyTask^.task DO
    tcSPLower := ADR(MyTask^.stack);
    tcSPUpper := tcSPLower + ADDRESS(StackSize);
    tcSPReg   := tcSPUpper;
    WITH tcNode DO
      lnType := NTTask;
      lnPri  := BYTE(5);
      lnName := ADR(TaskName);
    END;
  END;
  AddTask(ADR(MyTask^.task),ADR(GloveTask),NIL);
END StartGloveTask;

(*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*)

PROCEDURE CleanUp();
BEGIN

  IF MyTask # NIL THEN
    Halting := TRUE;  
    REPEAT
      Delay(20);
    UNTIL HaltOK;
    FreeMem(MyTask,SIZE(MyTaskRec));   
    MyTask := NIL;
  END;
  
  IF SelectSignal # CARDINAL(NoSignals) THEN 
    FreeSignal(SelectSignal);
    SelectSignal := CARDINAL(NoSignals)
  END;
  IF StartSignal  # CARDINAL(NoSignals) THEN 
    FreeSignal(StartSignal);
    StartSignal := CARDINAL(NoSignals)
  END;
  IF HaltSignal   # CARDINAL(NoSignals) THEN 
    FreeSignal(HaltSignal);
    HaltSignal := CARDINAL(NoSignals)
  END;
  
  IF OurPotBits # PotgoBitSet{} THEN
    WritePotgo(PotRXClr,PotRXMsk);  (* Set Pin 5 back low *)
    FreePotBits(OurPotBits);
    OurPotBits := PotgoBitSet{}
  END;
  EXCL(CIAA^.ciapra,CIAGamePort1);  (* Set Pin 6 low *)
  EXCL(CIAA^.ciaddra,CIAGamePort1); (* Set Pin 6 to Input *)
END CleanUp;

(*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*)

BEGIN
  IF FindTask(ADR(TaskName)) # NIL THEN 
    UhOh("PowerMouse is already running");
    HALT
  END;
  
  EBPtr := ExecBase;
  
  IsFastProcessor :=
    ((AttnFlagSet{AF68020,AF68030} * EBPtr^.AttnFlags) # AttnFlagSet{});
  

  SelectButton := FALSE; 
  StartButton  := FALSE;
  Halting      := FALSE;
  HaltOK       := TRUE;
  MyTask       := NIL;
  
  TERMPROC(CleanUp);

END Glove.