/*
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.debugger;
import java.awt.*;
import java.awt.event.*;
import java.io.DataInputStream;
import javax.swing.*;
import org.jpc.emulator.PC;
import org.jpc.emulator.processor.Processor;
import org.jpc.support.Clock;
import org.jpc.emulator.execution.codeblock.CodeBlock;
public class RunMenu extends JMenu implements ActionListener {
private Runner runner;
private JCheckBoxMenuItem background, timetravel, pauseTimer;
private JMenuItem run, step, reset, multiStep;
private FunctionKeys functionKeys;
private Processor processor;
private CodeBlockRecord codeBlockRecord;
private Clock clock;
private int multiSteps;
private Processor cpu = null;
public RunMenu() {
super("Run");
run = add("Start");
run.addActionListener(this);
run.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0, false));
step = add("Single Step");
step.addActionListener(this);
step.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0, false));
multiSteps = 10;
multiStep = add("Multiple Steps (" + multiSteps + ")");
multiStep.addActionListener(this);
multiStep.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0, false));
addSeparator();
timetravel = new JCheckBoxMenuItem("Time travel mode");
add(timetravel);
timetravel.setSelected(false);
timetravel.addActionListener(this);
background = new JCheckBoxMenuItem("Background Execution");
add(background);
background.setSelected(true);
background.addActionListener(this);
addSeparator();
pauseTimer = new JCheckBoxMenuItem("Pause Virtual Clock");
pauseTimer.setSelected(false);
add(pauseTimer);
addSeparator();
reset = add("Reset");
reset.addActionListener(this);
runner = new Runner();
functionKeys = new FunctionKeys();
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(functionKeys);
}
public boolean isTimeTravel()
{
return timetravel.getState();
}
public void refresh() {
processor = (Processor) JPC.getObject(Processor.class);
codeBlockRecord = (CodeBlockRecord) JPC.getObject(CodeBlockRecord.class);
PC pc = (PC) JPC.getObject(PC.class);
if (pc != null) {
clock = (Clock) pc.getComponent(Clock.class);
}
if (codeBlockRecord != null) {
codeBlockRecord.advanceDecode();
}
}
public void dispose() {
stop();
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(functionKeys);
}
class FunctionKeys implements KeyEventDispatcher {
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
if (e.getKeyCode() == KeyEvent.VK_F8) {
executeStep();
return true;
} else if (e.getKeyCode() == KeyEvent.VK_F7) {
executeSteps(multiSteps);
return true;
}
}
return false;
}
}
class Runner implements Runnable {
volatile boolean running;
Thread runnerThread;
Runner() {
running = false;
runnerThread = null;
}
void startRunning() {
if (running) {
return;
}
if (runnerThread != null) {
running = false;
try {
runnerThread.join(1000);
} catch (Exception e) {
}
try {
runnerThread.stop();
} catch (Throwable t) {
}
runnerThread = null;
}
running = true;
runnerThread = new Thread(this);
runnerThread.setPriority(Thread.MIN_PRIORITY + 1);
runnerThread.start();
}
void stopRunning() {
running = false;
}
class U1 implements Runnable {
public void run() {
JPC.getInstance().refresh();
}
}
class Alerter implements Runnable {
int type;
String title, message;
Alerter(String title, String message, int type) {
this.title = title;
this.message = message;
this.type = type;
}
public void show() {
try {
SwingUtilities.invokeAndWait(this);
} catch (Exception e) {
}
}
public void run() {
JOptionPane.showMessageDialog(JPC.getInstance(), message, title, type);
}
}
public void run() {
if (codeBlockRecord == null) {
return;
}
PC pc = (PC) JPC.getObject(PC.class);
pc.start();
try {
int delay = 2000;
boolean bg;
BreakpointsFrame bps = (BreakpointsFrame) JPC.getObject(BreakpointsFrame.class);
WatchpointsFrame wps = (WatchpointsFrame) JPC.getObject(WatchpointsFrame.class);
U1 u = new U1();
long t1 = System.currentTimeMillis();
JPC.getInstance().notifyExecutionStarted();
for (long c = 0; running; c++) {
bg = background.getState();
try {
if (codeBlockRecord == null) {
return;
}
if (pauseTimer.isSelected()) {
clock.pause();
} else {
clock.resume();
}
if (codeBlockRecord.executeBlock() == null) {
throw new Exception("Unimplemented Opcode. IP = " + Integer.toHexString(processor.getInstructionPointer()).toUpperCase());
}
CodeBlock nextBlock = codeBlockRecord.advanceDecode();
if (wps != null) {
WatchpointsFrame.Watchpoint wp = wps.checkForWatch();
if (wp != null) {
SwingUtilities.invokeAndWait(u);
int old = wp.getValue();
wp.updateValue();
int newVal = wp.getValue();
if (newVal != old)
{
if (!wp.isWatchingForValue() || (wp.getWatchTarget() == (byte)newVal))
{
java.awt.Toolkit.getDefaultToolkit().beep();
new Alerter("Watchpoint", String.format("Watch at %08x: old value=%02x new value=%02x " + wp.getName(), wp.getAddress(), old, newVal), JOptionPane.INFORMATION_MESSAGE).show();
break;
}
}
}
}
if (bps != null) {
int blockStart = processor.getInstructionPointer();
int blockEnd = blockStart + 1;
if (nextBlock != null) {
blockEnd = blockStart + nextBlock.getX86Length();
}
Breakpoint bp = bps.checkForBreak(blockStart, blockEnd);
if (bp != null) {
SwingUtilities.invokeAndWait(u);
String addr = MemoryViewPanel.zeroPadHex(bp.getAddress(), 8);
String name = bp.getName();
java.awt.Toolkit.getDefaultToolkit().beep();
new Alerter("Breakpoint", "Break at " + addr + ": " + name, JOptionPane.INFORMATION_MESSAGE).show();
break;
}
}
} catch (Exception e) {
System.err.println("Exception @ 0x" + Integer.toHexString(processor.getInstructionPointer()));
e.printStackTrace();
new Alerter("Processor Exception", "Exception during execution: " + e, JOptionPane.ERROR_MESSAGE).show();
break;
}
if (bg) {
if ((c % 1000) != 0) {
continue;
}
long t2 = System.currentTimeMillis();
if (t2 - t1 < delay) {
continue;
}
t1 = t2;
}
try {
SwingUtilities.invokeAndWait(u);
} catch (Exception e) {
}
}
} finally {
pc.start();
running = false;
runnerThread = null;
run.setText("Start");
step.setEnabled(true);
JPC.getInstance().notifyExecutionStopped();
}
}
}
public int executeStep() {
return executeSteps(1);
}
public int executeSteps(int count) {
if (codeBlockRecord == null) {
return 0;
}
if (cpu == null) {
cpu = (Processor) JPC.getObject(Processor.class);
}
PC pc = (PC) JPC.getObject(PC.class);
pc.start();
JPC.getInstance().notifyExecutionStarted();
int instructions = 0;
try {
BreakpointsFrame bps = (BreakpointsFrame) JPC.getObject(BreakpointsFrame.class);
if (pauseTimer.isSelected()) {
clock.pause();
} else {
clock.resume();
}
for (int i = 0; i < count; i++) {
CodeBlock block = codeBlockRecord.executeBlock();
instructions += block.getX86Count();
if (block == null) {
throw new Exception("Unimplemented Opcode at " + Integer.toHexString(processor.getInstructionPointer()).toUpperCase());
}
CodeBlock nextBlock = codeBlockRecord.advanceDecode();
if (bps != null) {
int blockStart = processor.getInstructionPointer();
int blockEnd = blockStart + 1;
if (nextBlock != null) {
blockEnd = blockStart + nextBlock.getX86Length();
}
Breakpoint bp = bps.checkForBreak(blockStart, blockEnd);
if (bp != null) {
String addr = MemoryViewPanel.zeroPadHex(bp.getAddress(), 8);
String name = bp.getName();
java.awt.Toolkit.getDefaultToolkit().beep();
JOptionPane.showMessageDialog(JPC.getInstance(), "Break at " + addr + ": " + name, "Breakpoint", JOptionPane.INFORMATION_MESSAGE);
break;
}
}
if ((count % 1000) == 999) {
JPC.getInstance().refresh();
}
}
} catch (Exception e) {
System.err.println("Exception @ 0x" + Integer.toHexString(processor.getInstructionPointer()));
if (!(e instanceof IllegalStateException)) {
e.printStackTrace();
}
JOptionPane.showMessageDialog(JPC.getInstance(), "Exception During Step:\n" + e, "Execute", JOptionPane.ERROR_MESSAGE);
} finally {
pc.stop();
clock.pause();
JPC.getInstance().notifyExecutionStopped();
}
return instructions;
}
public void stop() {
if (runner.running) {
runner.stopRunning();
run.setText("Start");
step.setEnabled(true);
}
}
public void toggleRun() {
if (!runner.running) {
runner.startRunning();
run.setText("Stop");
step.setEnabled(false);
} else {
runner.stopRunning();
run.setText("Start");
step.setEnabled(true);
}
JPC.getInstance().refresh();
}
public void actionPerformed(ActionEvent evt) {
Object src = evt.getSource();
if (src == reset) {
stop();
try {
PC pc = (PC) JPC.getObject(PC.class);
if (pc != null) {
pc.reset();
}
JPC.getInstance().loadNewPC(pc);
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Failed to load BIOS: " + e, "PC Init Error", JOptionPane.ERROR_MESSAGE);
}
} else if (src == step) {
executeStep();
} else if (src == multiStep) {
try {
String val = JOptionPane.showInputDialog(JPC.getInstance(), "Enter the new number of steps to take each time", "" + multiSteps);
multiSteps = Math.max(1, Integer.parseInt(val.trim()));
multiStep.setText("Multiple Steps (" + multiSteps + ")");
} catch (Exception e) {
}
} else if (src == run) {
toggleRun();
} else if (src == timetravel)
{
ProcessorAccess pa = ProcessorAccess.create(timetravel.getState(), processor);
JPC.getInstance().objects().removeObject(ProcessorAccess.class);
JPC.getInstance().objects().addObject(ProcessorAccess.class, pa);
((ProcessorFrame)JPC.getObject(ProcessorFrame.class)).refreshAccess();
}
}
}