rendered paste bodyFrom 321994eebe33a8f96b1c7a8b2007cd4bf9c1291f Mon Sep 17 00:00:00 2001From: Alex Smith <ais523@bham.ac.uk>Date: Mon, 28 Jun 2010 04:31:57 +0100Subject: [PATCH] Fix emulation bug with self-modifying codeThis patch adds an option FLUSHONMODIFY (defaulting to true for newly assembledrecordings) that causes the emulator to automatically invalidate caches of codethat modifies itself, even while running. This is necessary to get calls to__int86 under DJGPP to emulate correctly, among other things; note that thepipelining behaviour might not be exactly the same as on all models of x86chips, though.--- org/jpc/emulator/PC.java | 26 +++++++++++++++---- org/jpc/emulator/memory/LazyCodeBlockMemory.java | 17 ++++++++++--- org/jpc/emulator/memory/codeblock/CodeBlock.java | 10 ++++++- .../memory/codeblock/SpanningCodeBlock.java | 4 +++ .../codeblock/optimised/ProtectedModeUBlock.java | 14 +++++++++- .../memory/codeblock/optimised/RealModeUBlock.java | 20 ++++++++++++--- .../codeblock/optimised/Virtual8086ModeUBlock.java | 18 +++++++++++-- org/jpc/emulator/processor/Processor.java | 2 + org/jpc/emulator/processor/ProcessorException.java | 3 +- org/jpc/pluginsaux/PCConfigDialog.java | 3 ++ 10 files changed, 95 insertions(+), 22 deletions(-)diff --git a/org/jpc/emulator/PC.java b/org/jpc/emulator/PC.javaindex ac30b6d..202f753 100644--- a/org/jpc/emulator/PC.java+++ b/org/jpc/emulator/PC.java@@ -88,6 +88,7 @@ public class PC implements SRDumpable public DriveSet.BootType bootType; public boolean ioportDelayed; public boolean vgaHretrace;+ public boolean flushOnModify; public void dumpStatusPartial(StatusDumper output2) throws IOException {@@ -145,6 +146,8 @@ public class PC implements SRDumpable output.println("IOPORTDELAY"); if(vgaHretrace) output.println("VGAHRETRACE");+ if(flushOnModify)+ output.println("FLUSHONMODIFY"); } public void dumpStatus(StatusDumper output)@@ -190,6 +193,7 @@ public class PC implements SRDumpable output.dumpByte(DriveSet.BootType.toNumeric(bootType)); output.dumpBoolean(ioportDelayed); output.dumpBoolean(vgaHretrace);+ output.dumpBoolean(flushOnModify); } public PCHardwareInfo()@@ -233,12 +237,13 @@ public class PC implements SRDumpable bootType = DriveSet.BootType.fromNumeric(input.loadByte()); ioportDelayed = false; vgaHretrace = false;- if(input.objectEndsHere())- return;+ flushOnModify = false;+ if(input.objectEndsHere()) return; ioportDelayed = input.loadBoolean();- if(input.objectEndsHere())- return;+ if(input.objectEndsHere()) return; vgaHretrace = input.loadBoolean();+ if(input.objectEndsHere()) return;+ flushOnModify = input.loadBoolean(); } public void makeHWInfoSegment(UTFOutputLineStream output, DiskChanger changer) throws IOException@@ -286,6 +291,8 @@ public class PC implements SRDumpable output.encodeLine("IOPORTDELAY"); if(vgaHretrace) output.encodeLine("VGAHRETRACE");+ if(flushOnModify)+ output.encodeLine("FLUSHONMODIFY"); } public static int componentsForLine(String op)@@ -330,6 +337,8 @@ public class PC implements SRDumpable return 1; if("VGAHRETRACE".equals(op)) return 1;+ if("FLUSHONMODIFY".equals(op))+ return 1; return 0; } @@ -463,6 +472,8 @@ public class PC implements SRDumpable hw.ioportDelayed = true; } else if("VGAHRETRACE".equals(components[0])) { hw.vgaHretrace = true;+ } else if("FLUSHONMODIFY".equals(components[0])) {+ hw.flushOnModify = true; } components = nextParseLine(input); }@@ -580,7 +591,7 @@ public class PC implements SRDumpable */ public PC(DriveSet drives, int ramPages, int clockDivide, String sysBIOSImg, String vgaBIOSImg, long initTime, DiskImageSet images, Map<String, Set<String>> hwModules, String fpuClass,- boolean ioportDelayed, boolean vgaHretrace)+ boolean ioportDelayed, boolean vgaHretrace, boolean flushOnModify) throws IOException { parts = new LinkedHashSet<HardwareComponent>();@@ -611,6 +622,8 @@ public class PC implements SRDumpable processor = new Processor(vmClock, cpuClockDivider); parts.add(processor); manager = new CodeBlockManager();+ + processor.reloadCurrentBlockOnModification = flushOnModify; System.err.println("Informational: Creating FPU..."); try {@@ -986,7 +999,7 @@ public class PC implements SRDumpable DriveSet drives = new DriveSet(hw.bootType, hda, hdb, hdc, hdd); pc = new PC(drives, hw.memoryPages, hw.cpuDivider, biosID, vgaBIOSID, hw.initRTCTime, hw.images,- hw.hwModules, hw.fpuEmulator, hw.ioportDelayed, hw.vgaHretrace);+ hw.hwModules, hw.fpuEmulator, hw.ioportDelayed, hw.vgaHretrace, hw.flushOnModify); FloppyController fdc = (FloppyController)pc.getComponent(FloppyController.class); DiskImage img1 = pc.getDisks().lookupDisk(hw.initFDAIndex);@@ -1019,6 +1032,7 @@ public class PC implements SRDumpable hw2.fpuEmulator = hw.fpuEmulator; hw2.ioportDelayed = hw.ioportDelayed; hw2.vgaHretrace = hw.vgaHretrace;+ hw2.flushOnModify = hw.flushOnModify; return pc; } diff --git a/org/jpc/emulator/memory/LazyCodeBlockMemory.java b/org/jpc/emulator/memory/LazyCodeBlockMemory.javaindex a5ae654..3a03d29 100644--- a/org/jpc/emulator/memory/LazyCodeBlockMemory.java+++ b/org/jpc/emulator/memory/LazyCodeBlockMemory.java@@ -324,7 +324,6 @@ public class LazyCodeBlockMemory extends AbstractMemory { 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++)@@ -403,8 +402,10 @@ public class LazyCodeBlockMemory extends AbstractMemory { if(b == PLACEHOLDER) continue; - if(!b.handleMemoryRegionChange(start, end))+ if(!b.handleMemoryRegionChange(start, end)) { removeRealCodeBlockAt(i);+ b.invalidate();+ } } } @@ -421,8 +422,10 @@ public class LazyCodeBlockMemory extends AbstractMemory { if(b == PLACEHOLDER) continue; - if(!b.handleMemoryRegionChange(start, end))+ if(!b.handleMemoryRegionChange(start, end)) { removeProtectedCodeBlockAt(i);+ b.invalidate();+ } } } @@ -439,8 +442,10 @@ public class LazyCodeBlockMemory extends AbstractMemory { if(b == PLACEHOLDER) continue; - if(!b.handleMemoryRegionChange(start, end))+ if(!b.handleMemoryRegionChange(start, end)) { removeVirtual8086CodeBlockAt(i);+ b.invalidate();+ } } } }@@ -478,6 +483,10 @@ public class LazyCodeBlockMemory extends AbstractMemory { throw executeException; } + public void invalidate()+ {+ }+ public boolean handleMemoryRegionChange(int startAddress, int endAddress) { return false;diff --git a/org/jpc/emulator/memory/codeblock/CodeBlock.java b/org/jpc/emulator/memory/codeblock/CodeBlock.javaindex 2698527..cfed97e 100644--- a/org/jpc/emulator/memory/codeblock/CodeBlock.java+++ b/org/jpc/emulator/memory/codeblock/CodeBlock.java@@ -70,13 +70,19 @@ public interface CodeBlock public String getDisplayString(); /**- * Returns true if this block has been rendered invalid.+ * Returns true if this block can automatically adapt to its backing+ * memory region being changed. * <p> * If modification of memory within the given range causes this block to- * become invalid then returns <code>true</code>.+ * become invalid then returns <code>false</code>. * @param startAddress inclusive start address of memory region * @param endAddress exclusive end address of memory region * @return <code>true</code> if this block is invalid */ public boolean handleMemoryRegionChange(int startAddress, int endAddress);+ + /**+ * Informs this codeblock that its backing region has changed.+ */+ public void invalidate(); }\ No newline at end of filediff --git a/org/jpc/emulator/memory/codeblock/SpanningCodeBlock.java b/org/jpc/emulator/memory/codeblock/SpanningCodeBlock.javaindex 80ff4e8..6bfc554 100644--- a/org/jpc/emulator/memory/codeblock/SpanningCodeBlock.java+++ b/org/jpc/emulator/memory/codeblock/SpanningCodeBlock.java@@ -80,6 +80,10 @@ public abstract class SpanningCodeBlock implements CodeBlock return true; } + public void invalidate()+ {+ }+ public String getDisplayString() { if(lastBlock != null)diff --git a/org/jpc/emulator/memory/codeblock/optimised/ProtectedModeUBlock.java b/org/jpc/emulator/memory/codeblock/optimised/ProtectedModeUBlock.javaindex 8b128dd..5b1cc3e 100644--- a/org/jpc/emulator/memory/codeblock/optimised/ProtectedModeUBlock.java+++ b/org/jpc/emulator/memory/codeblock/optimised/ProtectedModeUBlock.java@@ -99,6 +99,10 @@ public class ProtectedModeUBlock implements ProtectedModeCodeBlock { return false; }+ public void invalidate()+ {+ invalidated = true;+ } public String getDisplayString() {@@ -131,6 +135,7 @@ public class ProtectedModeUBlock implements ProtectedModeCodeBlock return result; } + private boolean invalidated = false; public int execute(Processor cpu) { this.fpu = cpu.fpu;@@ -1257,6 +1262,10 @@ public class ProtectedModeUBlock implements ProtectedModeCodeBlock case INSTRUCTION_START: executeCount++; if(cpu.eflagsMachineHalt) throw ProcessorException.TRACESTOP;+ if(invalidated && cpu.reloadCurrentBlockOnModification) {+ invalidated = false;+ throw ProcessorException.SELFMODIFIED;+ } //HALT being aborted is special. if(!cpu.eflagsWaiting) cpu.instructionExecuted();@@ -1298,7 +1307,7 @@ public class ProtectedModeUBlock implements ProtectedModeCodeBlock if(e.getType() == ProcessorException.Type.TASK_SWITCH) e.printStackTrace(); - if(e.getType() != ProcessorException.Type.PAGE_FAULT && e.getType() != ProcessorException.Type.TRACESTOP && e.getType() != ProcessorException.Type.NO_FPU) {+ if(e.getType() != ProcessorException.Type.PAGE_FAULT && e.getType() != ProcessorException.Type.TRACESTOP && e.getType() != ProcessorException.Type.NO_FPU && e.getType() != ProcessorException.Type.SELFMODIFIED) { boolean isGPF = (e.getType() == ProcessorException.Type.GENERAL_PROTECTION); boolean isHalt = isGPF && haltComplained.containsKey(position -1); boolean isFirstHalt = isHalt && (haltComplained.get(position - 1) == 1);@@ -1313,7 +1322,8 @@ public class ProtectedModeUBlock implements ProtectedModeCodeBlock } } - if(e.getType() != ProcessorException.Type.TRACESTOP) //Swallow trace stops!+ if (e.getType() != ProcessorException.Type.SELFMODIFIED &&+ e.getType() != ProcessorException.Type.TRACESTOP) //Swallow trace stops! cpu.handleProtectedModeException(e); else { executeCount--;diff --git a/org/jpc/emulator/memory/codeblock/optimised/RealModeUBlock.java b/org/jpc/emulator/memory/codeblock/optimised/RealModeUBlock.javaindex b141a2a..4e6da7b 100644--- a/org/jpc/emulator/memory/codeblock/optimised/RealModeUBlock.java+++ b/org/jpc/emulator/memory/codeblock/optimised/RealModeUBlock.java@@ -105,6 +105,11 @@ public final class RealModeUBlock implements RealModeCodeBlock return false; } + public void invalidate()+ {+ invalidated = true;+ }+ public String toString() { return "Real Mode Interpreted Block: "+hashCode();@@ -134,6 +139,8 @@ public final class RealModeUBlock implements RealModeCodeBlock private boolean transferEipUpdated = false; private int transferPosition = 0; + private boolean invalidated = false;+ private int uCodeXferReg0 = 0, uCodeXferReg1 = 0, uCodeXferReg2 = 0; private boolean uCodeXferLoaded = false; @@ -994,8 +1001,8 @@ public final class RealModeUBlock implements RealModeCodeBlock public int execute(Processor cpu) {- this.fpu = cpu.fpu;- this.cpu = cpu;+ this.fpu = cpu.fpu;+ this.cpu = cpu; if (opcodeCounter != null) opcodeCounter.addBlock(getMicrocodes());@@ -1156,6 +1163,10 @@ public final class RealModeUBlock implements RealModeCodeBlock case INSTRUCTION_START: executeCount++; if(cpu.eflagsMachineHalt) throw ProcessorException.TRACESTOP;+ if(invalidated && cpu.reloadCurrentBlockOnModification) {+ invalidated = false;+ throw ProcessorException.SELFMODIFIED;+ } //Handle special case of continuing WAIT after abort. if(!cpu.eflagsWaiting) cpu.instructionExecuted();@@ -1206,10 +1217,11 @@ public final class RealModeUBlock implements RealModeCodeBlock } } - if(e.getType() != ProcessorException.Type.PAGE_FAULT && e.getType() != ProcessorException.Type.TRACESTOP && e.getType() != ProcessorException.Type.NO_FPU)+ if(e.getType() != ProcessorException.Type.PAGE_FAULT && e.getType() != ProcessorException.Type.TRACESTOP && e.getType() != ProcessorException.Type.NO_FPU && e.getType() != ProcessorException.Type.SELFMODIFIED) System.err.println("Emulated: processor exception at 0x" + Integer.toHexString(cpu.cs.translateAddressRead(cpu.eip)) + ":" + e);- if(e.getType() != ProcessorException.Type.TRACESTOP) //Swallow trace stops!+ if (e.getType() != ProcessorException.Type.SELFMODIFIED &&+ e.getType() != ProcessorException.Type.TRACESTOP) //Swallow trace stops! cpu.handleRealModeException(e); else { cpu.eflagsLastAborted = true;diff --git a/org/jpc/emulator/memory/codeblock/optimised/Virtual8086ModeUBlock.java b/org/jpc/emulator/memory/codeblock/optimised/Virtual8086ModeUBlock.javaindex ea90851..58fcbcd 100644--- a/org/jpc/emulator/memory/codeblock/optimised/Virtual8086ModeUBlock.java+++ b/org/jpc/emulator/memory/codeblock/optimised/Virtual8086ModeUBlock.java@@ -105,6 +105,10 @@ public class Virtual8086ModeUBlock implements Virtual8086ModeCodeBlock { return false; }+ public void invalidate()+ {+ invalidated = true;+ } public String toString() {@@ -135,6 +139,8 @@ public class Virtual8086ModeUBlock implements Virtual8086ModeCodeBlock private boolean transferEipUpdated = false; private int transferPosition = 0; + private boolean invalidated = false;+ private int uCodeXferReg0 = 0, uCodeXferReg1 = 0, uCodeXferReg2 = 0; private boolean uCodeXferLoaded = false; @@ -1140,7 +1146,12 @@ public class Virtual8086ModeUBlock implements Virtual8086ModeCodeBlock case SHR_O16_FLAGS: shr_flags((short)reg0, reg2, reg1); break; case JA_O8: ja_o8((byte)reg0); break; case JNA_O8: jna_o8((byte)reg0); break;- case INSTRUCTION_START: if(cpu.eflagsMachineHalt) throw ProcessorException.TRACESTOP;+ case INSTRUCTION_START:+ if(cpu.eflagsMachineHalt) throw ProcessorException.TRACESTOP;+ if(invalidated && cpu.reloadCurrentBlockOnModification) {+ invalidated = false;+ throw ProcessorException.SELFMODIFIED;+ } cpu.instructionExecuted(); executeCount++; break; @@ -1186,9 +1197,10 @@ public class Virtual8086ModeUBlock implements Virtual8086ModeCodeBlock cpu.eip += cumulativeX86Length[selfPosition]; break; }- if(e.getType() != ProcessorException.Type.TRACESTOP) //Swallow trace stops!+ if (e.getType() != ProcessorException.Type.SELFMODIFIED &&+ e.getType() != ProcessorException.Type.TRACESTOP) //Swallow trace stops! cpu.handleVirtual8086ModeException(e);- else+ else cpu.eflagsLastAborted = true; } diff --git a/org/jpc/emulator/processor/Processor.java b/org/jpc/emulator/processor/Processor.javaindex e52bc35..d2e7c61 100644--- a/org/jpc/emulator/processor/Processor.java+++ b/org/jpc/emulator/processor/Processor.java@@ -139,6 +139,8 @@ public class Processor implements HardwareComponent private boolean started = false; private Clock vmClock; + public boolean reloadCurrentBlockOnModification = false;+ public FpuState fpu; private long fpuUsedNotPresent; //Not saved.diff --git a/org/jpc/emulator/processor/ProcessorException.java b/org/jpc/emulator/processor/ProcessorException.javaindex 9c92819..9a30e4a 100644--- a/org/jpc/emulator/processor/ProcessorException.java+++ b/org/jpc/emulator/processor/ProcessorException.java@@ -52,6 +52,7 @@ public final class ProcessorException extends RuntimeException implements SRDump public static final ProcessorException ALIGNMENT_CHECK_0 = new ProcessorException(Type.ALIGNMENT_CHECK, 0, true); public static final ProcessorException FPU_NA_0 = new ProcessorException(Type.NO_FPU, true); public static final ProcessorException TRACESTOP = new ProcessorException(Type.TRACESTOP, true);+ public static final ProcessorException SELFMODIFIED = new ProcessorException(Type.SELFMODIFIED, true); public static enum Type {@@ -60,7 +61,7 @@ public final class ProcessorException extends RuntimeException implements SRDump FPU_SEGMENT_OVERRUN(0x09), TASK_SWITCH(0x0a), NOT_PRESENT(0x0b), STACK_SEGMENT(0x0c), GENERAL_PROTECTION(0x0d), PAGE_FAULT(0x0e), FLOATING_POINT(0x10), ALIGNMENT_CHECK(0x11), MACHINE_CHECK(0x12),- SIMD_FLOATING_POINT(0x13), TRACESTOP(0x14);+ SIMD_FLOATING_POINT(0x13), TRACESTOP(0x14), SELFMODIFIED(0x15); //Traps: BREAKPOINT, OVERFLOW diff --git a/org/jpc/pluginsaux/PCConfigDialog.java b/org/jpc/pluginsaux/PCConfigDialog.javaindex 46f6e92..413b66b 100644--- a/org/jpc/pluginsaux/PCConfigDialog.java+++ b/org/jpc/pluginsaux/PCConfigDialog.java@@ -166,6 +166,8 @@ public class PCConfigDialog implements ActionListener, WindowListener addOption("Modules", "MODULES", ""); addBoolean("Emulate I/O delay", "IODELAY"); addBoolean("Emulate VGA Hretrace", "VGAHRETRACE");+ addBoolean("Shorten pipeline for self-modifying code", "FLUSHONMODIFY");+ settings3.get("FLUSHONMODIFY").setSelected(true); JLabel label1 = new JLabel("Boot device"); bootDevice = new JComboBox(new String[]{"fda", "hda", "cdrom"});@@ -352,6 +354,7 @@ public class PCConfigDialog implements ActionListener, WindowListener hw.ioportDelayed = booleanValue("IODELAY"); hw.vgaHretrace = booleanValue("VGAHRETRACE");+ hw.flushOnModify = booleanValue("FLUSHONMODIFY"); } catch(Exception e) { errorDialog(e, "Problem with settings.", window, "Dismiss"); return false;-- 1.7.0.4