-- File: MemoryErrorInfo.mesa,  Last Edit: HGM  March 31, 1981  3:34 PM

-- See page 40 of Alto Hardware Manual, and/or [Ivy]<IFS>Sources>IFSMemoryError.bcpl

DIRECTORY
  Inline USING [BITAND, BITNOT, BITSHIFT, BITXOR],
  Menu USING [Handle, ItemObject, MCRType, Create, Instantiate],
  Process USING [Detach, SecondsToTicks, SetTimeout],
  Put USING [CR, Date, Line, LongDecimal, LongNumber, Number, Text],
  SegmentDefs USING [GetMemoryConfig, MemoryConfig],
  Storage USING [Node],
  String USING [AppendDecimal, AppendOctal, AppendString],
  Time USING [Current],
  UserInput USING [GetDefaultWindow],
  Window USING [Handle],

  GateDefs USING [typescript];

MemoryErrorInfo: MONITOR
  IMPORTS
    Inline, Menu, Process, Put, SegmentDefs, Storage, String, Time, UserInput, GateDefs =
  BEGIN

  first: ErrorInfo ← NIL;
  maxErrorBlocks: CARDINAL = 10;
  badSyndrome: LONG CARDINAL ← 0;
  otherErrors: LONG CARDINAL ← 0;

  Card: TYPE = [1..4];
  Chip: TYPE = [11..90];
  Syndrome: TYPE = [0..64);

  ErrorInfo: TYPE = POINTER TO ErrorInfoBlock;
  ErrorInfoBlock: TYPE = RECORD [
    card: Card,
    chip: Chip,
    count: LONG CARDINAL,
    next: ErrorInfo];
  maxErrors: LONG CARDINAL = LAST[LONG CARDINAL];

  Address: TYPE = WORD;  -- It's really a pointer, but we do arithametic on it

  mear: POINTER TO Address = LOOPHOLE[177024B];
  mesr: POINTER TO UNSPECIFIED = LOOPHOLE[177025B];  -- Low TRUE
  MESR: TYPE = MACHINE DEPENDENT RECORD [
    hamming: [0..64),
    parityError: BOOLEAN,
    parityBit: [0..1),
    syndrome: Syndrome,
    bank: [0..4) ];

  WatchForMemoryErrors: ENTRY PROCEDURE =
    BEGIN
    sleep: CONDITION;
    info: MESR;
    first: BOOLEAN ← FALSE;
    Process.SetTimeout[@sleep,Process.SecondsToTicks[300]];
    mesr↑ ← 0;  -- reset error latches
    DO
      WAIT sleep;
      info ← Inline.BITNOT[mesr↑];
      IF Inline.BITAND[info,1374B] = 0 THEN LOOP;
      IF ~first THEN
        BEGIN
        first ← TRUE;
        SetupMenu[];
        END;
      RecordMemoryError[];
      ENDLOOP;
    END;

  RecordMemoryError: PROCEDURE =
    BEGIN
    address: Address ← mear↑;
    info: MESR ← Inline.BITNOT[mesr↑];
    PrintErrorInfo[address, info];
    BEGIN
    card: Card;
    chip: Chip;
    [card: card, chip: chip] ← ConvertInfoToCardAndChip[address, info !
      BadSyndrome => GOTO Bad];
    PrintCardInfo[card: card, chip: chip];
    AddToList[card: card, chip: chip];
    EXITS Bad => badSyndrome ← badSyndrome+1;
    END;
    mesr↑ ← 0;  -- reset error latches
    END;

  BadSyndrome: SIGNAL = CODE;
  ConvertInfoToCardAndChip: PROCEDURE [address: Address, info: MESR]
    RETURNS [card: Card, chip: Chip] =
    BEGIN
    -- The memory is 39 bits wide: 32 data bits, 6 hamming bits, and a parity bit
    BitNumber: TYPE = [0..39];  -- 39 is used to mark an invalid syndrome
    syndromeMapping: PACKED ARRAY Syndrome OF BitNumber = [
      38, 37, 36, 39, 35, 39, 18, 39,
      34, 29, 14, 39, 07, 39, 22, 39,
      33, 27, 12, 39, 05, 39, 20, 39,
      02, 31, 16, 39, 09, 39, 24, 39,
      32, 26, 11, 39, 04, 39, 19, 39,
      01, 30, 15, 39, 08, 39, 23, 39,
      00, 28, 13, 39, 06, 39, 21, 39,
      03, 39, 17, 39, 10, 39, 25, 39 ];
    bit: BitNumber ← syndromeMapping[info.syndrome];
    IF bit=39 THEN SIGNAL BadSyndrome;
    IF TheSwitch[] THEN address ← Inline.BITXOR[address,100000B];
    card ← (bit MOD 4)+1;
    -- This could be haired up to work with 4K chips
    chip ← bit/8 + 16 - (IF Odd[bit/4] THEN 5 ELSE 0)
      + 10*Inline.BITSHIFT[address,-15] + 20*info.bank;
    END;

  TheSwitch: PROCEDURE RETURNS [BOOLEAN] = INLINE
    BEGIN
    utlin: POINTER TO PACKED ARRAY [0..16) OF BOOLEAN = LOOPHOLE[177030B];
    RETURN[utlin[6]];
    END;

  Odd: PROCEDURE [x: UNSPECIFIED] RETURNS [BOOLEAN] = INLINE
    BEGIN
    RETURN[(x MOD 2)=1];
    END;

  AddToList: PROCEDURE [card: Card, chip: Chip] =
    BEGIN
    length: CARDINAL ← 0;
    FOR e: ErrorInfo ← first, e.next UNTIL e=NIL DO
      IF chip=e.chip AND card=e.card THEN
        BEGIN
        IF e.count<maxErrors THEN e.count ← e.count+1;
        RETURN;
        END;
      length ← length+1;
      ENDLOOP;
    IF length < maxErrorBlocks THEN
      BEGIN
      e: ErrorInfo ← Storage.Node[SIZE[ErrorInfoBlock]];
      e↑ ← [card: card, chip: chip, count: 1, next: first];
      first ← e;
      END
    ELSE otherErrors ← otherErrors+1;
    END;

  PrintErrorInfo: PROCEDURE [address: Address, info: MESR] =
    BEGIN
    temp: STRING = [50];
    PutHeader[NIL,"Main Memory Error "L, FALSE];
    String.AppendString[temp, "MEAR: "L];
    String.AppendOctal[temp, address];
    String.AppendString[temp, ", MESR: "L];
    String.AppendOctal[temp, info];
    Put.Line[NIL,temp];
    END;

  PrintCardInfo: PROCEDURE [card: Card, chip: Chip] =
    BEGIN
    temp: STRING = [50];
    String.AppendString[temp, "card: "L];
    String.AppendDecimal[temp, card];
    String.AppendString[temp, ", chip: "L];
    String.AppendDecimal[temp, chip];
    Put.Line[NIL,temp];
    END;

  PrintMemoryErrors: PUBLIC PROCEDURE [wh: Window.Handle] =
    BEGIN
    PutHeader[wh,"Main Memory Errors:"L];
    IF first#NIL THEN
      BEGIN
      Put.Line[wh,"Card Chip Errors"L];
      FOR e: ErrorInfo ← first, e.next UNTIL e=NIL DO
        Put.Number[wh, e.card, [10, FALSE, TRUE, 4]];
        Put.Number[wh, e.chip, [10, FALSE, TRUE, 4]];
        Put.LongNumber[wh, e.count, [10, FALSE, TRUE, 6]];
        Put.CR[wh];
        ENDLOOP;
      END;
    IF badSyndrome > 0 THEN
      BEGIN
      IF badSyndrome=1 THEN Put.Text[wh, "One main memory error"L];
      IF badSyndrome>1 THEN
        BEGIN
        Put.LongDecimal[wh, badSyndrome];
        Put.Text[wh, " main memory errors"L];
        END;
      Put.Line[wh, " with bad symdrome."L];
      END;
    IF otherErrors > 0 THEN
      BEGIN
      IF otherErrors=1 THEN Put.Text[wh, "One other main memory error"L];
      IF otherErrors>1 THEN
        BEGIN
        Put.LongDecimal[wh, otherErrors];
        Put.Text[wh, " other main memory errors"L];
        END;
      Put.Line[wh, "."L];
      END;
    END;

  PutHeader: PROCEDURE [wh: Window.Handle, s: STRING, cr: BOOLEAN ← TRUE] =
    BEGIN
    Put.Date[wh, Time.Current[], dateTime];
    Put.Text[wh, "  "L];
    Put.Text[wh, s];
    IF cr THEN Put.CR[wh];
    END;

  items: ARRAY [0..1) OF Menu.ItemObject ← [["Errors", DoInfo]];

  SetupMenu: PROCEDURE =
    BEGIN
    menu: Menu.Handle ← Menu.Create[DESCRIPTOR[items], "MemErr"];
    Menu.Instantiate[menu, UserInput.GetDefaultWindow[]];
    Menu.Instantiate[menu, GateDefs.typescript];
    END;

  DoInfo: Menu.MCRType =
    BEGIN
    PrintMemoryErrors[NIL];
    END;

  Init: PROCEDURE =
    BEGIN
    config: SegmentDefs.MemoryConfig ← SegmentDefs.GetMemoryConfig[];
    IF config.AltoType#AltoIIXM THEN RETURN;
    Process.Detach[FORK WatchForMemoryErrors[]];
    END;


  Init[];
  END.