-- Copyright (C) 1984  by Xerox Corporation. All rights reserved. 
-- ProcessorHeadDicentra.mesa, HGM, 16-Nov-84  0:34:46

DIRECTORY
  BitBlt USING [AlignedBBTable, BITBLT, BBptr, BBTableSpace],
  DicentraInputOutput USING [Input, IOAddress, Output, numberVirtualPages],
  ESCAlpha USING [aBYTBLTR, aBYTBLT, aDMUL, aSDIV, aSDDIV, aUDDIV],
  Environment USING [bitsPerByte, bytesPerWord, Byte, PageCount, PageNumber],
  Frame USING [GetReturnFrame, ReadPC, WritePC],
  Inline USING [
    BITAND, BITSHIFT, BITXOR, DBITSHIFT, DIVMOD, HighHalf,
    LongCOPY, LongCOPYReverse, LongNumber, LongDiv, LongDivMod, LongMult],
  Mopcodes USING [zDIS, zDIS2],
  PrincOps USING [ControlLink, ESCTrapTable, LocalFrameHandle, StateVector],
  ProcessOperations USING [DisableInterrupts],
  ProcessorFace USING [GetClockPulses, ProcessorID, SpecialSetMP],
  SDDefs USING [SD, sOpcodeTrap],
  
  MultibusAddresses USING [eprom, first3ComBoard, processorIdLocation, timeout];

ProcessorHeadDicentra: PROGRAM
  IMPORTS
    BitBlt, DicentraInputOutput, Frame,
    Inline, ProcessOperations, ProcessorFace
  EXPORTS ProcessorFace
  SHARES ProcessorFace =
BEGIN

-- ProcessorFace Implementation
Start: PUBLIC PROCEDURE =
  BEGIN
  temp: ARRAY [0..3) OF WORD;
  PrincOps.ESCTrapTable↑ ←
    ALL[LOOPHOLE[UnimplementedTrapHandler, PrincOps.ControlLink]];
  PrincOps.ESCTrapTable[ESCAlpha.aSDIV] ← LOOPHOLE[SDIV];
  PrincOps.ESCTrapTable[ESCAlpha.aSDDIV] ← LOOPHOLE[SDDIV];
  PrincOps.ESCTrapTable[ESCAlpha.aUDDIV] ← LOOPHOLE[UDDIV];
  PrincOps.ESCTrapTable[ESCAlpha.aDMUL] ← LOOPHOLE[DMUL];
  -- Note: BITBLT is not simulated.
  PrincOps.ESCTrapTable[ESCAlpha.aBYTBLT] ← LOOPHOLE[BYTBLT];
  PrincOps.ESCTrapTable[ESCAlpha.aBYTBLTR] ← LOOPHOLE[BYTBLTR];
  DicentraInputOutput.Output[400B, first3ComBoard];  -- Reset
  temp ← [
      DicentraInputOutput.Input[ethernetHostNumber+0],
      DicentraInputOutput.Input[ethernetHostNumber+1],
      DicentraInputOutput.Input[ethernetHostNumber+2]];
  processorID ← LOOPHOLE[temp];
  temp[1] ← Inline.BITAND[temp[1], 0FF00H];
  IF temp[0] # 0260H OR temp[1] # 8C00H THEN
    BEGIN
    ProcessOperations.DisableInterrupts[];
    ProcessorFace.SpecialSetMP[898];
    DO ENDLOOP;
    END;
  END;
    

-- KROCK ***********
first3ComBoard: DicentraInputOutput.IOAddress = MultibusAddresses.first3ComBoard;
ethernetHostNumber: DicentraInputOutput.IOAddress = MultibusAddresses.processorIdLocation;
processorID: PUBLIC ProcessorFace.ProcessorID;

  mp: PUBLIC CARDINAL;
  SetMP: PUBLIC PROCEDURE [mpnew: CARDINAL] = 
    BEGIN
    ProcessorFace.SpecialSetMP[mp ← mpnew];
    END;


  -- Virtual memory layout
  GetNextAvailableVM: PUBLIC PROCEDURE [page: Environment.PageNumber]
    RETURNS [firstPage: Environment.PageNumber, count: Environment.PageCount] =
    BEGIN
    SELECT page FROM
      0 => RETURN[0, DicentraInputOutput.numberVirtualPages];
      ENDCASE => RETURN[0, 0];
    END;


  -- Interval time
  microsecondsPerHundredPulses: PUBLIC CARDINAL ← 1280;

  -- Naked notifies
  cvTimeoutMask: WORD = 100000B; -- Microcode uses bit 0
  reservedNakedNotifyMask: PUBLIC WORD ← cvTimeoutMask;

  -- Condition variable time
  millisecondsPerTick: PUBLIC CARDINAL ← 50;

  -- A lot of this has already been done by the initialization microcode
  InitializeClocks: PROCEDURE =
    BEGIN
    timeout: DicentraInputOutput.IOAddress = MultibusAddresses.timeout;
    eprom: DicentraInputOutput.IOAddress = MultibusAddresses.eprom;
    -- Now the Process ticker
    -- PClk = 400ns.  Counter runs off PClk/2.  50ms => count should be 62500 = F424.
    WriteMisc[0F4H, eprom + 01AH];  -- Cnt3 MSB
    WriteMisc[024H, eprom + 01BH];  -- Cnt3 LSB
    WriteMisc[080H, eprom + 01EH];  -- Cnt 3 Mode ← Cont, Pulse
    WriteMisc[000H, eprom + 004H];  -- Int Vect for Timers
    --  0 => Tick, 6 => 2+ Ticks (Error), 0FF => No interrupt pending
    WriteMisc[097H, eprom + 001H];  -- Master Config Control ← Enables, Link Timers
    WriteMisc[080H, eprom + 000H];  -- Master Interrupt Control ← MIE
    WriteMisc[0C6H, eprom + 00CH];  -- Counter 3 Cntrl ← Set IE, GCB, Trgr
    BEGIN  -- Yetch.  The trigger doesn't really happen until the first clock pulse.
    start: LONG CARDINAL ← ProcessorFace.GetClockPulses[];
    DO
      now: LONG CARDINAL ← ProcessorFace.GetClockPulses[];
      IF Inline.HighHalf[now] # Inline.HighHalf[start] THEN EXIT;
      ENDLOOP;
    END;
    END;

  WriteMisc: PROCEDURE [data: UNSPECIFIED, address: DicentraInputOutput.IOAddress] =
    BEGIN
    DicentraInputOutput.Output[data, address];
    END;
  

  -- Booting and power control

  BootButton: PUBLIC PROCEDURE =
    BEGIN
    timeout: DicentraInputOutput.IOAddress = MultibusAddresses.timeout;
    ProcessOperations.DisableInterrupts[];
    DO
      WriteMisc[001H, timeout + 000H];  -- Master Interrupt Control ← Reset
      WriteMisc[000H, timeout + 000H];  -- Master Interrupt Control ← No Reset
      WriteMisc[00EH, timeout + 006H];  -- Port C Direction ← Bit 0 ← output
      WriteMisc[000H, timeout + 00FH];  -- Port C Data ← 0
      WriteMisc[010H, timeout + 001H];  -- Master Config Control ← Enable Port C
      ENDLOOP;
    END;


-- Software assist for microcode
-- assumes return link is a frame, and has been started

InstructionNotImplemented: ERROR = CODE; -- This should actually be in traps

UnimplementedTrapHandler: PRIVATE PROCEDURE = {
  v: RECORD [a: ARRAY [0..3) OF WORD, state: PrincOps.StateVector];
  v.state ← STATE; ERROR InstructionNotImplemented};


-- Implemention of instructions not implemented in microcode

Number: TYPE = Inline.LongNumber;

  SDIV: PROC [q, r: INTEGER] RETURNS [INTEGER <<, INTEGER>>] =
    BEGIN
    -- remainder returned above the stack.
    Pop: PROC [INTEGER, INTEGER] RETURNS [INTEGER] = MACHINE CODE {Mopcodes.zDIS};
    lf: PrincOps.LocalFrameHandle;
    state: PrincOps.StateVector;
    negnum, negden: BOOLEAN;
    state ← STATE;
    IF negden ← (r < 0) THEN r ← -r;
    IF negnum ← (q < 0) THEN q ← -q;
    [quotient: q, remainder: r] ← Inline.DIVMOD[num: q, den: r];
    IF Inline.BITXOR[negnum, negden] # 0 THEN q ← -q;
    IF negnum THEN r ← -r;
    lf ← Frame.GetReturnFrame[];
    Frame.WritePC[pc: [Frame.ReadPC[lf] + 2], lf: lf];
    STATE ← state;
    RETURN Pop[q, r];
    END;  -- SDIV--

  DMUL:PROC [a, b: Number] RETURNS [product: Number] =
    BEGIN
    lf: PrincOps.LocalFrameHandle;
    state: PrincOps.StateVector;
    state ← STATE;
    product.lc ← Inline.LongMult[a.lowbits, b.lowbits];
    product.highbits ←
      product.highbits + a.lowbits*b.highbits + a.highbits*b.lowbits;
    lf ← Frame.GetReturnFrame[];
    Frame.WritePC[pc: [Frame.ReadPC[lf] + 2], lf: lf];
    STATE ← state;
    END;  --DMUL--

  SDDIV: PROC [num, den: Number] RETURNS [quotient <<, remainder>>: Number] =
    BEGIN
    -- remainder returned above the stack
    Pop2: PROC [Number, Number] RETURNS [Number] = MACHINE CODE {Mopcodes.zDIS2};
    remainder: Number;
    lf: PrincOps.LocalFrameHandle;
    negNum, negDen: BOOLEAN ← FALSE;
    state: PrincOps.StateVector;
    state ← STATE;
    IF INTEGER[num.highbits] < 0 THEN {negNum ← TRUE; num.li ← -num.li};
    IF INTEGER[den.highbits] < 0 THEN {negDen ← TRUE; den.li ← -den.li};
    [quotient: quotient, remainder: remainder] ← DUDivMod[num, den];
    IF Inline.BITXOR[negNum,  negDen] # 0 THEN quotient.li ← -quotient.li;
    IF negNum THEN remainder.li ← -remainder.li;
    lf ← Frame.GetReturnFrame[];
    Frame.WritePC[pc: [Frame.ReadPC[lf] + 2], lf: lf];
    STATE ← state;
    RETURN Pop2[quotient, remainder];
    END;  --SDDIV--

  UDDIV: PROC [num, den: Number]
    RETURNS [quotient <<, remainder>>: Number] =
    BEGIN
    Pop2: PROC [Number, Number] RETURNS [Number] = MACHINE CODE {Mopcodes.zDIS2};
    remainder: Number;
    lf: PrincOps.LocalFrameHandle;
    state: PrincOps.StateVector;
    state ← STATE;
    [quotient: quotient, remainder: remainder] ← DUDivMod[num, den];
    lf ← Frame.GetReturnFrame[];
    Frame.WritePC[pc: [Frame.ReadPC[lf] + 2], lf: lf];
    STATE ← state;
    RETURN Pop2[quotient, remainder];
    END;  --UDDIV--

  DUDivMod: PROC [num, den: Number] RETURNS [quotient, remainder: Number] =
    BEGIN
    OPEN Inline;
    qq: CARDINAL;
    count: [0..31);
    lTemp: Number;
    IF den.highbits = 0 THEN
      BEGIN
      [quotient.highbits, qq] ← LongDivMod[
        num: LOOPHOLE[Number[num[lowbits: num.highbits, highbits: 0]]],
        den: den.lowbits];
      [quotient.lowbits, remainder.lowbits] ← LongDivMod[
        num: LOOPHOLE[Number[num[lowbits: num.lowbits, highbits: qq]]],
        den: den.lowbits];
      remainder.highbits ← 0;
      END
    ELSE
      BEGIN
      count ← 0;
      quotient.highbits ← 0;
      lTemp ← den;
      WHILE lTemp.highbits # 0 DO -- normalize
        lTemp.lc ← DBITSHIFT[lTemp.lc, -1];
        count ← count + 1;
        ENDLOOP;
      IF num.highbits >= lTemp.lowbits THEN {
        -- subtract off 2↑16*divisor and fix up count
        div: Number ← Number[num[lowbits: 0, highbits: lTemp.lowbits]];
        qq ← LongDiv[num.lc - div.lc, lTemp.lowbits]/2 + 100000B;
        count ← count - 1}
      ELSE qq ← LongDiv[num.lc, lTemp.lowbits]; -- trial quotient
      qq ← BITSHIFT[qq, -count];
      lTemp.lc ← LongMult[den.lowbits, qq]; -- multiply by trial quotient
      lTemp.highbits ← lTemp.highbits + den.highbits*qq;
      UNTIL lTemp.lc <= num.lc DO
        -- decrease quotient until product is small enough
        lTemp.lc ← lTemp.lc - den.lc;
        qq ← qq - 1;
        ENDLOOP;
      quotient.lowbits ← qq;
      remainder.lc ← num.lc - lTemp.lc;
      END;
    END;  --DUDivMod--

  BYTBLT: PROC [
    destBase: LONG ORDERED POINTER, destIndex, count: CARDINAL,
    sourceBase: LONG ORDERED POINTER, sourceIndex: CARDINAL] =
    BEGIN
    OPEN Environment;
    lf: PrincOps.LocalFrameHandle = Frame.GetReturnFrame[];
    bba: BitBlt.BBTableSpace;
    bbt: BitBlt.BBptr = BitBlt.AlignedBBTable[@bba];
    MaxBytesPerScanLine: CARDINAL = 40B;
    bytesPerScanLine: CARDINAL ← MaxBytesPerScanLine;
    bitsPerScanLine: CARDINAL;
    lines, tail: CARDINAL;
    destBase ← destBase + destIndex/2;
    destIndex ← destIndex MOD 2;
    sourceBase ← sourceBase + sourceIndex/2;
    sourceIndex ← sourceIndex MOD 2;
  
    IF destIndex = sourceIndex THEN
      BEGIN      -- Can use Inline.LongCOPY
      s: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte ←
        LOOPHOLE[sourceBase];
      d: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte ←
        LOOPHOLE[destBase];
      words: CARDINAL;
      moved: CARDINAL ← 0;
      IF destIndex # 0 THEN
        BEGIN
        -- Thus destIndex = sourceIndex = 1
	d[1] ← s[1];
	d ← d+1;
	s ← s+1;
	moved ← 1;
	END;
      words ← (count-moved)/2;
      Inline.LongCOPY[to: d, from: s, nwords: words];
      IF (moved + 2*words) # count THEN  --
        d[2*words] ← s[2*words];  -- Move the one remaining byte
      Frame.WritePC[pc: [Frame.ReadPC[lf] + 2], lf: lf];
      RETURN;
      END;
     
    << Can't use Inline.LongCOPY, because source and destination
    are "not aligned", i.e., one starts at a word boundary,
    the other in the middle of a word. >>

      SELECT destBase FROM
        > sourceBase =>
          BEGIN
          wDiff: LONG CARDINAL = destBase - sourceBase;
          IF wDiff <= LAST[CARDINAL]/2 THEN
            bytesPerScanLine ←
              CARDINAL[wDiff]* bytesPerWord + destIndex - sourceIndex;
	  END;
        < sourceBase => NULL;
        ENDCASE => IF sourceIndex < destIndex THEN bytesPerScanLine ← 1;
      bytesPerScanLine ← MIN[bytesPerScanLine, MaxBytesPerScanLine];
      bitsPerScanLine ← bytesPerScanLine*bitsPerByte;
      [quotient: lines, remainder: tail] ←
        Inline.DIVMOD[num: count, den: bytesPerScanLine];
      bbt↑ ← [
        dst: [word: destBase, bit: destIndex*bitsPerByte],
        dstBpl: bitsPerScanLine,
        src: [word: sourceBase, bit: sourceIndex*bitsPerByte],
        srcDesc: [srcBpl[bitsPerScanLine]], width: bitsPerScanLine, height: lines,
        flags: [
          direction: forward, disjoint: FALSE, disjointItems: FALSE, gray: FALSE,
          srcFunc: null, dstFunc: null]];
      IF lines # 0 THEN BitBlt.BITBLT[bbt];
      IF tail # 0 THEN
        BEGIN
        count ← lines*bytesPerScanLine;
        bbt.dst.word ← bbt.dst.word + count/2;
        bbt.src.word ← bbt.src.word + count/2;
        IF count MOD 2 = 1 THEN
	  BEGIN
          IF destIndex = 0 THEN bbt.dst.bit ← bitsPerByte
          ELSE bbt.dst ← [word: bbt.dst.word + 1, bit: 0];
          IF sourceIndex = 0 THEN bbt.src.bit ← bitsPerByte
          ELSE bbt.src ← [word: bbt.src.word + 1, bit: 0];
	  END;
        bbt.width ← bitsPerByte*tail;
        bbt.height ← 1;
        BitBlt.BITBLT[bbt];
	END;
    Frame.WritePC[pc: [Frame.ReadPC[lf] + 2], lf: lf];
    END;  --BYTBLT--

  BYTBLTR: PROC [
    destBase: LONG ORDERED POINTER, destIndex, count: CARDINAL,
    sourceBase: LONG ORDERED POINTER, sourceIndex: CARDINAL] =
    BEGIN
    OPEN Environment;
    lf: PrincOps.LocalFrameHandle ← Frame.GetReturnFrame[];
    bba: BitBlt.BBTableSpace;
    bbt: BitBlt.BBptr = BitBlt.AlignedBBTable[@bba];
    MaxBytesPerScanLine: CARDINAL = 40B;
    bytesPerScanLine: CARDINAL ← MaxBytesPerScanLine;
    bitsPerScanLine: CARDINAL;
    lines, tail: CARDINAL;
    destBase ← destBase + destIndex/2;
    destIndex ← destIndex MOD 2;
    sourceBase ← sourceBase + sourceIndex/2;
    sourceIndex ← sourceIndex MOD 2;
    IF destIndex = sourceIndex THEN
      BEGIN      -- Can use Inline.LongCOPYReverse
      s: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte ←
        LOOPHOLE[sourceBase];
      d: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte ←
        LOOPHOLE[destBase];
      words: CARDINAL;
      odd: CARDINAL ← count MOD 2;
      IF sourceIndex # odd THEN
        BEGIN
	d[count-odd] ← s[count-odd];
	count ← count - 1;
	END;
      words ← count/2;
      IF sourceIndex # 0 THEN BEGIN
        Inline.LongCOPYReverse[to: d+1, from: s+1, nwords: words];
        d[1] ← s[1];  -- Move the one remaining byte
        END
      ELSE Inline.LongCOPYReverse[to: d, from: s, nwords: words];
      Frame.WritePC[pc: [Frame.ReadPC[lf] + 2], lf: lf];
      RETURN;
      END;
      SELECT sourceBase FROM
        > destBase =>
	  BEGIN
          wDiff: LONG CARDINAL = sourceBase - destBase;
          IF wDiff <= LAST[CARDINAL]/2 THEN
            bytesPerScanLine ←
              CARDINAL[wDiff]*bytesPerWord + sourceIndex - destIndex;
	  END;
        < destBase => NULL;
        ENDCASE => IF destIndex < sourceIndex THEN bytesPerScanLine ← 1;
      bytesPerScanLine ← MIN[bytesPerScanLine, MaxBytesPerScanLine];
      bitsPerScanLine ← bytesPerScanLine*bitsPerByte;
      [quotient: lines, remainder: tail] ←
        Inline.DIVMOD[num: count, den: bytesPerScanLine];
      bbt↑ ← [
        dst: [
          word: destBase + (count + destIndex - bytesPerScanLine)/2,
          bit: ((count + destIndex - bytesPerScanLine) MOD 2)*bitsPerByte],
        dstBpl: -INTEGER[bitsPerScanLine],
        src: [
          word: sourceBase + (count + sourceIndex - bytesPerScanLine)/2,
          bit: ((count + sourceIndex - bytesPerScanLine) MOD 2)*bitsPerByte],
        srcDesc: [srcBpl[-INTEGER[bitsPerScanLine]]],
        width: bitsPerScanLine, height: lines,
        flags: [
          direction: backward, disjoint: FALSE, disjointItems: FALSE, gray: FALSE,
          srcFunc: null, dstFunc: null]];
      IF lines # 0 THEN BitBlt.BITBLT[bbt];
      IF tail # 0 THEN
	BEGIN
        bbt.dst ← [word: destBase, bit: destIndex*bitsPerByte];
        bbt.src ← [word: sourceBase, bit: sourceIndex*bitsPerByte];
        bbt.width ← bitsPerByte*tail;
        bbt.height ← 1;
        BitBlt.BITBLT[bbt];
	END;
    Frame.WritePC[pc: [Frame.ReadPC[lf] + 2], lf: lf];
    END;  --BYTBLTR--

-- Main body code

InitializeClocks[];
SDDefs.SD[SDDefs.sOpcodeTrap] ← LOOPHOLE[UnimplementedTrapHandler];

END....