IMPLEMENTATION MODULE IndexItems;


FROM items IMPORT ITEM, MODE, REGISTER, Operator, UsesSlavedAddrReg,
                  InDataReg, Max8BitInt, BytesPerWord, immediate;
FROM PsStk IMPORT PseudStk, top, DoAnyPendingOp, pop,
                  DyadicOpPending, FreeAnyRegUsedBy,
                  DecTopOfPsStk, push, IncTop;
FROM RegSlv IMPORT FreeReg, UseBitSet, slave,
                   ResetUseBits, protect, Slave, DeleteFromSlave;
FROM GenProcs IMPORT GenMove, GenShiftImm, GenNeg,
                     GenMoveByte, GenAddImm, GenAndImm;
FROM AddrReg IMPORT GetAddrOnPsStkIntoUseableForm, ProtectAddress, 
                    UnprotectAddress, IncUseCount;
FROM Jumps IMPORT RegJustLoadedOnStack;
FROM MCMnemonics IMPORT LXB, LXW, SXB, SXW, SXD;


TYPE INDEXSIZE = (Byte, Word, Double);

CONST ForIndex = TRUE;
      LSByte = 3;


PROCEDURE IndexLoad (Instruction : CARDINAL);
    VAR size: INDEXSIZE;
BEGIN
    IF Instruction = LXB THEN size := Byte
    ELSIF Instruction = LXW THEN size := Word
    ELSE size := Double
    END;
    LoadIndexedItem (size)
END IndexLoad;


PROCEDURE LoadIndexedItem (size : INDEXSIZE);
    VAR Reg1, Reg2, PsStkTop, Address : ITEM;
    BEGIN
        DoAnyPendingOp ();
        IF immediate (PseudStk [top]) THEN
            LoadWithImmedIndex (size);
            RETURN;
        END (* if *);
        IF size = Double THEN
            FreeReg (Reg2, UseBitSet)
        END (* if *);
        GetAddrOnPsStkIntoUseableForm (top-1, ForIndex);
        Address := PseudStk [top-1];
        PsStkTop := PseudStk [top];
        slave (PsStkTop);
        IF InDataReg (PsStkTop) THEN
            Reg1 := PsStkTop
        ELSE 
            FreeReg (Reg1, NOT UseBitSet);
            GenMove (PsStkTop, Reg1)
        END (* if *);
        DecTopOfPsStk (2);
        IF size = Word THEN
            GenShiftImm (Shl, 2, Reg1)
        ELSIF size = Double THEN
            GenShiftImm (Shl, 3, Reg1)
        END (* if *);
        WITH Address DO
            Mode := AddrRegIndex; 
            Index := Reg1.Register
        END (* with *);
        IF size = Byte THEN
            Address.DispOrData := Address.DispOrData * BytesPerWord;
            (* For GenMoveByte and GenMoveByte only displacements are 
               converted to byte displacements before handing over to
               GenProcs
            *)
            GenMoveByte (Address, Reg1);
            GenAndImm (255, Reg1)
        ELSIF size = Word THEN
            GenMove (Address, Reg1);
            RegJustLoadedOnStack := TRUE;
        ELSE (* size = Double *)
            GenMove (Address, Reg2);
            push (Reg2);
            IF Address.DispOrData * BytesPerWord + BytesPerWord
                                                    <= Max8BitInt THEN
	        INC (Address.DispOrData);
	    ELSE 
                GenAddImm (BytesPerWord, Reg1)
            END (* if *);
            GenMove (Address, Reg1);
            ResetUseBits ();
        END (* if *);
        push (Reg1);
    END LoadIndexedItem;


PROCEDURE IndexStore (Instruction : CARDINAL);
    VAR size: INDEXSIZE;
BEGIN
    IF Instruction = SXB THEN size := Byte;
    ELSIF Instruction = SXW THEN size := Word;
    ELSE size := Double;
    END;
    StoreIndexedItem (size)
END IndexStore;


PROCEDURE StoreIndexedItem (size : INDEXSIZE);
   VAR point, count : CARDINAL;
       PsStkPoint, Reg, SlvItem, 
       PsStkItem, Address, Source : ITEM;
    BEGIN
        IF DyadicOpPending THEN
            point := top - 2
        ELSE 
            point := top - 1
        END (* if *);
        IF size = Double THEN 
            DEC (point)
        END (* if *);
        GetAddrOnPsStkIntoUseableForm (point-1, ForIndex);
        Address := PseudStk [point-1];
        ProtectAddress (Address);
        PsStkPoint := PseudStk [point];
        slave (PsStkPoint);
        IF InDataReg (PsStkPoint) THEN
            Reg := PsStkPoint;
            protect (Reg);
            DeleteFromSlave (Reg.Register);
        ELSE 
            FOR count := point + 1 TO top DO
                PsStkItem := PseudStk [count];
                protect (PsStkItem);
                Slave (PsStkItem, SlvItem);
                protect (SlvItem)
            END (* for *);
            FreeReg (Reg, UseBitSet);
            GenMove (PsStkPoint, Reg);
        END (* if *);
        FreeAnyRegUsedBy (point);
        IF size = Word THEN
            GenShiftImm (Shl, 2, Reg)
        ELSIF size = Double THEN
            GenShiftImm (Shl, 3, Reg)
        END (* if *);
        WITH Address DO
            Mode := AddrRegIndex; 
            Index := Reg.Register
        END (* with *);
        IF size = Byte THEN
            DoAnyPendingOp ();
            Source := PseudStk [top];
            WITH Source DO
                IF Mode = AddrRegDisp THEN
                    DispOrData := DispOrData * BytesPerWord + LSByte;
                END (* if *);
            END (* with *);
            Address.DispOrData := Address.DispOrData * BytesPerWord;
            (* For GenMoveByte and GenMoveByte only displacements are 
               converted to byte displacements before handing over to
               GenProcs
            *)
            GenMoveByte (Source, Address);
            DecTopOfPsStk (1);
        ELSIF size = Word THEN
            pop (Address)
        ELSE (* size = Double *)
            GenMove (PseudStk [point+1], Address);
	    IF Address.DispOrData*BytesPerWord + BytesPerWord <= Max8BitInt
                                                                        THEN
	        INC (Address.DispOrData)
	    ELSE 
                GenAddImm (BytesPerWord, Reg);
            END (* if *);
            pop (Address);
	    DecTopOfPsStk (1);
        END (* if *);
        DecTopOfPsStk (2);
        UnprotectAddress (Address);
        ResetUseBits ();
    END StoreIndexedItem;

PROCEDURE LoadWithImmedIndex (size : INDEXSIZE);
    VAR offset : INTEGER;
        Address, Reg : ITEM;
    BEGIN
        offset := PseudStk [top].DispOrData;
        DecTopOfPsStk (1);
        GetAddrOnPsStkIntoUseableForm (top, NOT ForIndex);
        Address := PseudStk [top];
        WITH Address DO
            Mode := AddrRegDisp;
            IF size = Byte THEN
                FreeReg (Reg, NOT UseBitSet);
                DecTopOfPsStk (1);
                DispOrData := (DispOrData*BytesPerWord) + offset; 
                (* real byte offset *);
                GenMoveByte (Address, Reg);
                GenAndImm (255, Reg);
                push (Reg);
            ELSIF size = Word THEN
                INC (DispOrData, offset);
                PseudStk [top] := Address;
            ELSE (* size = Double *)
                INC (DispOrData, offset * 2);
                PseudStk [top] := Address;
                INC (DispOrData);
                IncTop;
                PseudStk [top] := Address;
                IF UsesSlavedAddrReg (Address) THEN
                    IncUseCount (Register, top);
                END (* if *);
            END (* if *);
        END (* with *);
    END LoadWithImmedIndex;
 
END IndexItems.
