/**
* Sam Memory: 32-bit wide, implemented as an array of integers.
*
* Properties:
*
* - Size: MEMORYLIMIT locations (defined in Memory)
*
- Each location: 2 integers
*
* - Even address in array contain types
*
- Odd addresses in array contain data
*
* - Each type is a byte (8 bits) but stored as integer
*
- Each datum is 32-bits large (integer compatible)
*
- Overall array size: INTERNALLIMIT = 2 * MEMORYLIMIT
*/
public class SamMemory implements Memory {
/* Memory implementation */
protected final static int INTERNALLIMIT = 20000;
protected int[] memory = new int[INTERNALLIMIT];
protected Processor cpu;
/* Garbage collection constants */
final static int HZONELIMIT = (MEMORYLIMIT - STACKLIMIT) / 2; //size of 1 zone
final static int GTHRESHOLD = (int) ((1.0 / 10.0) * (double)HZONELIMIT); //garbage col. threshold
final static int HZBOUNDARY = (STACKLIMIT + HZONELIMIT); //boundary between zones
/* Heap Active Zone - 0 or 1 */
private byte activezone = 0;
public SamMemory(Sys sys)
{ cpu = sys.cpu(); }
/* Processor access */
protected final static byte SP = Processor.SP;
protected final static byte HP = Processor.HP;
/* Low-level management */
public void reset() {
for (int i = 0; i < INTERNALLIMIT; i++)
memory[i] = 0;
activezone = 0;
}
public int getMem(int pos) throws SystemException {
if (pos < 0 || pos > MEMORYLIMIT - 1)
throw new SystemException("getMem(): Invalid memory address: " + pos);
return memory[2 * pos + 1];
}
public byte getType(int pos) throws SystemException {
if (pos < 0 || pos > MEMORYLIMIT - 1)
throw new SystemException("getMem(): Invalid memory address: " + pos);
return (byte)memory[2 * pos];
}
public void setMem(int pos, byte type, int value) throws SystemException {
if (pos < 0 || pos > MEMORYLIMIT - 1)
throw new SystemException("setMem(): Invalid memory address: " + pos);
memory[2 * pos] = type;
memory[2 * pos + 1] = value;
}
public void setMem(int pos, int value) throws SystemException
{ setMem(pos, INT, value); }
/* Memory dump */
public int[] getStack()
{ return getMemoryRange(0, cpu.get(SP)); }
public int[] getHeap()
{ return getMemoryRange(STACKLIMIT+1, STACKLIMIT + activezone*HZONELIMIT + 1); }
public byte[] getStackTypes()
{ return getTypesRange(0, cpu.get(SP)); }
public byte[] getHeapTypes()
{ return getTypesRange(STACKLIMIT+1, STACKLIMIT + activezone*HZONELIMIT + 1); }
public int[] getMemoryRange (int low, int high) {
final int size = high - low;
int [] mem = new int[size];
for (int a=low; a < high; a++)
mem[a] = memory[a * 2 + 1];
return mem;
}
public byte[] getTypesRange (int low, int high) {
final int size = high - low;
byte [] types = new byte[size];
for (int a=low; a < high; a++)
types[a] = (byte)memory[a * 2];
return types;
}
/* Stack zone */
public int pop() throws SystemException
{ return getMem(cpu.dec(SP)); }
public void push(byte type, int value) throws SystemException {
setMem(cpu.get(SP), type, value);
cpu.inc(SP);
}
public void push(int value) throws SystemException
{ push(INT, value); }
/* Heap Zones */
/* The heap grows upwards in each zone - implementation decision. */
/* Heap Object - definition: one int (type INT) for size + the rest for data */
/* Forwarding Pointer - definition: an object signature (location 0) of type MA */
public void malloc(int size) throws SystemException {
/* Preliminary calculations */
int freespace = HZBOUNDARY + HZONELIMIT * activezone - cpu.get(HP) - 1;
size++; //account for signature
/* Trigger for garbage collector */
if (freespace < Math.max(GTHRESHOLD, size)) {
gcollect();
freespace = HZBOUNDARY + HZONELIMIT * activezone - cpu.get(HP) - 1; //recalculate
}
/* Out of memory check for HP */
if (freespace < size)
throw new SystemException("Insufficient heap memory to allocate object of size " + size);
setMem(cpu.get(HP), INT, size); //object size signature
push(MA, cpu.get(HP)); //push the HP
cpu.set(HP, cpu.get(HP) + size); //update the HP
}
/* Stop - And - Copy tracing garbage collection with breadth-first traversal */
private void gcollect() throws SystemException {
/* Preliminary computations */
final int start = STACKLIMIT + HZONELIMIT * activezone;
final int end = STACKLIMIT + HZONELIMIT * (activezone + 1);
final byte newzone = (byte) (activezone == 0 ? 1 : 0);
final int offset = (newzone - activezone) * HZONELIMIT;
int move = start + offset; //Last location of moved objects on heap (live zone)
int scan = start + offset; //Last location scanned for references on heap (live zone)
/* Move over live objects found on the stack and leave forwarding pointers.. */
final int limit = cpu.get(SP);
for (int a = 0; a < limit; a++)
if (getType(a) == MA && getMem(a) >= start && getMem(a) < end)
move = update(a, move);
/* Now keep doing the same in the heap zones until everything's moved */
while (scan < move) {
if (getType(scan) == MA && getMem(scan) >= start && getMem(scan) < end)
move = update(scan, move);
scan++;
}
/* Switch the active zone and update the HP */
activezone = newzone;
cpu.set(HP, scan);
}
/* Garbage collection helper function */
/* Takes a pointer and updates it, or moves the object that's there to the live zone
Returns the new position in the live zone. */
private int update(int ptr, int pos) throws SystemException {
int size;
final int location = getMem(ptr); //see where pointer points
if (getType(location) == MA) //and if it points to a pointer
setMem(ptr, MA, getMem(location)); //reroute to proper place
else { //if it doesn't point to a pointer
size = getMem(location); //get size of object
for (int i = location; i < location + size; i++) //move object
setMem(pos++, getType(i), getMem(i));
setMem(location, MA, pos - size); //leave forwarding pointer
setMem(ptr, MA, pos - size); //update the original pointer
}
return pos; //return new position
}
}