-- ProcessorHeadDorado.mesa
-- Last Edited by: Taft, February 27, 1983 3:29 pm

DIRECTORY
  BitBlt USING [BitBltTable, BitBltTablePtr, BITBLT],
  DeviceCleanup USING [Perform],
  DisplayFace USING [Cursor, CursorPtr, Point],
  DoradoInputOutput USING [GetMemConfig, Halt, pagesPerBank, RWMufMan],
  Environment USING [Byte, PageCount, maxPagesInVM, PageNumber],
  Frame USING [GetReturnFrame, GetReturnLink, MyLocalFrame],
  HeadStartChain USING [],
  Inline USING [BITAND, BITOR, COPY, DIVMOD, LongCOPY, LongDiv, LongMult, LongNumber,
    LowHalf, HighHalf],
  MicrocodeBooting USING [BootFileNumber, MicrocodeType, nullBootFileNumber],
  MicrocodeVersion USING [VersionResult],
  MiscAlpha USING [aSETMP, aTEXTBLT],
  Mopcodes USING [zMISC],
  PilotMP USING [cClient, Code, cPowerOff],
  PrincOps USING [ControlLink, FrameHandle, StateVector],
  ProcessInternal USING [DisableInterrupts, EnableInterrupts],
  ProcessorFace USING [GetClockPulses, gmtEpoch, GreenwichMeanTime, ProcessorID, SetMP],
  SDDefs USING [SD, sUnimplemented],
  SoftwareTextBlt USING [TextBlt],
  System USING [gmtEpoch],
  TrapSupport USING [BumpPC, OpTrapTable, opTrapTable];

ProcessorHeadDorado: PROGRAM
  IMPORTS BitBlt, DeviceCleanup, DoradoInputOutput, Frame, Inline, ProcessInternal,
    ProcessorFace, SoftwareTextBlt, TrapSupport
  EXPORTS DoradoInputOutput, HeadStartChain, MicrocodeBooting, ProcessorFace
  SHARES ProcessorFace =

  BEGIN OPEN DoradoInputOutput;

  -- ProcessorFace

  -- 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[];
  baseProcessorIDDorado: ProcessorFace.ProcessorID = [a: 0, b: 52612B, c: 100000B];
  GetProcessorID: PRIVATE PROC RETURNS [ProcessorFace.ProcessorID] =
    BEGIN
    doradoSN: [0..377B] ← 0;
    FOR i: CARDINAL IN [0..7] DO
      doradoSN ← 2*doradoSN + RWMufMan[[useDMD: FALSE, dMuxAddr: 2220B+i]].dMuxData;
      ENDLOOP;
    RETURN[[a: baseProcessorIDDorado.a,
      b: baseProcessorIDDorado.b,
      c: baseProcessorIDDorado.c + doradoSN]] -- note most significant first
    END;

  -- Real memory configuration

  dedicatedRealMemory: PUBLIC Environment.PageCount ← 0;

  -- Virtual memory layout
  GetNextAvailableVM: PUBLIC PROC [page: Environment.PageNumber]
    RETURNS [firstPage: Environment.PageNumber, count: Environment.PageCount] =
    BEGIN
    IF page < page1 THEN RETURN[page, page1 - page];
    page ← MAX[page, page1 + 1];
    IF page < page376B THEN RETURN[page, page376B - page];
    page ← MAX[page, page376B + 1];
    IF page < pageIO THEN RETURN[page, pageIO - page];
    page ← MAX[page, pageIO + 1];
    IF page < pageFirstUnimplemented THEN
      RETURN[page, pageFirstUnimplemented - page];
    RETURN[LAST[Environment.PageNumber], 0];
    END;

  -- I/O page for Alto-compatible display, cursor, mouse coords.
  -- NOTE: even though we declare this page off-limits, Pilot boot files may
  -- contain data for it.  Therefore, Pilot boot devices (disk and Ethernet)
  -- CANNOT use page1 for their CSBs.
  page1: Environment.PageNumber = 1B;

  -- I/O page for Alto-compatible keyboard/mouse bitmap
  page376B: Environment.PageNumber = 376B;

  -- I/O page for newly-written Pilot device microcode
  pageIO: Environment.PageNumber = 377B;

  -- First nonexistent VM page.
  -- Note that Environment.PageNumber is presently a CARDINAL rather than a LONG CARDINAL.
  pageFirstUnimplemented: Environment.PageNumber =
    IF GetMemConfig[].virtualBanks > Environment.maxPagesInVM/pagesPerBank
    THEN Environment.maxPagesInVM ELSE GetMemConfig[].virtualBanks * pagesPerBank;

  -- Interval time
  microsecondsPerHundredPulses: PUBLIC CARDINAL ← 32*100;

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

  -- Condition variable time
  -- millisecondsPerTick is exported by UserTerminalHeadDorado,
  --   since its value depends on the display refresh rate.
  InitializeCVTimeouts: PROC =
    BEGIN
    DIW: LONG POINTER TO WORD = LOOPHOLE[LONG[421B]];
    DIW↑ ← Inline.BITOR[DIW↑, cvTimeoutMask];
    END;

  -- Greenwich mean time

  GetGreenwichMeanTime: PUBLIC PROC RETURNS [ProcessorFace.GreenwichMeanTime] =
    BEGIN OPEN ProcessorFace;
    IF gmtSimulated ~= gmtEpoch THEN  -- don't update clock unless it has been set
      BEGIN
      seconds: GreenwichMeanTime =
        (GetClockPulses[] - pulsesGmtSimulated)/pulsesPerSecond;
      pulsesGmtSimulated ← pulsesGmtSimulated + seconds*pulsesPerSecond;  -- long multiply!
      gmtSimulated ← gmtSimulated + seconds;
      END;
    RETURN[gmtSimulated]
    END;

  SetGreenwichMeanTime: PUBLIC PROCEDURE [gmt: ProcessorFace.GreenwichMeanTime] =
    BEGIN pulsesGmtSimulated ← ProcessorFace.GetClockPulses[]; gmtSimulated ← gmt; END;

  gmtSimulated: ProcessorFace.GreenwichMeanTime ← ProcessorFace.gmtEpoch;  -- => not set
  pulsesGmtSimulated: LONG CARDINAL;  -- interval timer value corresponding to gmtSimulated
  pulsesPerSecond: LONG CARDINAL = Inline.LongDiv[1D6*100, microsecondsPerHundredPulses];

  -- Booting and power control

  BootButton: PUBLIC PROC =
    BEGIN
    ProcessInternal.DisableInterrupts[];
    [] ← RWMufMan[[useDMD: TRUE, dMuxAddr: 2264B]];  -- Request BaseBoard boot
    DO ENDLOOP;
    END;

  PowerOff: PUBLIC PROC =
    BEGIN OPEN ProcessorFace;
    -- Double-by-single multiply procedure, specially defined because the compiler would
    -- otherwise generate calls to nonresident software routines in Instructions.mesa.
    MultLCxC: PROC [lc: LONG CARDINAL, c: CARDINAL] RETURNS [LONG CARDINAL] = INLINE {
      result: Inline.LongNumber ← [lc[Inline.LongMult[Inline.LowHalf[lc], c]]];
      result.highbits ← result.highbits + Inline.HighHalf[lc]*c;
      RETURN [result.lc]};
    timeToGo: LONG INTEGER;
    ProcessInternal.DisableInterrupts[];
    DeviceCleanup.Perform[turnOff];
    SetMP[PilotMP.cPowerOff];
    timeToGo ← gmtAutomaticPowerOn - GetGreenwichMeanTime[];
    -- Manifold 2262 turns the machine off indefinitely.
    -- Manifold 2263 turns the machine off for a specified interval in units of 25.6 sec.
    -- The interval must be left on the BMux when the machine halts.
    IF gmtAutomaticPowerOn = gmtEpoch-1 THEN [] ← RWMufMan[[useDMD: TRUE, dMuxAddr: 2262B]]
    ELSE IF timeToGo<26 THEN BootButton[]
    ELSE [] ← RWMufMan[[useDMD: TRUE,
      dMuxAddr: IF timeToGo >= (200000B*256)/10 THEN 2262B ELSE 2263B]];
    Halt[Inline.LongDiv[MultLCxC[timeToGo, 10], 256]];
    END;

  gmtAutomaticPowerOn: ProcessorFace.GreenwichMeanTime;

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

  ResetAutomaticPowerOn: PUBLIC PROCEDURE =
    BEGIN
    gmtAutomaticPowerOn ← ProcessorFace.gmtEpoch - 1;  -- infinity
    END;


  -- MicrocodeBooting

  BootSpecificMicrocode: PUBLIC PROCEDURE [bfn: MicrocodeBooting.BootFileNumber] =
    BEGIN
    microcodeBFNOffset: CARDINAL = 3000B; -- microcode boot server uses offset BFNs
    ubfn: CARDINAL ← bfn-microcodeBFNOffset;
    bootParameterSeal: CARDINAL = 56623B;
    PushBootParams: PROCEDURE [ubfn, seal, checksum: CARDINAL] = MACHINE CODE {};
    -- Push sealed and checksum boot file parameters on the stack for Initial to look at.
    -- Note: in general calling BootButton with a non-empty stack would be illegal,
    -- but is OK here because BootButton calls only machine code procedures.
    IF bfn#MicrocodeBooting.nullBootFileNumber THEN
      PushBootParams[ubfn, bootParameterSeal, 0-(ubfn+bootParameterSeal)];
    BootButton[];
    END;

  GetBootFileNumber: PUBLIC PROCEDURE [type: MicrocodeBooting.MicrocodeType]
    RETURNS [bfn: MicrocodeBooting.BootFileNumber] =
    BEGIN
    RETURN[[
      SELECT type FROM
        altoMesa => 3110B,
	lisp => 3112B,
	smalltalk76 => 3111B,
	smalltalk80 => MicrocodeBooting.nullBootFileNumber,
	pilotMesa, cedarMesa => 3113B,
        ENDCASE => 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;

  -- Fixed-frame handler for SetMP trap.
  -- (Needs to be fixed-frame because SetMP may occur in contexts where
  -- frame allocation is impossible.)
  InitSetMPTrap: PROC =
    BEGIN
    GetCode: PROC RETURNS [code: PilotMP.Code] = MACHINE CODE BEGIN END;
    code: PilotMP.Code;
    cursorPtr: DisplayFace.CursorPtr = LOOPHOLE[LONG[431B]];
    state: MACHINE DEPENDENT RECORD [a, b: UNSPECIFIED, v: PrincOps.StateVector];
    
    TrapSupport.opTrapTable.misc[MiscAlpha.aSETMP].frame ← Frame.MyLocalFrame[];
    state.v ← STATE;
    ProcessInternal.DisableInterrupts[];
    
    DO
    state.v.dest ← Frame.GetReturnLink[];
    ProcessInternal.EnableInterrupts[];
    TRANSFER WITH state.v;
    -- SetMP trap enters here with code on top of stack.
    ProcessInternal.DisableInterrupts[];
    code ← GetCode[];
    state.v ← STATE;
    TrapSupport.BumpPC[2];
    IF code=PilotMP.cClient THEN cursorPtr↑ ← savedCursor
    ELSE BEGIN
      -- I should use the procedures exported through DisplayFace to do this,
      -- but DisplayHeadDorado may not have been STARTed yet (or may not exist).
      cursorPosition: LONG POINTER TO DisplayFace.Point = LOOPHOLE[LONG[426B]];
      digit, digitOffset: CARDINAL;
      cursorPosition↑ ← [x: 304, y: 404];
      FOR i: CARDINAL IN [0..15] DO cursorPtr[i] ← 0; ENDLOOP;
      FOR i: CARDINAL DECREASING IN [0..2] DO
        [code, digit] ← Inline.DIVMOD[code, 10];
        digitOffset ← digit*5;
        bbPtr↑ ← [
          dst: [word: cursorPtr, bit: 5*i],
          dstBpl: 16,
          src: [word: @digitFont + digitOffset/16, bit: digitOffset MOD 16],
          srcDesc: [srcBpl[64]],
          width: 5, height: 6, flags: [disjoint: TRUE, dstFunc: or]];
        BitBlt.BITBLT[bbPtr];
        ENDLOOP;
      END;
    ENDLOOP;
    END;

  -- These belong in SETMPTrap's local frame, but are here in the global frame
  -- because if SETMPTrap's local frame is too large it causes frame allocation traps
  -- during early initialization when they can't be handled.
  -- Note: PrincOps BBTable must be 16-word aligned, but Dorado requires only 2-word.
  bbTable: ARRAY[0..SIZE[BitBlt.BitBltTable]+1) OF UNSPECIFIED;
  bbPtr: BitBlt.BitBltTablePtr = Inline.BITAND[@bbTable+1, 177776B];
  digitFont: ARRAY [0..24) OF CARDINAL ← [
    -- Strike-format font, densely packed, characters 6 high, 5 wide
     30614B,  61474B, 167461B, 117000B,
     45222B, 112441B,    512B,  57000B,
     54202B,  22471B, 141062B,  57000B,
     64214B,  14405B,  21111B, 157000B,
     44220B, 117645B,  22110B,  57000B,
     31736B,  60430B, 142063B, 117000B];

  -- SetCursorPattern stores a copy of the real cursor here, and the SetMP trap handler
  -- copies this into the real cursor whenever a code of cClient is posted, thereby
  -- restoring the client's cursor across world-swaps.  This can't be done by
  -- UserTerminalHeadDorado's cleanup procedure, because calls to cleanup procedures
  -- are surrounded by SetMPs, thereby clobbering the cursor.
  savedCursor: PUBLIC DisplayFace.Cursor ← ALL[0];

  -- 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: dorado, majorVersion: 0, unused: 0,
      floatingPoint: FALSE, cedar: FALSE, releaseDate: System.gmtEpoch/86400]];
    END;

  --
  -- Initialization

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

  END.

December 5, 1980  5:39 PM	Taft	Convert for Dorado; mostly remove D0 I/O junk
December 11, 1980  12:55 PM	Taft	Maint panel in cursor
 9-Jun-81 18:59:01	Taft	Export savedCursor; use it when SetMP code = cClient
 7-Jul-81 19:50:43	Taft	Use TrapSupport; pass TextBlt trap off to SoftwareTextBlt; fully implement GetNextAvailableVM; add PowerOff and residual stuff from GMTUsingIntervalTimer.
17-Aug-81 13:55:50	Taft	Fixed frame handler for SetMP trap.
21-Aug-81  9:54:25	Taft	BootButton now requests BaseBoard boot.
26-Sep-81 13:22:52	Taft	Fix bug in PowerOff; fully implement SetAutomaticPowerOn.
22-Jun-82 14:59:02	Taft	Implement and export MicrocodeBooting.
14-Jul-82 12:36:50	Taft	Add trap support for LocalBlkZ, LongBlkZ, and UVersion opcodes.
September 28, 1982 3:55 pm  Taft  Do arithmetic right in PowerOff
February 13, 1983 1:21 pm  Taft  Remove machine code procs to DoradoInputOutput
February 27, 1983 2:31 pm  Taft  Export HeadStartChain.Start; absorb TrapSupportImpl