package org.jpc.emulator.memory; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import org.jpc.emulator.execution.Executable; import org.jpc.emulator.execution.decoder.Instruction; import org.jpc.emulator.execution.codeblock.*; import org.jpc.emulator.processor.Processor; import org.jpc.j2se.Option; /** * <code>Memory</code> object with simple execute capabilities. Uses a * {@link org.jpc.emulator.execution.codeblock.CodeBlockManager} instance to generate * {@link org.jpc.emulator.execution.codeblock.CodeBlock} objects which are then * stored in mirror arrays of the memory structure. * @author Chris Dennis * @author Rhys Newman * @author Ian Preston */ public class LazyCodeBlockMemory extends AbstractMemory { public static final boolean LOG_DISAM_ADDRESSES = Option.log_disam_addresses.value(); private CodeBlockManager codeBlockManager; private static final BlankCodeBlock PLACEHOLDER = new BlankCodeBlock(); private RealModeCodeBlock[] realCodeBuffer; private ProtectedModeCodeBlock[] protectedCodeBuffer; private Virtual8086ModeCodeBlock[] virtual8086CodeBuffer; private static final int ALLOCATION_THRESHOLD = 10; private final int size; private byte[] buffer = null; private int nullReadCount = 0; private List<SpanningCodeBlock> spanning = new LinkedList(); /** * Constructs an instance <code>size</code> bytes long. * @param size */ public LazyCodeBlockMemory(int size, CodeBlockManager manager) { this.size = size; this.codeBlockManager = manager; } protected void constructCodeBlocksArray() { realCodeBuffer = new RealModeCodeBlock[(int) getSize()]; protectedCodeBuffer = new ProtectedModeCodeBlock[(int) getSize()]; virtual8086CodeBuffer = new Virtual8086ModeCodeBlock[(int) getSize()]; } private void constructRealCodeBlocksArray() { realCodeBuffer = new RealModeCodeBlock[(int) getSize()]; } private void constructVirtual8086CodeBlocksArray() { virtual8086CodeBuffer = new Virtual8086ModeCodeBlock[(int) getSize()]; } private void constructProtectedCodeBlocksArray() { protectedCodeBuffer = new ProtectedModeCodeBlock[(int) getSize()]; } public int executeProtected(Processor cpu, int offset) { int x86Count = 0; int ip = cpu.getInstructionPointer(); offset = ip & AddressSpace.BLOCK_MASK; ProtectedModeCodeBlock block = getProtectedModeCodeBlockAt(offset); try { try { block.execute(cpu); x86Count += block.getX86Count(); } catch (NullPointerException e) { if (LOG_DISAM_ADDRESSES) System.out.printf("Disassembling PM from %08x with opsize=%s\n", cpu.getInstructionPointer(), cpu.cs.getDefaultSizeFlag()); try { block = codeBlockManager.getProtectedModeCodeBlockAt(this, offset, cpu.cs.getDefaultSizeFlag()); }catch (SpanningDecodeException s) { setProtectedCodeBlockAt(offset, (ProtectedModeCodeBlock) s.getBlock()); throw s; } setProtectedCodeBlockAt(offset, block); block.execute(cpu); x86Count += block.getX86Count(); } } catch (CodeBlockReplacementException e) { block = (ProtectedModeCodeBlock) e.getReplacement(); protectedCodeBuffer[offset] = block; block.execute(cpu); x86Count += block.getX86Count(); } return x86Count; } public int executeReal(Processor cpu, int offset) { int x86Count = 0; int ip = cpu.getInstructionPointer(); offset = ip & AddressSpace.BLOCK_MASK; RealModeCodeBlock block = getRealModeCodeBlockAt(offset); try { try { block.execute(cpu); x86Count += block.getX86Count(); } catch (NullPointerException e) { if (LOG_DISAM_ADDRESSES) System.out.printf("Disassembling RM from %08x\n", cpu.getInstructionPointer()); try { block = codeBlockManager.getRealModeCodeBlockAt(this, offset); } catch (SpanningDecodeException s) { setRealCodeBlockAt(offset, (RealModeCodeBlock) s.getBlock()); throw s; } setRealCodeBlockAt(offset, block); block.execute(cpu); x86Count += block.getX86Count(); } } catch (CodeBlockReplacementException e) { block = (RealModeCodeBlock) e.getReplacement(); realCodeBuffer[offset] = block; block.execute(cpu); x86Count += block.getX86Count(); } return x86Count; } public int executeVirtual8086(Processor cpu, int offset) { int x86Count = 0; int ip = cpu.getInstructionPointer(); offset = ip & AddressSpace.BLOCK_MASK; Virtual8086ModeCodeBlock block = getVirtual8086ModeCodeBlockAt(offset); try { try { block.execute(cpu); x86Count += block.getX86Count(); } catch (NullPointerException e) { if (LOG_DISAM_ADDRESSES) System.out.printf("Disassembling VM86 from %08x\n", cpu.getInstructionPointer()); try { block = codeBlockManager.getVirtual8086ModeCodeBlockAt(this, offset); } catch (SpanningDecodeException s) { setVirtual8086CodeBlockAt(offset, (Virtual8086ModeCodeBlock) s.getBlock()); throw s; } setVirtual8086CodeBlockAt(offset, block); block.execute(cpu); x86Count += block.getX86Count(); } } catch (CodeBlockReplacementException e) { block = (Virtual8086ModeCodeBlock) e.getReplacement(); virtual8086CodeBuffer[offset] = block; block.execute(cpu); x86Count += block.getX86Count(); } return x86Count; } private RealModeCodeBlock getRealModeCodeBlockAt(int offset) { try { return realCodeBuffer[offset]; } catch (NullPointerException e) { constructRealCodeBlocksArray(); return realCodeBuffer[offset]; } } private ProtectedModeCodeBlock getProtectedModeCodeBlockAt(int offset) { try { return protectedCodeBuffer[offset]; } catch (NullPointerException e) { constructProtectedCodeBlocksArray(); return protectedCodeBuffer[offset]; } } private Virtual8086ModeCodeBlock getVirtual8086ModeCodeBlockAt(int offset) { try { return virtual8086CodeBuffer[offset]; } catch (NullPointerException e) { constructVirtual8086CodeBlocksArray(); return virtual8086CodeBuffer[offset]; } } public void addSpanningBlock(SpanningCodeBlock b, int remainingLength) { spanning.add(b); // add markers up to the codeblock's length if (remainingLength > 4096) remainingLength = 4096; if (b instanceof RealModeCodeBlock) { if (realCodeBuffer == null) { allocateBuffer(); realCodeBuffer = new RealModeCodeBlock[(int) getSize()]; } for (int i=0; i < remainingLength; i++) if (realCodeBuffer[i] == null) realCodeBuffer[i] = PLACEHOLDER; } else if (b instanceof ProtectedModeCodeBlock) { if (protectedCodeBuffer == null) { allocateBuffer(); protectedCodeBuffer = new ProtectedModeCodeBlock[(int) getSize()]; } for (int i=0; i < remainingLength; i++) if (protectedCodeBuffer[i] == null) protectedCodeBuffer[i] = PLACEHOLDER; } else if (b instanceof Virtual8086ModeCodeBlock) { if (virtual8086CodeBuffer == null) { allocateBuffer(); virtual8086CodeBuffer = new Virtual8086ModeCodeBlock[(int) getSize()]; } for (int i=0; i < remainingLength; i++) if (virtual8086CodeBuffer[i] == null) virtual8086CodeBuffer[i] = PLACEHOLDER; } } private void removeVirtual8086CodeBlockAt(int offset) { Virtual8086ModeCodeBlock b = virtual8086CodeBuffer[offset]; if ((b == null) || (b == PLACEHOLDER)) { return; } virtual8086CodeBuffer[offset] = null; int len = b.getX86Length(); for (int i = offset + 1; (i < offset + len) && (i < virtual8086CodeBuffer.length); i++) { if (virtual8086CodeBuffer[i] == PLACEHOLDER) { virtual8086CodeBuffer[i] = null; } } for (int i = Math.min(offset + len, virtual8086CodeBuffer.length) - 1; i >= 0; i--) { if (virtual8086CodeBuffer[i] == null) { if (i < offset) { break; } else { continue; } } if (virtual8086CodeBuffer[i] == PLACEHOLDER) { continue; } Virtual8086ModeCodeBlock bb = virtual8086CodeBuffer[i]; len = bb.getX86Length(); for (int j = i + 1; (j < i + len) && (j < virtual8086CodeBuffer.length); j++) { if (virtual8086CodeBuffer[j] == null) { virtual8086CodeBuffer[j] = PLACEHOLDER; } } } } private void removeProtectedCodeBlockAt(int offset) { ProtectedModeCodeBlock b = protectedCodeBuffer[offset]; if ((b == null) || (b == PLACEHOLDER)) { return; } protectedCodeBuffer[offset] = null; int len = b.getX86Length(); for (int i = offset + 1; (i < offset + len) && (i < protectedCodeBuffer.length); i++) { if (protectedCodeBuffer[i] == PLACEHOLDER) { protectedCodeBuffer[i] = null; } } for (int i = Math.min(offset + len, protectedCodeBuffer.length) - 1; i >= 0; i--) { if (protectedCodeBuffer[i] == null) { if (i < offset) { break; } else { continue; } } if (protectedCodeBuffer[i] == PLACEHOLDER) { continue; } ProtectedModeCodeBlock bb = protectedCodeBuffer[i]; len = bb.getX86Length(); for (int j = i + 1; (j < i + len) && (j < protectedCodeBuffer.length); j++) { if (protectedCodeBuffer[j] == null) { protectedCodeBuffer[j] = PLACEHOLDER; } } } } private void removeRealCodeBlockAt(int offset) { RealModeCodeBlock b = realCodeBuffer[offset]; if ((b == null) || (b == PLACEHOLDER)) { return; } realCodeBuffer[offset] = null; int len = b.getX86Length(); for (int i = offset + 1; (i < offset + len) && (i < realCodeBuffer.length); i++) { if (realCodeBuffer[i] == PLACEHOLDER) { realCodeBuffer[i] = null; } } for (int i = Math.min(offset + len, realCodeBuffer.length) - 1; i >= 0; i--) { if (realCodeBuffer[i] == null) { if (i < offset) { break; } else { continue; } } if (realCodeBuffer[i] == PLACEHOLDER) { continue; } RealModeCodeBlock bb = realCodeBuffer[i]; len = bb.getX86Length(); for (int j = i + 1; (j < i + len) && (j < realCodeBuffer.length); j++) { if (realCodeBuffer[j] == null) { realCodeBuffer[j] = PLACEHOLDER; } } } } private void setVirtual8086CodeBlockAt(int offset, Virtual8086ModeCodeBlock block) { removeVirtual8086CodeBlockAt(offset); if (block == null) { return; } virtual8086CodeBuffer[offset] = block; int len = block.getX86Length(); for (int i = offset + 1; (i < offset + len) && (i < virtual8086CodeBuffer.length); i++) { if (virtual8086CodeBuffer[i] == null) { virtual8086CodeBuffer[i] = PLACEHOLDER; } } } private void setProtectedCodeBlockAt(int offset, ProtectedModeCodeBlock block) { removeProtectedCodeBlockAt(offset); if (block == null) { return; } protectedCodeBuffer[offset] = block; int len = block.getX86Length(); for (int i = offset + 1; (i < offset + len) && (i < protectedCodeBuffer.length); i++) { if (protectedCodeBuffer[i] == null) { protectedCodeBuffer[i] = PLACEHOLDER; } } } private void setRealCodeBlockAt(int offset, RealModeCodeBlock block) { removeRealCodeBlockAt(offset); if (block == null) { return; } realCodeBuffer[offset] = block; int len = block.getX86Length(); for (int i = offset + 1; (i < offset + len) && (i < realCodeBuffer.length); i++) { if (realCodeBuffer[i] == null) { realCodeBuffer[i] = PLACEHOLDER; } } } private void regionAltered(int start, int end) { if (realCodeBuffer != null) { for (int i = end; i >= 0; i--) { if (i == 0) { for (SpanningCodeBlock b : spanning) b.invalidate(); } RealModeCodeBlock b = realCodeBuffer[i]; if (b == null) { if (i < start) { break; } else { continue; } } if (b == PLACEHOLDER) { continue; } if (!b.handleMemoryRegionChange(start, end)) { removeRealCodeBlockAt(i); } } } if (protectedCodeBuffer != null) { for (int i = end; i >= 0; i--) { if (i == 0) { for (SpanningCodeBlock b : spanning) b.invalidate(); } ProtectedModeCodeBlock b = protectedCodeBuffer[i]; if (b == null) { if (i < start) { break; } else { continue; } } if (b == PLACEHOLDER) { continue; } if (!b.handleMemoryRegionChange(start, end)) { removeProtectedCodeBlockAt(i); } } } if (virtual8086CodeBuffer != null) { for (int i = end; i >= 0; i--) { if (i == 0) { for (SpanningCodeBlock b : spanning) b.invalidate(); } Virtual8086ModeCodeBlock b = virtual8086CodeBuffer[i]; if (b == null) { if (i < start) { break; } else { continue; } } if (b == PLACEHOLDER) { continue; } if (!b.handleMemoryRegionChange(start, end)) { removeVirtual8086CodeBlockAt(i); } } } } public void clear() { realCodeBuffer = null; protectedCodeBuffer = null; virtual8086CodeBuffer = null; buffer = null; } public String toString() { return "LazyCodeBlockMemory[" + getSize() + "]"; } private static class BlankCodeBlock implements RealModeCodeBlock, ProtectedModeCodeBlock, Virtual8086ModeCodeBlock { private static final RuntimeException executeException = new NullPointerException(); public int getX86Length() { return 0; } public int getX86Count() { return 0; } public Executable.Branch execute(Processor cpu) { throw executeException; } public boolean handleMemoryRegionChange(int startAddress, int endAddress) { return false; } public String getDisplayString() { return "\n\n<<Blank Block>>\n\n"; } public String toString() { return " -- Blank --\n"; } public Instruction getInstructions() { return null; } } public ProtectedModeCodeBlock getProtectedBlock(int offset, boolean size) { if (protectedCodeBuffer == null) { allocateBuffer(); protectedCodeBuffer = new ProtectedModeCodeBlock[(int) getSize()]; } ProtectedModeCodeBlock block = protectedCodeBuffer[offset]; if ((block != null) && (block != PLACEHOLDER)) { return block; } block = codeBlockManager.getProtectedModeCodeBlockAt(this, offset, size); setProtectedCodeBlockAt(offset, block); return block; } public Virtual8086ModeCodeBlock getVirtual8086Block(int offset) { if (virtual8086CodeBuffer == null) { allocateBuffer(); virtual8086CodeBuffer = new Virtual8086ModeCodeBlock[(int) getSize()]; } Virtual8086ModeCodeBlock block = virtual8086CodeBuffer[offset]; if ((block != null) && (block != PLACEHOLDER)) { return block; } block = codeBlockManager.getVirtual8086ModeCodeBlockAt(this, offset); setVirtual8086CodeBlockAt(offset, block); return block; } public RealModeCodeBlock getRealBlock(int offset) { if (realCodeBuffer == null) { allocateBuffer(); realCodeBuffer = new RealModeCodeBlock[(int) getSize()]; } RealModeCodeBlock block = realCodeBuffer[offset]; if ((block != null) && (block != PLACEHOLDER)) { return block; } block = codeBlockManager.getRealModeCodeBlockAt(this, offset); setRealCodeBlockAt(offset, block); return block; } //begin lazy memory methods private final void allocateBuffer() { if (buffer == null) { buffer = new byte[size]; } } public void copyContentsIntoArray(int address, byte[] buf, int off, int len) { try { System.arraycopy(buffer, address, buf, off, len); } catch (NullPointerException e) { if (++nullReadCount == ALLOCATION_THRESHOLD) { allocateBuffer(); System.arraycopy(buffer, address, buf, off, len); } else { Arrays.fill(buf, off, off + len, (byte) 0); } } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Array bounds exception reading from lazycodeblockmemory: address=0x" + Integer.toHexString(address) + ", off="+Integer.toHexString(off) + ", len="+len); } } public void loadInitialContents(int address, byte[] buf, int off, int len) { try { System.arraycopy(buf, off, buffer, address, len); } catch (NullPointerException e) { allocateBuffer(); System.arraycopy(buf, off, buffer, address, len); } } public void copyArrayIntoContents(int address, byte[] buf, int off, int len) { try { System.arraycopy(buf, off, buffer, address, len); } catch (NullPointerException e) { allocateBuffer(); System.arraycopy(buf, off, buffer, address, len); } regionAltered(address, address + len - 1); } public long getSize() { return size; } public boolean isAllocated() { return (buffer != null); } public byte getByte(int offset) { try { return buffer[offset]; } catch (NullPointerException e) { if (++nullReadCount == ALLOCATION_THRESHOLD) { allocateBuffer(); return buffer[offset]; } else { return 0; } } } public void setByte(int offset, byte data) { if (getByte(offset) == data) { return; } try { buffer[offset] = data; } catch (NullPointerException e) { allocateBuffer(); buffer[offset] = data; } regionAltered(offset, offset); } public short getWord(int offset) { try { int result = 0xFF & buffer[offset]; offset++; result |= buffer[offset] << 8; return (short) result; } catch (NullPointerException e) { if (++nullReadCount == ALLOCATION_THRESHOLD) { allocateBuffer(); int result = 0xFF & buffer[offset]; offset++; result |= buffer[offset] << 8; return (short) result; } else { return 0; } } } public int getDoubleWord(int offset) { try { int result = 0xFF & buffer[offset]; offset++; result |= (0xFF & buffer[offset]) << 8; offset++; result |= (0xFF & buffer[offset]) << 16; offset++; result |= (buffer[offset]) << 24; return result; } catch (NullPointerException e) { if (++nullReadCount == ALLOCATION_THRESHOLD) { allocateBuffer(); int result = 0xFF & buffer[offset]; offset++; result |= (0xFF & buffer[offset]) << 8; offset++; result |= (0xFF & buffer[offset]) << 16; offset++; result |= (buffer[offset]) << 24; return result; } else { return 0; } } } public void setWord(int offset, short data) { if (getWord(offset) == data) { return; } try { buffer[offset] = (byte) data; offset++; buffer[offset] = (byte) (data >> 8); } catch (NullPointerException e) { allocateBuffer(); buffer[offset] = (byte) data; offset++; buffer[offset] = (byte) (data >> 8); } regionAltered(offset, offset + 1); } public void setDoubleWord(int offset, int data) { if (getDoubleWord(offset) == data) { return; } try { buffer[offset] = (byte) data; offset++; data >>= 8; buffer[offset] = (byte) (data); offset++; data >>= 8; buffer[offset] = (byte) (data); offset++; data >>= 8; buffer[offset] = (byte) (data); } catch (NullPointerException e) { allocateBuffer(); buffer[offset] = (byte) data; offset++; data >>= 8; buffer[offset] = (byte) (data); offset++; data >>= 8; buffer[offset] = (byte) (data); offset++; data >>= 8; buffer[offset] = (byte) (data); } regionAltered(offset, offset + 3); } }