-- ProcessorHeadD0.mesa
-- Last Edited by: Taft, February 27, 1983 3:26 pm

DIRECTORY
  D0InputOutput USING [ControllerNumber, ControllerType, CSBArray, Input, null,
    nullControllerNumber, rdc, rdcWithServiceLate],
  Environment USING [Byte, PageCount, PageNumber, wordsPerPage],
  Frame USING [GetReturnFrame, GetReturnLink, MyLocalFrame],
  HeadStartChain USING [],
  Inline USING [BITOR, COPY, LongCOPY, LongDiv],
  MicrocodeBooting USING [BootFileNumber, MicrocodeType, nullBootFileNumber],
  MicrocodeVersion USING [VersionResult],
  MiscAlpha USING [aLOADRAMJ, aREADRAM],
  Mopcodes USING [zMISC],
  PrincOps USING [ControlLink, FrameHandle, StateVector],
  ProcessorFace USING [ProcessorID],
  SDDefs USING [SD, sUnimplemented],
  System USING [gmtEpoch],
  TrapSupport USING [BumpPC, OpTrapTable, opTrapTable];

ProcessorHeadD0: PROGRAM
  IMPORTS Frame, D0InputOutput, Inline, TrapSupport
  EXPORTS D0InputOutput, HeadStartChain, MicrocodeBooting, ProcessorFace
  SHARES ProcessorFace =
  PUBLIC BEGIN OPEN D0InputOutput;

  --
  -- D0InputOutput

  IOPage: LONG POINTER TO CSBArray ← LOOPHOLE[LONG[pageIO*Environment.wordsPerPage]];
  firstController: ControllerNumber = 4B; -- tasks 0B thru 3B taken up by emulator
  lastController: ControllerNumber = 15B; -- tasks 16B and 17B taken by timers and faults
  ControllerIndex: TYPE = ControllerNumber [firstController..lastController];
  controllerRoster: ARRAY ControllerIndex OF ControllerType ← ALL[null];

  GetNextController: PROC [
    type: ControllerType, prev: ControllerNumber] RETURNS [ControllerNumber] =
    BEGIN
    i: ControllerIndex;
    FOR i DECREASING IN ControllerIndex DO
      IF type = controllerRoster[i]
        AND (prev = nullControllerNumber OR prev > i) THEN RETURN[i];
      ENDLOOP;
    RETURN[nullControllerNumber]
    END;
    
  InitializeD0InputOutput: PRIVATE PROC =
    BEGIN
    IDRegister: TYPE = MACHINE DEPENDENT RECORD [
      type: ControllerType, lowByte: [0..256)];
    r: IDRegister; -- code generator bug in 6.0n requires this
    idRegNo: [0..17B] = 0;
    i: ControllerIndex;
    t: ControllerType;
    -- Initialization microcode really should have put roster into memory somewhere.
    FOR i IN ControllerIndex DO
      r ← LOOPHOLE[Input[[controller: i, register: idRegNo]], IDRegister];
      t ← r.type;
      IF t = rdcWithServiceLate THEN t ← rdc; -- #*%$@!! RDC
      controllerRoster[i] ← t;
      ENDLOOP;
    END;
    
  --
  -- 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: PROC = {-- By now start trap has occurred and main-body code has run--};
    
  -- Processor id
  processorID: ProcessorFace.ProcessorID ← GetProcessorID[];
  GetProcessorID: PRIVATE PROC RETURNS [ProcessorFace.ProcessorID] =
    BEGIN
    csw: CSWord = ReadRam[7777B];
    CSWord: TYPE = MACHINE DEPENDENT RECORD [
      bits16To31: CARDINAL,
      bits0To15: CARDINAL,
      bits32To35: [0..17B],
      address: [0..7777B]];
    ReadRam: PROC [address: [0..7777B]] RETURNS [CSWord] =
      MACHINE CODE BEGIN Mopcodes.zMISC, MiscAlpha.aREADRAM END;
    RETURN[[0, csw.bits0To15, csw.bits16To31]] -- note most significant first
    END;
    
  -- Real memory configuration
  dedicatedRealMemory: Environment.PageCount ← 0;

  -- Virtual memory layout
  GetNextAvailableVM: 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;
    
  page1: PRIVATE Environment.PageNumber = 1B;
    -- I/O page for Alto-compatible Diablo31, display, cursor, mouse coords
  page376B: PRIVATE Environment.PageNumber = 376B;
    -- I/O page for Alto-compatible keyboard/mouse bitmap
  pageIO: PRIVATE Environment.PageNumber = 377B; -- "official" D0 I/O page
  pageFirstUnimplemented: PRIVATE Environment.PageNumber = 40000B;
    -- [pageFirstUnimplemented..LAST[PageNumber]] not implemented

  -- Interval time
  microsecondsPerHundredPulses: CARDINAL;
  InitializeClockRate: PRIVATE PROC =
    BEGIN
    ReadRegister: PROC [address: [0..377B]] RETURNS [UNSPECIFIED] = MACHINE CODE {
      Mopcodes.zMISC, 106B};
    -- The number read here is proportional to clock ticks per unit of time,
    -- where the value 320 denotes 1 tick every 64 microseconds.
    clockRate: CARDINAL = ReadRegister[323B];
    microsecondsPerHundredPulses ← Inline.LongDiv[LONG[100]*64*320, clockRate];
    END;

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

  -- Condition variable time
  -- millisecondsPerTick is exported by UserTerminalHeadD0,
  --   since its value depends on the display refresh rate.
  InitializeCVTimeouts: PRIVATE PROC =
    BEGIN
    DIW: LONG POINTER TO WORD = LOOPHOLE[LONG[421B]];
    DIW↑ ← Inline.BITOR[DIW↑, cvTimeoutMask];
    END;
    
  -- Booting and power control
  BootButton: PROC =
    BEGIN
    LoadRam: PROC [pMicrocode: LONG POINTER, andJump: BOOLEAN] =
      MACHINE CODE BEGIN Mopcodes.zMISC, MiscAlpha.aLOADRAMJ END;
    bootMe: ARRAY [0..7) OF WORD ←
      [000000B, 170000B, 000047B, 015000B, 177760B, 163351B, 007400B];
    LoadRam[@bootMe, TRUE] -- never returns
    END;


  -- 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;

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

  READRTrap: PROC [reg: [0..377]] RETURNS [UNSPECIFIED] =
    BEGIN
    -- If READR traps, assume we are reading the clock rate (see InitializeClockRate above)
    -- and return a value denoting a 40 MHz crystal and 2560 cycles per clock tick.
    TrapSupport.BumpPC[2];
    RETURN [320];
    END;

  --
  -- Initialization

  InitializeCVTimeouts[];
  InitializeD0InputOutput[];
  SDDefs.SD[SDDefs.sUnimplemented] ← UnimplementedTrap;
  SDDefs.SD[137B] ← @trapTable;
  TrapSupport.opTrapTable.misc[100B] ← LOOPHOLE[LocalBlkZ];
  TrapSupport.opTrapTable.misc[102B] ← LOOPHOLE[LongBlkZ];
  TrapSupport.opTrapTable.misc[104B] ← LOOPHOLE[UVersion];
  TrapSupport.opTrapTable.misc[106B] ← LOOPHOLE[READRTrap];
  InitializeClockRate[];

  END.

August 8, 1979  1:47 AM   Redell   Create file

October 17, 1979  6:54 PM   Redell   Add patch to detect RDC thinking it's an old ethernet

February 4, 1980  10:36 AM   McJones   Rename OISProcessorFaceD0Impl=>OISProcessorHeadD0; delete device registry/enumeration; add new OISProcessorFace items

February 25, 1980  3:06 PM   McJones   Move export of millisecondsPerTick to UserTerminalHeadD0

April 30, 1980  10:17 AM   Forrest   Add InitializeCVTimeout

May 14, 1980  4:27 PM   McJones   Add reservedNakedNotifyMask; remove Start from HeadStartChain (export to OISProcessorFace now); change GetNextController to deliver highest controller number first

June 25, 1980  4:23 PM   McJones   Drop "OIS" from name; expand processor id to 48 bits.

August 12, 1980  10:26 AM   McJones   Add dedicatedRealMemory; use MiscAlpha.

August 26, 1980  11:17 AM   McJones   Change cvTimeoutMask for new process microcode.

October 3, 1980  5:17 PM   Forrest   Move in unimplemented trap.

February 4, 1981  1:08 PM   Knutsen   PrincOps fields changed names.

19-Jul-81 14:43:33  Taft  Use TrapSupport to handle unimplemented opcode traps.
22-Jun-82 16:53:22	Taft	Implement and export MicrocodeBooting; degenerate implementation for now.
14-Jul-82 15:56:50	Taft	Add trap support for LocalBlkZ, LongBlkZ, and UVersion opcodes.
October 4, 1982 12:50 pm  Taft  Init microsecondsPerHundredPulses according to system clock rate.
October 8, 1982 3:07 pm  Taft  Don't InitializeClockRate until after trap support set up.
February 27, 1983 2:31 pm  Taft  Export HeadStartChain.Start; absorb TrapSupportImpl