-- ProcessorHeadDLion.mesa
-- Last Edited by: Taft, February 27, 1983 3:40 pm

DIRECTORY
  Checksum USING [ComputeChecksumProc],
  DeviceCleanup USING [Perform, Reason],
  DLionInputOutput,
  Environment USING [Byte, PageCount],
  Frame USING [GetReturnFrame, GetReturnLink, MyLocalFrame],
  HeadStartChain USING [],
  Inline USING [BITOR, COPY, HighHalf, LongCOPY, LowHalf],
  MicrocodeBooting USING [BootFileNumber, MicrocodeType, nullBootFileNumber],
  MicrocodeVersion USING [VersionResult],
  MiscAlpha USING [aCHKSUM, aTEXTBLT],
  Mopcodes USING [zMISC],
  PilotMP USING [cPowerOff],
  PrincOps USING [ControlLink, FrameHandle, StateVector],
  ProcessInternal USING [DisableInterrupts],
  ProcessorFace USING [gmtEpoch, GreenwichMeanTime, ProcessorID, SetMP],
  SDDefs USING [SD, sUnimplemented],
  SoftwareTextBlt USING [TextBlt],
  System USING [gmtEpoch],
  TrapSupport USING [BumpPC, OpTrapTable, opTrapTable];

ProcessorHeadDLion: PROGRAM
  IMPORTS Checksum, DeviceCleanup, Frame, Inline, ProcessInternal, ProcessorFace,
    SoftwareTextBlt, TrapSupport
  EXPORTS DLionInputOutput, HeadStartChain, MicrocodeBooting, ProcessorFace
  SHARES Checksum, ProcessorFace =
BEGIN

-- DLionInputOutput

IOPage: PUBLIC LONG POINTER ← LOOPHOLE[LONG[177400B]];

ProcessorCSBOffset: CARDINAL = 127B; -- 57X

ProcessorCSB: TYPE = MACHINE DEPENDENT RECORD [
  data2(0): WORD,
  data1(1): WORD,
  data0(2): WORD,
  command(3): ProcessorCommand];

ProcessorCommand: TYPE = MACHINE DEPENDENT RECORD [
  busy(0:0..0): BOOLEAN ← TRUE,
  op(0:1..15): ProcessorOperation];

ProcessorOperation: TYPE = MACHINE DEPENDENT {
  setTOD(1), readTOD(2), readPID(3), boot(4), (32767)};

TimeOfDayOffset: CARDINAL = 320B; -- 0D0X

TimeOfDayClock: TYPE = MACHINE DEPENDENT RECORD [
  valid(0): WORD, -- 0 => invalid, 177777B => valid
  time(1): LONG CARDINAL,
  lowCheck(3): CARDINAL];

-- Initialization
-- This is exported BOTH to ProcessorFace and to HeadStartChain.
-- Consequently it must not do anything besides cause a start trap on the first call.

Start: PUBLIC PROC = { -- By now start trap has occurred and main-body code has run--};

-- Processor ID
processorID: PUBLIC ProcessorFace.ProcessorID ← GetProcessorID[];
GetProcessorID: PROC RETURNS [ProcessorFace.ProcessorID] =
  BEGIN
  pCSB: LONG POINTER TO ProcessorCSB = IOPage + ProcessorCSBOffset;
  WHILE pCSB.command.busy DO NULL ENDLOOP;
  pCSB.command ← [op: readPID];
  WHILE pCSB.command.busy DO NULL ENDLOOP;
  RETURN[[pCSB.data0, pCSB.data1, pCSB.data2]];
  END;

-- Real memory configuration
dedicatedRealMemory: PUBLIC Environment.PageCount ←
  DLionInputOutput.reservedPageCount;

-- Virtual memory layout (not yet implemented)

-- GreenwichMeanTime

GreenwichMeanTime: TYPE = ProcessorFace.GreenwichMeanTime;
gmtEpoch: GreenwichMeanTime = ProcessorFace.gmtEpoch;

GetGreenwichMeanTime: PUBLIC PROC RETURNS [gmt: GreenwichMeanTime] =
  BEGIN
  tod: LONG POINTER TO TimeOfDayClock = IOPage + TimeOfDayOffset;
  IF tod.valid = 0 THEN RETURN[gmtEpoch];
  DO
    gmt ← tod.time;
    IF Inline.LowHalf[gmt] = tod.lowCheck THEN EXIT;
    ENDLOOP;
  RETURN
  END;

SetGreenwichMeanTime: PUBLIC PROC [gmt: GreenwichMeanTime] =
  BEGIN
  pCSB: LONG POINTER TO ProcessorCSB = IOPage + ProcessorCSBOffset;
  tod: LONG POINTER TO TimeOfDayClock = IOPage + TimeOfDayOffset;
  WHILE pCSB.command.busy DO NULL ENDLOOP;
  pCSB.data2 ← Inline.LowHalf[gmt];
  pCSB.data1 ← Inline.HighHalf[gmt];
  pCSB.command ← [op: setTOD];
  tod.valid ← 0;
  WHILE pCSB.command.busy DO NULL ENDLOOP;
  WHILE tod.valid = 0 DO NULL ENDLOOP;
  RETURN
  END;

-- Interval time
microsecondsPerHundredPulses: PUBLIC CARDINAL ← 2878;

-- Naked notifies
cvTimeoutMask: WORD = 100000B; -- DLion microcode assumes level 0
reservedNakedNotifyMask: PUBLIC WORD ← cvTimeoutMask;

-- Condition variable time
millisecondsPerTick: PUBLIC CARDINAL ← 52;
InitializeCVTimeouts: PROC =
  BEGIN
  DIW: LONG POINTER TO WORD = LOOPHOLE[LONG[177753B]];
  DIW↑ ← Inline.BITOR[DIW↑, cvTimeoutMask];
  END;

-- Booting and power control

BootButton: PUBLIC PROC =
  BEGIN
  pCSB: LONG POINTER TO ProcessorCSB = IOPage + ProcessorCSBOffset;
  ProcessInternal.DisableInterrupts[];
  WHILE pCSB.command.busy DO NULL ENDLOOP;
  pCSB.command ← [op: boot];
  DO ENDLOOP;
  END;

PowerOff: PUBLIC PROCEDURE =
  BEGIN
  -- NOTE: This code depends on the greenwich mean time clock running
  -- with interrupts disabled and devices turned off.
  ProcessInternal.DisableInterrupts[];
  DeviceCleanup.Perform[turnOff];
  ProcessorFace.SetMP[PilotMP.cPowerOff];
  DO
    -- forever
    IF (GetGreenwichMeanTime[] - ProcessorFace.gmtEpoch)
          >= (gmtAutomaticPowerOn - ProcessorFace.gmtEpoch)
       AND (~externalEventRequired OR ExternalEvent[]) THEN BootButton[]
    ENDLOOP
  END;

gmtAutomaticPowerOn: ProcessorFace.GreenwichMeanTime;
externalEventRequired: BOOLEAN;
ExternalEvent: PROCEDURE RETURNS [BOOLEAN] = {RETURN[FALSE]};

SetAutomaticPowerOn: PUBLIC PROCEDURE [
  gmt: ProcessorFace.GreenwichMeanTime, externalEvent: BOOLEAN] =
  BEGIN gmtAutomaticPowerOn ← gmt; externalEventRequired ← externalEvent; END;

ResetAutomaticPowerOn: PUBLIC PROCEDURE =
  {gmtAutomaticPowerOn ← ProcessorFace.gmtEpoch - 1}; -- infinity


  -- MicrocodeBooting; degenerate implementation for now

  BootSpecificMicrocode: PUBLIC PROCEDURE [bfn: MicrocodeBooting.BootFileNumber] =
    BEGIN
    BootButton[];
    END;

  GetBootFileNumber: PUBLIC PROCEDURE [type: MicrocodeBooting.MicrocodeType]
    RETURNS [bfn: MicrocodeBooting.BootFileNumber] =
    BEGIN
    RETURN[MicrocodeBooting.nullBootFileNumber];
    END;


  -- TrapSupport.

  UnimplementedInstruction: ERROR = CODE;

  Unimplemented: PROCEDURE =
    BEGIN
    v: MACHINE DEPENDENT RECORD [a, b: UNSPECIFIED, state: PrincOps.StateVector];
    v.state ← STATE;
    ERROR UnimplementedInstruction;
    END;

  trapTable: TrapSupport.OpTrapTable ←
    [ALL[LOOPHOLE[Unimplemented]], ALL[LOOPHOLE[Unimplemented]]];


  -- Software assist for microcode
  -- assumes return link is a frame, and has been started.
  -- This trap procedure is called only if the Cedar TrapSupport microcode is NOT present.
  -- If the microcode IS present, it calls the appropriate trap routine directly.
  UnimplementedTrap: PROC =
    BEGIN
    l: PrincOps.ControlLink;
    code: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte;
    opcode: [0..400B);
    state: PrincOps.StateVector;
    state ← STATE;
    state.source ← l ← Frame.GetReturnLink[];
    code ← LOOPHOLE[l.frame.accesslink.code.longbase];
    opcode ← code[l.frame.pc];
    state.dest ← IF opcode # Mopcodes.zMISC THEN TrapSupport.opTrapTable.main[opcode]
      ELSE TrapSupport.opTrapTable.misc[code[l.frame.pc+1]];
    -- If handler is fixed-frame, manually plant the return link.
    -- (Microcode does that for traps, but not for ordinary XFERs to frames.)
    IF ~state.dest.proc THEN state.dest.frame.returnlink ← l;
    RETURN WITH state;  -- Flush my frame and transfer to real trap handler
    END;

  -- Handler for TEXTBLT trap.
  -- The opcode actually takes a pile of arguments and returns a pile of results.
  -- The trap handler is defined strangely in order to avoid needless pushing and popping.
  TEXTBLTTrap: PROC =
    BEGIN
    -- TEXTBLT is a minimal-stack instruction, so no need to save state vector.
    TextBlt: PROC = LOOPHOLE[SoftwareTextBlt.TextBlt];
    TrapSupport.BumpPC[2];
    TextBlt[];
    END;

  -- Trap support for Cedar-related opcodes

  LocalBlkZ: PROC [count: CARDINAL] =
    BEGIN
    state: PrincOps.StateVector;
    p: POINTER;
    state ← STATE;
    TrapSupport.BumpPC[2];
    IF count#0 THEN
      BEGIN
      p ← @(Frame.GetReturnFrame[].local[0]);
      p↑ ← 0;
      IF count#1 THEN Inline.COPY[from: p, to: p+1, nwords: count-1];
      END;
    state.dest.frame ← Frame.GetReturnFrame[];
    RETURN WITH state;
    END;

  LongBlkZ: PROC [p: LONG POINTER, count: CARDINAL] RETURNS [resultP: LONG POINTER] =
    BEGIN
    state: PrincOps.StateVector;
    state ← STATE;
    TrapSupport.BumpPC[2];
    IF count#0 THEN
      BEGIN
      p↑ ← 0;
      IF count#1 THEN Inline.LongCOPY[from: p, to: p+1, nwords: count-1];
      END;
    resultP ← p;
    state.dest.frame ← Frame.MyLocalFrame[];
    TRANSFER WITH state;
    END;

  UVersion: PROC RETURNS [MicrocodeVersion.VersionResult] =
    BEGIN
    TrapSupport.BumpPC[2];
    RETURN [[
      machineType: dandelion, majorVersion: 0, unused: 0,
      floatingPoint: FALSE, cedar: FALSE, releaseDate: System.gmtEpoch/86400]];
    END;


ComputeChecksum: PRIVATE PROC [cs, nWords: CARDINAL, p: LONG POINTER]
  RETURNS [CARDINAL] = {RETURN[Checksum.ComputeChecksumProc[cs, nWords, p]]};


-- Main body code

InitializeCVTimeouts[];
SDDefs.SD[SDDefs.sUnimplemented] ← UnimplementedTrap;
SDDefs.SD[137B] ← @trapTable;
TrapSupport.opTrapTable.misc[MiscAlpha.aTEXTBLT] ← LOOPHOLE[TEXTBLTTrap];
TrapSupport.opTrapTable.misc[MiscAlpha.aCHKSUM] ← LOOPHOLE[ComputeChecksum];
TrapSupport.opTrapTable.misc[100B] ← LOOPHOLE[LocalBlkZ];
TrapSupport.opTrapTable.misc[102B] ← LOOPHOLE[LongBlkZ];
TrapSupport.opTrapTable.misc[104B] ← LOOPHOLE[UVersion];

END.

LOG

August 5, 1980  10:28 AM   Forrest   Created file.

August 12, 1980  10:41 AM   McJones   Added dedicatedRealMemory.

September 18, 1980  1:26 PM   Sandman   Changed cvTimeoutMask, millisecondsPerTick for new PSB format.

October 2, 1980  2:43 PM   Forrest   Changed microsecondsPerHundredPulses to 2878 from 2880.

October 3, 1980  5:26 PM   Forrest   Moved in unimplemented trap.

November 10, 1980  4:10 PM   Forrest   Added memory allocation junk.

January 23, 1981 approx   Gobbel   ?

February 6, 1981  12:04 PM   Knutsen   Make compatible with new PrincOps.

August 27, 1982 9:38 am  Taft  Add TrapSupport, MicrocodeBooting, MicrocodeVersion, etc.
February 27, 1983 2:31 pm  Taft  Export HeadStartChain.Start; absorb TrapSupportImpl