/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Copyright (C) 2012-2013 Ian Preston
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
End of licence header
*/
package tools;
import javax.swing.*;
import java.io.*;
import java.util.*;
import java.net.*;
import java.lang.reflect.*;
public class CompareToBochs
{
public static String newJar = "JPCApplication.jar";
public static final boolean compareFlags = true;
public static final boolean compareStack = false;
public static final boolean compareCMOS = false;
public static final boolean compareIntState = false;
public static final boolean followBochsInts = true;
public static final boolean comparePIT = false;
public static final String[] perf = {"-fda", "floppy.img", "-boot", "fda", "-hda", "dir:dos"};
public static final String[] doom = {"-fda", "floppy.img", "-boot", "fda", "-hda", "../../tmpdrives/doom10m.img"};
public static final String[] doom2 = {"-fda", "floppy.img", "-boot", "fda", "-hda", "../../tmpdrives/doom2.img"};
public static final String[] prince1 = {"-fda", "floppy.img", "-boot", "fda", "-hda", "../../tmpdrives/prince1.img"};
public static final String[] pascalcrash = {"-fda", "floppy.img", "-boot", "fda", "-hda", "tests/CRASHES.img"};
public static final String[] sodium_fat12 = {"-fda", "sodium_fat12.img", "-boot", "fda", "-ips", "150000000"};
public static final String[] sodium_fat16 = {"-hda", "caching:sodium_fat16.img", "-boot", "hda", "-ips", "1193181"};
public static final String[] worms = {"-fda", "floppy.img", "-boot", "fda", "-hda", "worms.img"};
public static final String[] war2 = {"-fda", "floppy.img", "-boot", "fda", "-hda", "war2demo.img"};
public static final String[] linux = {"-hda", "../../tmpdrives/linux.img", "-boot", "hda"};
public static final String[] linux02 = {"-hda", "../../tmpdrives/linux-0.2.img", "-boot", "hda"};
public static final String[] bsd = {"-hda", "../../tmpdrives/netbsd.img", "-boot", "hda"};
public static final String[] mosa = {"-hda", "mosa-project.img", "-boot", "hda"};
public static final String[] dsl = {"-hda", "dsl-desktop-demo2.img", "-boot", "hda"};
public static final String[] alpinelinux = {"-cdrom", "/home/ian/jpc/tmpdrives/alpinelinux.iso", "-boot", "cdrom"};
public static final String[] isolinux = {"-cdrom", "isolinux.iso", "-boot", "cdrom"};
public static final String[] dslCD = {"-cdrom", "../../tmpdrives/dsl-n-01RC4.iso", "-boot", "cdrom"};
public static final String[] hurd = {"-cdrom", "hurd.iso", "-boot", "cdrom"};
public static final String[] ubuntu = {"-cdrom", "ubuntu-8.10-desktop-i386.iso", "-boot", "cdrom", "-ram", "64"};
public static final String[] bartpe = {"-cdrom", "bartpe.iso", "-boot", "cdrom", "-ram", "64"};
public static final String[] tty = {"-cdrom", "ttylinux-i386-5.3.iso", "-boot", "cdrom"};
public static final String[] win311 = {"-hda", "win311.img", "-boot", "hda", "-ips", "1193181", "-ram", "2"};
public static final String[] win311_crash = {"-hda", "caching:64MBDOS5WFW311.img", "-boot", "hda", "-ips", "1193181", "-ram", "2", "-cpulevel", "5"};
public static final String[] win95 = {"-hda", "win95harddisk.img", "-boot", "hda", "-ips", "1193181"};
public static final String[] dosPascal = {"-hda", "freedos.img", "-boot", "hda", "-fda", "floppy.img", "-ips", "1193181"};
public static final String[] sf2turbo = {"-hda", "sf2turbo.img", "-boot", "hda", "-fda", "floppy.img", "-ips", "1193181"};
public static final String[] wolf3d = {"-hda", "WOLF3D.img", "-boot", "hda", "-fda", "floppy.img", "-ips", "1193181"};
public static final Map<String, String[]> possibleArgs = new HashMap();
static {
possibleArgs.put("linux-0.2", linux02);
possibleArgs.put("alpinelinux", alpinelinux);
possibleArgs.put("linux", linux);
possibleArgs.put("doom", doom);
possibleArgs.put("prince", prince1);
possibleArgs.put("win311", win311);
possibleArgs.put("win311_crash", win311_crash);
possibleArgs.put("win95", win95);
possibleArgs.put("dosPascal", dosPascal);
possibleArgs.put("sf2turbo", sf2turbo);
possibleArgs.put("sodium_fat12", sodium_fat12);
possibleArgs.put("sodium_fat16", sodium_fat16);
possibleArgs.put("ubuntu", ubuntu);
possibleArgs.put("bartpe", bartpe);
possibleArgs.put("wolf3d", wolf3d);
}
public static final int flagMask = ~0x000; // OF IF
public static final int flagAdoptMask = ~0x10; // OF AF
public static long startTime = System.nanoTime();
public static int nextMilestone = 0;
public static List<Integer> ignoredIOPorts = new ArrayList<Integer>();
static{
ignoredIOPorts.add(0x60); // keyboard
ignoredIOPorts.add(0x64); // keyboard
ignoredIOPorts.add(0x61); // PC speaker
}
public final static Map<String, Integer> flagIgnores = new HashMap();
static
{
flagIgnores.put("test", ~0x10); // not defined in spec
flagIgnores.put("and", ~0x10); // not defined in spec
flagIgnores.put("sar", ~0x10); // not defined in spec for non zero shifts
flagIgnores.put("xor", ~0x10); // not defined in spec
flagIgnores.put("or", ~0x10); // not defined in spec
flagIgnores.put("mul", ~0xd4); // not defined in spec
flagIgnores.put("imul", ~0xd4); // not defined in spec
flagIgnores.put("popfw", ~0x895);
flagIgnores.put("bsf", 0x40); // not defined in spec
flagIgnores.put("bsr", 0x40); // not defined in spec
flagIgnores.put("mul", ~0x80); // not defined in spec
flagIgnores.put("ror", ~0x800); // not defined in spec for shifts != 1
flagIgnores.put("shl", ~0x810); // not defined in spec for shifts != 1
//flagIgnores.put("bt", ~0x894);
// not sure
//flagIgnores.put("bts", ~0x1);
// errors with the old JPC
//flagIgnores.put("btr", ~0x1);
flagIgnores.put("rcl", ~0x800);
flagIgnores.put("shr", ~0x810);
//flagIgnores.put("shrd", ~0x810);
flagIgnores.put("shld", ~0x810);
flagIgnores.put("lss", ~0x200);
//flagIgnores.put("iret", ~0x10); // who cares about before the interrupt
//flagIgnores.put("iretw", ~0x810); // who cares about before the interrupt
flagIgnores.put("iretd", ~0x10000); // RF flag
}
public static TreeSet<KeyBoardEvent> keyPresses = new TreeSet<KeyBoardEvent>();
public static TreeSet<KeyBoardEvent> keyReleases = new TreeSet<KeyBoardEvent>();
public static TreeSet<MouseEvent> mouseInput = new TreeSet<MouseEvent>();
public static void main(String[] args) throws Exception
{
boolean mem = false;
if ((args.length >0) && args[0].equals("-mem"))
{
mem = true;
String[] temp = new String[args.length];
System.arraycopy(args, 1, temp, 0, temp.length-1);
temp[temp.length-1] = "-track-writes"; // Force JPC Physical memory to track dirty pages
args = temp;
}
URL[] urls1 = new URL[]{new File(newJar).toURL()};
ClassLoader cl1 = new URLClassLoader(urls1, Comparison.class.getClassLoader());
Class opts = cl1.loadClass("org.jpc.j2se.Option");
Method parse = opts.getMethod("parse", String[].class);
String[] pcargs = possibleArgs.get(args[0]);
String[] tmp = new String[args.length + pcargs.length];
tmp[0] = "-bochs";
System.arraycopy(args, 1, tmp, 1, args.length-1);
System.arraycopy(pcargs, 0, tmp, args.length, pcargs.length);
parse.invoke(opts, (Object)tmp);
Calendar start1 = Calendar.getInstance();
start1.setTimeInMillis(1370072774000L); // hard coded into bochssrc
Class c1 = cl1.loadClass("org.jpc.emulator.PC");
Constructor ctor = c1.getConstructor(String[].class, Calendar.class);
Object newpc = ctor.newInstance((Object)pcargs, start1);
EmulatorControl bochs = new Bochs(args[0]+".cfg");
Method m1 = c1.getMethod("hello");
m1.invoke(newpc);
Method ints1 = c1.getMethod("checkInterrupts", Integer.class, Boolean.class);
Method state1 = c1.getMethod("getState");
Method cmos1 = c1.getMethod("getCMOS");
Method getPIT1 = c1.getMethod("getPit");
Method setState1 = c1.getMethod("setState", int[].class);
Method execute1 = c1.getMethod("executeBlock");
Method spurious1 = c1.getMethod("triggerSpuriousInterrupt");
Method spuriousMaster1 = c1.getMethod("triggerSpuriousMasterInterrupt");
Method dirty1 = c1.getMethod("getDirtyPages", Set.class);
Method save1 = c1.getMethod("getPhysicalPage", Integer.class, byte[].class, Boolean.class);
Method load1 = c1.getMethod("loadPage", Integer.class, byte[].class, Boolean.class);
Method pitExpiry1 = c1.getMethod("setNextPITExpiry", Long.class);
Method pitIrq1 = c1.getMethod("getPITIrqLevel");
Method startClock1 = c1.getMethod("start");
startClock1.invoke(newpc);
Method break1 = c1.getMethod("eipBreak", Integer.class);
Method instructionInfo = c1.getMethod("getInstructionInfo", Integer.class);
Method keysDown1 = c1.getMethod("sendKeysDown", String.class);
Method keysUp1 = c1.getMethod("sendKeysUp", String.class);
Method minput1 = c1.getMethod("sendMouse", Integer.class, Integer.class, Integer.class, Integer.class);
// setup screen from new JPC
JPanel screen = (JPanel)c1.getMethod("getNewMonitor").invoke(newpc);
JFrame frame = new JFrame();
frame.getContentPane().add("Center", new JScrollPane(screen));
frame.validate();
frame.setVisible(true);
frame.setBounds(100, 100, 760, 500);
if (mem)
System.out.println("Comparing memory"+(compareStack?", stack":"")+" and registers..");
else if (compareStack)
System.out.println("Comparing registers and stack..");
else
System.out.println("Comparing registers only..");
String line;
byte[] sdata1 = new byte[4096];
byte[] sdata2 = new byte[4096];
int[] fast, bochsState;
boolean previousLss = false;
int previousStackAddr = 0;
int lastPIT0Count = 0;
int lastPITIrq = 0;
while (true)
{
Exception e1 = null;
String nextBochs;
try {
nextBochs = bochs.executeInstruction();
} catch (Exception e)
{
printHistory();
e.printStackTrace();
System.out.println("Exception during Bochs execution... look above");
throw e;
}
bochsState = bochs.getState();
boolean bochsEnteredPitInt = false;
boolean bochsEnteredNonPitInt = false;
if (followBochsInts)
{
// if Bochs has gone into an interrupt from the PIT force JPC to trigger one at the same time
if (nextBochs.contains("vector=0x8") || nextBochs.contains("vector=0x50")) // relies on patch to exception.cc
// Win 3.11 appears to use 0x50 for the PIT int in protected mode
// bochs does an extra instruction after an int, which can end up more than byte further into the int handler
{
boolean irq = (Boolean)pitIrq1.invoke(newpc);
if (irq) // need to lower first
{
pitExpiry1.invoke(newpc, new Long(bochsState[16]-10));
// triggger PIT irq lower
ints1.invoke(newpc, new Integer(0), new Boolean(false));
}
pitExpiry1.invoke(newpc, new Long(bochsState[16]-10));
bochsEnteredPitInt = true;
} else if (currentInstruction().contains("hlt"))
{
// assume PIT caused timeout
boolean irq = (Boolean)pitIrq1.invoke(newpc);
if (irq) // need to lower first
{
pitExpiry1.invoke(newpc, new Long(bochsState[16]-10));
// triggger PIT irq lower
ints1.invoke(newpc, new Integer(0), new Boolean(false));
}
pitExpiry1.invoke(newpc, new Long(bochsState[16]+10));
}
else if (nextBochs.contains("spurious interrupt")) // modify pic.cc
{
spurious1.invoke(newpc);
bochsEnteredNonPitInt = true;
}
else if (nextBochs.contains("spurious master interrupt")) // modify pic.cc
{
spuriousMaster1.invoke(newpc);
bochsEnteredNonPitInt = true;
}
else if (nextBochs.contains("vector="))
{
bochsEnteredNonPitInt = true;
}
}
try {
// increment time and check ints first to mirror bochs' behaviour of checking for an interrupt prior to execution
boolean jpcInInt = (Boolean)ints1.invoke(newpc, new Integer(1), new Boolean(bochsEnteredPitInt));
if (bochsEnteredNonPitInt && !jpcInInt && !currentInstruction().contains("int_Ib"))
System.out.println("Missed a spurious interrupt?");
if ((!jpcInInt && bochsEnteredPitInt) && !previousInstruction().contains("hlt"))
{
System.out.println("Failed to force JPC to enter PIT interrupt!");
boolean irq = (Boolean)pitIrq1.invoke(newpc);
if (irq) // need to lower first
{
pitExpiry1.invoke(newpc, new Long(bochsState[16]-10));
// triggger PIT irq lower
ints1.invoke(newpc, new Integer(0), new Boolean(false));
}
pitExpiry1.invoke(newpc, new Long(bochsState[16]-10));
jpcInInt = (Boolean)ints1.invoke(newpc, new Integer(1), new Boolean(bochsEnteredPitInt));
}
int blockLength = (Integer)execute1.invoke(newpc);
if (blockLength > 1)
{
int index = (historyIndex-1+history.length) % history.length;
if ((blockLength == 2) && (history[index] != null) && (((String)history[index][2]).contains("sti")))
{
// don't trigger any interrupts until the next instruction, but still update clock
fast = (int[])state1.invoke(newpc);
fast[16]++;
setState1.invoke(newpc, (int[])fast);
}
else
for (int i=0; i < blockLength-1; i++)
{
fast = (int[])state1.invoke(newpc);
fast[16]++;
setState1.invoke(newpc, (int[])fast);
//ints1.invoke(newpc, new Integer(1));
}
}
} catch (Exception e)
{
printHistory();
e.printStackTrace();
System.out.println("Exception during new JPC execution... look above");
e1 = e;
}
fast = (int[])state1.invoke(newpc);
try {
line = instructionInfo.invoke(newpc, new Integer(1)) + " == " + nextBochs; // instructions per block
} catch (Exception e)
{
if (!e.toString().contains("PAGE_FAULT"))
{
e.printStackTrace();
System.out.printf("Error getting instruction info.. at cs:eip = %08x\n", fast[8]+(fast[10]<<4));
line = "Instruction decode error";
printHistory();
//continueExecution("after Invalid decode at cs:eip");
}
line = "PAGE_FAULT getting instruction" + " == " + nextBochs;
}
if (e1 != null)
throw e1;
// account for repeated strings
boolean missedIntDuringRep = false;
if ((fast[8] != bochsState[8]) && (currentInstruction().contains("rep")))
{
String bnext = "";
while (fast[8] != bochsState[8])
{
bnext = bochs.executeInstruction();
if (bnext.contains("vector="))
{
System.out.println("***** Missed interrupt during rep X: "+bnext);
missedIntDuringRep = true;
}
bochsState = bochs.getState();
}
nextBochs += bnext;
// now update ticks
fast[16] = bochsState[16];
setState1.invoke(newpc, (int[])fast);
}
// adjust ticks elapsed during halts
if (fast[16] != bochsState[16])
{
if (currentInstruction().contains("hlt"))
{
fast[16] = bochsState[16];
setState1.invoke(newpc, (int[])fast);
}
else if (previousInstruction().contains("hlt"))
{
fast[16] = bochsState[16] +1;
setState1.invoke(newpc, (int[])fast);
}
}
// sometimes JPC does 2 instructions at once for atomicity relative to interrupts
if (fast[16] == bochsState[16] +1)
{
try {
line += bochs.executeInstruction();
bochsState = bochs.getState();
} catch (Exception e)
{
printHistory();
e.printStackTrace();
System.out.println("Exception during Bochs execution... look above");
throw e;
}
}
if (fast[16] == bochsState[16] + 2) // probably a mov ss, X then sti then Y which must not have interrupts checked between them
{
for (int i=0; i < 2; i++)
try {
line += bochs.executeInstruction();
bochsState = bochs.getState();
} catch (Exception e)
{
printHistory();
e.printStackTrace();
System.out.println("Exception during Bochs execution... look above");
throw e;
}
}
// after an exception bochs does 1 more instruction, like with interrupts, need to catch JPC up
if ((fast[8] != bochsState[8]) && nextBochs.contains("PMvector=0xd"))
{
execute1.invoke(newpc);
// don't update ticks, as bochs doesn't
fast = (int[])state1.invoke(newpc);
}
// if (!keyPresses.isEmpty())
// {
// KeyBoardEvent k = keyPresses.first();
// if (fast[16] > k.time)
// {
// keysDown1.invoke(newpc, k.text);
// bochs.keysDown(k.text);
// System.out.println("Sent key presses: "+k.text);
// keyPresses.remove(k);
// }
// }
// if (!keyReleases.isEmpty())
// {
// KeyBoardEvent k = keyReleases.first();
// if (fast[16] > k.time)
// {
// keysUp1.invoke(newpc, k.text);
// bochs.keysUp(k.text);
// System.out.println("Sent key releases: "+k.text);
// keyReleases.remove(k);
// }
// }
// if (!mouseInput.isEmpty())
// {
// MouseEvent k = mouseInput.first();
// if (fast[16] > k.time)
// {
// minput1.invoke(newpc, k.dx, k.dy, k.dz, k.buttons);
// bochs.sendMouse(k.dx, k.dy, k.dz, k.buttons);
// mouseInput.remove(k);
// }
// }
if (fast[16] > nextMilestone)
{
System.out.printf("Reached %x ticks! Averaging %d IPS\n", fast[16], fast[16]*(long)1000000000/(System.nanoTime()-startTime));
//nextMilestone += 0x10000;
nextMilestone = (fast[16] & 0xffff0000) + 0x10000;
}
if (history[historyIndex] == null)
history[historyIndex] = new Object[3];
history[historyIndex][0] = fast;
history[historyIndex][1] = bochsState;
history[historyIndex][2] = line;
historyIndex = (historyIndex+1)%history.length;
Set<Integer> diff = new HashSet<Integer>();
int[] prevBochs = previousBochsState();
int[] prevFast = previousState();
if (!sameStates(fast, bochsState, prevFast, prevBochs, compareFlags, diff))
{
if ((diff.size() == 1) && diff.contains(9))
{
// adopt flags
String prevInstr = previousInstruction().split(" ")[0];
String secondPrevInstr = previousInstruction(2).split(" ")[0];
if (prevInstr.startsWith("rep"))
prevInstr += ((String)(history[(historyIndex-2)&(history.length-1)][2])).split(" ")[1];
if (prevInstr.startsWith("cli") || secondPrevInstr.startsWith("cli"))
{
if ((fast[9]^bochsState[9]) == 0x200)
{
fast[9] = bochsState[9];
setState1.invoke(newpc, (int[])fast);
}
}
if (previousLss)
{
previousLss = false;
fast[9] = bochsState[9];
setState1.invoke(newpc, (int[])fast);
}
else if (flagIgnores.containsKey(prevInstr))
{
int mask = flagIgnores.get(prevInstr);
if ((fast[9]& mask) == (bochsState[9] & mask))
{
fast[9] = bochsState[9];
setState1.invoke(newpc, (int[])fast);
}
} else if ((fast[9]& flagAdoptMask) == (bochsState[9] & flagAdoptMask))
{
fast[9] = bochsState[9];
setState1.invoke(newpc, (int[])fast);
}
if (prevInstr.equals("lss"))
previousLss = true;
}
else if ((diff.size() == 1) && diff.contains(0))
{
if ((fast[0]^bochsState[0]) == 0x10)
{
//often eax is loaded with flags which contain arbitrary AF values, ignore these
fast[0] = bochsState[0];
setState1.invoke(newpc, (int[])fast);
}
else if (previousInstruction().startsWith("in ")) // IO port read
{
// print before and after state, then adopt reg
if (!ignoredIOPorts.contains(fast[2])) // port is in dx
{
System.out.printf("IO read discrepancy: port=%08x eax=%08x#%08x from %s\n", fast[2], fast[0], bochsState[0], previousInstruction());
//printLast2();
}
fast[0] = bochsState[0];
setState1.invoke(newpc, (int[])fast);
}
else if (previousInstruction().contains("ds:0x46c")) // a read from a CMOS counter to eax
{
fast[0] = bochsState[0];
setState1.invoke(newpc, (int[])fast);
}
}
else if ((fast[0] >= 0xa8000) && (fast[0] < 0xb0000) && previousInstruction(1).startsWith("movzx edx,BYTE PTR [eax]")) // see smm_init in rombios32.c
{
fast[2] = bochsState[2];
setState1.invoke(newpc, (int[])fast);
}
else if ((previousState()[2] == 0xb2) && previousInstruction(2).startsWith("out dx")) // entered SMM
{
String bochsDisam = nextBochs;
String prev = null;
while (fast[8] != bochsState[8])
{
prev = bochsDisam;
bochsDisam = bochs.executeInstruction();
bochsState = bochs.getState();
}
fast[16] = bochsState[16];
setState1.invoke(newpc, (int[])fast);
System.out.println("Remote returned from SMM with: "+prev + " and ticks: "+fast[16]);
}
diff.clear();
if (!sameStates(fast, bochsState, prevFast, prevBochs, compareFlags, diff))
{
printHistory();
for (int diffIndex: diff)
System.out.printf("Difference: %s %08x - %08x : ^ %08x\n", EmulatorControl.names[diffIndex], fast[diffIndex], bochsState[diffIndex], fast[diffIndex]^bochsState[diffIndex]);
setState1.invoke(newpc, (int[])bochsState);
if (diff.contains(8))
{
System.out.println("going to STOP!!");
}
if (diff.contains(8))
{
//printPITs((int[]) getPIT1.invoke(newpc), bochs.getPit());
throw new IllegalStateException("Different EIP!");
}
}
}
// compare other devices
if (compareCMOS)
{
// CMOS
byte[] jpcCMOS = (byte[]) cmos1.invoke(newpc);
byte[] bochsCMOS = bochs.getCMOS();
boolean same = true;
for (int i=0; i < 128; i++)
{
if (jpcCMOS[i] != bochsCMOS[i])
{
same = false;
break;
}
}
if (!same)
{
printLast2();
System.out.println("Different CMOS");
System.out.println("JPC CMOS :: Bochs CMOS");
for (int i=0; i < 8; i++)
{
System.out.printf("%02x = ", i*16);
for (int j=0; j < 16; j++)
System.out.printf("%02x ", jpcCMOS[i*16+j]);
System.out.printf("= ");
for (int j=0; j < 16; j++)
System.out.printf("%02x ", bochsCMOS[i*16+j]);
System.out.println();
}
throw new IllegalStateException("Different CMOS");
}
}
if (comparePIT)
{
lastPIT0Count = comparePITS(lastPIT0Count, bochs, newpc, getPIT1);
int irq = getPITIrq(bochs);
if (irq != lastPITIrq)
System.out.printf("Bochs PIT irq changed to %d cycles=%x\n", irq, bochsState[16]);
lastPITIrq = irq;
}
if (compareStack)
{
boolean pm = (fast[36] & 1) != 0;
int ssBase = fast[35];
int esp = fast[6] + ssBase;
int espPageIndex;
if (pm)
espPageIndex = esp;
else
espPageIndex = esp >>> 12;
if (previousStackAddr != espPageIndex)
{
// we've changed stacks, compare the old one as well
compareStacks(previousStackAddr, previousStackAddr, save1, newpc, sdata1, bochs, sdata2, pm, load1);
previousStackAddr = espPageIndex;
}
compareStacks(espPageIndex, esp, save1, newpc, sdata1, bochs, sdata2, pm, load1);
}
if (bochsState[16] == 0xA23792)
System.out.printf("");
if (!mem)
continue;
Set<Integer> dirtyPages = new HashSet<Integer>();
dirty1.invoke(newpc, dirtyPages);
// relevant to win311 RM
if (missedIntDuringRep) // add stack to catch changes due to interrupts during the rep X
dirtyPages.add((bochsState[32] + bochsState[4]) >> 12);
// relevant to win311 PM
if ((bochsState[36] & 1) != 0)
{
dirtyPages.add(0x1fa);
//// dirtyPages.add(0x1ad);
// dirtyPages.add(0x1fb);
// dirtyPages.add(0x1f8);
}
for (int i : dirtyPages)
{
Integer l1 = (Integer)save1.invoke(newpc, new Integer(i<<12), sdata1);
Integer l2 = bochs.getPhysicalPage(new Integer(i << 12), sdata2);
if ((l2 > 0) && (l1 > 0))
{
List<Integer> addrs = new ArrayList<Integer>();
if (!samePage(i, sdata1, sdata2, addrs))
{
if (missedIntDuringRep && (i == (bochsState[32] + bochsState[4]) >> 12))
{
load1.invoke(newpc, new Integer(i<<12), sdata2, false);
System.out.println("Adopted stack page after rep instruction (assuming difference came from pit int during rep in bochs): " + nextBochs);
}
System.out.printf("Comparing");
for (int j: dirtyPages)
System.out.printf(" %08x", j << 12);
System.out.println(" after " + previousInstruction());
if ((addrs.size() != 1) || !addrs.contains(0x46c))
{
printHistory();
System.out.println("Error here... look above for instruction causing diff");
printPage(sdata1, sdata2, i << 12);
if (continueExecution("memory"))
load1.invoke(newpc, new Integer(i<<12), sdata2, false);
else
System.exit(0);
}
else
load1.invoke(newpc, new Integer(i<<12), sdata2, false);
}
}
}
}
}
private static boolean inInt(int[] prev, int[] curr)
{
int prevESP = prev[4];
int ESP = curr[4];
int prevEIP = prev[8];
int EIP = curr[8];
// if (Math.abs(ESP-prevESP) < 4) STI, POP would give =4
// return false;
if ((EIP == 0xfea5) || (EIP == 0xfea6) || (EIP == 0xfea8))
return true;
return false;
}
private static int getPITIrq(EmulatorControl bochs) throws Exception
{
return bochs.getPit()[2];
}
private static int comparePITS(int lastPIT0Count, EmulatorControl bochs, Object newpc, Method pit1) throws Exception
{
int[] jpcPIT = (int[]) pit1.invoke(newpc);
int[] bochsPIT = bochs.getPit();
if (bochsPIT[0] != lastPIT0Count)
{
lastPIT0Count = bochsPIT[0];
boolean same = true;
for (int i=0; i < jpcPIT.length; i++)
{
if ((jpcPIT[i] != bochsPIT[i]) && (i % 4 != 2) && (i % 4 != 3)) // ignore next_change_time slot, and outPin
{
same = false;
break;
}
}
if (!same)
{
printLast2();
System.out.println("Different PIT");
printPITs(jpcPIT, bochsPIT);
}
}
return lastPIT0Count;
}
private static void printPITs(int[] jpcPIT, int[] bochsPIT)
{
System.out.println("JPC Pit :: Bochs Pit");
for (int i=0; i < 3; i++)
{
for (int j=0; j < 4; j++)
{
System.out.printf("%08x ", jpcPIT[i*4+j]);
System.out.printf("= ");
System.out.printf("%08x ", bochsPIT[i*4+j]);
System.out.println();
}
}
}
private static void compareStacks(int espPageIndex, int esp, Method save1, Object newpc, byte[] sdata1, EmulatorControl bochs,byte[] sdata2, boolean pm, Method load1) throws Exception
{
Integer sl1 = (Integer)save1.invoke(newpc, new Integer(espPageIndex), sdata1, pm);
Integer sl2;
if (pm)
sl2 = bochs.getLinearPage(new Integer(espPageIndex), sdata2);
else
sl2 = bochs.getPhysicalPage(new Integer(espPageIndex), sdata2);
List<Integer> addrs = new ArrayList();
if (sl2 > 0)
if (!samePage(espPageIndex, sdata1, sdata2, addrs))
{
int addr = addrs.get(0);
if ((addrs.size() == 1) && ((sdata1[addr]^sdata2[addr]) == 0x10))
{ // ignore differences from pushing different AF to stack
System.out.println("ignoring different AF on stack...");
load1.invoke(newpc, new Integer(espPageIndex), sdata2, pm);
}
else
{
printHistory();
System.out.println("Error here... look above");
printPage(sdata1, sdata2, esp);
load1.invoke(newpc, new Integer(espPageIndex), sdata2, pm);
}
}
}
private static String currentInstruction()
{
Object[] prev = history[(((historyIndex-1)%history.length) + history.length) % history.length];
if (prev == null)
return "null";
return (String)prev[2];
}
private static String previousInstruction()
{
return previousInstruction(1);
}
private static String previousInstruction(int i)
{
Object[] prev = history[(((historyIndex-(1+i))%history.length) + history.length) % history.length];
if (prev == null)
return "null";
return (String)prev[2];
}
private static int[] previousState()
{
Object[] prev = history[(((historyIndex-2)%history.length) + history.length) % history.length];
if (prev == null)
return null;
return (int[])prev[0];
}
private static int[] previousBochsState()
{
Object[] prev = history[(((historyIndex-2)%history.length) + history.length) % history.length];
if (prev == null)
return null;
return (int[])prev[1];
}
static Object[][] history = new Object[10][];
static int historyIndex=0;
private static void printLast2()
{
int index2 = decrementHistoryIndex(historyIndex);
int index1 = decrementHistoryIndex(index2);
printState(history[index1]);
printState(history[index2]);
}
private static int decrementHistoryIndex(int index)
{
return (index-1+history.length)%history.length;
}
private static void printHistory()
{
printState(history[historyIndex]);
int end = historyIndex;
for (int j = (end+1)%history.length; j != end ; j = (j+1)%history.length)
{
printState(history[j]);
}
}
private static void printState(Object s)
{
if (s == null)
return;
Object[] sarr = (Object[]) s;
int[] fast = (int[]) sarr[0];
int[] old = (int[]) sarr[1];
String line = (String) sarr[2];
System.out.println("New JPC:");
Fuzzer.printState(fast);
System.out.println("Old JPC:");
Fuzzer.printState(old);
System.out.println(line);
}
public static void printPage(byte[] fast, byte[] old, int esp)
{
int address = esp&0xfffff000;
// print page
for (int i=0; i < 1 << 8; i++)
{
int v1 = getInt(fast, 16*i);
int v2 = getInt(fast, 16*i+4);
int v3 = getInt(fast, 16*i+8);
int v4 = getInt(fast, 16*i+12);
int r1 = getInt(old, 16*i);
int r2 = getInt(old, 16*i+4);
int r3 = getInt(old, 16*i+8);
int r4 = getInt(old, 16*i+12);
System.out.printf("0x%8x: %8x %8x %8x %8x -- %8x %8x %8x %8x ==== ", address + 16*i, v1, v2, v3, v4, r1, r2, r3, r4);
printIntChars(v1, r1);
printIntChars(v2, r2);
printIntChars(v3, r3);
printIntChars(v4, r4);
System.out.print(" -- ");
printIntChars(r1, v1);
printIntChars(r2, v2);
printIntChars(r3, v3);
printIntChars(r4, v4);
System.out.println();
}
System.out.println("Memory differences:");
// print differences
for (int i =0; i < 1<< 12; i++)
{
byte b1 = fast[i];
byte b2 = old[i];
if (b1 != b2)
{
System.out.println("Memory not the same at 0x" + Integer.toHexString(address+i) + ", values: " + Integer.toHexString(b1 & 0xff) + " " + Integer.toHexString(b2 & 0xff));
}
}
}
public static int getInt(byte[] data, int offset)
{
return ((data[offset] & 0xff) << 24) | ((data[offset+1] & 0xff) << 16) | ((data[offset+2] & 0xff) << 8) | ((data[offset+3] & 0xff) << 0);
}
public static void printIntChars(int i, int c)
{
int[] ia = new int[] {((i >> 24) & 0xFF), ((i >> 16) & 0xFF), ((i >> 8) & 0xFF), ((i >> 0) & 0xFF)};
int[] ca = new int[] {((c >> 24) & 0xFF), ((c >> 16) & 0xFF), ((c >> 8) & 0xFF), ((c >> 0) & 0xFF)};
for (int a = 0; a < 4; a++)
if (ia[a] == ca[a])
System.out.printf("%c", (ia[a] == 0 ? ' ' : (char)ia[a]));
else
System.out.printf("\u001b[1;44m%c\u001b[1;49m", (ia[a] == 0 ? ' ' : (char)ia[a]));
System.out.printf(" ");
}
public static boolean samePage(int index, byte[] fast, byte[] old, List<Integer> addrs)
{
if (fast.length != old.length)
throw new IllegalStateException(String.format("different page data lengths %d != %d", fast.length, old.length));
for (int i=0; i < fast.length; i++)
if (fast[i] != old[i])
{
if (addrs!= null)
addrs.add(i);
System.out.printf("Memory not the same: %08x=> %02x - %02x\n", index*4096+i, fast[i], old[i]);
return false;
}
return true;
}
public static boolean sameStates(int[] fast, int[] old, int[] prevFast, int[] prevOld, boolean compareFlags, Set<Integer> diff)
{
if (fast.length != EmulatorControl.names.length)
throw new IllegalArgumentException(String.format("new state length: %d != %d",fast.length, EmulatorControl.names.length));
if (old.length != EmulatorControl.names.length)
throw new IllegalArgumentException("old state length = "+old.length);
boolean same = true;
for (int i=0; i < fast.length; i++)
if (i != 9)
{
if ((fast[i] != old[i]) && ((old[i] != prevOld[i]) || (prevFast[i] != fast[i]))) // don't keep reporting the same thing
{
diff.add(i);
same = false;
}
}
else
{
if (compareFlags && ((fast[i]&flagMask) != (old[i]&flagMask)))
{
if (same)
{
same = false;
diff.add(i);
}
}
}
return same;
}
public static boolean continueExecution(String state)
{
if (true)
return true;
System.out.println("Adopt "+state+"? (y/n)");
String line = null;
try {
line = new BufferedReader(new InputStreamReader(System.in)).readLine();
} catch (IOException f)
{
f.printStackTrace();
System.exit(0);
}
if (line.equals("y"))
return true;
else
return false;
}
public static class MouseEvent implements Comparable<MouseEvent>
{
public final long time;
public final int dx, dy, dz;
public final int buttons;
MouseEvent(long time, int dx, int dy, int dz, boolean leftDown, boolean middleDown, boolean rightDown)
{
this.time = time;
this.dx = dx;
this.dy = dy;
this.dz = dz;
int buttons = 0;
if (leftDown)
buttons |= 1;
if (middleDown)
buttons |= 2;
if (rightDown)
buttons |= 4;
this.buttons = buttons;
}
public int compareTo(MouseEvent o)
{
return (int)(time - o.time);
}
}
public static class KeyBoardEvent implements Comparable<KeyBoardEvent>
{
public final long time;
public final String text;
KeyBoardEvent(long time, String text)
{
this.time = time;
this.text = text;
}
public int compareTo(KeyBoardEvent o)
{
return (int)(time - o.time);
}
}
}