-- file: PeepholeZ.mesa
-- last edited by Sweet on 11-Aug-82 10:52:34
-- last edited by Satterthwaite on December 16, 1982 11:12 am

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [codeptr],
  CodeDefs: TYPE USING [Base, CCIndex, CCNull, CodeCCIndex, codeType],
  ComData: TYPE USING [textIndex, switches],
  ESCAlpha: TYPE,
  FOpCodes: TYPE,
  Log: TYPE USING [Error],
  Mopcodes: TYPE,
  OpCodeParams: TYPE USING [
    BYTE, ExternalProcBase, ExternalProcSlots,
    GlobalAddressSlots, GlobalBase, GlobalDoubleLoadSlots, GlobalLoadSlots,
    LocalAddressSlots, LocalBase, LocalDoubleLoadSlots, LocalDoublePutSlots,
    LocalDoubleStoreSlots, LocalLoadSlots, LocalProcBase, LocalProcSlots,
    LocalPutSlots, LocalStoreSlots, ReadLocalIndirect0Slots, ReadSlots,
    zEFCn, zGAn, zLAn, zLFCn, zLGDn, zLGn, zLLDn, zLLn,
    zPLDn, zPLn, zRLI0n, zRn, zSLDn, zSLn],
  OpTableDefs: TYPE USING [InstLength],
  P5: TYPE USING [NumberOfParams, P5Error, C0, C1, C1W, C2, LoadConstant],
  PeepholeDefs: TYPE USING [InitParameters, PackPair, PeepState],
  P5U: TYPE USING [DeleteCell];

PeepholeZ: PROGRAM
    IMPORTS CPtr: Code, MPtr: ComData, Log, OpTableDefs, P5, P5U, PeepholeDefs
    EXPORTS PeepholeDefs =
  BEGIN OPEN PeepholeDefs, OpCodeParams, CodeDefs;

  -- imported definitions

  BYTE: TYPE = OpCodeParams.BYTE;
  CodeCCIndex: TYPE = CodeDefs.CodeCCIndex;


  cb: CodeDefs.Base;		-- code base (local copy)

  PeepholeZNotify: PUBLIC Alloc.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    cb ← base[codeType];
    END;

  UnconvertedInstruction: SIGNAL [opcode: WORD] = CODE;

  PeepZ: PUBLIC PROC [start: CodeCCIndex] =
    BEGIN -- convert to real instructions (ie from qXXX to zXXX)
    OPEN ESCAlpha, Mopcodes, FOpCodes;
    next, ci: CCIndex;
    state: PeepState;
    recentSource: CARDINAL ← 0;
    next ← start;
    UNTIL (ci ← next) = CCNull DO
      next ← cb[ci].flink;
      WITH cc: cb[ci] SELECT FROM
	code =>
	 IF ~cc.realinst THEN
	  BEGIN OPEN state;
	  DC: PROC = INLINE {P5U.DeleteCell[c]};
	  InitParameters[@state, LOOPHOLE[ci], c];
	  SELECT cInst FROM
	    qACD => MakeReal[ zACD, c];
	    qADC => MakeReal[ zADC, c];
	    qADD => MakeReal[zADD, c];
	    qADDSB => MakeReal[zADDSB, c];
	    qAF => {Esc[aAF]; DC[]};
	    qAL0IB => MakeReal[ zAL0IB, c];
	    qAND => MakeReal[ zAND, c];
	    qBC => {Esc[aBC]; DC[]};
	    qBC => {Esc[aBC]; DC[]};
	    qBITBLT => {Esc[aBITBLT]; DC[]};
	    qBLTEL => {Esc[aBLEL]; DC[]};
	    qBLTECL => {Esc[aBLECL]; DC[]};
	    qBLT => MakeReal[ zBLT, c];
	    qBLTC => MakeReal[ zBLTC, c];
	    qBLTL => MakeReal[ zBLTL, c];
	    qBLTCL => MakeReal[ zBLTCL, c];
	    qBNDCK => MakeReal[ zBNDCK, c];
	    qCATCH => MakeReal[zCATCH, c];
	    qCATCHFSI => {
	      cc.inst ← zJ2; cc.isize ← 2; cc.realinst ← TRUE}; -- strange but true
	    qDADD => MakeReal[ zDADD, c];
	    qDB => 
	      IF cP[1] = 0 THEN {P5.C0[zD0]; DC[]}
	      ELSE MakeReal[ zDB, c];
	    qDBL => MakeReal[ zDBL, c];
	    qDBS => {EscL[bDBS, cP[1]]; DC[]};
	    qDCMP => MakeReal[ zDCMP, c];
	    qDDBL => MakeReal[ zDDBL, c];
	    qDDIV => {Esc[aSDDIV]; DC[]};
	    qDDUP => MakeReal[ zDDUP, c];
	    qDEC => MakeReal[ zDEC, c];
	    qDEXCH => MakeReal[ zDEXCH, c];
	    qDI => {Esc[aDI]; DC[]};
	    qDINC => MakeReal[ zDINC, c];
	    qDIS => MakeReal[ zDIS, c];
	    qDIS2 => MakeReal[ zDIS2, c];
	    qDMOD => {
	      Esc[aSDDIV]; P5.C0[zREC2]; P5.C0[zDEXCH]; P5.C0[zDIS2]; DC[]};
	    qDMUL => {Esc[aDMUL]; DC[]};
	    qDSHIFT => {Esc[aDSHIFT]; DC[]};
	    qDSK => {EscL[bDSK, cP[1]]; DC[]};
	    qDSUB => MakeReal[ zDSUB, c];
	    qDUDIV => {Esc[aUDDIV]; DC[]};
	    qDUMOD => {
	      Esc[aUDDIV]; P5.C0[zREC2]; P5.C0[zDEXCH]; P5.C0[zDIS2]; DC[]};
	    qDUP => MakeReal[ zDUP, c];
	    qEFC =>
		IF cP[1] IN ExternalProcSlots THEN
		  {P5.C0[zEFCn+cP[1]-ExternalProcBase]; DC[]}
		ELSE MakeReal[ zEFCB, c];
	    qEI => {Esc[aEI]; DC[]};
	    qEXCH => MakeReal[ zEXCH, c];
	    qEXDIS => MakeReal[ zEXDIS, c];
	    qFADD => {Esc[aFADD]; DC[]};
	    qFCOMP => {Esc[aFCOMP]; DC[]};
	    qFDIV => {Esc[aFDIV]; DC[]};
	    qFF => {Esc[aFF]; DC[]};
	    qFLOAT => {Esc[aFLOAT]; DC[]};
	    qFMUL => {Esc[aFMUL]; DC[]};
	    qFSUB => {Esc[aFSUB]; DC[]};
	    qGA => SELECT cP[1] FROM
	      IN GlobalAddressSlots => {P5.C0[zGAn+cP[1]-GlobalBase]; DC[]};
	      IN BYTE => MakeReal[ zGAB, c];
	      ENDCASE => {P5.C1W[zGAW, cP[1]]; DC[]};
	    qINC => MakeReal[ zINC, c];
	    qKFCB => IF MPtr.switches['y] THEN
	        {MPtr.textIndex ← recentSource; Log.Error[implicitCall]}
	      ELSE MakeReal[ zKFCB, c];
	    qLA => SELECT cP[1] FROM
	      IN LocalAddressSlots => {P5.C0[zLAn+cP[1]-LocalBase]; DC[]};
	      LocalBase+6 => {P5.C0[zLA6]; DC[]};
	      LocalBase+8 => {P5.C0[zLA8]; DC[]};
	      IN BYTE => MakeReal[ zLAB, c];
	      ENDCASE => {P5.C1W[zLAW, cP[1]]; DC[]};
	    qLCO => {
	      P5.C1W[zLIW, cP[1]];
	      WITH cb[CPtr.codeptr] SELECT FROM
	        code => lco ← TRUE;
		ENDCASE => ERROR; 
	      DC[]};
	    qLUDIV => {Esc[aLUDIV]; DC[]};
	    qLFC =>
		IF cP[1] IN LocalProcSlots THEN
		  {P5.C0[zLFCn+cP[1]-LocalProcBase]; DC[]}
		ELSE MakeReal[ zLFCB, c];
	    qLG => {MoveVar[global, load, single, cP[1]]; DC[]};
	    qLGD => {MoveVar[global, load, double, cP[1]]; DC[]};
	    qLI => {P5.LoadConstant[cP[1]]; DC[]};
	    qLID => 
	      IF cP[1] = 0 THEN {P5.C0[zLID0]; DC[]}
	      ELSE {P5.LoadConstant[cP[1]]; P5.C0[zLI0]; DC[]};
	    qLINT => {Esc[aLINT]; DC[]};
	    qLKB => MakeReal[ zLKB, c];
	    qLL => {MoveVar[local, load, single, cP[1]]; DC[]};
	    qLLD => {MoveVar[local, load, double, cP[1]]; DC[]};
	    qLLK => MakeReal[ zLLKB, c];
	    qLLOB => {EscL[bLLOB, cP[1]]; DC[]};
	    qLP => MakeReal[zLP, c];
	    qLST => {EscL[bLSK, cP[1]]; DC[]};
	    qLSTE => {EscL[bLSTE, cP[1]]; DC[]};
	    qLSTF => {EscL[bLSTF, cP[1]]; DC[]};
	    qME => {Esc[aME]; DC[]};
	    qMR => {Esc[aMR]; DC[]};
	    qMUL, qAMUL => MakeReal[ zMUL, c];
	    qMW => {Esc[aMW]; DC[]};
	    qMX => {Esc[aMX]; DC[]};
	    qNC => {Esc[aNC]; DC[]};
	    qNEG => MakeReal[ zNEG, c];
	    qNILCK => {Esc[aNILCK]; DC[]};
	    qNILCKL => {Esc[aNILCKL]; DC[]};
	    qIOR => MakeReal[ zIOR, c];
	    qPI => {Esc[aPI]; DC[]};
	    qPL => {MoveVar[local, put, single, cP[1]]; DC[]};
	    qPLD => {MoveVar[local, put, double, cP[1]]; DC[]};
	    qPO => {Esc[aPO]; DC[]};
	    qPS => {Move[sput, single, cP[1], 0]; DC[]};
	    qPSD => {Move[sput, double, cP[1], 0]; DC[]};
	    qPSDL => {Move[sputlong, double, cP[1], 0]; DC[]};
	    qPSF => {Move[sput, partial, cP[1], cP[2]]; DC[]};
	    qPSL => {Move[sputlong, single, cP[1], 0]; DC[]};
	    qPSLF => {Move[sputlong, partial, cP[1], cP[2]]; DC[]};
	    qR => {Move[read, single, cP[1], 0]; DC[]};
	    qRC => {EscL[bRC, cP[1]]; DC[]};
	    qRD => {Move[read, double, cP[1], 0]; DC[]};
	    qRDL => {Move[readlong, double, cP[1], 0]; DC[]};
	    qREC => MakeReal[ zREC, c];
	    qREC2 => MakeReal[ zREC2, c];
	    qREQ => {Esc[aREQ]; DC[]};
	    qRET => MakeReal[ zRET, c];
	    qRF => {Move[read, partial, cP[1], cP[2]]; DC[]};
	    qRCFS => {Esc[aRCFS]; DC[]};
	    qRLFS => MakeReal[zRLFS, c];
	    qRGI => MakeGPReal[zRGIP, c];
	    qRGIL => MakeGPReal[zRGILP, c];
	    qRKDI => MakeReal[zRKDIB, c];
	    qRKI => MakeReal[zRKIB, c];
	    qRL => {Move[readlong, single, cP[1], 0]; DC[]};
	    qRLDI => IF cP[1] = LocalBase AND cP[2] = 0 THEN {
	        P5.C0[zRLDI00]; DC[]}
	      ELSE MakeLPReal[zRLDIP, c];	   
	    qRLDIL => MakeLPReal[zRLDILP, c];
	    qRLF => {Move[readlong, partial, cP[1], cP[2]]; DC[]};
	    qRLI => 
	      IF cP[1] = LocalBase AND cP[2] IN ReadLocalIndirect0Slots THEN {
	        P5.C0[zRLI0n+cP[2]]; DC[]}
	      ELSE MakeLPReal[zRLIP, c];
	    qRLIF => MakeLPFReal[zRLIPF, c];
	    qRLIL => MakeLPReal[zRLILP, c];
	    qRLILF => MakeLPFReal[zRLILPF, c];
	    qRSTR => MakeReal[ zRS, c];
	    qRSTRL => MakeReal[ zRLS, c];
	    qSDIV => {Esc[aSDIV]; DC[]};
	    qSFC => MakeReal[ zSFC, c];
	    qSG => {MoveVar[global, store, single, cP[1]]; DC[]};
	    qSGD => {MoveVar[global, store, double, cP[1]]; DC[]};
	    qSHIFT => MakeReal[ zSHIFT, c];
	    qSHIFTSB => 
	      SELECT INTEGER[cP[1]] FROM
	        0 => DC[];
		1 => {P5.C0[zDBL]; DC[]};
	        IN [-15..15] => MakeReal[ zSHIFTSB, c];
	        ENDCASE => {P5.LoadConstant[cP[1]]; P5.C0[zSHIFT]; DC[]};
	    qSL => {MoveVar[local, store, single, cP[1]]; DC[]};
	    qSLD => {MoveVar[local, store, double, cP[1]]; DC[]};
	    qSUB => MakeReal[ zSUB, c];
	    qTRPL => MakeReal[ zTRPL, c];
	    qUDCMP => MakeReal[ zUDCMP, c];
	    qUDIV => {Esc[aUDIV]; DC[]};
	    qW => {Move[write, single, cP[1], 0]; DC[]};
	    qWD => {Move[write, double, cP[1], 0]; DC[]};
	    qWDL => {Move[writelong, double, cP[1], 0]; DC[]};
	    qWF => {Move[write, partial, cP[1], cP[2]]; DC[]};
	    qWLFS => MakeReal[zWLFS, c];
	    qWL => {Move[writelong, single, cP[1], 0]; DC[]};
	    qWLDIL => MakeLPReal[zWLDILP, c];
	    qWLF => {Move[writelong, partial, cP[1], cP[2]]; DC[]};
	    qWLI => MakeLPReal[zWLIP, c];
	    qWLIL => MakeLPReal[zWLILP, c];
	    qWS => {Move[swrite, single, cP[1], 0]; DC[]};
	    qWSD => {Move[swrite, double, cP[1], 0]; DC[]};
	    qWSDL => {Move[swritelong, double, cP[1], 0]; DC[]};
	    qWSF => {Move[swrite, partial, cP[1], cP[2]]; DC[]};
	    qWSL => {Move[swritelong, single, cP[1], 0]; DC[]};
	    qWSLF => {Move[swritelong, partial, cP[1], cP[2]]; DC[]};
	    qWSTR => MakeReal[ zWS, c];
	    qWSTRL => MakeReal[ zWLS, c];
	    qXOR => {Esc[aXOR]; DC[]};
	  -- Cedar extensions
	    qBLZL => {Esc[aBLZL]; DC[]};
	    qGCRT => MakeMiscReal[aCREFTYPE, c];
	    qPSCIDL => {MoveCounted[sputlong, TRUE, cP[1]]; DC[]};
	    qPSCDL => {MoveCounted[sputlong, FALSE, cP[1]]; DC[]};
	    qWCIDL => {MoveCounted[writelong, TRUE, cP[1]]; DC[]};
	    qWCDL => {MoveCounted[writelong, FALSE, cP[1]]; DC[]};
	    qWSCIDL => {MoveCounted[swritelong, TRUE, cP[1]]; DC[]};
	    qWSCDL => {MoveCounted[swritelong, FALSE, cP[1]]; DC[]};
	    ENDCASE => {SIGNAL UnconvertedInstruction[cInst]; DC[]};
	  END;
	other => WITH cc SELECT FROM
	  source => recentSource ← index;
	  ENDCASE => NULL;
	ENDCASE => NULL;
      ENDLOOP;
    END;
    
  Esc: PROC [alpha: BYTE] = INLINE {P5.C1[Mopcodes.zESC, alpha]};
  EscL: PROC [alpha, beta: BYTE] = INLINE {P5.C2[Mopcodes.zESCL, alpha, beta]};

  MakeReal: PROC [i: BYTE, c: CodeCCIndex] =
    BEGIN
    IF cb[c].realinst OR P5.NumberOfParams[cb[c].inst] # OpTableDefs.InstLength[i]-1
      THEN P5.P5Error[1025];
    cb[c].inst ← i;
    cb[c].realinst ← TRUE;
    END;

  MakeLPReal: PROC [i: BYTE, c: CodeCCIndex] =
    BEGIN
    IF cb[c].realinst OR P5.NumberOfParams[cb[c].inst] # OpTableDefs.InstLength[i]-1+1
      THEN P5.P5Error[1027];
    P5.C1[i, PackPair[cb[c].parameters[1]-LocalBase, cb[c].parameters[2]]];
    P5U.DeleteCell[c];
    END;

  MakeGPReal: PROC [i: BYTE, c: CodeCCIndex] =
    BEGIN
    IF cb[c].realinst OR P5.NumberOfParams[cb[c].inst] # OpTableDefs.InstLength[i]-1+1
      THEN P5.P5Error[1028];
    P5.C1[i, PackPair[cb[c].parameters[1]-GlobalBase, cb[c].parameters[2]]];
    P5U.DeleteCell[c];
    END;

  MakeLPFReal: PROC [i: BYTE, c: CodeCCIndex] =
    BEGIN
    IF cb[c].realinst OR P5.NumberOfParams[cb[c].inst] # OpTableDefs.InstLength[i]-1+1
      THEN P5.P5Error[1027];
    P5.C2[i, PackPair[cb[c].parameters[1]-LocalBase, cb[c].parameters[2]], cb[c].parameters[3]];
    P5U.DeleteCell[c];
    END;


  MakeGPFReal: PROC [i: BYTE, c: CodeCCIndex] =
    BEGIN
    IF cb[c].realinst OR P5.NumberOfParams[cb[c].inst] # OpTableDefs.InstLength[i]-1+1
      THEN P5.P5Error[1028];
    P5.C2[i, PackPair[cb[c].parameters[1]-GlobalBase, cb[c].parameters[2]], cb[c].parameters[3]];
    P5U.DeleteCell[c];
    END;


  cpeep9: PROC =
    BEGIN -- find 2-instruction sequences
    RETURN
    END;

  cpeep10: PROC =
    BEGIN -- find bit-testing jumps
    RETURN
    END;

  Mdirection: TYPE = {read, write, swrite, sput, readlong, writelong, swritelong, sputlong};
  Mtype: TYPE = {single, double, partial};
  MVdirection: TYPE = {load, store, put};
  MVtype: TYPE = {single, double};
  MVclass: TYPE = {global, local};

  MoveB: ARRAY MVclass OF ARRAY MVtype OF
    ARRAY MVdirection OF BYTE = [
      global: [
        single: [Mopcodes.zLGB, Mopcodes.zSGB, Mopcodes.zNOOP], 
        double: [Mopcodes.zLGDB, Mopcodes.zSGDB, Mopcodes.zNOOP]],
      local: [
        single: [Mopcodes.zLLB, Mopcodes.zSLB, Mopcodes.zPLB], 
        double: [Mopcodes.zLLDB, Mopcodes.zSLDB, Mopcodes.zPLDB]]];

  RWB: ARRAY MVdirection [load..store] OF ARRAY MVtype [single..double] OF BYTE = [
    [Mopcodes.zRB, Mopcodes.zRDB],
    [Mopcodes.zWB, Mopcodes.zWDB]];

  RW0: ARRAY MVdirection [load..store] OF ARRAY MVtype [single..double] OF BYTE = [
    [Mopcodes.zR0, Mopcodes.zRD0],
    [Mopcodes.zW0, Mopcodes.zNOOP]];

  MoveVar: PROC [c: MVclass, d: MVdirection, t: MVtype, offset: WORD] =
    BEGIN -- handles LG, SG, LL, SL, LGD, SGD, LLD, SLD, PL class instructions
    OPEN Mopcodes;
    SELECT c FROM
      local => SELECT t FROM
	single => SELECT d FROM
	  load => IF offset IN LocalLoadSlots THEN
	    BEGIN P5.C0[zLLn+offset-LocalBase]; RETURN END;
	  store => IF offset IN LocalStoreSlots THEN
	    BEGIN P5.C0[zSLn+offset-LocalBase]; RETURN END;
	  put => IF offset IN LocalPutSlots THEN
	    BEGIN P5.C0[zPLn+offset-LocalBase]; RETURN END;
	  ENDCASE;
	double => SELECT d FROM
	  load => SELECT offset FROM
	    IN LocalDoubleLoadSlots => {P5.C0[zLLDn+offset-LocalBase]; RETURN};
	    LocalBase+10 => {P5.C0[zLLD10]; RETURN};
	    ENDCASE;
	  store => SELECT offset FROM
	    IN LocalDoubleStoreSlots => {P5.C0[zSLDn+offset-LocalBase]; RETURN};
	    LocalBase+8 => {P5.C0[zSLD8]; RETURN};
	    ENDCASE;
	  put => IF offset IN LocalDoublePutSlots THEN
	    BEGIN P5.C0[zPLDn+offset-LocalBase]; RETURN END;
	  ENDCASE;
	ENDCASE;
      global => SELECT t FROM
	single => SELECT d FROM
	  load => IF offset IN GlobalLoadSlots THEN
	    BEGIN P5.C0[zLGn+offset-GlobalBase]; RETURN END;
	  ENDCASE;
	double => SELECT d FROM
	  load => SELECT offset FROM
	    IN GlobalDoubleLoadSlots => {P5.C0[zLGDn+offset-GlobalBase]; RETURN};
	    GlobalBase+2 => {P5.C0[zLGD2]; RETURN};
	    ENDCASE;
	  ENDCASE;
	ENDCASE;
      ENDCASE;
    IF offset NOT IN BYTE THEN
      BEGIN
      P5.C1W[IF c = global THEN zGAW ELSE zLAW, offset];
      IF d = store AND t = double THEN P5.C1[zWDB, 0] ELSE P5.C0[RW0[d][t]];
      RETURN
      END;
    P5.C1[MoveB[c][t][d], offset];
    END;

  Move: PROC [d: Mdirection, t: Mtype, offset, field: WORD] =
    BEGIN -- handles R, W, RF, WF, WS, WSF, PS, PSF class instructions
    OPEN Mopcodes;
    IF d = read AND t = single AND offset IN ReadSlots THEN
      BEGIN P5.C0[zRn+offset]; RETURN END;
    IF offset NOT IN BYTE THEN
      BEGIN
      P5.LoadConstant[offset];
      IF d >= readlong THEN
	BEGIN
	IF LOOPHOLE[offset, INTEGER]>0 THEN P5.C0[zADC]
	ELSE {
	  P5.LoadConstant[177777B];
	  P5.C0[zDADD]}
	END
      ELSE P5.C0[zADD];
      offset ← 0;
      END;
    IF offset = 0 THEN
      SELECT d FROM
	read => SELECT t FROM
	  single => P5.C0[zR0];
	  double => P5.C0[zRD0];
	  partial => P5.C1[zR0F, field];
	  ENDCASE; -- single tested above
	write => SELECT t FROM
	  single => P5.C0[zW0];
	  double => P5.C1[zWDB, offset];
	  partial => P5.C1[zW0F, field];
	  ENDCASE;
	readlong => SELECT t FROM
	  single => P5.C0[zRL0];
	  double => P5.C0[zRDL0];
	  partial => P5.C1[zRL0F, field];
	  ENDCASE;
	writelong => SELECT t FROM
	  single => P5.C1[zWLB, offset];
	  double => P5.C1[zWDLB, offset];
	  partial => P5.C1[zWL0F, field];
	  ENDCASE;
	sput => SELECT t FROM
	  single => P5.C1[zPSB, offset];
	  double => P5.C0[zPSD0];
	  partial => P5.C1[zPS0F, field];
	  ENDCASE;
	swrite => SELECT t FROM
	  single => {P5.C1[zPSB, offset]; P5.C0[zDIS]};
	  double => {P5.C0[zPSD0]; P5.C0[zDIS]};
	  partial => P5.C1[zWS0F, field];
	  ENDCASE;
	sputlong => SELECT t FROM
	  single => P5.C1[zPSLB, offset];
	  double => P5.C1[zPSDLB, offset];
	  partial => P5.C2[zPSLF, offset, field];
	  ENDCASE;
	swritelong => SELECT t FROM
	  single => {P5.C1[zPSLB, offset]; P5.C0[zDIS2]};
	  double => {P5.C1[zPSDLB, offset]; P5.C0[zDIS2]};
	  partial => {P5.C2[zPSLF, offset, field]; P5.C0[zDIS2]};
	  ENDCASE;
	ENDCASE
    ELSE
      SELECT d FROM
	read => SELECT t FROM
	  single => P5.C1[zRB, offset];
	  double => P5.C1[zRDB, offset];
	  partial => P5.C2[zRF, offset, field];
	  ENDCASE;
	write => SELECT t FROM
	  single => P5.C1[zWB, offset];
	  double => P5.C1[zWDB, offset];
	  partial => P5.C2[zWF, offset, field];
	  ENDCASE;
	sput => SELECT t FROM
	  single => P5.C1[zPSB, offset];
	  double => P5.C1[zPSDB, offset];
	  partial => P5.C2[zPSF, offset, field];
	  ENDCASE;
	swrite => SELECT t FROM
	  single => {P5.C1[zPSB, offset]; P5.C0[zDIS]};
	  double => {P5.C1[zPSDB, offset]; P5.C0[zDIS]};
	  partial => {P5.C2[zPSF, offset, field]; P5.C0[zDIS]};
	  ENDCASE;
	readlong => SELECT t FROM
	  single => P5.C1[zRLB, offset];
	  double => P5.C1[zRDLB, offset];
	  partial => P5.C2[zRLF, offset, field];
	  ENDCASE;
	writelong => SELECT t FROM
	  single => P5.C1[zWLB, offset];
	  double => P5.C1[zWDLB, offset];
	  partial => P5.C2[zWLF, offset, field];
	  ENDCASE;
	sputlong => SELECT t FROM
	  single => P5.C1[zPSLB, offset];
	  double => P5.C1[zPSDLB, offset];
	  partial => P5.C2[zPSLF, offset, field];
	  ENDCASE;
	swritelong => SELECT t FROM
	  single => {P5.C1[zPSLB, offset]; P5.C0[zDIS2]};
	  double => {P5.C1[zPSDLB, offset]; P5.C0[zDIS2]};
	  partial => {P5.C2[zPSLF, offset, field]; P5.C0[zDIS2]};
	  ENDCASE;
	ENDCASE;
    END;


 -- Cedar extensions
 
  MoveCounted: PROC [d: Mdirection, init: BOOL, offset: WORD] =
    BEGIN
    OPEN Mopcodes;
    SELECT d FROM
      sputlong, swritelong =>
        BEGIN
        countedPut: ARRAY BOOL OF BYTE = [FALSE: zPSCDLB, TRUE: zPSCIDLB];
        P5.C1[countedPut[init], offset];
        IF d = swritelong THEN P5.C0[zDIS2];
        END;
      writelong =>
        BEGIN
        countedWrite: ARRAY BOOL OF BYTE = [FALSE: zWCDLB, TRUE: zWCIDLB];
	IF offset NOT IN BYTE THEN
	  BEGIN
	  P5.LoadConstant[offset];
	  IF LOOPHOLE[offset, INTEGER] > 0 THEN P5.C0[zADC]
	  ELSE {P5.LoadConstant[177777b]; P5.C0[zDADD]};
	  offset ← 0;
	  END;
	P5.C1[countedWrite[init], offset];
	END;
      ENDCASE;
    END;

  MakeMiscReal: PROC [alpha: BYTE, c: CodeCCIndex] =
    BEGIN
    Esc[alpha]; P5U.DeleteCell[c];
    END;

  END.