-- MouseSimImpl.mesa
-- created by Haeberli:  30-Sep-81 15:46:15

DIRECTORY
  InlineDefs,
  Mouse;

MouseSimImpl: PROGRAM IMPORTS InlineDefs EXPORTS Mouse =
  {
  OPEN InlineDefs, Mouse;

  PinState: TYPE = ARRAY Mouse.Pin OF Mouse.Value;

  drivenPinState: PinState ← ALL[x];
  lastDrivenPinState: PinState ← drivenPinState;
  currentPinState: PinState ← ALL[x];
  lastPinState: PinState ← lastPinState;

  version: Mouse.ChipVersion ← Rev3;

  EndRecordPat: PUBLIC PROCEDURE = {};

  GetPin: PUBLIC PROCEDURE [pin: Mouse.Pin] RETURNS [value: Mouse.Value] = {
    RETURN[currentPinState[pin]]};

  PlayPat: PUBLIC PROCEDURE [pat: Pattern] = {};

  RecordPat: PUBLIC PROCEDURE [pat: Pattern] = {};

  SetVersion: PUBLIC PROCEDURE [v: Mouse.ChipVersion] = {version ← v; Reset[]};

  Reset: PUBLIC PROCEDURE = {
    drivenPinState ← ALL[x];
    lastDrivenPinState ← drivenPinState;
    currentPinState ← ALL[x];
    lastPinState ← currentPinState;
    testEnabled ← FALSE;
    testDataBit ← FALSE;
    lastTestDataBit ← FALSE;
    secondToLastTestDataBit ← FALSE;
    testWord ← 0;
    lastTestWord ← 0;
    newTestWord ← 0;
    xA ← zero;
    xB ← zero;
    xL ← zero;
    yA ← zero;
    yB ← zero;
    yL ← zero;
    phiLong ← one;
    phiShort ← zero;
    anyGood ← zero;
    jump ← zero};

  SetPin: PUBLIC PROCEDURE [pin: Mouse.Pin, value: Mouse.Value] = {
    IF value # drivenPinState[pin] THEN {
      lastDrivenPinState ← drivenPinState;
      drivenPinState[pin] ← value;
      PinChanged[pin, lastDrivenPinState[pin], value]}};

  SetPinMap: PUBLIC PROCEDURE [pinMap: Mouse.PinMap] = {};

  PinChanged: PROCEDURE [pin: Mouse.Pin, oldValue, newValue: Mouse.Value] = {
    SELECT pin FROM
      RedA => Debounce[RedA, oldValue, newValue, RedB];
      RedB => Debounce[RedB, oldValue, newValue, RedA];
      YellowA => Debounce[YellowA, oldValue, newValue, YellowB];
      YellowB => Debounce[YellowB, oldValue, newValue, YellowA];
      BlueA => Debounce[BlueA, oldValue, newValue, BlueB];
      BlueB => Debounce[BlueB, oldValue, newValue, BlueA];
      TestEnable => TestEnableC[oldValue, newValue];
      Gnd => GndC[oldValue, newValue];
      YA => Driver[YA];
      YB => Driver[YB];
      XA => Driver[XA];
      XB => Driver[XB];
      TestData =>
        IF testEnabled THEN TestDataC[oldValue, newValue] ELSE Driver[PhiLong];
      TestClock =>
        IF testEnabled THEN TestClockC[oldValue, newValue] ELSE Driver[PhiShort];
      GateTest => GateTestC[oldValue, newValue];
      Vdd => VddC[oldValue, newValue];
      AnyGood => Driver[AnyGood];
      Jump => Driver[Jump];
      ENDCASE};

  testEnabled: BOOLEAN ← FALSE;
  testDataBit: BOOLEAN ← FALSE;
  lastTestDataBit: BOOLEAN ← FALSE;
  secondToLastTestDataBit: BOOLEAN ← FALSE;
  testWord: WORD ← 0;
  lastTestWord: WORD ← 0;
  newTestWord: WORD ← 0;
  xA: Mouse.Value ← zero;
  xB: Mouse.Value ← zero;
  xL: Mouse.Value ← zero;
  yA: Mouse.Value ← zero;
  yB: Mouse.Value ← zero;
  yL: Mouse.Value ← zero;
  phiLong: Mouse.Value ← one;
  phiShort: Mouse.Value ← zero;
  anyGood: Mouse.Value ← zero;
  jump: Mouse.Value ← zero;

  Counter: TYPE = RECORD [A, B, L: POINTER TO Mouse.Value];
  xCounter: Counter = [@xA, @xB, @xL];
  yCounter: Counter = [@yA, @yB, @yL];
  Direction: TYPE = {Down, Stay, Up};
  Distance: TYPE = {Stay, Half, Full};
  pinSource: ARRAY Mouse.Pin OF POINTER TO Mouse.Value = [
    NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, yCounter.A, yCounter.B, xCounter.A,
    xCounter.B, @phiLong, @phiShort, NIL, NIL, @anyGood, @jump];

  VNOT: PROCEDURE [value: Value] RETURNS [valueInverse: Value] = {
    IF value # x THEN {value ← IF value = zero THEN one ELSE zero};
    RETURN[value]};

  State: TYPE = [0..7] ← 0;
  CountRecord: TYPE = RECORD [A, B, L: Mouse.Value];
  StateFromCounter: ARRAY Value OF ARRAY Value OF ARRAY Value OF State = [
    zero: [zero: [zero: 0, one: 7], one: [zero: 2, one: 1]],
    one: [zero: [zero: 6, one: 5], one: [zero: 4, one: 3]]];
  CounterFromState: ARRAY State OF CountRecord = [
    [zero, zero, zero], [zero, one, one], [zero, one, zero], [one, one, one], [
    one, one, zero], [one, zero, one], [one, zero, zero], [zero, zero, one]];

  Count: PROCEDURE [counter: Counter, dir: Direction, dist: Distance] = {
    OPEN InlineDefs;

    state: INTEGER;
    delta: INTEGER;
    cr: CountRecord;

    SELECT dist FROM
      Stay => RETURN;
      Half => delta ← 1;
      Full => delta ← 2;
      ENDCASE;

    SELECT dir FROM Down => delta ← (-delta); Stay => RETURN; Up => NULL ENDCASE;

    state ← StateFromCounter[counter.A↑][counter.B↑][counter.L↑];
    state ← (state + delta + 8) MOD 8;
    cr ← CounterFromState[state];
    counter.A↑ ← cr.A;
    counter.B↑ ← cr.B;
    counter.L↑ ← cr.L};

  Debounce: PROCEDURE [
    pin: Mouse.Pin, oldValue, newValue: Mouse.Value, otherPin: Mouse.Pin] = {
    IF newValue = x THEN {
      IF drivenPinState[otherPin] = x THEN {
        currentPinState[otherPin] ← VNOT[oldValue]};
      currentPinState[pin] ← VNOT[currentPinState[otherPin]]}
    ELSE {
      currentPinState[pin] ← newValue;
      IF drivenPinState[otherPin] = x THEN {
        currentPinState[otherPin] ← VNOT[currentPinState[pin]]}}};

  Driver: PROCEDURE [pin: Mouse.Pin] = {
    currentPinState[pin] ←
      IF drivenPinState[pin] = x THEN pinSource[pin]↑ ELSE drivenPinState[pin]};

  TestEnableC: PROCEDURE [oldValue, newValue: Mouse.Value] = {
    currentPinState[TestEnable] ← newValue;
    testEnabled ← newValue = one;
    IF NOT testEnabled THEN {
      currentPinState[TestData] ← pinSource[TestData]↑;
      currentPinState[TestClock] ← pinSource[TestClock]↑}};

  TestDataC: PROCEDURE [oldValue, newValue: Mouse.Value] = {};

  TestClockC: PROCEDURE [oldValue, newValue: Mouse.Value] = {
    IF (oldValue = zero AND newValue = one) THEN {
      lastTestDataBit ← NOT (drivenPinState[TestData] = one)};
    IF (oldValue = one AND newValue = zero) THEN {
    SELECT version FROM
      Rev3 => {testWord ← BITOR[BITSHIFT[testWord, 1], IF lastTestDataBit THEN 1 ELSE 0]};
      Rev4, Rev5 => {testWord ← BITOR[
          BITSHIFT[testWord, -1], IF testDataBit THEN 0 ELSE 100000B];
      testDataBit ← lastTestDataBit};
      Rev6, Rev7 => {testWord ← BITOR[
          BITSHIFT[testWord, -1], IF testDataBit THEN 0 ELSE 100000B];
      testDataBit ← secondToLastTestDataBit; secondToLastTestDataBit ← lastTestDataBit};
      ENDCASE;
}};

  MoveDirection: TYPE = {
    UpLeft, Left, DownLeft, Up, Stayed, Down, UpRight, Right, DownRight};
  ShiftCount: TYPE = [-15..15];
  Mask: ARRAY MoveDirection OF WORD = [
    3567B, 7777B, 7356B, 73567B, 177777B, 167356B, 73560B, 177760B, 167340B];
  Shift: ARRAY MoveDirection OF ShiftCount = [-5, -4, -3, -1, 0, 1, 3, 4, 5];
  Move: ARRAY MoveDirection OF BOOLEAN ← ALL[FALSE];

  MakeMove: PROCEDURE [md: MoveDirection, dist: Distance] = {
    SELECT md FROM
      UpLeft => {Count[yCounter, Down, dist]; Count[xCounter, Down, dist]};
      Left => {Count[xCounter, Down, dist]};
      DownLeft => {Count[yCounter, Up, dist]; Count[xCounter, Down, dist]};
      Up => {Count[yCounter, Down, dist]};
      Stayed => NULL;
      Down => {Count[yCounter, Up, dist]};
      UpRight => {Count[yCounter, Down, dist]; Count[xCounter, Up, dist]};
      Right => {Count[xCounter, Up, dist]};
      DownRight => {Count[yCounter, Up, dist]; Count[xCounter, Up, dist]}
      ENDCASE;
    currentPinState[Jump] ← zero;
    currentPinState[AnyGood] ← one;
    };

  ResolveMove: PROCEDURE [md1, md2: MoveDirection] = {
    IF md2 = Left THEN {md2 ← md1; md1 ← Left};
    IF md2 = Stayed THEN {md2 ← md1; md1 ← Stayed};
    SELECT md1 FROM
      Stayed => MakeMove[md2, Half];
      Left => {
        SELECT md2 FROM
          Up => MakeMove[UpLeft, Half];
          Down => MakeMove[DownLeft, Half]
          ENDCASE};
      Right => {
        SELECT md2 FROM
          Up => MakeMove[UpRight, Half];
          Down => MakeMove[DownRight, Half]
          ENDCASE}
      ENDCASE};

  Moved: PROCEDURE [last, new: WORD] = {
    OPEN InlineDefs;
    cMoves: CARDINAL ← 0;
    mdA, mdB: MoveDirection ← Stayed;

    FOR d: MoveDirection IN MoveDirection DO
      Move[d] ← BITAND[BITAND[last, BITSHIFT[new, Shift[d]]], Mask[d]] # 0;
      ENDLOOP;
    FOR d: MoveDirection IN MoveDirection DO
      IF Move[d] THEN {cMoves ← cMoves + 1; mdB ← mdA; mdA ← d}; ENDLOOP;
    currentPinState[Jump] ← one;
    currentPinState[AnyGood] ← zero;
    SELECT cMoves FROM
      0 => NULL;
      1 => MakeMove[mdA, Full];
      2 => ResolveMove[mdA, mdB]
      ENDCASE;
    Driver[XA];
    Driver[XB];
    Driver[YA];
    Driver[YB];
    Driver[Jump];
    Driver[AnyGood]};

  GateTestC: PROCEDURE [oldValue, newValue: Mouse.Value] = {
    IF (oldValue = zero AND newValue = one) THEN {newTestWord ← testWord; };
    IF (oldValue = one AND newValue = zero) THEN {
      Moved[lastTestWord, newTestWord]; lastTestWord ← newTestWord}};

  GndC: PROCEDURE [oldValue, newValue: Mouse.Value] = {
    currentPinState[Gnd] ← zero};

  VddC: PROCEDURE [oldValue, newValue: Mouse.Value] = {
    currentPinState[Gnd] ← one};

  Reset;

  }.

MPH   30-Sep-81 15:46:15
	created initially