(***************************************************************************

     $RCSfile: ExecUtil.mod $
  Description: Support for clients of exec.library

   Created by: fjc (Frank Copeland)
    $Revision: 3.2 $
      $Author: fjc $
        $Date: 1994/08/08 16:09:11 $

  Copyright © 1994, Frank Copeland.
  This file is part of the Oberon-A Library.
  See Oberon-A.doc for conditions of use and distribution.

***************************************************************************)

MODULE ExecUtil;

(*
** $C- CaseChk       $I- IndexChk  $L+ LongAdr   $N- NilChk
** $P- PortableCode  $R- RangeChk  $S- StackChk  $T- TypeChk
** $V- OvflChk       $Z- ZeroVars
*)

IMPORT E := Exec, SYS := SYSTEM;

TYPE

  CompareProc * = PROCEDURE ( n1, n2 : E.MinNodePtr ) : INTEGER;


(*--------------------------------------------------------------------*)
(*
  Exec List handling procedures
*)


(*------------------------------------*)
PROCEDURE NewList* (VAR list : E.MinList);

BEGIN (* NewList *)
  list.head := SYS.ADR (list.tail);
  list.tail := NIL;
  list.tailPred := SYS.ADR (list.head)
END NewList;


(*------------------------------------*)
PROCEDURE GetSucc * ( node : E.MinNodePtr ) : E.MinNodePtr;

BEGIN (* GetSucc *)
  IF node # NIL THEN
    node := node.succ; IF node.succ = NIL THEN node := NIL END
  END; (* IF *)
  RETURN node;
END GetSucc;


(*------------------------------------*)
PROCEDURE GetPred * ( node : E.MinNodePtr ) : E.MinNodePtr;

BEGIN (* GetPred *)
  IF node # NIL THEN
    node := node.pred; IF node.pred = NIL THEN node := NIL END
  END; (* IF *)
  RETURN node;
END GetPred;


(*------------------------------------*)
PROCEDURE GetHead * ( VAR list : E.MinList ) : E.MinNodePtr;

VAR node : E.MinNodePtr;

BEGIN (* GetHead *)
  node := list.head; IF node.succ = NIL THEN node := NIL END;
  RETURN node;
END GetHead;


(*------------------------------------*)
PROCEDURE GetTail * ( VAR list : E.MinList ) : E.MinNodePtr;

VAR node : E.MinNodePtr;

BEGIN (* GetTail *)
  node := list.tailPred; IF node.pred = NIL THEN node := NIL END;
  RETURN node;
END GetTail;


(*------------------------------------*)
PROCEDURE ListLength * ( VAR list : E.MinList ) : LONGINT;

VAR node : E.MinNodePtr; count : LONGINT;

BEGIN (* ListLength *)
  count := 0; node := list.head;
  WHILE node.succ # NIL DO INC (count); node := node.succ END;
  RETURN count;
END ListLength;


(*------------------------------------*)
PROCEDURE NodeAt * ( VAR list : E.MinList; pos : LONGINT )
  : E.MinNodePtr;

VAR node : E.MinNodePtr; count : LONGINT;

BEGIN (* NodeAt *)
  count := pos; node := list.head;
  IF node # NIL THEN
    WHILE (node.succ # NIL) & (count > 0) DO
      DEC( count ); node := node.succ;
    END;
    IF node.succ = NIL THEN node := NIL END
  END;
  RETURN node
END NodeAt;


(*------------------------------------*)
PROCEDURE InsertAt *
  ( VAR list : E.MinList; node : E.MinNodePtr; pos : LONGINT );

VAR oldNode : E.MinNodePtr;

BEGIN (* InsertAt *)
  oldNode := NodeAt (list, pos);
  IF oldNode = NIL THEN E.base.AddTail (list, node)
  ELSE E.base.Insert (list, node, oldNode.pred)
  END
END InsertAt;


(*------------------------------------*)
PROCEDURE InsertOrdered *
  ( VAR list : E.MinList; node : E.MinNodePtr; Compare : CompareProc )
  : LONGINT;

VAR prevNode, nextNode : E.MinNodePtr; position : LONGINT;

BEGIN (* InsertOrdered *)
  position := 0; prevNode := NIL; nextNode := GetHead (list);
  WHILE (nextNode # NIL) & (Compare (node, nextNode) >= 0) DO
    prevNode := nextNode; nextNode := GetSucc (nextNode);
    INC (position)
  END;
  E.base.Insert (list, node, prevNode);
  RETURN position;
END InsertOrdered;


(*------------------------------------*)
PROCEDURE RemoveAt * ( VAR list : E.MinList; pos : LONGINT )
  : E.MinNodePtr;

VAR node : E.MinNodePtr;

BEGIN (* RemoveAt *)
  node := NodeAt( list, pos );
  IF node # NIL THEN E.base.Remove (node) END;
  RETURN node;
END RemoveAt;


(*--------------------------------------------------------------------*)
(*
  Exec MessagePort procedures.
*)


(*------------------------------------*)
(*$D-*)
PROCEDURE CreatePort * (portName : ARRAY OF CHAR; priority : SHORTINT)
  : E.MsgPortPtr;

  VAR sigBit : SHORTINT; mp : E.MsgPortPtr; name : E.STRPTR;

BEGIN (* CreatePort *)
  sigBit := E.base.AllocSignal (-1);
  IF sigBit = -1 THEN RETURN NIL END;

  SYS.NEW (mp, SIZE (E.MsgPort), {E.memPublic, E.memClear});
  IF mp = NIL THEN E.base.FreeSignal (sigBit); RETURN NIL END;

  IF portName = "" THEN name := NIL ELSE name := SYS.ADR (portName) END;
  mp.name := name;
  mp.pri := priority;
  mp.type := E.ntMsgPort;
  mp.mpFlags := E.paSignal;
  mp.sigBit := sigBit;
  mp.sigTask := E.base.FindTask (NIL); (* Find THIS task. *)

  IF name # NIL THEN E.base.AddPort (mp)
  ELSE NewList (mp.msgList)
  END;

  RETURN mp
END CreatePort;

(*------------------------------------*)
PROCEDURE DeletePort * (mp : E.MsgPortPtr);

BEGIN (* DeletePort *)
  IF mp = NIL THEN RETURN END;

  (* if it was public ... *)
  IF mp.name # NIL THEN E.base.RemPort (mp) END;

  (* make it difficult to re-use the port *)
  mp.sigTask := SYS.VAL (E.TaskPtr, -1);
  mp.msgList.head := SYS.VAL (E.MinNodePtr, -1);

  E.base.FreeSignal (mp.sigBit);
  SYS.DISPOSE (mp)
END DeletePort;

(*--------------------------------------------------------------------*)
(*
  Exec IO procedures.
*)


(*------------------------------------*)
PROCEDURE BeginIO * ( ioReq : E.IORequestPtr );

BEGIN (* BeginIO *)
  SYS.PUTREG (9, ioReq); (* MOVE.L  ioReq(A5), A1 *)
  SYS.INLINE (
    2C69H, 0014H,        (* MOVE.L  0014(A1), A6  *)
    4EAEH, -001EH );     (* JSR     FFE2(A6)      *)
END BeginIO;

(*------------------------------------*)
PROCEDURE CreateExtIO *
  ( port   : E.MsgPortPtr;
    ioSize : INTEGER )
  : E.APTR;

  VAR ioReq : E.IORequestPtr;

BEGIN (* CreateExtIO *)
  IF port = NIL THEN RETURN NIL END;
  SYS.NEW (ioReq, ioSize, {E.memPublic, E.memClear});
  IF ioReq # NIL THEN
    ioReq.type := E.ntReplyMsg;
    ioReq.mnLength := ioSize;
    ioReq.replyPort := port
  END;
  RETURN ioReq
END CreateExtIO;

(*------------------------------------*)
PROCEDURE DeleteExtIO ( ioReq : E.APTR );

  VAR req : E.IORequestPtr;

BEGIN (* DeleteExtIO *)
  IF ioReq # NIL THEN
    req := ioReq;
    req.succ := SYS.VAL (E.MinNodePtr, -1);
    req.replyPort := SYS.VAL (E.MsgPortPtr, -1);
    SYS.DISPOSE (req)
  END
END DeleteExtIO;

(*------------------------------------*)
PROCEDURE CreateStdIO* ( port : E.MsgPortPtr ) : E.IOStdReqPtr;

BEGIN (* CreateStdIO *)
  RETURN CreateExtIO (port, SIZE (E.IOStdReq))
END CreateStdIO;

(*------------------------------------*)
PROCEDURE DeleteStdIO* ( ioReq : E.IOStdReqPtr );

BEGIN (* DeleteStdIO *)
  DeleteExtIO (ioReq)
END DeleteStdIO;

END ExecUtil.

(*------------------------------------*)
PROCEDURE CreateTask *
  ( name      : ARRAY OF CHAR;
    pri       : SHORTINT;
    initPC    : E.PROC;
    stackSize : ULONG )
  : E.TaskPtr;

  VAR
    taskMemList : RECORD (E.Node)
      numEntries : INTEGER;
      entries : ARRAY 2 OF RECORD
        reqs : SET;
        size : LONGINT;
      END;
    END;
    memList : CPOINTER TO RECORD (E.MemList)
      entries : ARRAY 2 OF E.MemEntry;
    END;
    newTask : E.TaskPtr;

BEGIN (* CreateTask *)
  stackSize := SYS.AND (stackSize + 3, 0FFFFFFFCH);
  taskMemList.type := E.ntUnknown;
  taskMemList.pri := 0;
  taskMemList.name := NIL;
  taskMemList.numEntries := 2;
  taskMemList.entries[0].reqs := {E.memPublic, E.memClear};
  taskMemList.entries[0].size := SIZE (E.Task);
  taskMemList.entries[1].reqs := {E.memClear};
  taskMemList.entries[1].size := stackSize;
  memList := E.base.AllocEntry (SYS.ADR (taskMemList));
  IF 31 IN SYS.VAL (SET, memList) THEN RETURN NIL END;

  newTask := memList.entries[0].addr;
  newTask.type := E.ntTask;
  newTask.pri := pri;
  newTask.name := SYS.ADR (name);
  newTask.spLower := memList.entries[1].addr;
  newTask.spUpper :=
    SYS.VAL (E.APTR, SYS.VAL (LONGINT, newTask.spLower) + stackSize);
  newTask.spReg := newTask.spUpper;

  NewList (newTask.memEntry);
  E.base.AddHead (newTask.memEntry, memList);
  E.base.AddTask (newTask, initPC, NIL);
  RETURN newTask
END CreateTask;

(*------------------------------------*)
PROCEDURE DeleteTask * ( task : E.TaskPtr );

BEGIN (* DeleteTask *)
  E.base.RemTask (task)
END DeleteTask;
