/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Release Version 2.4
A project from the Physics Dept, The University of Oxford
Copyright (C) 2007-2010 The University of Oxford
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/
Conceived and Developed by:
Rhys Newman, Ian Preston, Chris Dennis
End of licence header
*/
package org.jpc.emulator;
import org.jpc.debugger.LinearMemoryViewer;
import org.jpc.emulator.execution.decoder.BasicBlock;
import org.jpc.emulator.execution.decoder.DebugBasicBlock;
import org.jpc.emulator.execution.decoder.Disassembler;
import org.jpc.emulator.execution.decoder.Instruction;
import org.jpc.emulator.execution.codeblock.*;
import org.jpc.emulator.motherboard.*;
import org.jpc.emulator.memory.*;
import org.jpc.emulator.pci.peripheral.*;
import org.jpc.emulator.pci.*;
import org.jpc.emulator.peripheral.*;
import org.jpc.emulator.processor.*;
import org.jpc.j2se.KeyMapping;
import org.jpc.j2se.Option;
import org.jpc.j2se.PCMonitor;
import org.jpc.support.*;
import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.logging.*;
import java.util.zip.*;
import org.jpc.emulator.execution.codeblock.CodeBlockManager;
import org.jpc.j2se.VirtualClock;
import javax.swing.*;
/**
* This class represents the emulated PC and holds references to the hardware components.
* @author Ian Preston
*/
public class PC {
public static int SYS_RAM_SIZE;
public static final int DEFAULT_RAM_SIZE = Option.ram.intValue(16) * 1024 * 1024;
public static final int INSTRUCTIONS_BETWEEN_INTERRUPTS = 1;
public static final boolean ETHERNET = Option.ethernet.isSet();
public static volatile boolean compile = Option.compile.isSet();
public static final boolean HISTORY = Option.history.isSet();
public static final int HISTORY_SIZE = 200;
private static final int[] ipHistory = new int[HISTORY_SIZE];
private static int historyIndex = 0;
private static int insHistoryIndex = 0;
private static CodeBlock[] prevBlocks = new CodeBlock[HISTORY_SIZE];
private static final Logger LOGGING = Logger.getLogger(PC.class.getName());
private final Processor processor;
private final PhysicalAddressSpace physicalAddr;
private final LinearAddressSpace linearAddr;
private final Clock vmClock;
private final InterruptController pic;
private final List<HardwareComponent> parts;
private final CodeBlockManager manager;
private EthernetCard ethernet;
private final Keyboard keyboard;
/**
* Constructs a new <code>PC</code> instance with the specified external time-source and
* drive set.
* @param clock <code>Clock</code> object used as a time source
* @param drives drive set for this instance.
* @throws java.io.IOException propogated from bios resource loading
*/
public PC(Clock clock, DriveSet drives, Calendar startTime) throws IOException {
this(clock, drives, DEFAULT_RAM_SIZE, startTime);
}
public PC(Clock clock, DriveSet drives) throws IOException {
this(clock, drives, DEFAULT_RAM_SIZE, getStartTime());
}
/**
* Constructs a new <code>PC</code> instance with the specified external time-source and
* drive set.
* @param clock <code>Clock</code> object used as a time source
* @param drives drive set for this instance.
* @param ramSize the size of the system ram for the virtual machine in bytes.
* @throws java.io.IOException propagated from bios resource loading
*/
public PC(Clock clock, DriveSet drives, int ramSize, Calendar startTime) throws IOException {
SYS_RAM_SIZE = ramSize;
parts = new LinkedList<HardwareComponent>();
vmClock = clock;
parts.add(vmClock);
processor = new Processor(vmClock);
parts.add(processor);
manager = new CodeBlockManager();
physicalAddr = new PhysicalAddressSpace(manager);
parts.add(physicalAddr);
linearAddr = new LinearAddressSpace();
parts.add(linearAddr);
parts.add(drives);
//Motherboard
parts.add(new IOPortHandler());
pic = new InterruptController();
parts.add(pic);
parts.add(new DMAController(false, true));
parts.add(new DMAController(false, false));
parts.add(new RTC(0x70, 8, startTime));
parts.add(new IntervalTimer(0x40, 0));
parts.add(new GateA20Handler());
//Peripherals
parts.add(new PIIX3IDEInterface());
if (Option.ethernet.isSet())
parts.add(ethernet = new EthernetCard());
parts.add(new DefaultVGACard());
parts.add(new SerialPort(0));
parts.add(new SerialPort(1));
parts.add(new SerialPort(2));
parts.add(new SerialPort(3));
keyboard = new Keyboard();
parts.add(keyboard);
parts.add(new FloppyController());
parts.add(new PCSpeaker());
//PCI Stuff
parts.add(new PCIHostBridge());
parts.add(new PCIISABridge());
parts.add(new PCIBus());
if (Option.ethernet.isSet())
parts.add(new EthernetCard());
//BIOSes
parts.add(new SystemBIOS(Option.bios.value("/resources/bios/bios.bin")));
parts.add(new VGABIOS("/resources/bios/vgabios.bin"));
if (Option.sound.value())
{
Midi.MIDI_Init();
Mixer.MIXER_Init();
String device = Option.sounddevice.value("sb16");
if (device.equals("sb16"))
{
parts.add(new Mixer());
parts.add(new MPU401());
parts.add(new SBlaster());
parts.add(new Adlib());
}
}
if (!configure()) {
throw new IllegalStateException("PC Configuration failed");
}
}
public PC(Clock clock, DriveSet drives, int ramSize) throws IOException {
this(clock, drives, ramSize, getStartTime());
}
/**
* Constructs a new <code>PC</code> instance with the specified external time-source and
* a drive set constructed by parsing args.
* @param clock <code>Clock</code> object used as a time source
* @param args command-line args specifying the drive set to use.
* @throws java.io.IOException propogates from <code>DriveSet</code> construction
*/
public PC(Clock clock, String[] args, Calendar startTime) throws IOException {
this(clock, DriveSet.buildFromArgs(args), startTime);
}
public PC(Clock clock, String[] args) throws IOException {
this(clock, DriveSet.buildFromArgs(args), getStartTime());
}
/**
* Constructs a new <code>PC</code> instance with the specified external time-source and
* a drive set constructed by parsing args.
* @param clock <code>Clock</code> object used as a time source
* @param args command-line args specifying the drive set to use.
* @param ramSize the size of the system ram for the virtual machine in bytes.
* @throws java.io.IOException propagates from <code>DriveSet</code> construction
*/
public PC(Clock clock, String[] args, int ramSize) throws IOException {
this(clock, DriveSet.buildFromArgs(args), ramSize);
}
public PC(String[] args, Calendar startTime) throws IOException {
this(new VirtualClock(), args, startTime);
}
public PC(String[] args) throws IOException {
this(new VirtualClock(), args, getStartTime());
}
private static Calendar getStartTime()
{
Calendar start = Calendar.getInstance();
if (Option.startTime.isSet())
start.setTimeInMillis(Long.parseLong(Option.startTime.value()));
return start;
}
public void hello()
{
System.out.println("Hello from the new JPC!");
}
public Instruction getInstruction()
{
return getInstruction(processor.getInstructionPointer());
}
public Instruction getInstruction(int addr)
{
if (processor.isProtectedMode())
{
byte[] code = new byte[15];
linearAddr.copyContentsIntoArray(addr, code, 0, code.length);
if (processor.cs.getDefaultSizeFlag())
return Disassembler.disassemble32(new Disassembler.ByteArrayPeekStream(code));
else
return Disassembler.disassemble16(new Disassembler.ByteArrayPeekStream(code));
}
byte[] code = new byte[15];
physicalAddr.copyContentsIntoArray(addr, code, 0, code.length);
return Disassembler.disassemble16(new Disassembler.ByteArrayPeekStream(code));
}
public void setState(int[] s)
{
processor.r_eax.set32(s[0]);
processor.r_ecx.set32(s[1]);
processor.r_edx.set32(s[2]);
processor.r_ebx.set32(s[3]);
processor.r_esp.set32(s[4]);
processor.r_ebp.set32(s[5]);
processor.r_esi.set32(s[6]);
processor.r_edi.set32(s[7]);
processor.eip = s[8];
try {
processor.setEFlags(s[9]);
} catch (ProcessorException e) {}
vmClock.update(s[16]-(int)vmClock.getTicks());
if (!processor.isProtectedMode() && !processor.isVirtual8086Mode())
{
processor.es(s[10]);
processor.cs(s[11]);
processor.ss(s[12]);
processor.ds(s[13]);
processor.fs(s[14]);
processor.gs(s[15]);
}
try {
processor.setCR0(s[36]);
} catch (ModeSwitchException e) {}
double[] newFPUStack = new double[8];
for (int i=0; i < 8; i++)
newFPUStack[i] = Double.longBitsToDouble(0xffffffffL & s[2*i+37] | ((0xffffffffL & s[2*i+38]) << 32));
processor.fpu.setStack(newFPUStack);
}
public void setPhysicalMemory(Integer addr, byte[] data)
{
physicalAddr.copyArrayIntoContents(addr, data, 0, data.length);
}
public Boolean getPITIrqLevel()
{
return BochsPIT.getIrqLevel();
}
public void setCode(byte[] code)
{
if (processor.isProtectedMode())
{
// assume paging is off
physicalAddr.copyArrayIntoContents(processor.getInstructionPointer(), code, 0, code.length);
}
else
{
physicalAddr.copyArrayIntoContents(processor.getInstructionPointer(), code, 0, code.length);
}
}
public String getScreenText()
{
return ((VGACard)getComponent(VGACard.class)).getText();
}
public void sendKeysDown(String text)
{
for (char c: text.toCharArray())
{
int[] keycodes = KeyMapping.getJavaKeycodes(c);
for (int i = 0; i < keycodes.length; i++)
keyboard.keyPressed(KeyMapping.getScancode(keycodes[i]));
}
}
public void sendKeysUp(String text)
{
for (char c: text.toCharArray())
{
int[] keycodes = KeyMapping.getJavaKeycodes(c);
for (int i = 0; i < keycodes.length; i++)
keyboard.keyReleased(KeyMapping.getScancode(keycodes[i]));
}
}
public void sendMouse(Integer dx, Integer dy, Integer dz, Integer buttons)
{
keyboard.putMouseEvent(dx, dy, dz, buttons);
}
public int[] getState()
{
int[] res = new int[]
{
processor.r_eax.get32(), processor.r_ecx.get32(), processor.r_edx.get32(), processor.r_ebx.get32(),
processor.r_esp.get32(), processor.r_ebp.get32(), processor.r_esi.get32(), processor.r_edi.get32(),
processor.eip, processor.getEFlags(),
processor.es.getSelector(), processor.cs.getSelector(),
processor.ss.getSelector(), processor.ds.getSelector(),
processor.fs.getSelector(), processor.gs.getSelector(),
(int)getTicks(),
getLimit(processor.es), getLimit(processor.cs),
getLimit(processor.ss), getLimit(processor.ds),
getLimit(processor.fs), getLimit(processor.gs),
(processor.cs.getDefaultSizeFlag()? 1: 0) | (processor.ss.getDefaultSizeFlag()? 0x10: 0),
getBase(processor.gdtr),getLimit(processor.gdtr),
getBase(processor.idtr),getLimit(processor.idtr),
getBase(processor.ldtr),getLimit(processor.ldtr),
getBase(processor.es), getBase(processor.cs),
getBase(processor.ss), getBase(processor.ds),
getBase(processor.fs), getBase(processor.gs),
processor.getCR0(),
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
//(int)vmClock.nextExpiry()
};
double[] fpuStack = processor.fpu.getStack();
for (int i=0; i < 8; i++)
{
res[2*i+37] = (int)(Double.doubleToRawLongBits(fpuStack[i])>>>32);
res[2*i+38] = (int)Double.doubleToRawLongBits(fpuStack[i]);
}
return res;
}
private int getBase(Segment s)
{
if (s instanceof SegmentFactory.NullSegment)
return 0;
return s.getBase();
}
private int getLimit(Segment s)
{
if (s instanceof SegmentFactory.NullSegment)
return 0xffff;
return s.getLimit();
}
private long getTicks()
{
for (HardwareComponent c: parts)
if (c instanceof Clock)
{
return ((VirtualClock) c).getTicks();
}
return 0;
}
public byte[] getCMOS()
{
RTC rtc = (RTC)getComponent(RTC.class);
return rtc.getCMOS();
}
public int[] getPit()
{
IntervalTimer pit = (IntervalTimer) getComponent(IntervalTimer.class);
return pit.getState();
}
public JPanel getNewMonitor()
{
PCMonitor mon = new PCMonitor(this);
mon.startUpdateThread();
return mon;
}
public Integer savePage(Integer page, byte[] data, Boolean linear) throws IOException
{
if (!linear)
return physicalAddr.getPage(page, data);
return physicalAddr.getPage(LinearMemoryViewer.translateLinearAddressToInt(physicalAddr, processor, page), data);
}
public void loadPage(Integer page, byte[] data, Boolean linear) throws IOException
{
if (!linear)
physicalAddr.setPage(page, data);
else
physicalAddr.setPage(LinearMemoryViewer.translateLinearAddressToInt(physicalAddr, processor, page), data);
}
public void triggerSpuriousInterrupt()
{
pic.triggerSpuriousInterrupt();
}
public void triggerSpuriousMasterInterrupt()
{
pic.triggerSpuriousMasterInterrupt();
}
/**
* Starts this PC's attached clock instance.
*/
public void start() {
vmClock.resume();
if (Option.sound.value())
AudioLayer.open(Option.mixer_javabuffer.intValue(8820), Option.mixer_rate.intValue(SBlaster.OPL_RATE));
}
/**
* Stops this PC's attached clock instance
*/
public void stop() {
vmClock.pause();
if (Option.sound.value())
AudioLayer.stop();
}
/**
* Inserts the specified floppy disk into the drive identified.
* @param disk new floppy disk to be inserted.
* @param index drive which the disk is inserted into.
*/
public void changeFloppyDisk(org.jpc.support.BlockDevice disk, int index) {
((FloppyController) getComponent(FloppyController.class)).changeDisk(disk, index);
}
private boolean configure() {
boolean fullyInitialised;
int count = 0;
do {
fullyInitialised = true;
for (HardwareComponent outer : parts) {
if (outer.initialised()) {
continue;
}
for (HardwareComponent inner : parts) {
outer.acceptComponent(inner);
}
fullyInitialised &= outer.initialised();
}
count++;
} while ((fullyInitialised == false) && (count < 100));
if (!fullyInitialised) {
StringBuilder sb = new StringBuilder("pc >> component configuration errors\n");
List<HardwareComponent> args = new ArrayList<HardwareComponent>();
for (HardwareComponent hwc : parts) {
if (!hwc.initialised()) {
sb.append("component {" + args.size() + "} not configured");
args.add(hwc);
}
}
LOGGING.log(Level.WARNING, sb.toString(), args.toArray());
return false;
}
for (HardwareComponent hwc : parts) {
if (hwc instanceof PCIBus) {
((PCIBus) hwc).biosInit();
}
}
return true;
}
/**
* Saves the state of this PC and all of its associated components out to the
* specified stream.
* @param out stream the serialised state is written to
* @throws java.io.IOException propogated from the supplied stream.
*/
public void saveState(OutputStream out) throws IOException {
LOGGING.log(Level.INFO, "snapshot saving");
ZipOutputStream zout = new ZipOutputStream(out);
for (HardwareComponent hwc : parts) {
saveComponent(zout, hwc);
}
zout.finish();
LOGGING.log(Level.INFO, "snapshot done");
}
private void saveComponent(ZipOutputStream zip, HardwareComponent component) throws IOException {
LOGGING.log(Level.FINE, "snapshot saving {0}", component);
int i = 0;
while (true) {
ZipEntry entry = new ZipEntry(component.getClass().getName() + "#" + i);
try {
zip.putNextEntry(entry);
break;
} catch (ZipException e) {
if (e.getMessage().matches(".*(duplicate entry).*")) {
i++;
} else {
throw e;
}
}
}
DataOutputStream dout = new DataOutputStream(zip);
component.saveState(dout);
dout.flush();
zip.closeEntry();
}
/**
* Loads the state of this PC and all of its associated components from the
* specified stream.
* @param in stream the serialised data is read from.
* @throws java.io.IOException propogated from the supplied stream.
*/
public void loadState(InputStream in) throws IOException {
LOGGING.log(Level.INFO, "snapshot loading");
physicalAddr.reset();
ZipInputStream zin = new ZipInputStream(in);
Set<HardwareComponent> newParts = new HashSet<HardwareComponent>();
IOPortHandler ioHandler = (IOPortHandler) getComponent(IOPortHandler.class);
ioHandler.reset();
newParts.add(ioHandler);
try {
for (ZipEntry entry = zin.getNextEntry(); entry != null; entry = zin.getNextEntry()) {
DataInputStream din = new DataInputStream(zin);
String cls = entry.getName().split("#")[0];
Class clz;
try {
clz = Class.forName(cls);
} catch (ClassNotFoundException e) {
LOGGING.log(Level.WARNING, "unknown class in snapshot", e);
continue;
}
HardwareComponent hwc = getComponent(clz);
if (hwc instanceof PIIX3IDEInterface) {
((PIIX3IDEInterface) hwc).loadIOPorts(ioHandler, din);
} else if (hwc instanceof EthernetCard) {
((EthernetCard) hwc).loadIOPorts(ioHandler, din);
} else if (hwc instanceof VirtualClock) {
((VirtualClock) hwc).loadState(din, this);
} else if (hwc instanceof PhysicalAddressSpace) {
((PhysicalAddressSpace) hwc).loadState(din, manager);
} else {
hwc.loadState(din);
}
if (hwc instanceof IODevice) {
ioHandler.registerIOPortCapable((IODevice) hwc);
}
parts.remove(hwc);
newParts.add(hwc);
}
parts.clear();
parts.addAll(newParts);
linkComponents();
LOGGING.log(Level.INFO, "snapshot load done");
//pciBus.biosInit();
} catch (IOException e) {
LOGGING.log(Level.WARNING, "snapshot load failed", e);
throw e;
}
}
private void linkComponents() {
boolean fullyInitialised;
int count = 0;
do {
fullyInitialised = true;
for (HardwareComponent outer : parts) {
if (outer.updated()) {
continue;
}
for (HardwareComponent inner : parts) {
outer.updateComponent(inner);
}
fullyInitialised &= outer.updated();
}
count++;
} while ((fullyInitialised == false) && (count < 100));
if (!fullyInitialised) {
StringBuilder sb = new StringBuilder("pc >> component linking errors\n");
List<HardwareComponent> args = new ArrayList<HardwareComponent>();
for (HardwareComponent hwc : parts) {
if (!hwc.initialised()) {
sb.append("component {" + args.size() + "} not linked");
args.add(hwc);
}
}
LOGGING.log(Level.WARNING, sb.toString(), args.toArray());
}
}
public void destroy()
{
for (HardwareComponent hwc : parts) {
if (hwc instanceof DriveSet)
((DriveSet) hwc).close();
}
}
/**
* Reset this PC back to its initial state.
* <p>
* This is roughly equivalent to a hard-reset (power down-up cycle).
*/
public void reset() {
for (HardwareComponent hwc : parts) {
hwc.reset();
}
configure();
}
/**
* Get an subclass of <code>cls</code> from this instance's parts list.
* <p>
* If <code>cls</code> is not assignment compatible with <code>HardwareComponent</code>
* then this method will return null immediately.
* @param cls component type required.
* @return an instance of class <code>cls</code>, or <code>null</code> on failure
*/
public HardwareComponent getComponent(Class<? extends HardwareComponent> cls) {
if (!HardwareComponent.class.isAssignableFrom(cls)) {
return null;
}
for (HardwareComponent hwc : parts) {
if (cls.isInstance(hwc)) {
return hwc;
}
}
return null;
}
/**
* Gets the processor instance associated with this PC.
* @return associated processor instance.
*/
public Processor getProcessor() {
return processor;
}
private static int staticClockx86Count=0;
public int eipBreak(Integer breakEip)
{
int instrs = 0;
while (processor.eip != breakEip)
instrs += executeBlock();
return instrs;
}
public String getInstructionInfo(Integer instrs)
{
StringBuilder b = new StringBuilder();
int eip = processor.getInstructionPointer();
for (int c=0; c < instrs; c++)
{
PeekableMemoryStream input = new PeekableMemoryStream();
try
{
Instruction in;
String e;
if (processor.isProtectedMode()) {
if (processor.isVirtual8086Mode()) {
input.set(linearAddr, eip);
in = Disassembler.disassemble16(input);
e = Disassembler.getExecutableName(3, in);
} else {
input.set(linearAddr, eip);
boolean opSize = processor.cs.getDefaultSizeFlag();
if (opSize)
in = Disassembler.disassemble32(input);
else
in = Disassembler.disassemble16(input);
e = Disassembler.getExecutableName(2, in);
}
} else {
input.set(physicalAddr, eip);
in = Disassembler.disassemble16(input);
e = Disassembler.getExecutableName(1, in);
}
b.append(in.toString());
b.append(" == ");
b.append(e);
b.append(" == ");
input.seek(-in.x86Length);
for(int i=0; i < in.x86Length; i++)
b.append(String.format("%02x ", input.read8()));
b.append("\n");
eip += in.x86Length;
if (in.toString().equals("sti"))
c--; // do one more instruction
if (in.isBranch())
break;
} catch (IllegalStateException e)
{
b.append(e.getMessage());
}
}
return b.toString();
}
public void getDirtyPages(Set<Integer> res)
{
physicalAddr.getDirtyPages(res);
}
public static String disam(byte[] code, Integer ops, Boolean is32Bit)
{
Disassembler.ByteArrayPeekStream mem = new Disassembler.ByteArrayPeekStream(code);
StringBuilder b = new StringBuilder();
for (int i=0; i < ops; i++)
{
Instruction disam = is32Bit ? Disassembler.disassemble32(mem) : Disassembler.disassemble16(mem);
mem.seek(disam.x86Length);
b.append(disam.toString()+"\n");
}
return b.toString();
}
public static Integer x86Length(byte[] code, Boolean is32Bit)
{
Disassembler.ByteArrayPeekStream mem = new Disassembler.ByteArrayPeekStream(code);
Instruction disam = is32Bit ? Disassembler.disassemble32(mem) : Disassembler.disassemble16(mem);
return disam.x86Length;
}
public int executeBlock()
{
if (processor.isProtectedMode()) {
if (processor.isVirtual8086Mode()) {
return executeVirtual8086Block();
} else {
return executeProtectedBlock();
}
} else {
return executeRealBlock();
}
}
// returns whether an interrupt was entered
public boolean checkInterrupts(Integer cycles, Boolean bochsinPitInt)
{
if (!Option.singlesteptime.value())
for (int i=0; i < cycles; i++)
vmClock.update(1);
try {
if (processor.isProtectedMode()) {
if (processor.isVirtual8086Mode()) {
return processor.processVirtual8086ModeInterrupts(0);
} else {
return processor.processProtectedModeInterrupts(0, bochsinPitInt);
}
} else {
return processor.processRealModeInterrupts(0, bochsinPitInt);
}
} catch (ModeSwitchException e)
{
// System.out.println("Switched mode: "+e.getMessage());
return true; // must have been triggered by the interrupt handler
}
}
public int executeRealBlock()
{
try
{
int block = physicalAddr.executeReal(processor, processor.getInstructionPointer());
// staticClockx86Count += block;
// if (staticClockx86Count >= INSTRUCTIONS_BETWEEN_INTERRUPTS)
// {
// //processor.processRealModeInterrupts(staticClockx86Count);
// // for multi instruction blocks simulate checking interrupts at each instruction
// // this ensures timers expire the same as in single stepped execution
// if (!Option.singlesteptime.value())
// for (int i=0; i < staticClockx86Count; i++)
// vmClock.update(1);
// staticClockx86Count = 0;
// }
return block;
} catch (ProcessorException p)
{
processor.handleRealModeException(p);
}
catch (ModeSwitchException e)
{
// uncomment for compare to single stepping or comparing to Bochs...
// staticClockx86Count += e.getX86Count();
// if (Option.singlesteptime.value())
// vmClock.updateAndProcess(1);
// else if (staticClockx86Count >0)
// {
// for (int i=0; i < staticClockx86Count; i++)
// vmClock.updateAndProcess(1);
// }
// else
// vmClock.updateAndProcess(0);
// staticClockx86Count = 0;
LOGGING.log(Level.FINE, "Mode switch in RM @ cs:eip " + Integer.toHexString(processor.cs.getBase()) + ":" + Integer.toHexString(processor.eip));
return e.getX86Count();
}
return 0;
}
public int executeVirtual8086Block()
{
try
{
int block = linearAddr.executeVirtual8086(processor, processor.getInstructionPointer());
// staticClockx86Count += block;
// if (staticClockx86Count >= INSTRUCTIONS_BETWEEN_INTERRUPTS)
// {
// //processor.processVirtual8086ModeInterrupts(staticClockx86Count);
// // for multi instruction blocks simulate checking interrupts at each instruction
// // this ensures timers expire the same as in single stepped execution
// if (!Option.singlesteptime.value())
// for (int i=0; i < staticClockx86Count; i++)
// vmClock.update(1);
// staticClockx86Count = 0;
// }
return block;
} catch (ProcessorException p)
{
processor.handleVirtual8086ModeException(p);
}
catch (ModeSwitchException e)
{
// uncomment for compare to single stepping...
// staticClockx86Count += e.getX86Count();
// if (Option.singlesteptime.value())
// vmClock.updateAndProcess(1);
// else if (staticClockx86Count > 0)
// {
// for (int i=0; i < staticClockx86Count; i++)
// vmClock.updateAndProcess(1);
// }
// else
// vmClock.updateAndProcess(0);
// staticClockx86Count = 0;
LOGGING.log(Level.FINE, "Mode switch in VM @ cs:eip " + Integer.toHexString(processor.cs.getBase()) + ":" + Integer.toHexString(processor.eip));
return e.getX86Count();
}
return 0;
}
public int executeProtectedBlock()
{
try
{
int block = linearAddr.executeProtected(processor, processor.getInstructionPointer());
// staticClockx86Count += block;
// if (staticClockx86Count >= INSTRUCTIONS_BETWEEN_INTERRUPTS)
// {
// //processor.processProtectedModeInterrupts(staticClockx86Count);
// // for multi instruction blocks simulate checking interrupts at each instruction
// // this ensures timers expire the same as in single stepped execution
// if (!Option.singlesteptime.value())
// for (int i=0; i < staticClockx86Count; i++)
// vmClock.update(1);
// staticClockx86Count = 0;
// }
return block;
} catch (ProcessorException p)
{
processor.handleProtectedModeException(p);
}
catch (ModeSwitchException e)
{
// uncomment for compare to single stepping...
// staticClockx86Count += e.getX86Count();
// if (Option.singlesteptime.value())
// vmClock.updateAndProcess(1);
// else if (staticClockx86Count >0)
// {
// for (int i=0; i < staticClockx86Count; i++)
// vmClock.updateAndProcess(1);
// }
// else
// vmClock.updateAndProcess(0);
// staticClockx86Count = 0;
LOGGING.log(Level.FINE, "Mode switch in PM @ cs:eip " + Integer.toHexString(processor.cs.getBase()) + ":" + Integer.toHexString(processor.eip));
return e.getX86Count();
}
return 0;
}
/**
* Execute an arbitrarily large amount of code on this instance.
* <p>
* This method will execute continuously until there is either a mode switch,
* or a unspecified large number of instructions have completed. It should
* never run indefinitely.
* @return total number of x86 instructions executed.
*/
public final int execute() {
try {
if (processor.isProtectedMode()) {
if (processor.isVirtual8086Mode()) {
return executeVirtual8086();
} else {
return executeProtected();
}
} else {
return executeReal();
}
} catch (RuntimeException e)
{
System.out.printf("Error at cs:eip = %08x\n", processor.getInstructionPointer());
System.out.printf("Last exit eip = %08x\n", BasicBlock.lastExitEip);
//printHistory();
System.out.println("*****");
printInsHistory();
System.out.printf("Error at cs:eip = %08x\n", processor.getInstructionPointer());
throw e;
}
}
private void printInsHistory()
{
for (int i = historyIndex; i != (historyIndex-1+HISTORY_SIZE)%HISTORY_SIZE; i = (i+1)%HISTORY_SIZE)
{
if (prevBlocks[i] == null) continue;
DebugBasicBlock deb = (DebugBasicBlock)prevBlocks[i];
// CodeBlock block = prevBlocks[i];
// if (block instanceof AbstractCodeBlockWrapper)
// deb = (DebugBasicBlock)(((InterpretedRealModeBlock)((AbstractCodeBlockWrapper)block).getTargetBlock()).b);
// else
// System.out.println(block.getClass().getName());
// Instruction disam = deb.start;
// int length = disam.x86Length;
// StringBuilder b = new StringBuilder();
// while (!disam.isBranch())
// {
// b.append(String.format(" %s\n", disam));
// length += disam.x86Length;
// disam = disam.next;
// }
// b.append(String.format(" %s\n", disam));
System.out.printf("Block at %08x length: %d\n", ipHistory[i], deb.getX86Length());
System.out.printf(deb.getDisplayString());
}
}
// private void printHistory()
// {
// for (int i = historyIndex; i != (historyIndex-1+HISTORY_SIZE)%HISTORY_SIZE; i = (i+1)%HISTORY_SIZE)
// {
// if (ipHistory[i] == 0) continue;
// int addr = ipHistory[i];
// Instruction disam = getInstruction(addr); // won't work if there was a recent mode switch
// int length = disam.x86Length;
// StringBuilder b = new StringBuilder();
// while (!disam.isBranch())
// {
// b.append(String.format(" %s\n", disam));
// addr += disam.x86Length;
// length += disam.x86Length;
// disam = getInstruction(addr);
// }
// b.append(String.format(" %s\n", disam));
// System.out.printf("Block at %08x length: %d\n", addr, length);
// System.out.printf(b.toString());
// }
// }
private static void logIP(int cseip)
{
ipHistory[historyIndex % HISTORY_SIZE] = cseip;
historyIndex++;
historyIndex %= HISTORY_SIZE;
}
public static void logBlock(int cseip, CodeBlock block)
{
logIP(cseip);
prevBlocks[insHistoryIndex % HISTORY_SIZE] = block;
insHistoryIndex++;
insHistoryIndex %= HISTORY_SIZE;
}
public final int executeReal()
{
int x86Count = 0;
int clockx86Count = 0;
int nextClockCheck = INSTRUCTIONS_BETWEEN_INTERRUPTS;
try
{
for (int i = 0; i < 100; i++)
{
if (ETHERNET)
ethernet.checkForPackets();
// if (!princeAddrs.contains(processor.getInstructionPointer()))
// throw new IllegalStateException("new address reached "+Integer.toHexString(processor.getInstructionPointer()));
int block = physicalAddr.executeReal(processor, processor.getInstructionPointer());
x86Count += block;
clockx86Count += block;
if (x86Count > nextClockCheck)
{
nextClockCheck = x86Count + INSTRUCTIONS_BETWEEN_INTERRUPTS;
processor.processRealModeInterrupts(clockx86Count);
clockx86Count = 0;
}
}
} catch (ProcessorException p) {
System.out.printf("Proc exception %s\n", p);
processor.handleRealModeException(p);
}
catch (ModeSwitchException e)
{
//State.print(processor);
//e.printStackTrace();
LOGGING.log(Level.FINE, "Mode switch in RM @ cs:eip " + Integer.toHexString(processor.cs.getBase()) + ":" + Integer.toHexString(processor.eip));
}
return x86Count;
}
public final int executeProtected() {
int x86Count = 0;
int clockx86Count = 0;
int nextClockCheck = INSTRUCTIONS_BETWEEN_INTERRUPTS;
try
{
for (int i = 0; i < 100; i++)
{
int block= linearAddr.executeProtected(processor, processor.getInstructionPointer());
x86Count += block;
clockx86Count += block;
if (x86Count > nextClockCheck)
{
nextClockCheck = x86Count + INSTRUCTIONS_BETWEEN_INTERRUPTS;
if (ETHERNET)
ethernet.checkForPackets();
processor.processProtectedModeInterrupts(clockx86Count);
clockx86Count = 0;
}
}
} catch (ProcessorException p) {
processor.handleProtectedModeException(p);
}
catch (ModeSwitchException e)
{
LOGGING.log(Level.FINE, "Mode switch in PM @ cs:eip " + Integer.toHexString(processor.cs.getBase()) + ":" + Integer.toHexString(processor.eip));
}
return x86Count;
}
public final int executeVirtual8086() {
int x86Count = 0;
int clockx86Count = 0;
int nextClockCheck = INSTRUCTIONS_BETWEEN_INTERRUPTS;
try
{
for (int i = 0; i < 100; i++)
{
int block = linearAddr.executeVirtual8086(processor, processor.getInstructionPointer());
x86Count += block;
clockx86Count += block;
if (x86Count > nextClockCheck)
{
nextClockCheck = x86Count + INSTRUCTIONS_BETWEEN_INTERRUPTS;
if (ETHERNET)
ethernet.checkForPackets();
processor.processVirtual8086ModeInterrupts(clockx86Count);
clockx86Count = 0;
}
}
}
catch (ProcessorException p)
{
processor.handleVirtual8086ModeException(p);
}
catch (ModeSwitchException e)
{
LOGGING.log(Level.FINE, "Mode switch in VM8086 @ cs:eip " + Integer.toHexString(processor.cs.getBase()) + ":" + Integer.toHexString(processor.eip));
}
return x86Count;
}
public static void main(String[] args) {
try {
if (args.length == 0) {
ClassLoader cl = PC.class.getClassLoader();
if (cl instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) cl).getURLs()) {
InputStream in = url.openStream();
try {
JarInputStream jar = new JarInputStream(in);
Manifest manifest = jar.getManifest();
if (manifest == null) {
continue;
}
String defaultArgs = manifest.getMainAttributes().getValue("Default-Args");
if (defaultArgs == null) {
continue;
}
args = defaultArgs.split("\\s");
break;
} catch (IOException e) {
System.err.println("Not a JAR file " + url);
} finally {
try {
in.close();
} catch (IOException e) {
}
}
}
}
if (args.length == 0) {
LOGGING.log(Level.INFO, "No configuration specified, using defaults");
args = new String[]{"-fda", "mem:resources/images/floppy.img",
"-hda", "mem:resources/images/dosgames.img", "-boot", "fda"
};
} else {
LOGGING.log(Level.INFO, "Using configuration specified in manifest");
}
} else {
LOGGING.log(Level.INFO, "Using configuration specified on command line");
}
if (ArgProcessor.findVariable(args, "compile", "yes").equalsIgnoreCase("no")) {
compile = false;
}
PC pc = new PC(new VirtualClock(), args, Calendar.getInstance());
pc.start();
try {
while (true) {
pc.execute();
}
} finally {
pc.stop();
LOGGING.log(Level.INFO, "PC Stopped");
pc.getProcessor().printState();
}
} catch (IOException e) {
System.err.println("IOError starting PC");
}
}
}