MODULE MineSweeper;

(* This is the first version of the source I am releasing for MineSweeper.

   I don't really recommend anyone try use more than snippets of this.
   I feel safe you won't, as you would have to port it first ;-)
   
   Any comments you find in the code are likely to have been added just
   prior to this release, and are likely to be minimal.
   
   I don't want any mail regarding any of this code!
   
   Bear in mind that much of the most messy bits of code could be
   vastly improved if I removed the requirement for compatability
   with 1.3, but at this point it's not worth it.
   
   13 December 1993
   John Matthews
   tribble@gphs.vuw.ac.nz

*)

IMPORT Intuition;
IMPORT FastRandom;
IMPORT Ports;
IMPORT Text;
IMPORT Pens;
IMPORT Rasters;
IMPORT Interrupts;
IMPORT EasyText;
IMPORT InputEvents;
IMPORT Storage;
IMPORT Graphics;
IMPORT Blitter;
IMPORT Memory;
IMPORT BlitHardware;
IMPORT Views;
IMPORT EasyTimer;
IMPORT Tasks;
IMPORT TaskUtils;
IMPORT DOS;
IMPORT DOSProcess;
IMPORT EasyMenus;
IMPORT ImageTools;
IMPORT Pointer;
IMPORT RunTime;
IMPORT ExecBase;
IMPORT Console;
IMPORT LinkedObjects;
IMPORT Utility;
FROM SYSTEM IMPORT
	ADR, BYTE, ADDRESS, SHIFT, SETREG, REGISTER, STRPTR, LONG, CODE;


FROM ChipData IMPORT
	dataPtr;

CONST
	ColoursID = 0636F6C73H;

TYPE
	ColourNames = (* Attempted colours under V39 *)
					  (* The actual values are linked in, but are just 96bit primaries *)
	(	cnNull,
		cnWhite,
		cnBlue,
		cnRed,
		cnGreen,
		cnYellow,
		cnViolet,
		cnPaleBlue,
		cnBlack
	);
	ColourNameSet = SET OF ColourNames;
	AColour =
	RECORD
		acR, acG, acB : LONGCARD;
		(* Ala 3.0, FFFFFFFFH represents full strength component *)
	END;
	ColourArrayPtr = POINTER TO ColourArray;
	ColourArray = ARRAY ColourNames OF AColour;
	ColourInfoRecPtr = POINTER TO ColourInfoRec;
	ColourInfoRec =
	RECORD
		id : LONGCARD;  (* This is just for linking purposes, my compiler is very old *)
		cols : ColourArrayPtr;
	END;


TYPE
	SkillLevel =
	(	Beginner,
		Intermediate,
		Expert,
		Custom
	);
	MenuVals =
	(	mNewGame,
		mBeginner,
		mIntermediate,
		mExpert,
		mCustom,
		mQuestions,
		mCursor,
		mScores,
		mAbout,
		mShrink,
		mQuit);

	HA = (* for overloaded display/input window *)
	(	modeAbout,
		modeScores,
		modeGetName,
		modeGetCustom
	);

CONST
	HighScoreVersion = 3; (* For the highscore file *)
	strID = 30;
   InitLevel = Intermediate; (* Default if no highscore file *)

	(* Some string constants, let's keep them together *)
	Version = "$VER: MineSweeper 2.5 (13.12.93)";
	TaskName = "Sweep Seconds";
	GameText = "Game";
	GameTextLen = 4;
	CompleteText = "Completion";
	CompleteTextLen = 10;
	MineSweeperName = "MineSweeper";
	MaxStringLength = 30;
	MineScores = "s:MineSweeper.highscores";
   IntermediateString = "Intermediate";
   BeginnerString = "Beginner";
   ExpertString = "Expert";
   CustomString = "Custom";
	SquareFontName = "Courier.font";
	SquareFontSize = 13;

	MinFieldWidth = 8;  (* Arbitrary *)
	MinFieldHeight = 8;

	MaxFieldWidth = 32; (* NOT Arbitrary, 32bits per register! *)
	MaxFieldHeight = 32;

	MinMines = 1;
	MaxMines = 999;

	SquareWidth = 16;   (* blitting constants *)
	SquareWidth3 = SquareWidth * 3;

	TopWindowArea = 32;

	NewGameID = 101; (* Gadget ID, not really used *)

	UnknownImage = 0; (* Image numbers, images are linked in *)
	BlankImage = 1;
	WonderImage = 2;
	GuessImage = 3;
	MineImage = 4;
	FoundMineImage = 5;
	BadGuessImage = 6;
	SureImage = 7;
	SmileyImage = 16;
	AstonishedImage = 17;
	UnhappyImage = 18;
	SunGlassesImage = 19;
	FirstImage = 0;
	LastImage = 7;  (* Keep lower res images after 1:1 ones *)
	FadePat = 20;
	DigitOffset = 21; (* Numbers as images faster/smoother/less flicker than Text() *)
	OneSecond = 1;
	NoReStart = FALSE;
	DoReStart = TRUE;

	MaxStackDepth = 256; (* Should be enough with Queue functions. Stack may need more *)

	CopyAtoD = Blitter.StraightCopy;

	NormalIDCMPs =
       Intuition.IDCMPFlagSet{Intuition.GadgetDown,Intuition.GadgetUp,
                              Intuition.MouseMove, Intuition.MenuPick,
                              Intuition.CloseWindowFlag, Intuition.MouseButtons,
                              Intuition.NewSize, Intuition.RefreshWindow,
                              Intuition.RawKey
                             };
	WaitingIDCMPs =
       Intuition.IDCMPFlagSet{Intuition.GadgetUp,
                              Intuition.CloseWindowFlag, Intuition.MenuPick,
                              Intuition.NewSize, Intuition.RefreshWindow,
                              Intuition.RawKey
                             };

TYPE
	MovingStateType =
	(	NoMove,
		NormalMove,
		SafeMove,
		MineMove
	);
	MovingStateSet = SET OF MovingStateType;

	CompleteMethodType =
	(	AllMinesFound,
		AllNonMinesFound,
		AllSquaresFound, (* Both   *)
		EitherCompletion (* Either *)
	);
	FieldType =
	(	fMines,
		fCovered,
		fGuessed,
		fWondered
	);

	MineFieldRow =
	RECORD
		Mines,
		Covered,
		Guessed,
		Wondered : LONGBITSET;
	END;
	SquareState = (UnknownState, BlankState, WonderState, GuessState, MineState, FoundMineState, BadGuessState);
	SquarePtr = POINTER TO SquareRecord;
	SquareRecord =
	RECORD
	  CASE : BOOLEAN OF
	    FALSE : l : LONGCARD
	  | TRUE  : xv,yv : INTEGER
	  END
	END;
	Score =
	RECORD
	  NameLength : BYTE;
	  Pad : BYTE;
	  Seconds : CARDINAL;
	  Name : STRPTR;
	END;
	HighScoreRecordPtr = POINTER TO HighScoreRecord;
	HighScoreRecord =
	RECORD
	  InitX, InitY : INTEGER;
	  DefLevel : SkillLevel;
	  DefQuestions : BOOLEAN;
	  scores : ARRAY SkillLevel OF Score;
	END;
        FullStringGadPtr = POINTER TO FullStringGad;
        FullStringGad =
        RECORD
          Gad : Intuition.Gadget;
          SI : Intuition.StringInfo;
          se : Intuition.StringExtend;
          borders : ARRAY [0..1] OF Intuition.Border;
          slines : ARRAY [0..9] OF LONGCARD;
          fsBuffer, fsUndoBuffer : ARRAY [0..MaxStringLength] OF CHAR;
        END;
	SmallString = ARRAY [0..7] OF CHAR;
	CustomRec =
	RECORD
	  version : CARDINAL;
	  Width,Height,Mines : INTEGER;
	END;

VAR
	FieldWindow : Intuition.WindowPtr;
	NewGameGad : Intuition.GadgetPtr;
	ClipStore : ADDRESS;
	FieldBM : Graphics.BitMapPtr;
	ClipRPort,
	FieldRPort : Rasters.RastPortPtr;
	MainTask, TimerTask : Tasks.TaskPtr;
	MainProcess : DOSProcess.ProcessPtr;
	OLDprWindow : ADDRESS;
	SquareFont : Text.TextFontPtr;
	SquareFontAttr : Text.TextAttr;
	SquareFontAttrPtr : Text.TextAttrPtr;
	menu : Intuition.MenuPtr;
	HighScores : HighScoreRecordPtr;
	EntryGad : FullStringGadPtr;
	SquareImages : ARRAY [FirstImage..LastImage] OF Intuition.ImagePtr;
	CoveredRow : LONGBITSET;
	FadePattern : ADDRESS;
	CT : ADDRESS;
	FaceImage : Intuition.ImagePtr;

	FieldRows : ARRAY [0..MaxFieldHeight - 1] OF MineFieldRow;
	RealStackBuffer : ARRAY [0..MaxStackDepth - 1] OF SquareRecord;

  (* The comment identifiers Stack and Queue control how
     MineSweeper looks around a square with no mines around it.
     Queues are fastest, result in less depth, and a direct ripple
     outwards.
     Stacks look more interesting ;-)
   *)

(*Stack+
	StackBuffer : SquarePtr;
	Stack : SquarePtr;
*Stack-*)

(*Queue+*)
	StackBuffer : POINTER TO ARRAY [0..MaxStackDepth - 1] OF SquareRecord;
	StackIn, StackOut : CARDINAL;
(*Queue-*)
	
	customrec : CustomRec;
	
	FieldWidth, FieldHeight, NumMines, MinesThisLevel, LastFieldWidth,
	LastFieldHeight, LeftFieldBorder, TopFieldBorder, TopWindowBorder,
	RightFieldBorder, FillParam : INTEGER;
	FieldWindowWidth, FieldWindowHeight, RightWindowBorder, BottomWindowBorder,
	LeftTextBorder, RightTextBorder, TopTextBorder, TextWidth, TextHeight,
	LeftSquareText, TopSquareText, SquareHeight, SquareHeight3, LastSquareCount,
	GuessedMines, GuessedRightMines : INTEGER;
	QuitNow, WaitingForNew, V36Colours,
	TimerError, FirstHit, TimerIsGoing, Restarting,
	V36, Questions, Laced, Shrunk, WindowSized, FromMouse, MovingAllowed : BOOLEAN;
	CompleteMethod : CompleteMethodType;
	Lastx, Lasty, Lastxp, Lastyp : INTEGER;
	ClipBM : Graphics.BitMap;
	MovingState, LastState : MovingStateType;
	Pen1,Pen2 : CARDINAL;
	ClipTempRas : Rasters.TmpRas;
	TimerAckBit, CountBit, StartBit, StopBit, WindowBit : Tasks.SIGNAL;
	WaitBits, TimerAcks : Tasks.SignalSet;
	NonMinesCovered,Seconds, NewHigh : CARDINAL;
	Level : SkillLevel;
	SmileX, SmileY : CARDINAL;
	ColourPens : ARRAY ColourNames OF CARDINAL;
	ObtainedPens : ColourNameSet;
	
	ChunkyBuf : ARRAY [0..31] OF SquareState;

(*StackCheck+
	StackDepth, StackDepthReached : CARDINAL;
*StackCheck-*)



PROCEDURE FlashDisplay(num : CARDINAL);
BEGIN
  WHILE num > 0 DO
    DEC(num);
    Views.WaitTOF;
    Blitter.ClipBlit(
       FieldRPort,LeftFieldBorder,TopFieldBorder,
       FieldRPort,LeftFieldBorder,TopFieldBorder,
       FieldWindowWidth, FieldWindowHeight,
       BYTE(48)
    );
    Views.WaitTOF;
  END;
END FlashDisplay;

PROCEDURE TestGameOver(); FORWARD;

PROCEDURE RefreshDisplay(refresh : BOOLEAN); FORWARD;

PROCEDURE DoFill(rp : Rasters.RastPortPtr; pt : ADDRESS; ph : INTEGER; pen,l,t,r,b : CARDINAL);
BEGIN
  IF RunTime.ExecVersion < 39 THEN
    Pens.SetDrMd(rp,Rasters.Jam2);
    Pens.SetAPen(rp,pen);
    Pens.SetBPen(rp,0);
  ELSE
    Pens.SetABPenDrMd(rp,pen,0,Rasters.Jam2)
  END;
  Pens.SetAfPat(rp,pt,ph);
  Blitter.BltPattern(rp,NIL,l,t,r,b,2);
END DoFill;


(*$LONG_ADDR+*)
PROCEDURE ConvNum(VAR str : ARRAY OF CHAR; v : INTEGER);
VAR
	sp : POINTER TO CHAR;
	t : INTEGER;
	n : BOOLEAN;

  PROCEDURE AddDigit(d : INTEGER);
  BEGIN
    sp^ := CHR(ORD('0') + d);;
    INC(sp);
  END AddDigit;
  

BEGIN
  sp := ADR(str);
  n := v < 0;
  v := ABS(v);
  t := v DIV 1000;
  DEC(v,t * 1000);
  IF n THEN
    sp^ := '-';
    INC(sp);
  ELSE
    AddDigit(t);
  END;
  t := v DIV 100;
  DEC(v,t * 100);
  AddDigit(t);
  t := v DIV 10;
  DEC(v,t * 10);
  AddDigit(t);
  AddDigit(v);
  sp^ := 0C
END ConvNum;
(*$LONG_ADDR-*)

PROCEDURE WriteNum(v : INTEGER; x,y : CARDINAL);
VAR
	t : INTEGER;
	n : BOOLEAN;

  PROCEDURE AddDigit(d : INTEGER);
  BEGIN
    Intuition.DrawImage(FieldRPort,dataPtr^.Images[DigitOffset + d],x,y);
    INC(x,10);
  END AddDigit;
  

BEGIN
  n := v < 0;
  v := ABS(v);
  t := v DIV 1000;
  DEC(v,t * 1000);
  IF n THEN
    t := 10; (* negative digit *)
  END;
  AddDigit(t);
  t := v DIV 100;
  DEC(v,t * 100);
  AddDigit(t);
  t := v DIV 10;
  DEC(v,t * 10);
  AddDigit(t);
  AddDigit(v);
END WriteNum;

PROCEDURE SetSize(l : SkillLevel);
BEGIN
  Level := l;
  CASE l OF
     Beginner :
       FieldWidth := 8;
       FieldHeight := 8;
       MinesThisLevel := 10;
   | Intermediate :
       FieldWidth := 16;
       FieldHeight := 16;
       MinesThisLevel := 40;
   | Expert :
       FieldWidth := 32;
       FieldHeight := 16;
       MinesThisLevel := 99;
   | Custom :
       WITH customrec DO
         FieldWidth := Width;
         FieldHeight := Height;
         MinesThisLevel := Mines;
       END; 
  END
END SetSize;


PROCEDURE UpdateMineCountDisplay();
VAR
	Unfound : INTEGER;
	str : SmallString;
BEGIN
  Unfound := NumMines - GuessedMines;
  WriteNum(Unfound,LeftTextBorder, TopTextBorder);
END UpdateMineCountDisplay;

PROCEDURE UpdateTime();
VAR
	str : SmallString;
BEGIN
  WriteNum(Seconds,RightTextBorder, TopTextBorder);
END UpdateTime;

PROCEDURE TimerProc();
VAR
	WaitSigs, GotSigs : Tasks.SignalSet;
	Counting : BOOLEAN;

  PROCEDURE TimerExit();
  BEGIN
    Interrupts.Forbid;
    TimerIsGoing := FALSE;
    IF StartBit # Tasks.NoSignals THEN
      Tasks.FreeSignal(StartBit)
    END;
    IF StopBit # Tasks.NoSignals THEN
      Tasks.FreeSignal(StopBit)
    END;
    EasyTimer.DeleteTimer;
    Tasks.Signal(MainTask,TimerAcks);
    TaskUtils.DeleteTask(TimerTask);
  END TimerExit;
  

BEGIN
  Counting := FALSE;
  StartBit := Tasks.AllocSignal(Tasks.AnySignal);
  StopBit := Tasks.AllocSignal(Tasks.AnySignal);
  IF (StartBit = Tasks.NoSignals) OR (StopBit = Tasks.NoSignals)
    OR ~EasyTimer.CreateTimer() THEN
    TimerError := TRUE;
    TimerExit;
  END;
  Tasks.Signal(MainTask,TimerAcks);
  TimerIsGoing := TRUE;
  WaitSigs := Tasks.SignalSet{CARDINAL(EasyTimer.TimerBit),CARDINAL(StartBit),CARDINAL(StopBit)};
  LOOP
    GotSigs := Tasks.Wait(WaitSigs);
    IF CARDINAL(StartBit) IN GotSigs THEN
      EasyTimer.SendTimeDelay(OneSecond);
      Counting := TRUE;
      Tasks.Signal(MainTask,TimerAcks);
    END;
    IF CARDINAL(EasyTimer.TimerBit) IN GotSigs THEN
      EasyTimer.TimerAck;
      IF Counting THEN
        EasyTimer.SendTimeDelay(OneSecond);
        IF (Seconds < 9999) THEN
          INC(Seconds)
        END;
        Tasks.Signal(MainTask,Tasks.SignalSet{CARDINAL(CountBit)});
      END;
    END;
    IF CARDINAL(StopBit) IN GotSigs THEN
      Counting := FALSE;
      IF QuitNow THEN
        TimerExit;
      ELSE
        Tasks.Signal(MainTask,TimerAcks);
      END
    END;
  END
END TimerProc;

PROCEDURE StartTimer();
VAR
	sigs : Tasks.SignalSet;
BEGIN
  Tasks.Signal(TimerTask,Tasks.SignalSet{CARDINAL(StartBit)});
  sigs := Tasks.Wait(TimerAcks);
  UpdateTime;
END StartTimer;

PROCEDURE StopTimer();
VAR
	sigs : Tasks.SignalSet;
BEGIN
  IF TimerIsGoing THEN
    Tasks.Signal(TimerTask,Tasks.SignalSet{CARDINAL(StopBit)});
    sigs := Tasks.Wait(TimerAcks);
  END;
END StopTimer;

PROCEDURE EndGame(StartNew : BOOLEAN);
BEGIN
  StopTimer;
  IF ~StartNew THEN
    Intuition.ModifyIDCMP(FieldWindow,WaitingIDCMPs);
    FieldWindow^.Flags :=
         FieldWindow^.Flags
       - Intuition.WindowFlagSet{Intuition.RMBTrap,Intuition.ReportMouseFlag};
    WaitingForNew := TRUE;
    MovingState := NoMove;
  END;
  RETURN
END EndGame;

PROCEDURE GetPens(cm : Views.ColorMapPtr);
VAR
	tags : ARRAY [0..1] OF Utility.TagItem;
	res : LONGINT;
	cir : ColourInfoRecPtr;
	i : ColourNames;
BEGIN
  IF ObtainedPens # ColourNameSet{} THEN
    RETURN
  END;
  tags[0].tiTag := Pens.OBP_Precision;
  tags[0].tiData := Pens.Precision_Gui;
  tags[1].tiTag := Utility.TAGDone;
  cir := LinkedObjects.FindObject(ColoursID);
  IF (RunTime.ExecVersion < 39) OR (cir = NIL) THEN
    FOR i := MIN(ColourNames) TO MAX(ColourNames) DO
      ColourPens[i] := Pen2;
    END
  ELSE
    cm := FieldWindow^.WScreen^.VPort.ColorMap;
    i := MIN(ColourNames);
    WHILE i < MAX(ColourNames) DO
      INC(i);
      WITH cir^.cols^[i] DO
        res := Pens.ObtainBestPen(cm,acR,acG,acB,ADR(tags));
        IF res >= 0 THEN
          ColourPens[i] := CARDINAL(res);
          INCL(ObtainedPens,i);
        END;
      END
    END
  END;
END GetPens;


PROCEDURE FindColourScheme;
VAR
  CM : Views.ColorMapPtr;

  PROCEDURE Intensity(Entry : LONGINT) : INTEGER;
  VAR
    temp, result, i : INTEGER;
  BEGIN
    result := 1;
    temp := Views.GetRGB4(CM,Entry);
    FOR i := 0 TO 2 DO
      result := result * INTEGER(BITSET(temp) * BITSET{0..3});
      temp := SHIFT(temp,-4);
    END;
    RETURN result
  END Intensity;
  
VAR
  dummy : LONGCARD;
  i : CARDINAL;
BEGIN
  CM := FieldWindow^.WScreen^.VPort.ColorMap;
  GetPens(CM);
  V36Colours := (Intensity(2) > Intensity(1));
  IF V36Colours THEN
    Pen1 := 1;
    Pen2 := 2;
  ELSE
    i := 0;
    WHILE i < CARDINAL(dataPtr^.NumImages) DO
      ImageTools.ReversePlanes(dataPtr^.Images[i]);
      INC(i)
    END;
    Pen2 := 1;
    Pen1 := 2;
  END;
END FindColourScheme;


PROCEDURE InitClip(d:CARDINAL) : BOOLEAN;
VAR
	rs,frs : LONGCARD;
	w,h : CARDINAL;
BEGIN
  w := SquareWidth3;
  h := SquareHeight3;
  rs := Graphics.RASSIZE(w,h);
  frs := rs * LONGCARD(d);
  ClipStore := Storage.TrackAllocMem(frs,Memory.MemReqSet{Memory.MemChip});
  IF ClipStore = NIL THEN RETURN FALSE END;
  Storage.ALLOCATE(ClipRPort,SIZE(ClipRPort^));
  IF ClipRPort = NIL THEN RETURN FALSE END;
  Rasters.InitRastPort(ClipRPort);
  ClipRPort^.BitMap := ADR(ClipBM);
  Rasters.InitTmpRas(ADR(ClipTempRas),Storage.TrackAllocMem(frs,Memory.MemReqSet{Memory.MemChip}),frs);
  ClipRPort^.TmpRas := ADR(ClipTempRas);
  frs := LONGCARD(ClipStore);
  Graphics.InitBitMap(ADR(ClipBM),d,w,h);
  WITH ClipBM DO
    w := 0;
    WHILE w < d DO
      Planes[w] := ADDRESS(frs);
      INC(frs,rs);
      INC(w);
    END
  END;
END InitClip;





PROCEDURE PushStack(xi,yi : INTEGER);
BEGIN
  IF (CARDINAL(xi) < CARDINAL(FieldWidth)) & (CARDINAL(yi) < CARDINAL(FieldHeight)) THEN
    WITH FieldRows[yi] DO
      IF (CARDINAL(xi) IN Covered) & ~(CARDINAL(xi) IN Guessed) THEN
        EXCL(Covered,CARDINAL(xi));
(*Queue+*)
        WITH StackBuffer^[StackIn] DO
          xv := xi;
          yv := yi;
        END;
        StackIn := (StackIn + 1) MOD MaxStackDepth;
(*Queue-*)
(*Stack+
        Stack^.l := LONG(xi,yi);
        INC(Stack,4);
*Stack-*)
(*StackCheck+
        INC(StackDepth);
        IF StackDepth >= MaxStackDepth THEN
          TermOut.WriteString("Stack Depth Exceeded\n");
        END;
        IF StackDepth >= StackDepthReached THEN
          StackDepthReached := StackDepth
        END;
*StackCheck-*)
      END;
    END
  END
END PushStack;

(***LookAsm+*)
(*$LONG_ADDR+*)
PROCEDURE LookAround(xi,yi : INTEGER);
BEGIN
(*Queue+*)
	SETREG(0,yi);
	SETREG(1,xi);
	SETREG(2,FieldWidth);
	SETREG(3,FieldHeight);
	SETREG(4,MaxStackDepth);
	SETREG(8,ADR(FieldRows));
	SETREG(9,StackBuffer);
	SETREG(10,ADR(StackIn));
(*     1 0000:                          ;d0 - y*)
(*     2 0000:                          ;d1 - x*)
(*     3 0000:                          ;d2 - FieldWidth*)
(*     4 0000:                          ;d3 - FieldHeight*)
(*     5 0000:                          ;d4 - MaxStackDepth*)
(*     6 0000:                          ;a0 - FieldRows*)
(*     7 0000:                          ;a1 - Stack*)
(*     8 0000:                          ;a2 - ADR(StackIn)*)
(*     9 0000:*)  CODE(05341H);(*                    subq.w #1,d1	; start left*)
(*    10 0002:*)  CODE(05340H);(*                    subq.w #1,d0	; top*)
(*    11 0004:*)  CODE(03a00H);(*                    move.w d0,d5	; initial offset into FieldRows*)
(*    12 0006:*)  CODE(0e945H);(*                    asl.w #4,d5		; 16 bytes per block *)
(*    13 0008:*)  CODE(0d0c5H);(*                    adda.w d5,a0	; add it IN*)
(*    14 000a:*)  CODE(0b043H);(*                    cmp.w d3,d0		; neg y ?*)
(*    15 000c:*)  CODE(06438H);(*                    bcc row2			; skip first row*)
(*    16 000e:*)  CODE(02a28H,00008H);(*             move.l 8(a0),d5; Guessed*)
(*    17 0012:*)  CODE(04685H);(*                    NOT.l d5			; ~Guessed*)
(*    18 0014:*)  CODE(02c28H,00004H);(*             move.l 4(a0),d6; Covered for this row*)
(*    19 0018:*)  CODE(0ca86H);(*                    AND.l d6,d5		; only do if covered & ~guessed*)
(*    20 001a:*)  CODE(0b242H);(*                    cmp.w d2,d1		; x neg ?*)
(*    21 001c:*)  CODE(06408H);(*                    bcc c12			; skip col*)
(*    22 001e:*)  CODE(00305H);(*                    btst.l d1,d5	; check square conditions*)
(*    23 0020:*)  CODE(06704H);(*                    beq c12			; failed, skip*)
(*    24 0022:*)  CODE(06100H,00092H);(*             bsr.s nq			; passed, enqueue*)
(*    25 0026:*)  CODE(05241H);(*              c12:  addq.w #1,d1	; next col*)
(*    26 0028:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    27 002a:*)  CODE(06408H);(*                    bcc c13*)
(*    28 002c:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    29 002e:*)  CODE(06704H);(*                    beq c13*)
(*    30 0030:*)  CODE(06100H,00084H);(*             bsr.s nq*)
(*    31 0034:*)  CODE(05241H);(*              c13:  addq.w #1,d1*)
(*    32 0036:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    33 0038:*)  CODE(06406H);(*                    bcc c1e*)
(*    34 003a:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    35 003c:*)  CODE(06702H);(*                    beq c1e*)
(*    36 003e:*)  CODE(06176H);(*                    bsr.s nq*)
(*    37 0040:*)  CODE(05541H);(*              c1e:  subq.w #2,d1*)
(*    38 0042:*)  CODE(02146H,00004H);(*             move.l d6,4(a0); save Covered for this row*)
(*    39 0046:*)  CODE(05088H);(*              row2: addq.l #8,a0*)
(*    40 0048:*)  CODE(05088H);(*                    addq.l #8,a0*)
(*    41 004a:*)  CODE(05240H);(*                    addq.w #1,d0*)
(*    42 004c:*)  CODE(0b043H);(*                    cmp.w d3,d0*)
(*    43 004e:*)  CODE(06428H);(*                    bcc row3*)
(*    44 0050:*)  CODE(02a28H,00008H);(*             move.l 8(a0),d5*)
(*    45 0054:*)  CODE(04685H);(*                    NOT.l d5*)
(*    46 0056:*)  CODE(02c28H,00004H);(*             move.l 4(a0),d6*)
(*    47 005a:*)  CODE(0ca86H);(*                    AND.l d6,d5*)
(*    48 005c:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    49 005e:*)  CODE(06406H);(*                    bcc c23*)
(*    50 0060:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    51 0062:*)  CODE(06702H);(*                    beq c23*)
(*    52 0064:*)  CODE(06150H);(*                    bsr.s nq*)
(*    53 0066:*)  CODE(05441H);(*              c23:  addq.w #2,d1*)
(*    54 0068:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    55 006a:*)  CODE(06406H);(*                    bcc c2e*)
(*    56 006c:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    57 006e:*)  CODE(06702H);(*                    beq c2e*)
(*    58 0070:*)  CODE(06144H);(*                    bsr.s nq*)
(*    59 0072:*)  CODE(05541H);(*              c2e:  subq.w #2,d1*)
(*    60 0074:*)  CODE(02146H,00004H);(*             move.l d6,4(a0)*)
(*    61 0078:*)  CODE(05240H);(*              row3: addq.w #1,d0*)
(*    62 007a:*)  CODE(0b043H);(*                    cmp.w d3,d0*)
(*    63 007c:*)  CODE(06452H);(*                    bcc fin*)
(*    64 007e:*)  CODE(05088H);(*                    addq.l #8,a0*)
(*    65 0080:*)  CODE(05088H);(*                    addq.l #8,a0*)
(*    66 0082:*)  CODE(02a28H,00008H);(*             move.l 8(a0),d5*)
(*    67 0086:*)  CODE(04685H);(*                    NOT.l d5*)
(*    68 0088:*)  CODE(02c28H,00004H);(*             move.l 4(a0),d6*)
(*    69 008c:*)  CODE(0ca86H);(*                    AND.l d6,d5*)
(*    70 008e:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    71 0090:*)  CODE(06406H);(*                    bcc c32*)
(*    72 0092:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    73 0094:*)  CODE(06702H);(*                    beq c32*)
(*    74 0096:*)  CODE(0611eH);(*                    bsr.s nq*)
(*    75 0098:*)  CODE(05241H);(*              c32:  addq.w #1,d1*)
(*    76 009a:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    77 009c:*)  CODE(06406H);(*                    bcc c33*)
(*    78 009e:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    79 00a0:*)  CODE(06702H);(*                    beq c33*)
(*    80 00a2:*)  CODE(06112H);(*                    bsr.s nq*)
(*    81 00a4:*)  CODE(05241H);(*              c33:  addq.w #1,d1*)
(*    82 00a6:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    83 00a8:*)  CODE(06406H);(*                    bcc c3e*)
(*    84 00aa:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    85 00ac:*)  CODE(06702H);(*                    beq c3e*)
(*    86 00ae:*)  CODE(06106H);(*                    bsr.s nq*)
(*    87 00b0:*)  CODE(02146H,00004H);(*       c3e:  move.l d6,4(a0)*)
(*    88 00b4:*)  CODE(0601aH);(*              		bra.s fin*)
(*    89 00b6:*)  CODE(03e12H);(*              nq:	move.w (a2),d7	; queue index*)
(*    90 00b8:*)  CODE(0e547H);(*              		asl.w #2,d7		; scale for longwords*)
(*    91 00ba:*)  CODE(047f1H,07000H);(*       		lea.l 0(a1,d7.w),a3 ; get pos*)
(*    92 00be:*)  CODE(036c1H);(*              		move.w d1,(a3)+	; save x*)
(*    93 00c0:*)  CODE(03680H);(*              		move.w d0,(a3)		; save y*)
(*    94 00c2:*)  CODE(00386H);(*                    bclr.l d1,d6		; uncover square*)
(*    95 00c4:*)  CODE(05252H);(*              		addq.w #1,(a2)		; inc stack pos*)
(*    96 00c6:*)  CODE(0b852H);(*              		cmp.w (a2),d4		; wrap ?*)
(*    97 00c8:*)  CODE(06702H);(*              		beq.s wrap*)
(*    98 00ca:*)  CODE(04e75H);(*              		rts*)
(*    99 00cc:*)  CODE(04252H);(*              wrap:	clr.w (a2)*)
(*   100 00ce:*)  CODE(04e75H);(*              		rts*)
(*   101 00d0:*)  CODE(04e71H);(*              fin:  nop*)
(*Queue-*)
(*Stack+
	SETREG(0,yi);
	SETREG(1,xi);
	SETREG(2,FieldWidth);
	SETREG(3,FieldHeight);
	SETREG(8,ADR(FieldRows));
	SETREG(10,ADR(Stack));
(*     1 0000:                          ;d0 - y*)
(*     2 0000:                          ;d1 - x*)
(*     3 0000:                          ;d2 - FieldWidth*)
(*     4 0000:                          ;d3 - FieldHeight*)
(*     5 0000:                          ;a0 - FieldRows*)
(*     6 0000:                          ;a1 - Stack*)
(*     7 0000:                          ;a2 - ADR(Stack)*)
(*     8 0000:*)  CODE(02252H);(*                    move.l (a2),a1*)
(*     9 0002:*)  CODE(05341H);(*                    subq.w #1,d1*)
(*    10 0004:*)  CODE(05340H);(*                    subq.w #1,d0*)
(*    11 0006:*)  CODE(03800H);(*                    move.w d0,d4*)
(*    12 0008:*)  CODE(0e944H);(*                    asl.w #4,d4*)
(*    13 000a:*)  CODE(0d0c4H);(*                    adda.w d4,a0*)
(*    14 000c:*)  CODE(0b043H);(*                    cmp.w d3,d0*)
(*    15 000e:*)  CODE(06440H);(*                    bcc row2*)
(*    16 0010:*)  CODE(02a28H,00008H);(*             move.l 8(a0),d5*)
(*    17 0014:*)  CODE(04685H);(*                    NOT.l d5*)
(*    18 0016:*)  CODE(02c28H,00004H);(*             move.l 4(a0),d6*)
(*    19 001a:*)  CODE(0ca86H);(*                    AND.l d6,d5*)
(*    20 001c:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    21 001e:*)  CODE(0640aH);(*                    bcc c12*)
(*    22 0020:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    23 0022:*)  CODE(06706H);(*                    beq c12*)
(*    24 0024:*)  CODE(032c1H);(*                    move.w d1,(a1)+*)
(*    25 0026:*)  CODE(032c0H);(*                    move.w d0,(a1)+*)
(*    26 0028:*)  CODE(00386H);(*                    bclr.l d1,d6*)
(*    27 002a:*)  CODE(05241H);(*              c12:  addq.w #1,d1*)
(*    28 002c:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    29 002e:*)  CODE(0640aH);(*                    bcc c13*)
(*    30 0030:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    31 0032:*)  CODE(06706H);(*                    beq c13*)
(*    32 0034:*)  CODE(032c1H);(*                    move.w d1,(a1)+*)
(*    33 0036:*)  CODE(032c0H);(*                    move.w d0,(a1)+*)
(*    34 0038:*)  CODE(00386H);(*                    bclr.l d1,d6*)
(*    35 003a:*)  CODE(05241H);(*              c13:  addq.w #1,d1*)
(*    36 003c:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    37 003e:*)  CODE(0640aH);(*                    bcc c1e*)
(*    38 0040:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    39 0042:*)  CODE(06706H);(*                    beq c1e*)
(*    40 0044:*)  CODE(032c1H);(*                    move.w d1,(a1)+*)
(*    41 0046:*)  CODE(032c0H);(*                    move.w d0,(a1)+*)
(*    42 0048:*)  CODE(00386H);(*                    bclr.l d1,d6*)
(*    43 004a:*)  CODE(05541H);(*              c1e:  subq.w #2,d1*)
(*    44 004c:*)  CODE(02146H,00004H);(*             move.l d6,4(a0)*)
(*    45 0050:*)  CODE(05088H);(*              row2: addq.l #8,a0*)
(*    46 0052:*)  CODE(05088H);(*                    addq.l #8,a0*)
(*    47 0054:*)  CODE(05240H);(*                    addq.w #1,d0*)
(*    48 0056:*)  CODE(0b043H);(*                    cmp.w d3,d0*)
(*    49 0058:*)  CODE(06430H);(*                    bcc row3*)
(*    50 005a:*)  CODE(02a28H,00008H);(*             move.l 8(a0),d5*)
(*    51 005e:*)  CODE(04685H);(*                    NOT.l d5*)
(*    52 0060:*)  CODE(02c28H,00004H);(*             move.l 4(a0),d6*)
(*    53 0064:*)  CODE(0ca86H);(*                    AND.l d6,d5*)
(*    54 0066:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    55 0068:*)  CODE(0640aH);(*                    bcc c23*)
(*    56 006a:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    57 006c:*)  CODE(06706H);(*                    beq c23*)
(*    58 006e:*)  CODE(032c1H);(*                    move.w d1,(a1)+*)
(*    59 0070:*)  CODE(032c0H);(*                    move.w d0,(a1)+*)
(*    60 0072:*)  CODE(00386H);(*                    bclr.l d1,d6*)
(*    61 0074:*)  CODE(05441H);(*              c23:  addq.w #2,d1*)
(*    62 0076:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    63 0078:*)  CODE(0640aH);(*                    bcc c2e*)
(*    64 007a:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    65 007c:*)  CODE(06706H);(*                    beq c2e*)
(*    66 007e:*)  CODE(032c1H);(*                    move.w d1,(a1)+*)
(*    67 0080:*)  CODE(032c0H);(*                    move.w d0,(a1)+*)
(*    68 0082:*)  CODE(00386H);(*                    bclr.l d1,d6*)
(*    69 0084:*)  CODE(05541H);(*              c2e:  subq.w #2,d1*)
(*    70 0086:*)  CODE(02146H,00004H);(*             move.l d6,4(a0)*)
(*    71 008a:*)  CODE(05240H);(*              row3: addq.w #1,d0*)
(*    72 008c:*)  CODE(0b043H);(*                    cmp.w d3,d0*)
(*    73 008e:*)  CODE(06442H);(*                    bcc fin*)
(*    74 0090:*)  CODE(05088H);(*                    addq.l #8,a0*)
(*    75 0092:*)  CODE(05088H);(*                    addq.l #8,a0*)
(*    76 0094:*)  CODE(02a28H,00008H);(*             move.l 8(a0),d5*)
(*    77 0098:*)  CODE(04685H);(*                    NOT.l d5*)
(*    78 009a:*)  CODE(02c28H,00004H);(*             move.l 4(a0),d6*)
(*    79 009e:*)  CODE(0ca86H);(*                    AND.l d6,d5*)
(*    80 00a0:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    81 00a2:*)  CODE(0640aH);(*                    bcc c32*)
(*    82 00a4:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    83 00a6:*)  CODE(06706H);(*                    beq c32*)
(*    84 00a8:*)  CODE(032c1H);(*                    move.w d1,(a1)+*)
(*    85 00aa:*)  CODE(032c0H);(*                    move.w d0,(a1)+*)
(*    86 00ac:*)  CODE(00386H);(*                    bclr.l d1,d6*)
(*    87 00ae:*)  CODE(05241H);(*              c32:  addq.w #1,d1*)
(*    88 00b0:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    89 00b2:*)  CODE(0640aH);(*                    bcc c33*)
(*    90 00b4:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    91 00b6:*)  CODE(06706H);(*                    beq c33*)
(*    92 00b8:*)  CODE(032c1H);(*                    move.w d1,(a1)+*)
(*    93 00ba:*)  CODE(032c0H);(*                    move.w d0,(a1)+*)
(*    94 00bc:*)  CODE(00386H);(*                    bclr.l d1,d6*)
(*    95 00be:*)  CODE(05241H);(*              c33:  addq.w #1,d1*)
(*    96 00c0:*)  CODE(0b242H);(*                    cmp.w d2,d1*)
(*    97 00c2:*)  CODE(0640aH);(*                    bcc c3e*)
(*    98 00c4:*)  CODE(00305H);(*                    btst.l d1,d5*)
(*    99 00c6:*)  CODE(06706H);(*                    beq c3e*)
(*   100 00c8:*)  CODE(032c1H);(*                    move.w d1,(a1)+*)
(*   101 00ca:*)  CODE(032c0H);(*                    move.w d0,(a1)+*)
(*   102 00cc:*)  CODE(00386H);(*                    bclr.l d1,d6*)
(*   103 00ce:*)  CODE(02146H,00004H);(*       c3e:  move.l d6,4(a0)*)
(*   104 00d2:*)  CODE(02489H);(*              fin:  move.l a1,(a2)*)
*Stack-*)
END LookAround;
(*$LONG_ADDR=*)
(***LookAsm-*)

(***LookM2+
PROCEDURE LookAround(xi,yi : INTEGER);
BEGIN
  PushStack(xi - 1,yi - 1);
  PushStack(xi + 1,yi - 1);
  PushStack(xi + 1,yi + 1);
  PushStack(xi - 1,yi + 1);

  PushStack(xi - 1,yi);
  PushStack(xi,yi - 1);
  PushStack(xi + 1,yi);
  PushStack(xi,yi + 1);


END LookAround;
*LookM2-*)

PROCEDURE PopStack(VAR x,y : INTEGER) : BOOLEAN;
BEGIN
(*Queue+*)
  IF StackIn = StackOut THEN
    RETURN FALSE
  END;
  WITH StackBuffer^[StackOut] DO
    x := xv;
    y := yv;
  END;
  StackOut := (StackOut + 1) MOD MaxStackDepth;
(*Queue-*)

(*Stack+
  IF Stack = StackBuffer THEN
    RETURN FALSE
  END;
  DEC(Stack,4);
  WITH Stack^ DO
    x := xv;
    y := yv;
  END;
*Stack-*)

(*StackCheck+
  DEC(StackDepth);
*StackCheck-*)

  RETURN TRUE
END PopStack;

PROCEDURE EmptyStack();
BEGIN
(*Queue+*)
  StackIn := 0;
  StackOut := 0;
(*Queue-*)
(*Stack+
  Stack := StackBuffer;
*Stack-*)

(*StackCheck+
    TermOut.WriteString("MAX Stack Depth = ");
    TermOut.WriteCard(StackDepthReached,1);
    TermOut.WriteLn;
    StackDepthReached := 0;
  StackDepth := 0;
*StackCheck-*)
END EmptyStack;


PROCEDURE Transform(VAR x,y : INTEGER) : BOOLEAN;
BEGIN
	DEC(x, LeftFieldBorder);
	IF x < 0 THEN RETURN FALSE END;
	DEC(y,TopFieldBorder);
	IF y < 0 THEN RETURN FALSE END;
	x := x DIV SquareWidth;
	y := y DIV SquareHeight;
   IF (x >= FieldWidth) OR (y >= FieldHeight) THEN
     RETURN FALSE
   END;
	RETURN TRUE
END Transform;

PROCEDURE InitEntryGad() : BOOLEAN;
VAR
	lp : POINTER TO LONGCARD;
	h : CARDINAL;
	strWidth : CARDINAL;

  PROCEDURE addpoint(x,y : INTEGER);
  BEGIN
    lp^ := LONG(x,y);
    INC(lp,4);
  END addpoint;
  
BEGIN
  IF EntryGad = NIL THEN
    strWidth :=  30 * SquareFont^.tfXSize;
    Storage.ALLOCATE(EntryGad,SIZE(EntryGad^));
    IF EntryGad = NIL THEN RETURN FALSE END;
    h := SquareFont^.tfYSize;
    WITH EntryGad^ DO
      lp := ADR(slines[0]);
      addpoint(0,0);
      addpoint(strWidth + 2,0);
      addpoint(strWidth + 2,h + 2);
      addpoint(0,h + 2);
      addpoint(0,0);
      addpoint(0,0);
      addpoint(strWidth + 2,0);
      addpoint(strWidth + 2,h + 2);
      addpoint(0,h + 2);
      addpoint(0,0);
      WITH borders[0] DO
        LeftEdge := -1;
        TopEdge := -1;
        FrontPen := BYTE(Pen1);
        DrawMode := Rasters.Jam1;
        Count := BYTE(5);
        XY := ADR(slines[0]);
        NextBorder := ADR(borders[1]);
      END;
      WITH borders[1] DO
        LeftEdge := -2;
        TopEdge := -2;
        FrontPen := BYTE(Pen2);
        DrawMode := Rasters.Jam1;
        Count := BYTE(5);
        XY := ADR(slines[5]);
      END;
      WITH se DO
        Font := SquareFont;
        Pens[0] := BYTE(1);
        Pens[1] := BYTE(0);
        ActivePens[0] := BYTE(2);
        ActivePens[1] := BYTE(3);
      END;
      WITH SI DO
        Buffer := ADR(fsBuffer);
        UndoBuffer := ADR(fsUndoBuffer);
        MaxChars := MaxStringLength + 1;
        Extension := ADR(se);
      END;
      WITH Gad DO
        TopEdge := - INTEGER(4 * h);
        LeftEdge := 5 * SquareFont^.tfXSize;
        Width := strWidth;
        Height := h;
        Flags := Intuition.GadgetFlagSet{Intuition.GRelBottom, Intuition.GFStringExtend};
        Activation := Intuition.ActivationFlagSet{Intuition.RelVerify, Intuition.StringCenter};
        GadgetType := Intuition.StrGadget;
        GadgetRenderB := ADR(borders[0]);
        GadgetText := NIL;
        SpecialInfoS := ADR(SI);
        GadgetID := strID;
      END;
    END;
  END;
  RETURN TRUE
END InitEntryGad;



PROCEDURE InitFields() : BOOLEAN;
VAR
	sec,mic : LONGCARD;
	i,j : INTEGER;
	x,y : CARDINAL;
BEGIN
  NumMines := MinesThisLevel;
  i := (FieldWidth * FieldHeight);
  IF NumMines > (i - 2) THEN
    NumMines := i - 2; (* Silly Person *)
  END;
  NonMinesCovered := i - NumMines;
  Restarting := TRUE;
  WaitingForNew := FALSE;
  Intuition.CurrentTime(sec,mic);
  sec := LONGCARD(LONGBITSET(sec)/LONGBITSET(mic));
  FastRandom.seed := sec;
  CoveredRow := LONGBITSET{0..FieldWidth - 1};
  FOR i := 0 TO FieldHeight - 1 DO
    WITH FieldRows[i] DO
      Mines := LONGBITSET{};
      Covered := CoveredRow;
      Guessed := LONGBITSET{};
      Wondered := LONGBITSET{};
    END
  END;
  i := 0;
  WHILE i < NumMines DO
    x := FastRandom.Random(FieldWidth);
    y := FastRandom.Random(FieldHeight);
    WITH FieldRows[y] DO
      IF ~(x IN Mines) THEN
        INCL(Mines,x);
        INC(i);
      END;
    END;
  END;
  GuessedMines := 0;
  GuessedRightMines := 0;
  RETURN TRUE
END InitFields;

PROCEDURE ShowSquare(x,y : CARDINAL; state : SquareState);
VAR
	square : Intuition.ImagePtr;
BEGIN
  x := SquareWidth * x + CARDINAL(LeftFieldBorder);
  y := CARDINAL(SquareHeight) * y + CARDINAL(TopFieldBorder);
  Intuition.DrawImage(FieldRPort,SquareImages[ORD(state)],x,y)
END ShowSquare;


PROCEDURE MySizeWindow(tw,th : INTEGER) : BOOLEAN;
VAR
	msg : Intuition.IntuiMessagePtr;
	port : Ports.MsgPortPtr;
	dx,dy : INTEGER;
BEGIN
  WITH FieldWindow^ DO
    IF tw > WScreen^.Width THEN
      RETURN FALSE
    END;
    IF th > WScreen^.Height THEN
      RETURN FALSE
    END;
    port := UserPort;
    dx := tw - Width;
    dy := th - Height;
    IF LeftEdge + tw > WScreen^.Width THEN
      Intuition.MoveWindow(FieldWindow,WScreen^.Width - tw - LeftEdge,0);
    END;
    IF TopEdge + th > WScreen^.Height THEN
      Intuition.MoveWindow(FieldWindow,0,WScreen^.Height - th - TopEdge);
    END;
  END;
  Intuition.SizeWindow(FieldWindow,dx,dy);
  WindowSized := TRUE;
(*
  LOOP
    LOOP
      msg := Ports.GetMsg(port);
      IF msg # NIL THEN EXIT END;
      msg := Ports.WaitPort(port);
    END;
    IF Intuition.RefreshWindow IN msg^.Class THEN
      Intuition.BeginRefresh(FieldWindow);
      Intuition.EndRefresh(FieldWindow, TRUE);
    END;
    IF Intuition.NewSize IN msg^.Class THEN
      EXIT
    END;
    Ports.ReplyMsg(msg);
  END;
  Ports.ReplyMsg(msg);
*)
  RETURN TRUE;
END MySizeWindow;

PROCEDURE MenuCheckedIf(b:BOOLEAN);
BEGIN
  IF b THEN
    INCL(EasyMenus.nextItemFlags,Intuition.Checked)
  ELSE
    EXCL(EasyMenus.nextItemFlags,Intuition.Checked)
  END
END MenuCheckedIf;

PROCEDURE OpenFonts();
VAR
	vp : Views.ViewPtr;
BEGIN
(*
  CountFontAttrPtr := ADR(CountFontAttr);
*)
  WITH SquareFontAttr DO
    taName  := ADR(SquareFontName);
    taYSize := SquareFontSize;
    taStyle := Text.NormalStyle;
    taFlags := Text.FontFlagSet{Text.DiskFont};
    vp := Intuition.ViewAddress();
    IF Views.HiRes IN vp^.Modes THEN
      IF ~(Views.Lace IN vp^.Modes) THEN
        INCL(taFlags,Text.TallDot)
      END
    ELSE
      IF Views.Lace IN vp^.Modes THEN
        INCL(taFlags,Text.LongDot)
      END
    END
  END;

  SquareFontAttrPtr := NIL;
  IF Laced THEN
    SquareFontAttrPtr := ADR(SquareFontAttr);
  END;

  SquareFont := EasyText.EasyOpenFont(SquareFontAttrPtr);
  IF SquareFont = NIL THEN
    SquareFontAttrPtr := NIL;
    SquareFont := EasyText.EasyOpenFont(SquareFontAttrPtr)
  END;
  WITH SquareFont^ DO
    LeftSquareText := LeftFieldBorder + (SquareWidth - INTEGER(tfXSize)) DIV 2;
    TopSquareText := TopFieldBorder + (SquareHeight - INTEGER(tfYSize)) DIV 2;
  END;
END OpenFonts;

PROCEDURE CreateMenus();
BEGIN
  menu := NIL;
(*  IF V36 THEN*)
    EasyMenus.nextTextAttr := ADR(SquareFontAttr);
    EasyMenus.nextItemHeight := SquareFont^.tfYSize + 2;
(*  END;*)

      
  EasyMenus.StartStrip;

  EasyMenus.nextFrontPen := 2;
  EasyMenus.nextBackPen := 1;
  EasyMenus.nextIntuiTopEdge := 0;

  EasyMenus.AddMenu(GameText, Intuition.CheckWidth + (Intuition.CommWidth * 2) + SquareFont^.tfXSize * 12);

  EasyMenus.AddItem("New",'N');

  INCL(EasyMenus.nextItemFlags,Intuition.CheckIt);

  MenuCheckedIf(Level = Beginner);
  EasyMenus.AddItem(BeginnerString,'B');
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{2,3,4};

  MenuCheckedIf(Level = Intermediate);
  EasyMenus.AddItem(IntermediateString,'I');
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{1,3,4};

  MenuCheckedIf(Level = Expert);
  EasyMenus.AddItem(ExpertString,'E');
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{1,2,4};

  MenuCheckedIf(Level = Custom);
  EasyMenus.AddItem(CustomString,'C');
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{1,2,3};

  INCL(EasyMenus.nextItemFlags,Intuition.MenuToggle);

  MenuCheckedIf(Questions);
  EasyMenus.AddItem("Marks (?)",'M');
  
  EasyMenus.nextItemFlags := Intuition.ItemFlagSet{Intuition.ItemText, Intuition.ItemEnabled} + Intuition.HighComp;
  EasyMenus.AddItem("Cursor",0C);
  EasyMenus.nextSubLeftEdge := EasyMenus.nextItemLeftEdge + EasyMenus.nextItemWidth - 20;
  EasyMenus.nextSubHeight := EasyMenus.nextItemHeight;
  EasyMenus.nextSubWidth := SquareFont^.tfXSize * 9;
  EasyMenus.nextSubFlags := Intuition.ItemFlagSet{Intuition.ItemText, Intuition.ItemEnabled, Intuition.CheckIt} + Intuition.HighComp;
  IF MovingAllowed THEN
    INCL(EasyMenus.nextSubFlags,Intuition.Checked)
  ELSE
    EXCL(EasyMenus.nextSubFlags,Intuition.Checked)
  END;
  EasyMenus.AddSub("Mobile",0C);
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{1};
  IF MovingAllowed THEN
    EXCL(EasyMenus.nextSubFlags,Intuition.Checked)
  ELSE
    INCL(EasyMenus.nextSubFlags,Intuition.Checked)
  END;
  EasyMenus.AddSub("Static",0C);
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{0};

  EasyMenus.nextItemFlags := Intuition.ItemFlagSet{Intuition.ItemText, Intuition.ItemEnabled} + Intuition.HighComp;
  EasyMenus.AddItem("Best Times",'T');
  EasyMenus.AddItem("About MineSweeper",'?');
  EasyMenus.AddItem("Pause",'P');
  EasyMenus.AddItem("Quit",'Q');

(*  EasyMenus.AddItem("Test",0C);*)

  EasyMenus.AddMenu(CompleteText,Intuition.CheckWidth + (Intuition.CommWidth * 2) + SquareFont^.tfXSize * 12);
  
  EasyMenus.nextItemFlags := Intuition.ItemFlagSet{Intuition.ItemText, Intuition.ItemEnabled, Intuition.CheckIt} + Intuition.HighComp;
  MenuCheckedIf(CompleteMethod = AllMinesFound);
  EasyMenus.AddItem("All Mines Found",0C);
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{1,2,3};
  MenuCheckedIf(CompleteMethod = AllNonMinesFound);
  EasyMenus.AddItem("All Non Mines Found",0C);
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{0,2,3};
  MenuCheckedIf(CompleteMethod = AllSquaresFound);
  EasyMenus.AddItem("Both",0C);
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{0,1,3};
  MenuCheckedIf(CompleteMethod = EitherCompletion);
  EasyMenus.AddItem("Either",0C);
  EasyMenus.currentItem^.MutualExclude := LONGBITSET{0,1,2};
  
  IF ~EasyMenus.stripFailed THEN
    menu := EasyMenus.currentStrip;
  END;
END CreateMenus;

PROCEDURE CreateFieldWindow() : BOOLEAN;
VAR
	nw : Intuition.NewWindowPtr;
	FullWindowWidth, FullWindowHeight, gadpos, TextOffset : INTEGER;
	TooBig, Sized : BOOLEAN;
BEGIN
  Sized := FALSE;
  FieldWindowWidth := SquareWidth * FieldWidth;
  FieldWindowHeight := SquareHeight * FieldHeight;
  RightFieldBorder := LeftFieldBorder + FieldWindowWidth - 1;
  FullWindowWidth := FieldWindowWidth + LeftFieldBorder + RightWindowBorder;
  FullWindowHeight := FieldWindowHeight + TopFieldBorder + BottomWindowBorder;
  IF FieldWindow = NIL THEN
    Storage.ALLOCATE(nw,SIZE(nw^));
    IF nw = NIL THEN RETURN FALSE END;
    WITH nw^ DO
      LeftEdge := HighScores^.InitX;
      TopEdge := HighScores^.InitY;
      Width := FullWindowWidth;
      Height := FullWindowHeight;
      DetailPen := BYTE(2);
      BlockPen := BYTE(1);
      IDCMPFlags := NormalIDCMPs;
      Flags :=
       Intuition.WindowFlagSet{Intuition.WindowDrag,Intuition.WindowDepth,
                               Intuition.WindowClose,Intuition.Activate,
                               Intuition.ReportMouseFlag
                              } + Intuition.SimpleRefresh;
      CheckMark := SquareImages[GuessImage];
      Title := ADR(MineSweeperName);
      Type := Intuition.WBenchScreen;
    END;
    Storage.ALLOCATE(NewGameGad,SIZE(NewGameGad^) (* * 2*) );
    IF NewGameGad = NIL THEN
      RETURN FALSE
    END;
    FieldWindow := Intuition.OpenWindow(nw);
    IF FieldWindow = NIL THEN
      nw^.LeftEdge := 0;
      nw^.TopEdge := 0;
      FieldWindow := Intuition.OpenWindow(nw);
      IF FieldWindow = NIL THEN
        IF Level # Beginner THEN
          Storage.DEALLOCATE(nw,0);
          Storage.DEALLOCATE(NewGameGad,0);
          SetSize(Beginner);
          RETURN CreateFieldWindow()
        END;
      END
    END;
    IF menu # NIL THEN
      menu^.Width := Text.TextLength(ADR(FieldWindow^.WScreen^.RPort),ADR(GameText),GameTextLen) * 2;
      WITH menu^.NextMenu^ DO
        Width := Text.TextLength(ADR(FieldWindow^.WScreen^.RPort),ADR(CompleteText),CompleteTextLen) * 2;
        LeftEdge := menu^.LeftEdge + menu^.Width + 10;
      END;
      Intuition.SetMenuStrip(FieldWindow,menu)
    END;
    Pointer.SetMyPointer(FieldWindow);
    Storage.DEALLOCATE(nw,0);
    FieldRPort := FieldWindow^.RPort;
    Pens.SetWrMsk(FieldRPort,{0,1});
(*
    WITH FieldRPort^ DO
      TermOut.WriteString("Mask = ");
      TermOut.WriteHex(CARDINAL(Mask),1);
      TermOut.WriteString("\nAreaPtrn = ");
      TermOut.WriteLongHex(AreaPtrn,1);
      TermOut.WriteString("\nAreaPtSz = ");
      TermOut.WriteInt(INTEGER(AreaPtSz),1);
      TermOut.WriteLn;
    END;
*)
    FindColourScheme;


    
    WITH FieldWindow^ DO
      MainProcess^.prWindowPtr := FieldWindow;
      WindowBit := Tasks.SIGNAL(UserPort^.mpSigBit);
      IF ~InitClip(CARDINAL(WScreen^.BMap.Depth)) THEN
        RETURN FALSE
      END;
      (*Intuition.RefreshWindowFrame(FieldWindow);*)
      Pens.SetDrMd(RPort,Rasters.Jam1);
    END;
    WITH NewGameGad^ DO
      NextGadget := NIL;
      GadgetRenderI := dataPtr^.Images[SmileyImage];
      SelectRenderI := dataPtr^.Images[AstonishedImage];
      Width := GadgetRenderI^.Width;
      Height := GadgetRenderI^.Height;
      LeftEdge := (FieldWindowWidth DIV 2) + LeftFieldBorder - (Width DIV 2);
      TopEdge := TopWindowBorder + (TopWindowArea - Height) DIV 2;
      Flags := Intuition.GadgetFlagSet{Intuition.GadgImage} + Intuition.GadgHImage;
      Activation := Intuition.ActivationFlagSet{Intuition.RelVerify};
      GadgetType := Intuition.BoolGadget;
      GadgetID := NewGameID;
    END;
    gadpos := Intuition.AddGList(FieldWindow,NewGameGad,-1,2,NIL);
      IF ~MySizeWindow(FullWindowWidth, FullWindowHeight) THEN
        IF Level # Beginner THEN
          SetSize(Beginner);
          RETURN CreateFieldWindow()
        END;
        Intuition.CloseWindow(FieldWindow);
        RETURN FALSE
      END;
      Sized := TRUE;
  ELSE
    gadpos := Intuition.RemoveGadget(FieldWindow,NewGameGad);
    IF (LastFieldWidth # FieldWidth) OR (LastFieldHeight # FieldHeight) THEN
      IF ~MySizeWindow(FullWindowWidth, FullWindowHeight) THEN
        IF Level = Beginner THEN
          RETURN FALSE
        END;
        DEC(Level);
        SetSize(Level);
        RETURN CreateFieldWindow()
      END;
    END;
    WITH NewGameGad^ DO
      GadgetRenderI := dataPtr^.Images[SmileyImage];
      SelectRenderI := dataPtr^.Images[AstonishedImage];
      LeftEdge := (FieldWindowWidth DIV 2) + LeftFieldBorder - (Width DIV 2);
      TopEdge := TopWindowBorder + (TopWindowArea - Height) DIV 2;
    END;
    gadpos := Intuition.AddGadget(FieldWindow,NewGameGad,-1);
  END;
  LastFieldWidth := FieldWidth;
  LastFieldHeight := FieldHeight;
  TooBig := FALSE;
  TopTextBorder := TopWindowBorder + (TopWindowArea - 14 (*INTEGER(CountFont^.tfYSize)*)) DIV 2;
  TextWidth := (4 * 10 (*CountFont^.tfXSize*)) + 1;
  TextHeight := 14 (*CountFont^.tfYSize*) + 1;
  TextOffset := (NewGameGad^.LeftEdge - LeftFieldBorder - TextWidth) DIV 2;
  LeftTextBorder := LeftFieldBorder + TextOffset;
  RightTextBorder := NewGameGad^.LeftEdge + NewGameGad^.Width + TextOffset - 1;
  IF ODD(LeftTextBorder) THEN DEC(LeftTextBorder) END;
  IF ODD(RightTextBorder) THEN DEC(RightTextBorder) END;
  WITH NewGameGad^ DO
    SmileX := LeftEdge;
    SmileY := TopEdge;
  END;
  IF Shrunk THEN
    Shrunk := FALSE;
    (*RefreshDisplay(TRUE);*)
    IF ~WaitingForNew THEN StartTimer END;
    RETURN TRUE
  END;
  IF InitFields() THEN
    Intuition.ModifyIDCMP(FieldWindow,NormalIDCMPs);
    INCL(FieldWindow^.Flags,Intuition.ReportMouseFlag);
    RETURN TRUE
  ELSE
    RETURN FALSE
  END
END CreateFieldWindow;



(*
PROCEDURE Count(Field : FieldType; xi,yi : INTEGER) : CARDINAL;

  PROCEDURE TestSquare(xi,yi : INTEGER) : CARDINAL;
  VAR b : BOOLEAN;
  BEGIN
    IF (CARDINAL(xi) < CARDINAL(FieldWidth)) & (CARDINAL(yi) < CARDINAL(FieldHeight)) THEN
      WITH FieldRows[CARDINAL(yi)] DO
        CASE Field OF
        | fMines    : b := CARDINAL(xi) IN Mines;
        | fCovered  : b := CARDINAL(xi) IN Covered;
        | fGuessed  : b := CARDINAL(xi) IN Guessed;
        | fWondered : b := CARDINAL(xi) IN Wondered;
        END;
      END;
      RETURN CARDINAL(b);
    END;
    RETURN 0
  END TestSquare;

BEGIN
  RETURN
    TestSquare(xi-1,yi-1)
 +  TestSquare(xi,yi-1)
 +  TestSquare(xi+1,yi-1)
 +  TestSquare(xi-1,yi)
 +  TestSquare(xi+1,yi)
 +  TestSquare(xi-1,yi+1)
 +  TestSquare(xi,yi+1)
 +  TestSquare(xi+1,yi+1);
END Count;
*)

PROCEDURE Count(Field : FieldType; xi,yi : INTEGER) : CARDINAL;
BEGIN
	SETREG(8,ADR(FieldRows));
	SETREG(1,FieldWidth);
	SETREG(2,FieldHeight);
	SETREG(3,xi);
	SETREG(4,yi);
	SETREG(5,Field);
(*     2 0000:                    ;		a0 = address of FieldRows*)
(*     3 0000:                    ;		d0 = result*)
(*     4 0000:                    ;		d1 = FieldWidth*)
(*     5 0000:                    ;		d2 = FieldHeight*)
(*     6 0000:                    ;		d3 = x*)
(*     7 0000:                    ;		d4 = y*)
(*     8 0000:                    ;		d5 = offset of field in row, eg 0 = mines, 1 = Covered etc*)
(*     9 0000:*)  CODE(05343H);(*              		subq.w #1,d3*)
(*    10 0002:*)  CODE(05344H);(*              		subq.w #1,d4*)
(*    11 0004:*)  CODE(07000H);(*              		moveq.l #0,d0*)
(*    12 0006:*)  CODE(0611eH);(*              		bsr one	; x-1,y-1*)
(*    13 0008:*)  CODE(05243H);(*              		addq.w #1,d3*)
(*    14 000a:*)  CODE(0611aH);(*              		bsr one	;   x,y-1*)
(*    15 000c:*)  CODE(05243H);(*              		addq.w #1,d3*)
(*    16 000e:*)  CODE(06116H);(*              		bsr one	; x+1,y-1*)
(*    17 0010:*)  CODE(05244H);(*              		addq.w #1,d4*)
(*    18 0012:*)  CODE(06112H);(*              		bsr one	; x+1,y*)
(*    19 0014:*)  CODE(05543H);(*              		subq.w #2,d3*)
(*    20 0016:                    		;bsr one	;   x,y - don't test square itself*)
(*    21 0016:                    		;subq.w #1,d3*)
(*    22 0016:*)  CODE(0610eH);(*              		bsr one	; x-1,y*)
(*    23 0018:*)  CODE(05244H);(*              		addq.w #1,d4*)
(*    24 001a:*)  CODE(0610aH);(*              		bsr one	; x-1,y+1*)
(*    25 001c:*)  CODE(05243H);(*              		addq.w #1,d3*)
(*    26 001e:*)  CODE(06106H);(*              		bsr one	;   x,y+1*)
(*    27 0020:*)  CODE(05243H);(*              		addq.w #1,d3*)
(*    28 0022:*)  CODE(06102H);(*              		bsr one	; x+1,y+1*)
(*    29 0024:*)  CODE(0601cH);(*              		bra.s fin*)
(*    30 0026:                    *)
(*    31 0026:*)  CODE(0b641H);(*              one:	cmp.w d1,d3*)
(*    32 0028:*)  CODE(06416H);(*              		bcc.s out*)
(*    33 002a:*)  CODE(0b842H);(*              		cmp.w d2,d4*)
(*    34 002c:*)  CODE(06412H);(*              		bcc.s out*)
(*    35 002e:*)  CODE(03c04H);(*              		move.w d4,d6*)
(*    36 0030:*)  CODE(0e546H);(*              		asl.w #2,d6*)
(*    37 0032:*)  CODE(0dc45H);(*              		add.w d5,d6*)
(*    38 0034:*)  CODE(0e546H);(*              		asl.w #2,d6*)
(*    39 0036:*)  CODE(02c30H,06000H);(*      		move.l (0,a0,d6),d6*)
(*    40 003a:*)  CODE(00706H);(*              		btst.l d3,d6*)
(*    41 003c:*)  CODE(06702H);(*              		beq.s out*)
(*    42 003e:*)  CODE(05280H);(*              		addq.l #1,d0*)
(*    43 0040:*)  CODE(04e75H);(*              out:	rts*)
(*    44 0042:  4e71              fin:	nop*)
END Count;

(*
PROCEDURE StateOfSquare(x,y : CARDINAL) : SquareState;
VAR
	state : SquareState;
BEGIN
  WITH FieldRows[y] DO
    IF x IN Covered THEN
      state := UnknownState
      IF x IN Wondered THEN
        state := WonderState
        IF x IN Guessed THEN
          state := GuessState
        END
      END
    ELSE
      state := BlankState;
      IF x IN Mines THEN
        state := MineState
      END
    END;
  END;
  RETURN state;
END StateOfSquare;
*)

(*$X-*)
PROCEDURE ChunkyTable();
BEGIN
  CODE(0101H,0101H,0000H,0203H,0404H,0404H,0000H,0203H);
  CODE(0101H,0106H,0000H,0206H,0505H,0505H,0404H,0403H);
END ChunkyTable;


PROCEDURE StateOfSquare(x,y : CARDINAL) : SquareState;
(* This routine uses the Chunky table to convert from an
   element in a 2D bitmap to a SquareState
*)
BEGIN
	SETREG(1,y);
	SETREG(3,x);
	SETREG(8,ADR(FieldRows));
	SETREG(9,CT);
	SETREG(0,WaitingForNew);
(*     2 0000:*)  CODE(0e949H);(*              		lsl.w #4,d1*)
(*     3 0002:*)  CODE(0e908H);(*              		lsl.b #4,d0*)
(*     4 0004:*)  CODE(0d0c1H);(*              		adda.w d1,a0*)
(*     5 0006:*)  CODE(02418H);(*              		move.l (a0)+,d2*)
(*     6 0008:*)  CODE(00702H);(*              		btst.l d3,d2*)
(*     7 000a:*)  CODE(06704H);(*              		beq.s b0*)
(*     8 000c:*)  CODE(008c0H,00003H);(*       		bset.l #3,d0*)
(*     9 0010:*)  CODE(02418H);(*              b0:	move.l (a0)+,d2*)
(*    10 0012:*)  CODE(00702H);(*              		btst.l d3,d2*)
(*    11 0014:*)  CODE(06704H);(*              		beq.s b1*)
(*    12 0016:*)  CODE(008c0H,00002H);(*       		bset.l #2,d0*)
(*    13 001a:*)  CODE(02418H);(*              b1:	move.l (a0)+,d2*)
(*    14 001c:*)  CODE(00702H);(*              		btst.l d3,d2*)
(*    15 001e:*)  CODE(06704H);(*              		beq.s b2*)
(*    16 0020:*)  CODE(008c0H,00000H);(*       		bset.l #0,d0*)
(*    17 0024:*)  CODE(02410H);(*              b2:	move.l (a0),d2*)
(*    18 0026:*)  CODE(00702H);(*              		btst.l d3,d2*)
(*    19 0028:*)  CODE(06704H);(*              		beq.s b3*)
(*    20 002a:*)  CODE(008c0H,00001H);(*       		bset.l #1,d0*)
(*    21 002e:*)  CODE(01031H,00000H);(*       b3:	move.b 0(a1,d0.w),d0*)
END StateOfSquare;


PROCEDURE ShowASquare(x,y : CARDINAL);
VAR
	square : Intuition.ImagePtr;
	state : SquareState;
	xp,yp : CARDINAL;
	b : BOOLEAN;
	str : CHAR;
BEGIN
  state := StateOfSquare(x,y);
  xp := SquareWidth * x;
  yp := CARDINAL(SquareHeight) * y;
  Intuition.DrawImage(FieldRPort,SquareImages[ORD(state)],xp  + CARDINAL(LeftFieldBorder),yp  + CARDINAL(TopFieldBorder));
  IF state = BlankState THEN
    LastSquareCount := Count(fMines,x,y);
    IF LastSquareCount > 0 THEN
      str := CHR(ORD('0') + LastSquareCount);
      EasyText.DoShadowWrite
		(	SquareFont,FieldRPort,ADR(str),
			CARDINAL(LeftSquareText) + xp,CARDINAL(TopSquareText) + yp,
			CARDINAL(ColourPens[VAL(ColourNames,LastSquareCount)]),Pen1,1
		);
    END
  END
END ShowASquare;


PROCEDURE Chunky(row : CARDINAL);
BEGIN
  SETREG(8,ADR(FieldRows));
  SETREG(0,row);
  SETREG(1,WaitingForNew);
  SETREG(9,ADR(ChunkyBuf));
  SETREG(10,CT);
  SETREG(6,FieldWidth);
(*     1 0000:*)  CODE(0e980H);(*              		asl.l #4,d0*)
(*     2 0002:*)  CODE(0d1c0H);(*              		adda.l d0,a0*)
(*     3 0004:*)  CODE(0e901H);(*              		asl.b #4,d1*)
(*     4 0006:*)  CODE(0d5c1H);(*              		adda.l d1,a2*)
(*     5 0008:*)  CODE(07000H);(*              		moveq.l #0,d0*)
(*     6 000a:*)  CODE(02418H);(*              		move.l (a0)+,d2	;Mines*)
(*     7 000c:*)  CODE(02618H);(*              		move.l (a0)+,d3	;Covered*)
(*     8 000e:*)  CODE(02818H);(*              		move.l (a0)+,d4	;Guessed*)
(*     9 0010:*)  CODE(02a10H);(*              		move.l (a0),d5		;Wondered*)
(*    10 0012:*)  CODE(05386H);(*              		subq.l #1,d6*)
(*    11 0014:*)  CODE(07e00H);(*              l0:	moveq.l #0,d7*)
(*    12 0016:*)  CODE(00102H);(*              		btst.l d0,d2*)
(*    13 0018:*)  CODE(06702H);(*              		beq.s co*)
(*    14 001a:*)  CODE(05007H);(*              		addq.b #8,d7*)
(*    15 001c:*)  CODE(00103H);(*              co:	btst.l d0,d3*)
(*    16 001e:*)  CODE(06702H);(*              		beq.s wo*)
(*    17 0020:*)  CODE(05807H);(*              		addq.b #4,d7*)
(*    18 0022:*)  CODE(00105H);(*              wo:	btst.l d0,d5*)
(*    19 0024:*)  CODE(06702H);(*              		beq.s gu*)
(*    20 0026:*)  CODE(05407H);(*              		addq.b #2,d7*)
(*    21 0028:*)  CODE(00104H);(*              gu:	btst.l d0,d4*)
(*    22 002a:*)  CODE(06702H);(*              		beq.s wb*)
(*    23 002c:*)  CODE(05207H);(*              		addq.b #1,d7*)
(*    24 002e:*)  CODE(012f2H,07800H);(*       wb:	move.b 0(a2,d7.l),(a1)+*)
(*    25 0032:*)  CODE(05200H);(*              		addq.b #1,d0*)
(*    26 0034:*)  CODE(051ceH,0ffdeH);(*       		dbra d6,l0*)
END Chunky;

(*$X-*)
PROCEDURE TestChunkyTable();
BEGIN
  CODE(0001H,0203H,0405H,0607H,0809H,0A0BH,0C0DH,0E0FH);
  CODE(1011H,1213H,1415H,1617H,1819H,1A1BH,1C1DH,1E1FH);
END TestChunkyTable;


PROCEDURE ShowARow(y : CARDINAL);
(* Refresh one row of the display *)
VAR
	state : SquareState;
	x, xp,yp,xt,yt : CARDINAL;
	sqv : INTEGER;
	str : CHAR;
	cp : POINTER TO SquareState;
BEGIN
  x := 0;
  xp := CARDINAL(LeftFieldBorder);
  xt := CARDINAL(LeftSquareText);
  yp := (CARDINAL(SquareHeight) * y);
  yt := yp;
  INC(yp, CARDINAL(TopFieldBorder));
  INC(yt, CARDINAL(TopSquareText));
  Chunky(y);
  cp := ADR(ChunkyBuf);
  REPEAT
    state := cp^;
    INC(cp);
    Intuition.DrawImage(FieldRPort,SquareImages[ORD(state)],xp,yp);
    IF state = BlankState THEN
      sqv := Count(fMines,x,y);
      IF sqv > 0 THEN
        str := CHR(ORD('0') + sqv);
        EasyText.DoShadowWrite(SquareFont,FieldRPort,ADR(str),xt,yt,CARDINAL(ColourPens[VAL(ColourNames,sqv)]),Pen1,1);
      END
    END;
    INC(x);
    INC(xp,SquareWidth);
    INC(xt,SquareWidth);
  UNTIL x = CARDINAL(FieldWidth);
END ShowARow;

(*ShowStates+
PROCEDURE ShowStates();
VAR
	cp : POINTER TO SquareState;
	x,y : INTEGER;
BEGIN
  CT := ADR(TestChunkyTable);
  FOR y := 0 TO FieldHeight - 1 DO
    Chunky(y);
    cp := ADR(ChunkyBuf);
    FOR x := 0 TO FieldWidth - 1 DO
      TermOut.WriteHex(CARDINAL(cp^),2);
      INC(cp);
    END;
    TermOut.WriteLn;

    FOR x := 0 TO FieldWidth - 1 DO
      TermOut.WriteHex(CARDINAL(StateOfSquare(x,y)),2);
    END;
    TermOut.WriteLn;
    TermOut.WriteLn;
  END;
  CT := ADR(ChunkyTable);
END ShowStates;
*ShowStates-*)

(*
PROCEDURE ShowARow(y : CARDINAL);
VAR
	square : Intuition.ImagePtr;
	state : SquareState;
	x, xp,yp, xt, yt : CARDINAL;
	sqv : INTEGER;
	str : ARRAY [0..1] OF CHAR;
BEGIN
  x := 0;
  xp := 0;
  yp := CARDINAL(SquareHeight) * y;
  WITH FieldRows[y] DO
    REPEAT
      IF x IN Covered THEN
        state := UnknownState;
        IF x IN Wondered THEN
          state := WonderState;
          IF x IN Guessed THEN
            state := GuessState
          END
        END
      ELSE
        state := BlankState;
        IF x IN Mines THEN
          state := MineState
        END
      END;
      Intuition.DrawImage(FieldRPort,SquareImages[ORD(state)],xp  + CARDINAL(LeftFieldBorder),yp  + CARDINAL(TopFieldBorder));
      IF state = BlankState THEN
        sqv := Count(fMines,x,y);
        IF sqv > 0 THEN
          str[0] := CHR(ORD('0') + sqv);
          str[1] := 0C;
            xt := CARDINAL(LeftSquareText) + xp;
            yt := CARDINAL(TopSquareText) + yp;
            EasyText.DoWrite(SquareFont,FieldRPort,str,xt + 1,yt + 1,Pen1,-1,1);
            EasyText.DoWrite(SquareFont,FieldRPort,str,xt,yt,Pen2,-1,1);
        END
      END;
      INC(x);
      INC(xp,SquareWidth);
    UNTIL x = CARDINAL(FieldWidth);
  END;
END ShowARow;
*)

PROCEDURE ChangeFaceImage(ImageNum : CARDINAL);
VAR
	gadpos : INTEGER;
BEGIN
  FaceImage := dataPtr^.Images[ImageNum];
  IF NewGameGad^.GadgetRenderI # FaceImage THEN
    gadpos := Intuition.RemoveGadget(FieldWindow,NewGameGad);
    NewGameGad^.GadgetRenderI := FaceImage;
    gadpos := Intuition.AddGadget(FieldWindow,NewGameGad,gadpos);
  END;
  Intuition.RefreshGList(NewGameGad,FieldWindow,NIL,1);
END ChangeFaceImage;


PROCEDURE UncoveredAMine(x,y : CARDINAL);
VAR
	i,j : CARDINAL;
	cp : POINTER TO SquareState;
BEGIN
  EndGame(NoReStart);
  ChangeFaceImage(UnhappyImage);
  
  FOR j := 0 TO CARDINAL(FieldHeight) - 1 DO
    Chunky(j);
    cp := ADR(ChunkyBuf);
    FOR i := 0 TO CARDINAL(FieldWidth) - 1 DO
      CASE cp^ OF
      | UnknownState, BlankState, WonderState, GuessState :
      | MineState	:
			ShowSquare(i,j,MineState);
      | FoundMineState	:
      	ShowSquare(i,j,FoundMineState);
      | BadGuessState	:
      	ShowSquare(i,j,BadGuessState);
      END;
      INC(cp);
    END
  END;
  FlashDisplay(4);
END UncoveredAMine;


PROCEDURE ProcessStack();
VAR
	xi,yi : INTEGER;
BEGIN
  WHILE PopStack(xi,yi) DO
    IF CARDINAL(xi) IN FieldRows[CARDINAL(yi)].Mines THEN
      UncoveredAMine(xi,yi);
    ELSE
      DEC(NonMinesCovered);
      ShowASquare(xi,yi);
      IF LastSquareCount (*Count(fMines,xi,yi)*) = 0 THEN
        LookAround(xi,yi);
      END;
    END
  END;
  TestGameOver;
END ProcessStack;

PROCEDURE Guess(x,y : CARDINAL);
BEGIN
  WITH FieldRows[y] DO
    IF (x IN Covered) THEN
      IF x IN Guessed THEN
        EXCL(Guessed,x);
        IF ~Questions THEN
          EXCL(Wondered,x);
        END;
        DEC(GuessedMines);
        IF x IN Mines THEN
          DEC(GuessedRightMines)
        END;
      ELSIF x IN Wondered THEN
        EXCL(Wondered,x);
      ELSE
        INCL(Guessed,x);
        INCL(Wondered,x);
        INC(GuessedMines);
        IF x IN Mines THEN
          INC(GuessedRightMines)
        END
      END
    END;
  END;
  ShowASquare(x,y);
  UpdateMineCountDisplay;
  TestGameOver;
END Guess;



PROCEDURE RefreshDisplay(refresh : BOOLEAN);
VAR
	x,y : INTEGER;
	xp,yp : INTEGER;
BEGIN
  IF refresh THEN
    Intuition.BeginRefresh(FieldWindow);
    IF WindowSized THEN
      Intuition.EndRefresh(FieldWindow,TRUE);
    END;
  END;
  IF ~Shrunk THEN
    WITH FieldWindow^ DO
      DoFill(FieldRPort,FadePattern,1,Pen1,INTEGER(BorderLeft),INTEGER(BorderTop),Width - INTEGER(BorderRight) - 1,Height - INTEGER(BorderBottom) - 1);
    END;

    DoFill(FieldRPort,FadePattern,1,3,LeftFieldBorder,TopWindowBorder,RightFieldBorder,TopWindowBorder + TopWindowArea - 1);

    Pens.SetAPen(FieldRPort,Pen1);
    Pens.Move(FieldRPort,LeftFieldBorder,TopFieldBorder - 2);
    Pens.Draw(FieldRPort,LeftFieldBorder,TopWindowBorder);
    Pens.Draw(FieldRPort,RightFieldBorder,TopWindowBorder);


    Pens.SetAPen(FieldRPort,Pen2);
    Pens.Move(FieldRPort,RightFieldBorder, TopWindowBorder);
    Pens.Draw(FieldRPort,RightFieldBorder, TopFieldBorder - 2);
    Pens.Draw(FieldRPort,LeftFieldBorder,TopFieldBorder - 2);

    UpdateMineCountDisplay;
    UpdateTime;
    yp := TopFieldBorder;

    Pens.SetWrMsk(FieldRPort,{0..15});
    DoFill(FieldRPort,SquareImages[ORD(UnknownState)]^.ImageData,FillParam,3,LeftFieldBorder,TopFieldBorder,RightFieldBorder,TopFieldBorder + FieldWindowHeight - 1);
    Pens.SetWrMsk(FieldRPort,{0,1});

    y := FieldHeight;
    IF WaitingForNew THEN
      WHILE y > 0 DO
        DEC(y);
        WITH FieldRows[CARDINAL(y)] DO
          IF (Covered - Mines) # CoveredRow THEN
            ShowARow(y)
          END
        END;
      END;
    ELSE
      WHILE y > 0 DO
        DEC(y);
        IF FieldRows[CARDINAL(y)].Covered # CoveredRow THEN
          ShowARow(y)
        END;
      END;
    END;
    Restarting := FALSE;
  END;
  IF refresh & ~WindowSized THEN
    Intuition.EndRefresh(FieldWindow,TRUE);
  END;
  WindowSized := FALSE;
  Intuition.RefreshGList(NewGameGad,FieldWindow,NIL,1);
END RefreshDisplay;

PROCEDURE UnCover(x,y : CARDINAL);
VAR
	nx,ny : CARDINAL;
BEGIN
  WITH FieldRows[y] DO
(*SafeIfMarked+*)
    IF (x IN Wondered) OR (x IN Guessed) THEN
      RETURN
    END;
(**SafeIfMarked-*)
    IF FirstHit THEN
      IF x IN Mines THEN
(*NotSafeIfMarked+
        IF (x IN Wondered) & (x IN Guessed) THEN
          DEC(GuessedRightMines);
        END;
*NotSafeIfMarked-*)
        ny := FastRandom.Random(FieldHeight);
        WHILE FieldRows[ny].Mines = CoveredRow DO
          ny := (ny + 1) MOD CARDINAL(FieldHeight);
        END;
        nx := FastRandom.Random(FieldWidth);
        WHILE nx IN FieldRows[ny].Mines DO
          nx := (nx + 1) MOD CARDINAL(FieldWidth);
        END;
        IF (nx IN FieldRows[ny].Wondered) & (nx IN FieldRows[ny].Guessed) THEN
          INC(GuessedRightMines);
        END;
        INCL(FieldRows[ny].Mines,nx);
        EXCL(Mines,x);
      END;
      StartTimer;
      FirstHit := FALSE
    END;
    Lastx := -1;
(*NotSafeIfMarked+
    IF x IN Wondered THEN
      EXCL(Wondered,x);
      IF x IN Guessed THEN
        EXCL(Guessed,x);
        DEC(GuessedMines);
        IF x IN Mines THEN
          DEC(GuessedRightMines);
        END;
        UpdateMineCountDisplay;
      END;
    END;
*NotSafeIfMarked-*)
(*
    IF x IN Mines THEN
      INCL(Guessed,x);
      EXCL(Wondered,x);
    END;
*)
  END;
  PushStack(x,y);
  ProcessStack;
END UnCover;

PROCEDURE SureSquare(x,y : CARDINAL);
BEGIN
  IF ~(x IN FieldRows[y].Covered) & (Count(fGuessed,x,y) = Count(fMines,x,y)) THEN
    LookAround(x,y);
    ProcessStack;
  END;
END SureSquare;

PROCEDURE ShowAround(x,y : INTEGER; image : Intuition.ImagePtr);
VAR
	xp,yp : INTEGER;
	mfr : MineFieldRow;

  PROCEDURE ShowOne;
  VAR
    sqr : Intuition.ImagePtr;
  BEGIN
    SETREG(2,x);
    IF (CARDINAL(REGISTER(2)) < CARDINAL(FieldWidth)) & (CARDINAL(y) < CARDINAL(FieldHeight)) THEN
      IF CARDINAL(REGISTER(2)) IN mfr.Covered THEN
        sqr := image;
        IF CARDINAL(REGISTER(2)) IN mfr.Wondered THEN
          sqr := SquareImages[WonderImage + CARDINAL(CARDINAL(REGISTER(2)) IN mfr.Guessed)];
        END;
        Intuition.DrawImage(FieldRPort,sqr,xp,yp);
      END;
    END
  END ShowOne;
  
BEGIN
  DEC(x);
  DEC(y);
  xp := x * SquareWidth + LeftFieldBorder;
  yp := y * SquareHeight + TopFieldBorder;
  
  mfr := FieldRows[y];
  ShowOne;
  INC(x);
  INC(xp,SquareWidth);
  ShowOne;
  INC(x);
  INC(xp,SquareWidth);
  ShowOne;
  INC(y);
  INC(yp,SquareHeight);
  mfr := FieldRows[y];
  ShowOne;
  DEC(x);
  DEC(xp,SquareWidth);
  ShowOne;
  DEC(x);
  DEC(xp,SquareWidth);
  ShowOne;
  INC(y);
  INC(yp,SquareHeight);
  mfr := FieldRows[y];
  ShowOne;
  INC(x);
  INC(xp,SquareWidth);
  ShowOne;
  INC(x);
  INC(xp,SquareWidth);
  ShowOne;
END ShowAround;


PROCEDURE ShowPos(x,y : INTEGER; OnBoard : BOOLEAN);
VAR
	str : ARRAY [0..11] OF CHAR;
	covered, b : BOOLEAN;
	xp,yp : INTEGER;
	square : Intuition.ImagePtr;
BEGIN
	IF (Lastx = x) & (Lasty = y) THEN RETURN END;
	IF Lastx >= 0 THEN
	  CASE LastState OF
	    NormalMove,MineMove :
	      Blitter.BltBitMapRastPort(ADR(ClipBM),0,0,FieldRPort,Lastxp,Lastyp,SquareWidth,SquareHeight,CopyAtoD);
	  | SafeMove :
	      Blitter.BltBitMapRastPort(ADR(ClipBM),0,0,FieldRPort,Lastxp,Lastyp,SquareWidth3,SquareHeight3,CopyAtoD);
	  ELSE
	  END;
	END;
	IF OnBoard THEN
     xp := x * SquareWidth + LeftFieldBorder;
     yp := y * SquareHeight + TopFieldBorder;
     covered := (CARDINAL(x) IN FieldRows[CARDINAL(y)].Covered);
     CASE MovingState OF
     | NormalMove :
       IF covered THEN
         Blitter.ClipBlit(FieldRPort,xp,yp,ClipRPort,0,0,SquareWidth,SquareHeight,CopyAtoD);
         Intuition.DrawImage(FieldRPort,SquareImages[SureImage],xp,yp);
       ELSE
         x := -1
       END;
     | MineMove :
       IF covered THEN
         Blitter.ClipBlit(FieldRPort,xp,yp,ClipRPort,0,0,SquareWidth,SquareHeight,CopyAtoD);
         Intuition.DrawImage(FieldRPort,SquareImages[GuessImage],xp,yp);
       ELSE
         x := -1
       END;
     | SafeMove :
       DEC(xp,SquareWidth);
       DEC(yp,SquareHeight);
       Blitter.ClipBlit(FieldRPort,xp,yp,ClipRPort,0,0,SquareWidth3,SquareHeight3,CopyAtoD);
       ShowAround(x,y,SquareImages[SureImage]);
     ELSE
     END;
     Lastx := x;
     Lasty := y;
     Lastxp := xp;
     Lastyp := yp;
     LastState := MovingState;
   ELSE
     Lastx := -1
   END;
END ShowPos;

PROCEDURE MakeWindow() : Intuition.WindowPtr;
VAR
	nw : Intuition.NewWindowPtr;
	res : Intuition.WindowPtr;
BEGIN
  res := NIL;
  Storage.ALLOCATE(nw,SIZE(nw^));
  IF nw # NIL THEN
    WITH nw^ DO
      Width := 40 * SquareFont^.tfXSize;
      Height := 16 * SquareFont^.tfYSize;
      LeftEdge := FieldWindow^.LeftEdge + ((FieldWindow^.Width - Width) DIV 2);
      IF LeftEdge < 0 THEN LeftEdge := 0
      ELSIF (LeftEdge + Width) > FieldWindow^.WScreen^.Width THEN
        LeftEdge := FieldWindow^.WScreen^.Width - Width
      END;
      TopEdge := FieldWindow^.TopEdge + ((FieldWindow^.Height - Height) DIV 2);
      IF TopEdge < 0 THEN TopEdge := 0
      ELSIF (TopEdge + Height) > FieldWindow^.WScreen^.Height THEN
        TopEdge := FieldWindow^.WScreen^.Height - Height;
      END;
      Flags := Intuition.WindowFlagSet{Intuition.Activate, Intuition.NoCareRefresh};
      DetailPen := BYTE(Pen1);
      BlockPen := BYTE(Pen2);
      Type := Intuition.WBenchScreen;
    END;
    res := Intuition.OpenWindow(nw);
    Storage.DEALLOCATE(nw,0);
  END;
  RETURN res
END MakeWindow;

PROCEDURE myCloseWindow(win : Intuition.WindowPtr);
BEGIN
  win^.UserPort := NIL;
  Intuition.CloseWindow(win);
END myCloseWindow;

TYPE
	WaitMode = (waitWin,waitHigh,waitVal);

PROCEDURE WaitOnWindow(win : Intuition.WindowPtr; mode : WaitMode);
VAR
	port : Ports.MsgPortPtr;
	msg : Intuition.IntuiMessagePtr;
BEGIN
  port := FieldWindow^.UserPort;
  win^.UserPort := port;
  Intuition.ModifyIDCMP(win,Intuition.IDCMPFlagSet{Intuition.MouseButtons, Intuition.ActiveWindow, Intuition.GadgetUp});
  LOOP
    IF mode # waitWin THEN
      SETREG(0,Intuition.ActivateGadget(ADR(EntryGad^.Gad),win,NIL));
    END;
    LOOP
      msg := Ports.GetMsg(port);
      IF msg # NIL THEN
        EXIT
      END;
      msg := Ports.WaitPort(port);
    END;
    WITH msg^ DO
      IF  (IDCMPWindow = win) THEN
        IF  (Intuition.MouseButtons IN Class)
          & (Code = Intuition.SelectUp)
        THEN
          IF mode = waitWin THEN
            Ports.ReplyMsg(msg);
            EXIT
          END
        ELSIF (Intuition.GadgetUp IN Class) THEN
          Ports.ReplyMsg(msg);
          IF mode = waitHigh THEN
            WITH HighScores^.scores[Level] DO
              WITH EntryGad^.SI DO
                NameLength := BYTE(NumChars);
                IF Name # NIL THEN
                  Storage.DEALLOCATE(Name,0)
                END;
                Name := NIL;
                IF NameLength # BYTE(0) THEN
                  Storage.ALLOCATE(Name,LONGINT(CARDINAL(NameLength) + 1));
                  IF Name # NIL THEN
  
                    Memory.CopyMem(Buffer,Name,CARDINAL(NameLength) + 1);
  
                  ELSE
                    NameLength := BYTE(0)
                  END;
                END
              END
            END;
          END;
          EXIT
        END;
      ELSIF (IDCMPWindow = FieldWindow) THEN
        IF (Intuition.RefreshWindow IN Class) THEN
          RefreshDisplay(TRUE);
        END
      END;
    END;
    Ports.ReplyMsg(msg);
  END;
  IF mode # waitVal THEN
    myCloseWindow(win);
  END;
END WaitOnWindow;

CONST
	Spacer = "-----------";

TYPE
	EnterType = (eWidth,eHeight,eMines);

PROCEDURE ShowHighAbout(mode : HA);
VAR
	AboutWindow : Intuition.WindowPtr;
	line,linewidth,left,newval : INTEGER;
	rp : Rasters.RastPortPtr;
	Entering : EnterType;
	NewCustom : BOOLEAN;

   (*$STRING-*)
	PROCEDURE DoLine(str : ARRAY OF CHAR; length : INTEGER; colour : ColourNames);
	VAR
	   l : INTEGER;
	BEGIN
	  l := ((linewidth - (length * INTEGER(SquareFont^.tfXSize))) DIV 2) + left;
	  EasyText.DoShadowWrite(SquareFont,rp,ADR(str),l, line,ColourPens[colour],Pen1,length);
     INC(line,INTEGER(SquareFont^.tfYSize));
	END DoLine;

  (*$STRING-*)
  PROCEDURE ShowScore(str : ARRAY OF CHAR; strl : CARDINAL; level : SkillLevel);
  VAR
    sp : STRPTR;
    l : INTEGER;
    buf : ARRAY [0..39] OF CHAR;
  BEGIN
    DoLine(str,strl,cnBlue);
    WITH HighScores^.scores[level] DO
      
      ConvNum(buf,Seconds);
      Memory.CopyMem(ADR(" by "),ADR(buf[4]),4);
      sp := ADR("Anonymous");
      l := 9;
      IF NameLength # BYTE(0) THEN
        sp := Name;
        l := CARDINAL(NameLength);
      END;
      Memory.CopyMem(sp,ADR(buf[8]),l);
      DoLine(buf,l+8,cnRed);
      DoLine(Spacer,11,cnGreen);
    END
  END ShowScore;
  
  PROCEDURE ConvertNumber(VAR s : ARRAY OF CHAR; index,v : CARDINAL) : CARDINAL;
  VAR
    sp : POINTER TO CHAR;
    dig : BOOLEAN;
    l,i,chars : CARDINAL;
  BEGIN
    dig := FALSE;
    sp := ADR(s) + ADDRESS(index);
    chars := 0;
    l := 1000;
    WHILE l > 0 DO
      i := (v DIV l);
      IF dig OR (i > 0) THEN
        sp^ := CHR(ORD("0") + i);
        INC(sp);
        INC(chars);
        dig := TRUE;
      END;
      v := v MOD l;
      l := l DIV 10;
    END;
    sp^ := 0C;
    RETURN chars
  END ConvertNumber;
  
	
  (*$STRING-*)
  PROCEDURE ShowValue(str : ARRAY OF CHAR; strl,val : CARDINAL);
  VAR
    buf : ARRAY [0..39] OF CHAR;
  BEGIN
    Memory.CopyMem(ADR(str),ADR(buf),strl);
    INC(strl,ConvertNumber(buf,strl,val));
    DoLine(buf,strl,cnBlue);
  END ShowValue;

  PROCEDURE SetVal(v:INTEGER);
  VAR
    sp : POINTER TO CHAR;
    l, i : INTEGER;
  BEGIN
    newval := v;
    WITH EntryGad^.SI DO
      LongInt := LONGINT(v);
      NumChars := ConvertNumber(Buffer^,0,v);
      BufferPos := NumChars;
    END;
  END SetVal;

  PROCEDURE ClearWin();
  VAR
  	top, width, height : INTEGER;
  BEGIN
    WITH AboutWindow^ DO
      Pens.SetWrMsk(FieldRPort,{0..15});
      left := INTEGER(BorderLeft);
      width := Width - INTEGER(BorderRight) - 1;
      top := INTEGER(BorderTop);
      height := Height - INTEGER(BorderBottom) - 1;
      DoFill(RPort,FadePattern,1,ColourPens[cnWhite], left, top, width, height);
      INC(left,16);
      INC(top,8);
      DEC(width,32);
      DEC(height,16);
      linewidth := width; (*Width - (INTEGER(BorderLeft) + INTEGER(BorderRight));*)
      Pens.SetAPen(RPort,0);
      Pens.RectFill(RPort,left, top, left + width, top + height);
      Pens.Move(RPort,left,top + height);
      Pens.SetAPen(RPort,Pen2);
      Pens.Draw(RPort,left,top);
      Pens.Draw(RPort,left + width,top);
      Pens.SetAPen(RPort,Pen1);
      Pens.Draw(RPort,left + width,top + height);
      Pens.Draw(RPort,left,top + height);
      line := INTEGER(BorderTop) + 14 (*INTEGER(CountFont^.tfYSize)*);
    END;
  END ClearWin;
  

BEGIN
  AboutWindow := MakeWindow();
  IF AboutWindow # NIL THEN
    rp := AboutWindow^.RPort;
    ClearWin;
    CASE mode OF
      modeAbout :
         DoLine(" ",1,cnWhite);
         DoLine("MineSweeper Version 2.4",23,cnWhite);
         DoLine("28th September 1993",19,cnWhite);
         DoLine(Spacer,11,cnGreen);
         DoLine("John Matthews",13,cnBlue);
         DoLine("4 Wadham Grove,",15,cnBlue);
         DoLine("Tawa,",5,cnBlue);
         DoLine("New Zealand",11,cnBlue);
         DoLine("Phone 64 4 232 7805",19,cnBlue);
         DoLine(Spacer,11,cnGreen);
         DoLine("Internet: tribble@gphs.vuw.ac.nz",32,cnViolet);
         WaitOnWindow(AboutWindow, waitWin);
    | modeScores :
         DoLine("Best Times",10,cnWhite);
         DoLine(Spacer,11,cnGreen);
         ShowScore(BeginnerString,8,Beginner);
         ShowScore(IntermediateString,12,Intermediate);
         ShowScore(ExpertString,6,Expert);
         ShowScore(CustomString,6,Custom);
         WaitOnWindow(AboutWindow, waitWin);
    | modeGetName :
         IF InitEntryGad() THEN
           EXCL(EntryGad^.Gad.Activation,Intuition.LongInt);
           DoLine("Best Time !",11,cnWhite);
           DoLine(Spacer,11,cnGreen);
           ShowScore("Previous Best",13,Level);
           DoLine(" ",1,cnWhite);
           ShowValue("New Best ",9,NewHigh);
           DoLine(Spacer,11,cnGreen);
           DoLine("Enter your name...",18,cnPaleBlue);
           WITH EntryGad^.SI DO
             WITH HighScores^.scores[Level] DO
               NumChars := CARDINAL(NameLength);
               Seconds := NewHigh;
               DispPos := 0;
               UndoPos := 0;
               Memory.CopyMem(Name,Buffer,NumChars + 1);
             END
           END;
           SETREG(0,Intuition.AddGadget(AboutWindow,ADR(EntryGad^.Gad),-1));
           Intuition.RefreshGList(ADR(EntryGad^.Gad),AboutWindow,NIL,1);
         END;
         WaitOnWindow(AboutWindow, waitHigh);
    | modeGetCustom :
         IF InitEntryGad() THEN
           NewCustom := FALSE;
           INCL(EntryGad^.Gad.Activation,Intuition.LongInt);
           Entering := eWidth;
           LOOP
             DoLine(" ",1,cnWhite);
             DoLine("Custom Setup",12,cnWhite);
             DoLine(Spacer,11,cnGreen);
             ShowValue("Width   = ",10,customrec.Width);
             ShowValue("Height  = ",10,customrec.Height);
             ShowValue("# Mines = ",10,customrec.Mines);
             DoLine(Spacer,11,cnGreen);
             CASE Entering OF
             | eWidth  : DoLine("New Width   :",13,cnRed); SetVal(customrec.Width);
             | eHeight : DoLine("New Height  :",13,cnRed); SetVal(customrec.Height);
             | eMines  : DoLine("New # Mines :",13,cnRed); SetVal(customrec.Mines);
             END;
             EntryGad^.SI.LongInt := LONGINT(newval);
             SETREG(0,Intuition.AddGadget(AboutWindow,ADR(EntryGad^.Gad),-1));
             Intuition.RefreshGList(ADR(EntryGad^.Gad),AboutWindow,NIL,1);
             WaitOnWindow(AboutWindow, waitVal);
             SETREG(0,Intuition.RemoveGadget(AboutWindow,ADR(EntryGad^.Gad)));
             newval := INTEGER(EntryGad^.SI.LongInt);
             CASE Entering OF
             | eWidth :
					IF (newval < MinFieldWidth) THEN newval := MinFieldWidth END;
					IF (newval > MaxFieldWidth) THEN newval := MaxFieldWidth END;
					NewCustom := NewCustom OR (customrec.Width # newval);
					customrec.Width := newval;
             | eHeight :
					IF (newval < MinFieldHeight) THEN newval := MinFieldHeight END;
					IF (newval > MaxFieldHeight) THEN newval := MaxFieldHeight END;
					NewCustom := NewCustom OR (customrec.Height # newval);
					customrec.Height := newval;
				 | eMines :
				   IF (newval < MinMines) THEN newval := MinMines END;
				   IF (newval > MaxMines) THEN newval := MaxMines END;
				   NewCustom := NewCustom OR (customrec.Mines # newval);
				   customrec.Mines := newval;
				   myCloseWindow(AboutWindow);
				   IF NewCustom THEN
				     WITH HighScores^.scores[Custom] DO
				       Seconds := 9999;
				       NameLength := BYTE(0);
				       IF Name # NIL THEN
				         Storage.DEALLOCATE(Name,0);
				         Name := NIL;
				       END
				     END
				   END;
				   RETURN
				 END;
				 INC(Entering);
				 ClearWin();
			  END;
			END;
    END;
  END;
END ShowHighAbout;

PROCEDURE TestGameOver();
VAR
	f : LONGBITSET;
	i,j : INTEGER;
	r : BOOLEAN;
BEGIN
	IF ~WaitingForNew THEN
	  CASE CompleteMethod OF
	  | AllMinesFound :
	      r := (GuessedRightMines # NumMines)
	  | AllNonMinesFound :
	      r := (NonMinesCovered > 0)
	  | AllSquaresFound :
	      r := (GuessedRightMines # NumMines) OR (NonMinesCovered > 0)
	  | EitherCompletion :
	      r := ~((GuessedRightMines = NumMines) OR (NonMinesCovered = 0))
	  END;
	  IF r THEN RETURN END;
     EndGame(NoReStart);
     ChangeFaceImage(SunGlassesImage);
     FlashDisplay(2);
     FOR i := 0 TO FieldHeight - 1 DO
      WITH FieldRows[i] DO
        f := (Mines * Covered);
        Guessed := Guessed + f;
        Wondered := Wondered + f;
        FOR j := 0 TO FieldWidth - 1 DO
          IF CARDINAL(j) IN f THEN
            ShowSquare(j,i,GuessState)
          END
        END;
      END;
     END;
     IF Seconds < HighScores^.scores[Level].Seconds THEN
       NewHigh := Seconds;
       ShowHighAbout(modeGetName);
     END;
   END
END TestGameOver;


PROCEDURE ShrinkWindow();
VAR
	gadpos : INTEGER;
	bool : BOOLEAN;
BEGIN
  IF ~Shrunk THEN
    Shrunk := TRUE;
    StopTimer;
    LastFieldWidth := 0;
    gadpos := Intuition.RemoveGadget(FieldWindow,NewGameGad);
    WITH NewGameGad^ DO
      LeftEdge := LeftFieldBorder;
      TopEdge := TopWindowBorder;
      bool := MySizeWindow(LeftEdge + RightWindowBorder + Width - 1,
                           TopEdge + BottomWindowBorder + Height - 1);
    END;
    gadpos := Intuition.AddGadget(FieldWindow,NewGameGad,-1);
    WITH FieldWindow^ DO
      DoFill(FieldRPort,FadePattern,1,Pen1,INTEGER(BorderLeft),INTEGER(BorderTop),Width - INTEGER(BorderRight) - 1,Height - INTEGER(BorderBottom) - 1);
    END;
    Intuition.RefreshGadgets(NewGameGad,FieldWindow,NIL);
  END
END ShrinkWindow;


PROCEDURE ProcessMenus(menunum : CARDINAL) : BOOLEAN;
VAR
	itemnum : CARDINAL;
	mItem : Intuition.MenuItemPtr;
	newsize,newgame : BOOLEAN;
	sl : SkillLevel;
BEGIN
  newgame := FALSE;
  newsize := FALSE;
  WHILE menunum # Intuition.MenuNULL DO
    itemnum := Intuition.ITEMNUM(menunum);
    mItem := Intuition.ItemAddress(menu,menunum);
    IF Intuition.MENUNUM(menunum) = 0 THEN
      CASE VAL(MenuVals,itemnum) OF
        mNewGame :
          newgame := TRUE;
      | mBeginner .. mCustom :
          sl := VAL(SkillLevel,itemnum - ORD(mBeginner));
          newsize := TRUE;
          IF sl = Custom THEN
            ShowHighAbout(modeGetCustom)
          END;
      | mQuestions : Questions := Intuition.Checked IN mItem^.Flags
      | mCursor :
          MovingAllowed := (Intuition.SUBNUM(menunum) = 0);
      | mScores : ShowHighAbout(modeScores);
      | mAbout : ShowHighAbout(modeAbout);
      | mShrink :
         IF Shrunk THEN
           IF ~CreateFieldWindow() THEN
             RETURN FALSE
           END;
         ELSE
           ShrinkWindow;
         END;
      | mQuit : QuitNow := TRUE; RETURN TRUE
(*      | mTest : ShowStates;*)
      END;
    ELSE
      CompleteMethod := VAL(CompleteMethodType,itemnum)
    END;
    menunum := mItem^.NextSelect
  END;
  IF newsize THEN
    SetSize(sl);
    RETURN TRUE
  END;
  IF newgame THEN
    RETURN TRUE
  END;
  RETURN FALSE
END ProcessMenus;


PROCEDURE PlayGame() : BOOLEAN;
VAR
	port : Ports.MsgPortPtr;
	msgcopy : Intuition.IntuiMessage;
	OnBoard, Breaking : BOOLEAN;

  PROCEDURE AWaitEvent;
  VAR
	msg : Intuition.IntuiMessagePtr;
	sigs : Tasks.SignalSet;
  BEGIN
    sigs := Tasks.SetSignal(Tasks.SignalSet{},WaitBits);
    LOOP
      IF DOS.SIGBreakC IN sigs THEN
        Breaking := TRUE;
        RETURN
      END;
      IF CARDINAL(CountBit) IN sigs THEN
        UpdateTime;
      END;
      sigs := Tasks.SignalSet{};
      msg := Ports.GetMsg(port);
      IF msg # NIL THEN
        IF msg^.IDCMPWindow = FieldWindow THEN
          msgcopy := msg^;
          Ports.ReplyMsg(msg);
          OnBoard := Transform(msgcopy.MouseX,msgcopy.MouseY);
          IF OnBoard & ~WaitingForNew THEN
            INCL(FieldWindow^.Flags,Intuition.RMBTrap)
          ELSE
            EXCL(FieldWindow^.Flags,Intuition.RMBTrap)
          END;
          RETURN
        END;
        Ports.ReplyMsg(msg);
      ELSE
        sigs := Tasks.Wait(WaitBits);
      END;
    END
  END AWaitEvent;

  PROCEDURE ChangeMoveState( newstate : MovingStateType; AllowedStarting : MovingStateSet; MouseChange : BOOLEAN);
  VAR
  	x,y : INTEGER;
  BEGIN
    WITH msgcopy DO
      IF MovingState = NoMove THEN
        IF OnBoard & (newstate # NoMove) THEN
          Intuition.DrawImage(FieldRPort,dataPtr^.Images[AstonishedImage],SmileX, SmileY);
          FromMouse := MouseChange;
          MovingState := newstate;
          ShowPos(MouseX, MouseY, OnBoard);
        END
      ELSE
        x := Lastx;
        y := Lasty;
        ShowPos(-1,-1,FALSE);
        IF (newstate = NoMove)
            & (MovingState IN AllowedStarting)
            & (FromMouse = MouseChange)
        THEN
          IF OnBoard THEN
            CASE MovingState OF
            | NoMove     :
            | NormalMove : UnCover(x,y);
            | SafeMove   : SureSquare(x,y);
            | MineMove   : Guess(x,y);
            END;
          END;
        END;
        MovingState := NoMove;
        Intuition.DrawImage(FieldRPort,FaceImage,SmileX, SmileY);
      END
    END;
  END ChangeMoveState;
  

BEGIN
   ChangeFaceImage(SmileyImage);
   EmptyStack;
	MovingState := NoMove;
   WaitBits := Tasks.SignalSet{DOS.SIGBreakC,CARDINAL(CountBit),CARDINAL(WindowBit)};
	Seconds := 0;
	UpdateTime;
	port := FieldWindow^.UserPort;
	FirstHit := TRUE;
	Lastx := -1;
	Lasty := 0;
	LastState := NoMove;
	Breaking := FALSE;
	RefreshDisplay(FALSE);
	LOOP
	   AWaitEvent;
	   IF Breaking THEN
	     StopTimer;
	     RETURN TRUE
	   END;
      WITH msgcopy DO
         IF Intuition.CloseWindowFlag IN Class THEN
         	StopTimer;
            RETURN TRUE
         ELSIF Intuition.MenuPick IN Class THEN
            IF ProcessMenus(Code) THEN
              EndGame(DoReStart);
              RETURN FALSE
            END;
         ELSIF Intuition.NewSize IN Class THEN
            RefreshDisplay(TRUE);
         ELSIF Intuition.RefreshWindow IN Class THEN
            RefreshDisplay(TRUE);
         ELSIF Intuition.MouseButtons IN Class THEN
            IF (Code = Intuition.MenuUp) THEN
              ChangeMoveState(NoMove,MovingStateSet{SafeMove,MineMove},TRUE);
            ELSIF (Code = Intuition.MenuDown) THEN
              IF CARDINAL(MouseX) IN FieldRows[MouseY].Covered THEN
                ChangeMoveState(MineMove,MovingStateSet{},TRUE);
              ELSE
                ChangeMoveState(SafeMove,MovingStateSet{},TRUE);
              END;
            ELSIF (Code = Intuition.SelectUp) THEN
              ChangeMoveState(NoMove,MovingStateSet{NormalMove},TRUE);
            ELSIF (Code = Intuition.SelectDown) & OnBoard THEN
              ChangeMoveState(NormalMove,MovingStateSet{},TRUE);
            END;
         ELSIF Intuition.GadgetUp IN Class THEN
           ChangeMoveState(NoMove,MovingStateSet{MineMove,SafeMove,NormalMove},TRUE);
           IF Shrunk THEN
             IF ~CreateFieldWindow() THEN
               RETURN FALSE
             END;
           ELSE
             EndGame(DoReStart);
             RETURN FALSE
           END;
         ELSIF Intuition.GadgetDown IN Class THEN
             ChangeMoveState(NoMove,MovingStateSet{MineMove,SafeMove,NormalMove},TRUE);
         ELSIF Intuition.RawKey IN Class THEN
           IF BITSET(InputEvents.IECodeUpPrefix) * BITSET(Code) # BITSET{} THEN
             (* key up *)
             ChangeMoveState(NoMove,MovingStateSet{SafeMove,MineMove,NormalMove},FALSE);
             IF ~WaitingForNew THEN
               INCL(FieldWindow^.Flags,Intuition.RMBTrap)
             END
           ELSE
             IF OnBoard & (MovingState = NoMove) THEN
               CASE Console.RawToASCII(msgcopy) OF
               | 'm','M','g','G' :
                  ChangeMoveState(MineMove,MovingStateSet{},FALSE);
               | 'u','U','1' :
                  ChangeMoveState(NormalMove,MovingStateSet{},FALSE);
               | 'l','L','a','A' :
                  ChangeMoveState(SafeMove,MovingStateSet{},FALSE);
               | '2' :
                 IF CARDINAL(MouseX) IN FieldRows[MouseY].Covered THEN
                   ChangeMoveState(MineMove,MovingStateSet{},FALSE);
                 ELSE
                   ChangeMoveState(SafeMove,MovingStateSet{},FALSE);
                 END;
               | 'p','P' :
                 OnBoard := FALSE;
                 ChangeMoveState(NoMove,MovingStateSet{SafeMove,MineMove,NormalMove},FALSE);
                 IF Shrunk THEN
                   IF ~CreateFieldWindow() THEN
                     RETURN FALSE
                   END;
                 ELSE
                   ShrinkWindow;
                 END;
(***TestLookAround+
               | 't' : TestLookAround(MouseX,MouseY);
*TestLookAround-*)
               ELSE
                 EXCL(FieldWindow^.Flags,Intuition.RMBTrap)
               END
             END
           END
         ELSIF MovingAllowed & (Intuition.MouseMove IN Class) THEN
           IF (MovingState # NoMove) THEN
             ShowPos(MouseX, MouseY, OnBoard)
           END;
         END
      END
	END;
	RETURN FALSE
END PlayGame;

PROCEDURE CloseDisplay();
VAR
	port : Ports.MsgPortPtr;
	msg : ADDRESS;
	cm : Views.ColorMapPtr;
	cn : ColourNames;
BEGIN
	port := FieldWindow^.UserPort;
	Interrupts.Forbid;
	LOOP
		msg := Ports.GetMsg(port);
		IF msg = NIL THEN EXIT END;
		Ports.ReplyMsg(msg);
	END;
	cm := FieldWindow^.WScreen^.VPort.ColorMap;
   FOR cn := MIN(ColourNames) TO MAX(ColourNames) DO
     IF cn IN ObtainedPens THEN
       Pens.ReleasePen(cm,LONGINT(cn))
     END
   END;
   ObtainedPens := ColourNameSet{};
   
	Intuition.CloseWindow(FieldWindow);
	Interrupts.Permit;
END CloseDisplay;

PROCEDURE SetUpTimer() : BOOLEAN;
VAR
	sigs : Tasks.SignalSet;
	oldpri : INTEGER;
BEGIN
  TimerAcks := Tasks.SignalSet{CARDINAL(TimerAckBit)};
  oldpri := Tasks.SetTaskPri(MainTask,0);
  TimerTask := TaskUtils.CreateTask(ADR(TaskName),1,TimerProc,4096);
  sigs := Tasks.Wait(TimerAcks);
  RETURN ~TimerError;
END SetUpTimer;

PROCEDURE GetHighScores();
VAR
	fh : DOS.FileHandle;
	err : LONGINT;
	i : CARDINAL;
	l : SkillLevel;
	b : BYTE;

  PROCEDURE ReadScore(level : SkillLevel) : BOOLEAN;
  BEGIN
    WITH HighScores^.scores[level] DO
      Name := NIL;
      IF CARDINAL(NameLength) > 0 THEN
        err := -1;
        Storage.ALLOCATE(Name,LONGINT(CARDINAL(NameLength)) + 1);
        IF Name # NIL THEN
          err := DOS.Read(fh,Name,LONGINT(CARDINAL(NameLength)));
        END;
        IF err # LONGINT(CARDINAL(NameLength)) THEN
          RETURN FALSE
        END;
      END;
    END;
    RETURN TRUE
  END ReadScore;
  

BEGIN
  CompleteMethod := AllSquaresFound;
  Storage.ALLOCATE(HighScores, SIZE(HighScores^));
  IF HighScores = NIL THEN HALT END;
  FOR l := Beginner TO Custom DO
    WITH HighScores^.scores[l] DO
      Seconds := 9999;
    END;
  END;
  WITH HighScores^ DO
    DefLevel := Beginner;
    DefQuestions := TRUE;
  END;
  WITH customrec DO
    version := 1;
    Width := 8;
    Height := 8;
    Mines := 10;
  END;
  fh := DOS.Open(ADR(MineScores),DOS.ModeOldFile);
  IF fh # NIL THEN
    err := DOS.Read(fh,HighScores,SIZE(HighScores^) - SIZE(Score));
    IF err # SIZE(HighScores^) - SIZE(Score) THEN
      SETREG(0,DOS.Close(fh));
      RETURN
    END;
    FOR l := Beginner TO Expert DO
      IF ~ReadScore(l) THEN
        SETREG(0,DOS.Close(fh));
        RETURN
      END;
    END;
    err := DOS.Read(fh,ADR(HighScores^.scores[Custom]),SIZE(Score));
    HighScores^.scores[Custom].Name := NIL;
    IF (err # SIZE(Score)) OR ~ReadScore(Custom) THEN
      HighScores^.scores[Custom].NameLength := BYTE(0);
      SETREG(0,DOS.Close(fh));
      RETURN
    END;
    err :=DOS.Read(fh,ADR(customrec),SIZE(customrec));
    IF (err # SIZE(customrec)) THEN
      HighScores^.scores[Custom].NameLength := BYTE(0);
      SETREG(0,DOS.Close(fh));
      RETURN
    END;
    err := DOS.Read(fh,ADR(b),SIZE(b));
    IF (err = SIZE(b)) THEN
      CompleteMethod := VAL(CompleteMethodType,b)
    END;
    err := DOS.Read(fh,ADR(b),SIZE(b));
    IF (err = SIZE(b)) THEN
      MovingAllowed := (b = BYTE(1));
    END;
  END;
  SETREG(0,DOS.Close(fh));
END GetHighScores;

PROCEDURE SaveHighScores();
VAR
	fh : DOS.FileHandle;
	err : LONGINT;
	i : CARDINAL;
	l : SkillLevel;
	b : BYTE;

  PROCEDURE WriteScore(level : SkillLevel) : BOOLEAN;
  BEGIN
    WITH HighScores^.scores[level] DO
      IF CARDINAL(NameLength) > 0 THEN
        err := -1;
        err := DOS.Write(fh,Name,LONGINT(CARDINAL(NameLength)));
        IF err # LONGINT(CARDINAL(NameLength)) THEN
          RETURN FALSE
        END;
      END;
    END;
    RETURN TRUE;
  END WriteScore;
  

BEGIN
  IF HighScores = NIL THEN RETURN END;
  WITH HighScores^ DO
    InitX := FieldWindow^.LeftEdge;
    InitY := FieldWindow^.TopEdge;
    DefLevel := Level;
    DefQuestions := Questions;
  END;
  fh := DOS.Open(ADR(MineScores),DOS.ModeNewFile);
  IF fh # NIL THEN
    err := DOS.Write(fh,HighScores,SIZE(HighScores^) - SIZE(Score));
    IF err # (SIZE(HighScores^) - SIZE(Score)) THEN
      SETREG(0,DOS.Close(fh));
      RETURN
    END;
    FOR l := Beginner TO Expert DO
      IF ~WriteScore(l) THEN
        SETREG(0,DOS.Close(fh));
        RETURN
      END;
    END;
    err := DOS.Write(fh,ADR(HighScores^.scores[Custom]),SIZE(Score));
    IF (err # SIZE(Score)) OR ~WriteScore(Custom) THEN
      SETREG(0,DOS.Close(fh));
      RETURN
    END;
    customrec.version := HighScoreVersion;
    IF (DOS.Write(fh,ADR(customrec),SIZE(customrec)) = SIZE(customrec)) THEN
      b := BYTE(CompleteMethod);
      err := DOS.Write(fh,ADR(b),SIZE(b));
      IF err = SIZE(b) THEN
        b := BYTE(0);
        IF MovingAllowed THEN
          b := BYTE(1);
        END;
        err := DOS.Write(fh,ADR(b),SIZE(b));
      END
    END;
  END;
  SETREG(0,DOS.Close(fh));
END SaveHighScores;

PROCEDURE GetWindowSizes() : BOOLEAN;
VAR
	nw : Intuition.NewWindowPtr;
	win : Intuition.WindowPtr;
	vp : Views.ViewPortPtr;
BEGIN
  V36 := (RunTime.ExecVersion > 35);
  Storage.ALLOCATE(nw,SIZE(nw^));
  IF nw = NIL THEN RETURN FALSE END;
  WITH nw^ DO
    Width := 10;
    Height := 10;
    Flags := Intuition.WindowFlagSet{Intuition.BackDrop};
    Type := Intuition.WBenchScreen;
    Title := ADR(MineSweeperName);
  END;
  win := Intuition.OpenWindow(nw);
  Storage.DEALLOCATE(nw,0);
  IF win = NIL THEN
    RETURN FALSE
  END;
  vp := Intuition.ViewPortAddress(win);
  Laced := Views.Lace IN vp^.Modes;
  IF Laced THEN
    SquareHeight := 16;
    Memory.CopyMem(ADR(dataPtr^.Images[FirstImage]),ADR(SquareImages),SIZE(SquareImages));
    FillParam := -4;
  ELSE
    SquareHeight := 8;
    Memory.CopyMem(ADR(dataPtr^.Images[LastImage + 1]),ADR(SquareImages),SIZE(SquareImages));
    FillParam := -3;
  END;
  SquareHeight3 := SquareHeight * 3;
  WITH win^ DO
    LeftFieldBorder := INTEGER(BorderLeft) + 16 - (INTEGER(BorderLeft) MOD 16);
    TopWindowBorder := INTEGER(BorderTop) + 1;
    RightWindowBorder := INTEGER(BorderRight) + 16 - (INTEGER(BorderRight) MOD 16);
    BottomWindowBorder := INTEGER(BorderBottom) + 16 - (INTEGER(BorderBottom) MOD 16);
    TopFieldBorder := TopWindowBorder + TopWindowArea;
    INC(TopFieldBorder,SquareHeight - (TopFieldBorder MOD SquareHeight));
  END;
  Intuition.CloseWindow(win);
  RETURN TRUE
END GetWindowSizes;


BEGIN
  CT := ADR(ChunkyTable);
(*  Storage.ALLOCATE(StackBuffer,SIZE(SquareRecord) * MaxStackDepth);*)
  StackBuffer := ADR(RealStackBuffer);
(*StackCheck+
  StackDepthReached := 0;
*StackCheck-*)
  Shrunk := FALSE;
  EntryGad := NIL;
  MovingAllowed := TRUE;
  IF dataPtr = NIL THEN HALT END;
  FadePattern := dataPtr^.Images[FadePat]^.ImageData;
  IF ~GetWindowSizes() THEN HALT END;
  GetHighScores;
  WITH HighScores^ DO
    Questions := DefQuestions;
    SetSize(DefLevel);
  END;
  ObtainedPens := ColourNameSet{};
  OpenFonts;
  CreateMenus;
  MainTask := Tasks.FindTask(Tasks.CurrentTask);
  MainProcess := DOSProcess.ProcessPtr(MainTask);
  OLDprWindow := MainProcess^.prWindowPtr;
  QuitNow := FALSE;
  TimerIsGoing := FALSE;
  TimerError := FALSE;
  TimerAckBit := Tasks.AllocSignal(Tasks.AnySignal);
  CountBit := Tasks.AllocSignal(Tasks.AnySignal);
  IF (TimerAckBit # Tasks.NoSignals) & (CountBit # Tasks.NoSignals) THEN
    IF SetUpTimer() THEN
      FieldWindow := NIL;
      REPEAT
        QuitNow := (~CreateFieldWindow()) OR PlayGame() OR QuitNow;
      UNTIL QuitNow;
      StopTimer;
      MainProcess^.prWindowPtr := OLDprWindow;
      SaveHighScores;
      CloseDisplay;
      (*ShowFields*)
    END
  END;
  IF (TimerAckBit # Tasks.NoSignals) THEN
    Tasks.FreeSignal(TimerAckBit);
  END;
  IF (CountBit # Tasks.NoSignals) THEN
    Tasks.FreeSignal(CountBit);
  END;
(*    EasyText.EasyCloseFont(CountFont);*)
  EasyText.EasyCloseFont(SquareFont);
(*StackCheck+
  TermOut.WriteString("MAX Stack Depth = ");
  TermOut.WriteCard(StackDepthReached,1);
  TermOut.WriteLn;
*StackCheck-*)
END MineSweeper.
