From 401fdf45fc49bac795fd69b7981433720046dea7 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Mon, 28 Jun 2010 04:31:57 +0100 Subject: [PATCH] Fix emulation bug with self-modifying code This patch adds an option FLUSHONMODIFY (defaulting to true for newly assembled recordings) that causes the emulator to automatically invalidate caches of code that modifies itself, even while running. This is necessary to get calls to __int86 under DJGPP to emulate correctly, among other things; note that the pipelining behaviour might not be exactly the same as on all models of x86 chips, though. --- Changelog.utf8 | 1 + 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 | 16 +++++++++++-- org/jpc/emulator/processor/Processor.java | 2 ++ org/jpc/emulator/processor/ProcessorException.java | 3 ++- org/jpc/pluginsaux/PCConfigDialog.java | 3 +++ 11 files changed, 95 insertions(+), 21 deletions(-) diff --git a/Changelog.utf8 b/Changelog.utf8 index 0074b3d..dd357c1 100644 --- a/Changelog.utf8 +++ b/Changelog.utf8 @@ -1,6 +1,7 @@ Changes since JPC-RR Release 10.12: =================================== - Use new LDT when loading registers in jump to TSS. +- Add optional fast instruction reloading on selfmodify. Changes from JPC-RR Release 10.11 to JPC-RR Release 10.12: ========================================================== diff --git a/org/jpc/emulator/PC.java b/org/jpc/emulator/PC.java index ac30b6d..ce07a97 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> hwModules, String fpuClass, - boolean ioportDelayed, boolean vgaHretrace) + boolean ioportDelayed, boolean vgaHretrace, boolean flushOnModify) throws IOException { parts = new LinkedHashSet(); @@ -612,6 +623,8 @@ public class PC implements SRDumpable parts.add(processor); manager = new CodeBlockManager(); + processor.reloadCurrentBlockOnModification = flushOnModify; + System.err.println("Informational: Creating FPU..."); try { if(fpuClass != null) { @@ -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.java index 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.java index 2698527..40d52ed 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. *

* If modification of memory within the given range causes this block to - * become invalid then returns true. + * become invalid then returns false. * @param startAddress inclusive start address of memory region * @param endAddress exclusive end address of memory region * @return true 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 file diff --git a/org/jpc/emulator/memory/codeblock/SpanningCodeBlock.java b/org/jpc/emulator/memory/codeblock/SpanningCodeBlock.java index 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.java index 85f5411..8bfb2ab 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.java index b141a2a..d7c68f1 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.java index efc3f5b..7c80b4e 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,7 +1197,8 @@ 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 cpu.eflagsLastAborted = true; diff --git a/org/jpc/emulator/processor/Processor.java b/org/jpc/emulator/processor/Processor.java index 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.java index 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.java index 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; -- 2.11.4.GIT