BUGFIX: All rendering is halted when a file chooser is open for any reason. For whatever reason, background canvas updates kill the CPU when file dialog is open so this is fixed now.

NEW: Memory inspector view lets you monitor read/write operations on a specific memory cell
BUGFIX: Retina screens are detected to prevent the memoryview canvas from getting too large.
This commit is contained in:
Brendan Robert 2015-09-05 09:02:23 -05:00
parent b17c5d6d94
commit 8942d6462e
12 changed files with 242 additions and 57 deletions

View File

@ -163,7 +163,6 @@ public class EmulatorUILogic implements Reconfigurable {
// watchValue.setText("00");
// }
// }
// public static void updateBreakpointList(final DebuggerPanel panel) {
// java.awt.EventQueue.invokeLater(() -> {
// Integer address;
@ -200,7 +199,7 @@ public class EmulatorUILogic implements Reconfigurable {
public static void runFile() {
Emulator.computer.pause();
FileChooser select = new FileChooser();
File binary = select.showOpenDialog(null);
File binary = select.showOpenDialog(JaceApplication.getApplication().primaryStage);
if (binary == null) {
Emulator.computer.resume();
return;
@ -336,7 +335,7 @@ public class EmulatorUILogic implements Reconfigurable {
Emulator.computer.pause();
Image i = Emulator.computer.getVideo().getFrameBuffer();
// BufferedImage bufImageARGB = SwingFXUtils.fromFXImage(i, null);
File targetFile = select.showSaveDialog(null);
File targetFile = select.showSaveDialog(JaceApplication.getApplication().primaryStage);
if (targetFile == null) {
return;
}
@ -375,7 +374,7 @@ public class EmulatorUILogic implements Reconfigurable {
throw new RuntimeException(exception);
}
}
@InvokableAction(
name = "Open IDE",
category = "development",
@ -404,12 +403,15 @@ public class EmulatorUILogic implements Reconfigurable {
}
static final Map<Object, Set<Label>> INDICATORS = new HashMap<>();
static public void addIndicator(Object owner, Label icon) {
addIndicator(owner, icon, 250);
}
static public void addIndicator(Object owner, Label icon, long TTL) {
if (JaceApplication.singleton == null) return;
if (JaceApplication.getApplication() == null) {
return;
}
synchronized (INDICATORS) {
Set<Label> ind = INDICATORS.get(owner);
if (ind == null) {
@ -417,12 +419,14 @@ public class EmulatorUILogic implements Reconfigurable {
INDICATORS.put(owner, ind);
}
ind.add(icon);
JaceApplication.singleton.controller.addIndicator(icon);
JaceApplication.getApplication().controller.addIndicator(icon);
}
}
static public void removeIndicator(Object owner, Label icon) {
if (JaceApplication.singleton == null) return;
if (JaceApplication.singleton == null) {
return;
}
synchronized (INDICATORS) {
Set<Label> ind = INDICATORS.get(owner);
if (ind != null) {
@ -433,7 +437,9 @@ public class EmulatorUILogic implements Reconfigurable {
}
static public void removeIndicators(Object owner) {
if (JaceApplication.singleton == null) return;
if (JaceApplication.singleton == null) {
return;
}
synchronized (INDICATORS) {
Set<Label> ind = INDICATORS.get(owner);
if (ind == null) {
@ -445,7 +451,7 @@ public class EmulatorUILogic implements Reconfigurable {
INDICATORS.remove(owner);
}
}
static public void addMouseListener(EventHandler<MouseEvent> handler) {
if (JaceApplication.singleton != null) {
JaceApplication.singleton.controller.addMouseListener(handler);
@ -459,7 +465,7 @@ public class EmulatorUILogic implements Reconfigurable {
}
public static void simulateCtrlAppleReset() {
Computer computer = JaceApplication.singleton.controller.computer;
Computer computer = JaceApplication.singleton.controller.computer;
computer.keyboard.openApple(true);
computer.warmStart();
Platform.runLater(() -> {
@ -471,7 +477,7 @@ public class EmulatorUILogic implements Reconfigurable {
computer.keyboard.openApple(false);
});
}
@Override
public String getName() {
return "Jace User Interface";

View File

@ -29,7 +29,7 @@ public class JaceApplication extends Application {
static JaceApplication singleton;
Stage primaryStage;
public Stage primaryStage;
JaceUIController controller;
static boolean romStarted = false;

View File

@ -463,7 +463,7 @@ public class MOS65C02 extends CPU {
}
implied = false;
} else if (fmt.contains("R")) {
this.f1 = fmt.substring(0, fmt.indexOf("R"));
this.f1 = fmt.substring(0, fmt.indexOf('R'));
f2 = "";
relative = true;
implied = false;
@ -552,8 +552,7 @@ public class MOS65C02 extends CPU {
@Override
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
int mask = 0x0ff ^ (1 << bit);
value &= mask;
cpu.getMemory().write(address, (byte) value, true, false);
cpu.getMemory().write(address, (byte) (value & mask), true, false);
}
}
@ -568,8 +567,7 @@ public class MOS65C02 extends CPU {
@Override
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
int mask = 1 << bit;
value |= mask;
cpu.getMemory().write(address, (byte) value, true, false);
cpu.getMemory().write(address, (byte) (value | mask), true, false);
}
}
@ -823,7 +821,7 @@ public class MOS65C02 extends CPU {
cpu.push((byte) cpu.A);
}),
PHP((address, value, addressMode, cpu) -> {
cpu.push((byte) (cpu.getStatus()));
cpu.push((cpu.getStatus()));
}),
PHX((address, value, addressMode, cpu) -> {
cpu.push((byte) cpu.X);
@ -975,13 +973,11 @@ public class MOS65C02 extends CPU {
}),
TRB((address, value, addressMode, cpu) -> {
cpu.C = (value & cpu.A) != 0 ? 1 : 0;
value &= ~cpu.A;
cpu.getMemory().write(address, (byte) value, true, false);
cpu.getMemory().write(address, (byte) (value & ~cpu.A), true, false);
}),
TSB((address, value, addressMode, cpu) -> {
cpu.C = (value & cpu.A) != 0 ? 1 : 0;
value |= cpu.A;
cpu.getMemory().write(address, (byte) value, true, false);
cpu.getMemory().write(address, (byte) (value | cpu.A), true, false);
}),
TSX((address, value, addressMode, cpu) -> {
cpu.X = cpu.STACK;
@ -1098,9 +1094,6 @@ public class MOS65C02 extends CPU {
+ Integer.toHexString(op)
+ " at " + Integer.toHexString(pc));
}
if (isLogEnabled()) {
dumpTrace();
}
if (breakOnBadOpcode) {
OPCODE.BRK.execute(this);
}

View File

@ -18,6 +18,7 @@
*/
package jace.apple2e;
import jace.JaceApplication;
import jace.config.ConfigurableField;
import jace.core.Computer;
import jace.core.Device;
@ -59,7 +60,7 @@ public class Speaker extends Device {
fileOutputActive = false;
} else {
FileChooser fileChooser = new FileChooser();
File f = fileChooser.showSaveDialog(null);
File f = fileChooser.showSaveDialog(JaceApplication.getApplication().primaryStage);
if (f == null) {
return;
}

View File

@ -27,7 +27,9 @@ public class MemoryCell implements Comparable<MemoryCell> {
public IntegerProperty writeCount = new SimpleIntegerProperty();
public BooleanBinding hasCount = readCount.add(execCount).add(writeCount).greaterThan(0);
public ObservableList<Integer> readInstructions = FXCollections.observableList(new ArrayList<>());
public ObservableList<String> readInstructionsDisassembly = FXCollections.observableArrayList(new ArrayList<>());
public ObservableList<Integer> writeInstructions = FXCollections.observableList(new ArrayList<>());
public ObservableList<String> writeInstructionsDisassembly = FXCollections.observableArrayList(new ArrayList<>());
private int x;
private int y;
private int width;
@ -76,6 +78,5 @@ public class MemoryCell implements Comparable<MemoryCell> {
public boolean hasCounts() {
return hasCount.get();
}
}
}

View File

@ -2,6 +2,7 @@ package jace.cheat;
import jace.Emulator;
import jace.JaceApplication;
import jace.core.CPU;
import jace.core.Computer;
import jace.core.RAM;
import jace.core.RAMEvent;
@ -15,8 +16,11 @@ import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
@ -327,10 +331,18 @@ public class MetaCheat extends Cheats {
}
}
AtomicInteger pendingInspectorUpdates = new AtomicInteger(0);
public void onInspectorChanged() {
pendingInspectorUpdates.set(0);
}
private void processMemoryEvent(RAMEvent e) {
MemoryCell cell = getMemoryCell(e.getAddress());
if (cell != null) {
int programCounter = Emulator.computer.getCpu().getProgramCounter();
CPU cpu = Emulator.computer.getCpu();
if (!cpu.isLogEnabled()) {
cpu.traceLength = 1;
}
switch (e.getType()) {
case EXECUTE:
case READ_OPERAND:
@ -338,16 +350,52 @@ public class MetaCheat extends Cheats {
break;
case WRITE:
cell.writeCount.set(Math.min(255, cell.writeCount.get() + lightRate));
cell.writeInstructions.add(programCounter);
if (cell.writeInstructions.size() > historyLength) {
cell.writeInstructions.remove(0);
if (ui.isInspecting(cell.address)) {
if (pendingInspectorUpdates.incrementAndGet() < 5) {
int pc = cpu.getProgramCounter();
String trace = cpu.getLastTrace();
Platform.runLater(() -> {
pendingInspectorUpdates.decrementAndGet();
cell.writeInstructions.add(pc);
cell.writeInstructionsDisassembly.add(trace);
if (cell.writeInstructions.size() > historyLength) {
cell.writeInstructions.remove(0);
cell.writeInstructionsDisassembly.remove(0);
}
});
}
} else {
cell.writeInstructions.add(cpu.getProgramCounter());
cell.writeInstructionsDisassembly.add(cpu.getLastTrace());
if (cell.writeInstructions.size() > historyLength) {
cell.writeInstructions.remove(0);
cell.writeInstructionsDisassembly.remove(0);
}
}
break;
default:
cell.readCount.set(Math.min(255, cell.readCount.get() + lightRate));
cell.readInstructions.add(programCounter);
if (cell.readInstructions.size() > historyLength) {
cell.readInstructions.remove(0);
if (ui.isInspecting(cell.address)) {
if (pendingInspectorUpdates.incrementAndGet() < 5) {
int pc = cpu.getProgramCounter();
String trace = cpu.getLastTrace();
Platform.runLater(() -> {
pendingInspectorUpdates.decrementAndGet();
cell.readInstructions.add(pc);
cell.readInstructionsDisassembly.add(trace);
if (cell.readInstructions.size() > historyLength) {
cell.readInstructions.remove(0);
cell.readInstructionsDisassembly.remove(0);
}
});
}
} else {
cell.readInstructions.add(cpu.getProgramCounter());
cell.readInstructionsDisassembly.add(cpu.getLastTrace());
if (cell.readInstructions.size() > historyLength) {
cell.readInstructions.remove(0);
cell.readInstructionsDisassembly.remove(0);
}
}
}
cell.value.set(e.getNewValue());
@ -380,7 +428,7 @@ public class MetaCheat extends Cheats {
in = new BufferedReader(new FileReader(saveFile));
StringBuilder guts = new StringBuilder();
String line;
while ((line=in.readLine()) != null) {
while ((line = in.readLine()) != null) {
DynamicCheat cheat = DynamicCheat.deserialize(line);
addCheat(cheat);
}

View File

@ -71,6 +71,14 @@ public abstract class CPU extends Device {
}
traceLog.add(line);
}
public String getLastTrace() {
if (traceLog.isEmpty()) {
return "???";
} else {
return traceLog.get(traceLog.size()-1);
}
}
public void dumpTrace() {
computer.pause();

View File

@ -131,7 +131,9 @@ public abstract class Video extends Device {
public static int MIN_SCREEN_REFRESH = 15;
Runnable redrawScreen = () -> {
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
if (computer.getRunningProperty().get()) {
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
}
};
public void redraw() {
screenDirty = false;

View File

@ -5,6 +5,7 @@
*/
package jace.ide;
import jace.JaceApplication;
import jace.ide.Program.DocumentType;
import jace.ide.Program.Option;
import java.io.File;
@ -149,7 +150,7 @@ public class IdeController {
chooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("All Files", "*.*")
);
File file = chooser.showOpenDialog(null);
File file = chooser.showOpenDialog(JaceApplication.getApplication().primaryStage);
if (file != null && file.isFile() && file.exists()) {
DocumentType type = DocumentType.fromFile(file);
createTab(type, file);
@ -196,7 +197,7 @@ public class IdeController {
new FileChooser.ExtensionFilter(type.name(), type.extensions),
new FileChooser.ExtensionFilter("All Files", "*.*")
);
return chooser.showSaveDialog(null);
return chooser.showSaveDialog(JaceApplication.getApplication().primaryStage);
}
@FXML

View File

@ -2,6 +2,7 @@ package jace.ui;
import com.sun.glass.ui.Application;
import jace.Emulator;
import jace.JaceApplication;
import jace.cheat.DynamicCheat;
import jace.cheat.MemoryCell;
import jace.cheat.MetaCheat;
@ -47,11 +48,14 @@ import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.stage.FileChooser;
import javafx.stage.Screen;
import javafx.util.converter.DefaultStringConverter;
import javafx.util.converter.IntegerStringConverter;
public class MetacheatUI {
boolean isRetina;
double drawScale;
@FXML
private Button pauseButton;
@ -127,6 +131,15 @@ public class MetacheatUI {
@FXML
private TableView<DynamicCheat> cheatsTableView;
@FXML
private TextField codeInspectorAddress;
@FXML
private ListView<String> codeInspectorWriteList;
@FXML
private ListView<String> codeInspectorReadList;
@FXML
void createSnapshot(ActionEvent event) {
@ -154,24 +167,32 @@ public class MetacheatUI {
@FXML
void loadCheats(ActionEvent event) {
boolean resume = Emulator.computer.pause();
FileChooser chooser = new FileChooser();
chooser.setTitle("Load cheats");
chooser.setInitialFileName("cheat.txt");
File saveFile = chooser.showOpenDialog(null);
File saveFile = chooser.showOpenDialog(JaceApplication.getApplication().primaryStage);
if (saveFile != null) {
cheatEngine.loadCheats(saveFile);
}
}
if (resume) {
Emulator.computer.resume();
}
}
@FXML
void saveCheats(ActionEvent event) {
boolean resume = Emulator.computer.pause();
FileChooser chooser = new FileChooser();
chooser.setTitle("Save current cheats");
chooser.setInitialFileName("cheat.txt");
File saveFile = chooser.showSaveDialog(null);
File saveFile = chooser.showSaveDialog(JaceApplication.getApplication().primaryStage);
if (saveFile != null) {
cheatEngine.saveCheats(saveFile);
}
if (resume) {
Emulator.computer.resume();
}
}
@FXML
@ -234,9 +255,16 @@ public class MetacheatUI {
assert searchResultsListView != null : "fx:id=\"searchResultsListView\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert watchesPane != null : "fx:id=\"watchesPane\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert snapshotsListView != null : "fx:id=\"snapshotsListView\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert codeInspectorAddress != null : "fx:id=\"codeInspectorAddress\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert codeInspectorWriteList != null : "fx:id=\"codeInspectorWriteList\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert codeInspectorReadList != null : "fx:id=\"codeInspectorReadList\" was not injected: check your FXML file 'Metacheat.fxml'.";
assert cheatsTableView != null : "fx:id=\"cheatsTableView\" was not injected: check your FXML file 'Metacheat.fxml'.";
Emulator.computer.getRunningProperty().addListener((val, oldVal, newVal) -> pauseButton.setText(newVal ? "Pause" : "Resume"));
isRetina = Screen.getPrimary().getDpi() >= 110;
Emulator.computer.getRunningProperty().addListener((val, oldVal, newVal) -> {
Platform.runLater(() -> pauseButton.setText(newVal ? "Pause" : "Resume"));
});
searchTypesTabPane.getTabs().get(0).setUserData(SearchType.VALUE);
searchTypesTabPane.getTabs().get(1).setUserData(SearchType.CHANGE);
@ -281,7 +309,9 @@ public class MetacheatUI {
}
});
memoryViewPane.boundsInParentProperty().addListener((prop, oldVal, newVal) -> redrawMemoryView());
memoryViewCanvas.widthProperty().bind(memoryViewPane.widthProperty());
drawScale = isRetina ? 0.5 : 1.0;
memoryViewCanvas.widthProperty().bind(memoryViewPane.widthProperty().multiply(drawScale));
setZoom(1/drawScale);
watchesPane.setHgap(5);
watchesPane.setVgap(5);
@ -316,6 +346,18 @@ public class MetacheatUI {
TableColumn<DynamicCheat, String> exprColumn = (TableColumn<DynamicCheat, String>) cheatsTableView.getColumns().get(3);
exprColumn.setCellValueFactory(new PropertyValueFactory<>("expression"));
exprColumn.setCellFactory((TableColumn<DynamicCheat, String> param) -> new TextFieldTableCell<>(new DefaultStringConverter()));
codeInspectorAddress.textProperty().addListener((prop, oldValue, newValue) -> {
try {
int address = cheatEngine.parseInt(newValue);
MemoryCell cell = cheatEngine.getMemoryCell(address);
currentlyInspecting = address;
cheatEngine.onInspectorChanged();
codeInspectorReadList.setItems(cell.readInstructionsDisassembly);
codeInspectorWriteList.setItems(cell.writeInstructionsDisassembly);
} catch (NumberFormatException ex) {
}
});
}
MetaCheat cheatEngine = null;
@ -354,8 +396,8 @@ public class MetacheatUI {
currentWatch.disconnect();
}
double x = e.getX();
double y = e.getY();
double x = e.getX() / drawScale;
double y = e.getY() / drawScale;
int col = (int) (x / MEMORY_BOX_TOTAL_SIZE);
int row = (int) (y / MEMORY_BOX_TOTAL_SIZE);
int addr = cheatEngine.getStartAddress() + row * memoryViewColumns + col;
@ -388,10 +430,13 @@ public class MetacheatUI {
}
private void processMemoryViewUpdates() {
if (!Emulator.computer.getRunningProperty().get()) {
return;
}
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
Set<MemoryCell> draw = new HashSet<>(redrawNodes);
redrawNodes.clear();
Application.invokeLater(() -> {
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
Set<MemoryCell> draw = new HashSet<>(redrawNodes);
redrawNodes.clear();
draw.stream().forEach((jace.cheat.MemoryCell cell) -> {
if (showValuesCheckbox.isSelected()) {
int val = cell.value.get() & 0x0ff;
@ -416,7 +461,7 @@ public class MetacheatUI {
boolean resume = Emulator.computer.pause();
if (animationTimer == null) {
animationTimer = new ScheduledThreadPoolExecutor(10);
animationTimer = new ScheduledThreadPoolExecutor(1);
}
if (animationFuture != null) {
@ -427,9 +472,18 @@ public class MetacheatUI {
cheatEngine.initMemoryView();
int pixelsPerBlock = 16 * MEMORY_BOX_TOTAL_SIZE;
memoryViewColumns = ((int) memoryViewPane.getWidth()) / pixelsPerBlock * 16;
memoryViewColumns = (int) (memoryViewPane.getWidth() / pixelsPerBlock * 16);
memoryViewRows = ((cheatEngine.getEndAddress() - cheatEngine.getStartAddress()) / memoryViewColumns) + 1;
int canvasHeight = memoryViewRows * MEMORY_BOX_TOTAL_SIZE;
double canvasHeight = memoryViewRows * MEMORY_BOX_TOTAL_SIZE * drawScale;
// Guard against asking for a big texture because bad things will happen!
while (canvasHeight >= 16384) {
memoryViewColumns += 16;
memoryViewRows = ((cheatEngine.getEndAddress() - cheatEngine.getStartAddress()) / memoryViewColumns) + 1;
canvasHeight = memoryViewRows * MEMORY_BOX_TOTAL_SIZE * drawScale;
}
double canvasWidth = memoryViewColumns * MEMORY_BOX_TOTAL_SIZE * drawScale;
memoryViewContents.setPrefHeight(canvasHeight);
memoryViewCanvas.setHeight(canvasHeight);
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
@ -439,12 +493,18 @@ public class MetacheatUI {
int col = (addr - cheatEngine.getStartAddress()) % memoryViewColumns;
int row = (addr - cheatEngine.getStartAddress()) / memoryViewColumns;
MemoryCell cell = cheatEngine.getMemoryCell(addr);
cell.setRect(col * MEMORY_BOX_TOTAL_SIZE, row * MEMORY_BOX_TOTAL_SIZE, MEMORY_BOX_SIZE, MEMORY_BOX_SIZE);
cell.setRect(
(int) (col * MEMORY_BOX_TOTAL_SIZE * drawScale),
(int) (row * MEMORY_BOX_TOTAL_SIZE * drawScale),
(int) (MEMORY_BOX_SIZE * drawScale),
(int) (MEMORY_BOX_SIZE * drawScale));
redrawNodes.add(cell);
}
MemoryCell.setListener((javafx.beans.value.ObservableValue<? extends jace.cheat.MemoryCell> prop, jace.cheat.MemoryCell oldCell, jace.cheat.MemoryCell newCell) -> {
redrawNodes.add(newCell);
});
setZoom(1/drawScale);
if (resume) {
Emulator.computer.resume();
@ -455,6 +515,12 @@ public class MetacheatUI {
if (memoryViewCanvas != null) {
double zoom = memoryViewCanvas.getScaleX();
zoom += amount;
setZoom(zoom);
}
}
private void setZoom(double zoom) {
if (memoryViewCanvas != null) {
memoryViewCanvas.setScaleX(zoom);
memoryViewCanvas.setScaleY(zoom);
StackPane scrollArea = (StackPane) memoryViewCanvas.getParent();
@ -509,4 +575,14 @@ public class MetacheatUI {
private void addCheat(int addr, int val) {
cheatEngine.addCheat(new DynamicCheat(addr, String.valueOf(val)));
}
int currentlyInspecting = 0;
public void inspectAddress(int address) {
codeInspectorAddress.setText("$" + Integer.toHexString(address));
}
public boolean isInspecting(int address) {
return currentlyInspecting == address;
}
}

View File

@ -6,6 +6,7 @@
package jace.ui;
import jace.Emulator;
import jace.cheat.MemoryCell;
import jace.core.RAMListener;
import java.util.ArrayList;
import java.util.Collections;
@ -19,6 +20,7 @@ import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
@ -32,6 +34,7 @@ import javafx.scene.text.TextAlignment;
* @author blurry
*/
class Watch extends VBox {
private static final int GRAPH_WIDTH = 50;
private static final double GRAPH_HEIGHT = 50;
int address;
@ -41,11 +44,13 @@ class Watch extends VBox {
int value = 0;
BooleanProperty holding = null;
private final MetacheatUI outer;
MemoryCell cell;
public Watch(int address, final MetacheatUI outer) {
super();
this.outer = outer;
this.address = address;
cell = outer.cheatEngine.getMemoryCell(address);
redraw = outer.animationTimer.scheduleAtFixedRate(this::redraw, MetacheatUI.FRAME_RATE, MetacheatUI.FRAME_RATE, TimeUnit.MILLISECONDS);
setBackground(new Background(new BackgroundFill(Color.NAVY, CornerRadii.EMPTY, Insets.EMPTY)));
Label addrLabel = new Label("$" + Integer.toHexString(address));
@ -53,6 +58,12 @@ class Watch extends VBox {
addrLabel.setMinWidth(GRAPH_WIDTH);
addrLabel.setFont(new Font(Font.getDefault().getFamily(), 14));
addrLabel.setTextFill(Color.WHITE);
addrLabel.setMouseTransparent(false);
addrLabel.setOnMouseClicked((MouseEvent evt)-> {
if (evt.isPrimaryButtonDown()) {
outer.inspectAddress(address);
}
});
graph = new Canvas(GRAPH_WIDTH, GRAPH_HEIGHT);
getChildren().add(addrLabel);
getChildren().add(graph);
@ -68,7 +79,10 @@ class Watch extends VBox {
}
public void redraw() {
int val = outer.cheatEngine.getMemoryCell(address).value.get() & 0x0ff;
if (!Emulator.computer.getRunningProperty().get()) {
return;
}
int val = cell.value.get() & 0x0ff;
if (!holding.get()) {
value = val;
}
@ -117,5 +131,5 @@ class Watch extends VBox {
holding.set(false);
redraw.cancel(false);
}
}

View File

@ -194,6 +194,41 @@
</BorderPane>
</content>
</TitledPane>
<TitledPane expanded="false" text="Inspector" VBox.vgrow="SOMETIMES">
<content>
<VBox>
<children>
<HBox prefHeight="33.0" prefWidth="236.0" spacing="4.0" VBox.vgrow="NEVER">
<children>
<Label alignment="CENTER_RIGHT" prefHeight="26.0" prefWidth="68.0" text="Address" textAlignment="RIGHT" HBox.hgrow="NEVER">
<opaqueInsets>
<Insets />
</opaqueInsets>
</Label>
<TextField fx:id="codeInspectorAddress" prefHeight="26.0" prefWidth="94.0" HBox.hgrow="ALWAYS" />
</children>
<opaqueInsets>
<Insets />
</opaqueInsets>
</HBox>
<TabPane minHeight="-Infinity" prefHeight="102.0" prefWidth="236.0" side="LEFT" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="SOMETIMES">
<tabs>
<Tab text="W">
<content>
<ListView fx:id="codeInspectorWriteList" editable="true" />
</content>
</Tab>
<Tab text="R">
<content>
<ListView fx:id="codeInspectorReadList" editable="true" />
</content>
</Tab>
</tabs>
</TabPane>
</children>
</VBox>
</content>
</TitledPane>
<TitledPane expanded="false" text="Snapshots" VBox.vgrow="NEVER">
<content>
<BorderPane minWidth="-Infinity">