mirror of
https://github.com/badvision/jace.git
synced 2025-01-06 08:31:25 +00:00
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:
parent
b17c5d6d94
commit
8942d6462e
@ -163,7 +163,6 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
// watchValue.setText("00");
|
// watchValue.setText("00");
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// public static void updateBreakpointList(final DebuggerPanel panel) {
|
// public static void updateBreakpointList(final DebuggerPanel panel) {
|
||||||
// java.awt.EventQueue.invokeLater(() -> {
|
// java.awt.EventQueue.invokeLater(() -> {
|
||||||
// Integer address;
|
// Integer address;
|
||||||
@ -200,7 +199,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
public static void runFile() {
|
public static void runFile() {
|
||||||
Emulator.computer.pause();
|
Emulator.computer.pause();
|
||||||
FileChooser select = new FileChooser();
|
FileChooser select = new FileChooser();
|
||||||
File binary = select.showOpenDialog(null);
|
File binary = select.showOpenDialog(JaceApplication.getApplication().primaryStage);
|
||||||
if (binary == null) {
|
if (binary == null) {
|
||||||
Emulator.computer.resume();
|
Emulator.computer.resume();
|
||||||
return;
|
return;
|
||||||
@ -336,7 +335,7 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
Emulator.computer.pause();
|
Emulator.computer.pause();
|
||||||
Image i = Emulator.computer.getVideo().getFrameBuffer();
|
Image i = Emulator.computer.getVideo().getFrameBuffer();
|
||||||
// BufferedImage bufImageARGB = SwingFXUtils.fromFXImage(i, null);
|
// BufferedImage bufImageARGB = SwingFXUtils.fromFXImage(i, null);
|
||||||
File targetFile = select.showSaveDialog(null);
|
File targetFile = select.showSaveDialog(JaceApplication.getApplication().primaryStage);
|
||||||
if (targetFile == null) {
|
if (targetFile == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -404,12 +403,15 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final Map<Object, Set<Label>> INDICATORS = new HashMap<>();
|
static final Map<Object, Set<Label>> INDICATORS = new HashMap<>();
|
||||||
|
|
||||||
static public void addIndicator(Object owner, Label icon) {
|
static public void addIndicator(Object owner, Label icon) {
|
||||||
addIndicator(owner, icon, 250);
|
addIndicator(owner, icon, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
static public void addIndicator(Object owner, Label icon, long TTL) {
|
static public void addIndicator(Object owner, Label icon, long TTL) {
|
||||||
if (JaceApplication.singleton == null) return;
|
if (JaceApplication.getApplication() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
synchronized (INDICATORS) {
|
synchronized (INDICATORS) {
|
||||||
Set<Label> ind = INDICATORS.get(owner);
|
Set<Label> ind = INDICATORS.get(owner);
|
||||||
if (ind == null) {
|
if (ind == null) {
|
||||||
@ -417,12 +419,14 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
INDICATORS.put(owner, ind);
|
INDICATORS.put(owner, ind);
|
||||||
}
|
}
|
||||||
ind.add(icon);
|
ind.add(icon);
|
||||||
JaceApplication.singleton.controller.addIndicator(icon);
|
JaceApplication.getApplication().controller.addIndicator(icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static public void removeIndicator(Object owner, Label icon) {
|
static public void removeIndicator(Object owner, Label icon) {
|
||||||
if (JaceApplication.singleton == null) return;
|
if (JaceApplication.singleton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
synchronized (INDICATORS) {
|
synchronized (INDICATORS) {
|
||||||
Set<Label> ind = INDICATORS.get(owner);
|
Set<Label> ind = INDICATORS.get(owner);
|
||||||
if (ind != null) {
|
if (ind != null) {
|
||||||
@ -433,7 +437,9 @@ public class EmulatorUILogic implements Reconfigurable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static public void removeIndicators(Object owner) {
|
static public void removeIndicators(Object owner) {
|
||||||
if (JaceApplication.singleton == null) return;
|
if (JaceApplication.singleton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
synchronized (INDICATORS) {
|
synchronized (INDICATORS) {
|
||||||
Set<Label> ind = INDICATORS.get(owner);
|
Set<Label> ind = INDICATORS.get(owner);
|
||||||
if (ind == null) {
|
if (ind == null) {
|
||||||
|
@ -29,7 +29,7 @@ public class JaceApplication extends Application {
|
|||||||
|
|
||||||
static JaceApplication singleton;
|
static JaceApplication singleton;
|
||||||
|
|
||||||
Stage primaryStage;
|
public Stage primaryStage;
|
||||||
JaceUIController controller;
|
JaceUIController controller;
|
||||||
|
|
||||||
static boolean romStarted = false;
|
static boolean romStarted = false;
|
||||||
|
@ -463,7 +463,7 @@ public class MOS65C02 extends CPU {
|
|||||||
}
|
}
|
||||||
implied = false;
|
implied = false;
|
||||||
} else if (fmt.contains("R")) {
|
} else if (fmt.contains("R")) {
|
||||||
this.f1 = fmt.substring(0, fmt.indexOf("R"));
|
this.f1 = fmt.substring(0, fmt.indexOf('R'));
|
||||||
f2 = "";
|
f2 = "";
|
||||||
relative = true;
|
relative = true;
|
||||||
implied = false;
|
implied = false;
|
||||||
@ -552,8 +552,7 @@ public class MOS65C02 extends CPU {
|
|||||||
@Override
|
@Override
|
||||||
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
|
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
|
||||||
int mask = 0x0ff ^ (1 << bit);
|
int mask = 0x0ff ^ (1 << bit);
|
||||||
value &= mask;
|
cpu.getMemory().write(address, (byte) (value & mask), true, false);
|
||||||
cpu.getMemory().write(address, (byte) value, true, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,8 +567,7 @@ public class MOS65C02 extends CPU {
|
|||||||
@Override
|
@Override
|
||||||
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
|
public void processCommand(int address, int value, MODE addressMode, MOS65C02 cpu) {
|
||||||
int mask = 1 << bit;
|
int mask = 1 << bit;
|
||||||
value |= mask;
|
cpu.getMemory().write(address, (byte) (value | mask), true, false);
|
||||||
cpu.getMemory().write(address, (byte) value, true, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -823,7 +821,7 @@ public class MOS65C02 extends CPU {
|
|||||||
cpu.push((byte) cpu.A);
|
cpu.push((byte) cpu.A);
|
||||||
}),
|
}),
|
||||||
PHP((address, value, addressMode, cpu) -> {
|
PHP((address, value, addressMode, cpu) -> {
|
||||||
cpu.push((byte) (cpu.getStatus()));
|
cpu.push((cpu.getStatus()));
|
||||||
}),
|
}),
|
||||||
PHX((address, value, addressMode, cpu) -> {
|
PHX((address, value, addressMode, cpu) -> {
|
||||||
cpu.push((byte) cpu.X);
|
cpu.push((byte) cpu.X);
|
||||||
@ -975,13 +973,11 @@ public class MOS65C02 extends CPU {
|
|||||||
}),
|
}),
|
||||||
TRB((address, value, addressMode, cpu) -> {
|
TRB((address, value, addressMode, cpu) -> {
|
||||||
cpu.C = (value & cpu.A) != 0 ? 1 : 0;
|
cpu.C = (value & cpu.A) != 0 ? 1 : 0;
|
||||||
value &= ~cpu.A;
|
cpu.getMemory().write(address, (byte) (value & ~cpu.A), true, false);
|
||||||
cpu.getMemory().write(address, (byte) value, true, false);
|
|
||||||
}),
|
}),
|
||||||
TSB((address, value, addressMode, cpu) -> {
|
TSB((address, value, addressMode, cpu) -> {
|
||||||
cpu.C = (value & cpu.A) != 0 ? 1 : 0;
|
cpu.C = (value & cpu.A) != 0 ? 1 : 0;
|
||||||
value |= cpu.A;
|
cpu.getMemory().write(address, (byte) (value | cpu.A), true, false);
|
||||||
cpu.getMemory().write(address, (byte) value, true, false);
|
|
||||||
}),
|
}),
|
||||||
TSX((address, value, addressMode, cpu) -> {
|
TSX((address, value, addressMode, cpu) -> {
|
||||||
cpu.X = cpu.STACK;
|
cpu.X = cpu.STACK;
|
||||||
@ -1098,9 +1094,6 @@ public class MOS65C02 extends CPU {
|
|||||||
+ Integer.toHexString(op)
|
+ Integer.toHexString(op)
|
||||||
+ " at " + Integer.toHexString(pc));
|
+ " at " + Integer.toHexString(pc));
|
||||||
}
|
}
|
||||||
if (isLogEnabled()) {
|
|
||||||
dumpTrace();
|
|
||||||
}
|
|
||||||
if (breakOnBadOpcode) {
|
if (breakOnBadOpcode) {
|
||||||
OPCODE.BRK.execute(this);
|
OPCODE.BRK.execute(this);
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package jace.apple2e;
|
package jace.apple2e;
|
||||||
|
|
||||||
|
import jace.JaceApplication;
|
||||||
import jace.config.ConfigurableField;
|
import jace.config.ConfigurableField;
|
||||||
import jace.core.Computer;
|
import jace.core.Computer;
|
||||||
import jace.core.Device;
|
import jace.core.Device;
|
||||||
@ -59,7 +60,7 @@ public class Speaker extends Device {
|
|||||||
fileOutputActive = false;
|
fileOutputActive = false;
|
||||||
} else {
|
} else {
|
||||||
FileChooser fileChooser = new FileChooser();
|
FileChooser fileChooser = new FileChooser();
|
||||||
File f = fileChooser.showSaveDialog(null);
|
File f = fileChooser.showSaveDialog(JaceApplication.getApplication().primaryStage);
|
||||||
if (f == null) {
|
if (f == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,9 @@ public class MemoryCell implements Comparable<MemoryCell> {
|
|||||||
public IntegerProperty writeCount = new SimpleIntegerProperty();
|
public IntegerProperty writeCount = new SimpleIntegerProperty();
|
||||||
public BooleanBinding hasCount = readCount.add(execCount).add(writeCount).greaterThan(0);
|
public BooleanBinding hasCount = readCount.add(execCount).add(writeCount).greaterThan(0);
|
||||||
public ObservableList<Integer> readInstructions = FXCollections.observableList(new ArrayList<>());
|
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<Integer> writeInstructions = FXCollections.observableList(new ArrayList<>());
|
||||||
|
public ObservableList<String> writeInstructionsDisassembly = FXCollections.observableArrayList(new ArrayList<>());
|
||||||
private int x;
|
private int x;
|
||||||
private int y;
|
private int y;
|
||||||
private int width;
|
private int width;
|
||||||
@ -77,5 +79,4 @@ public class MemoryCell implements Comparable<MemoryCell> {
|
|||||||
public boolean hasCounts() {
|
public boolean hasCounts() {
|
||||||
return hasCount.get();
|
return hasCount.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package jace.cheat;
|
|||||||
|
|
||||||
import jace.Emulator;
|
import jace.Emulator;
|
||||||
import jace.JaceApplication;
|
import jace.JaceApplication;
|
||||||
|
import jace.core.CPU;
|
||||||
import jace.core.Computer;
|
import jace.core.Computer;
|
||||||
import jace.core.RAM;
|
import jace.core.RAM;
|
||||||
import jace.core.RAMEvent;
|
import jace.core.RAMEvent;
|
||||||
@ -15,8 +16,11 @@ import java.io.FileWriter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
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.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
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) {
|
private void processMemoryEvent(RAMEvent e) {
|
||||||
MemoryCell cell = getMemoryCell(e.getAddress());
|
MemoryCell cell = getMemoryCell(e.getAddress());
|
||||||
if (cell != null) {
|
if (cell != null) {
|
||||||
int programCounter = Emulator.computer.getCpu().getProgramCounter();
|
CPU cpu = Emulator.computer.getCpu();
|
||||||
|
if (!cpu.isLogEnabled()) {
|
||||||
|
cpu.traceLength = 1;
|
||||||
|
}
|
||||||
switch (e.getType()) {
|
switch (e.getType()) {
|
||||||
case EXECUTE:
|
case EXECUTE:
|
||||||
case READ_OPERAND:
|
case READ_OPERAND:
|
||||||
@ -338,16 +350,52 @@ public class MetaCheat extends Cheats {
|
|||||||
break;
|
break;
|
||||||
case WRITE:
|
case WRITE:
|
||||||
cell.writeCount.set(Math.min(255, cell.writeCount.get() + lightRate));
|
cell.writeCount.set(Math.min(255, cell.writeCount.get() + lightRate));
|
||||||
cell.writeInstructions.add(programCounter);
|
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) {
|
if (cell.writeInstructions.size() > historyLength) {
|
||||||
cell.writeInstructions.remove(0);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
cell.readCount.set(Math.min(255, cell.readCount.get() + lightRate));
|
cell.readCount.set(Math.min(255, cell.readCount.get() + lightRate));
|
||||||
cell.readInstructions.add(programCounter);
|
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) {
|
if (cell.readInstructions.size() > historyLength) {
|
||||||
cell.readInstructions.remove(0);
|
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());
|
cell.value.set(e.getNewValue());
|
||||||
|
@ -72,6 +72,14 @@ public abstract class CPU extends Device {
|
|||||||
traceLog.add(line);
|
traceLog.add(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLastTrace() {
|
||||||
|
if (traceLog.isEmpty()) {
|
||||||
|
return "???";
|
||||||
|
} else {
|
||||||
|
return traceLog.get(traceLog.size()-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void dumpTrace() {
|
public void dumpTrace() {
|
||||||
computer.pause();
|
computer.pause();
|
||||||
ArrayList<String> newLog = new ArrayList<>();
|
ArrayList<String> newLog = new ArrayList<>();
|
||||||
|
@ -131,7 +131,9 @@ public abstract class Video extends Device {
|
|||||||
public static int MIN_SCREEN_REFRESH = 15;
|
public static int MIN_SCREEN_REFRESH = 15;
|
||||||
|
|
||||||
Runnable redrawScreen = () -> {
|
Runnable redrawScreen = () -> {
|
||||||
|
if (computer.getRunningProperty().get()) {
|
||||||
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
|
visible.getPixelWriter().setPixels(0, 0, 560, 192, video.getPixelReader(), 0, 0);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
public void redraw() {
|
public void redraw() {
|
||||||
screenDirty = false;
|
screenDirty = false;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package jace.ide;
|
package jace.ide;
|
||||||
|
|
||||||
|
import jace.JaceApplication;
|
||||||
import jace.ide.Program.DocumentType;
|
import jace.ide.Program.DocumentType;
|
||||||
import jace.ide.Program.Option;
|
import jace.ide.Program.Option;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -149,7 +150,7 @@ public class IdeController {
|
|||||||
chooser.getExtensionFilters().addAll(
|
chooser.getExtensionFilters().addAll(
|
||||||
new FileChooser.ExtensionFilter("All Files", "*.*")
|
new FileChooser.ExtensionFilter("All Files", "*.*")
|
||||||
);
|
);
|
||||||
File file = chooser.showOpenDialog(null);
|
File file = chooser.showOpenDialog(JaceApplication.getApplication().primaryStage);
|
||||||
if (file != null && file.isFile() && file.exists()) {
|
if (file != null && file.isFile() && file.exists()) {
|
||||||
DocumentType type = DocumentType.fromFile(file);
|
DocumentType type = DocumentType.fromFile(file);
|
||||||
createTab(type, file);
|
createTab(type, file);
|
||||||
@ -196,7 +197,7 @@ public class IdeController {
|
|||||||
new FileChooser.ExtensionFilter(type.name(), type.extensions),
|
new FileChooser.ExtensionFilter(type.name(), type.extensions),
|
||||||
new FileChooser.ExtensionFilter("All Files", "*.*")
|
new FileChooser.ExtensionFilter("All Files", "*.*")
|
||||||
);
|
);
|
||||||
return chooser.showSaveDialog(null);
|
return chooser.showSaveDialog(JaceApplication.getApplication().primaryStage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -2,6 +2,7 @@ package jace.ui;
|
|||||||
|
|
||||||
import com.sun.glass.ui.Application;
|
import com.sun.glass.ui.Application;
|
||||||
import jace.Emulator;
|
import jace.Emulator;
|
||||||
|
import jace.JaceApplication;
|
||||||
import jace.cheat.DynamicCheat;
|
import jace.cheat.DynamicCheat;
|
||||||
import jace.cheat.MemoryCell;
|
import jace.cheat.MemoryCell;
|
||||||
import jace.cheat.MetaCheat;
|
import jace.cheat.MetaCheat;
|
||||||
@ -47,10 +48,13 @@ import javafx.scene.layout.StackPane;
|
|||||||
import javafx.scene.layout.TilePane;
|
import javafx.scene.layout.TilePane;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.stage.Screen;
|
||||||
import javafx.util.converter.DefaultStringConverter;
|
import javafx.util.converter.DefaultStringConverter;
|
||||||
import javafx.util.converter.IntegerStringConverter;
|
import javafx.util.converter.IntegerStringConverter;
|
||||||
|
|
||||||
public class MetacheatUI {
|
public class MetacheatUI {
|
||||||
|
boolean isRetina;
|
||||||
|
double drawScale;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Button pauseButton;
|
private Button pauseButton;
|
||||||
@ -127,6 +131,15 @@ public class MetacheatUI {
|
|||||||
@FXML
|
@FXML
|
||||||
private TableView<DynamicCheat> cheatsTableView;
|
private TableView<DynamicCheat> cheatsTableView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField codeInspectorAddress;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<String> codeInspectorWriteList;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<String> codeInspectorReadList;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void createSnapshot(ActionEvent event) {
|
void createSnapshot(ActionEvent event) {
|
||||||
|
|
||||||
@ -154,24 +167,32 @@ public class MetacheatUI {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void loadCheats(ActionEvent event) {
|
void loadCheats(ActionEvent event) {
|
||||||
|
boolean resume = Emulator.computer.pause();
|
||||||
FileChooser chooser = new FileChooser();
|
FileChooser chooser = new FileChooser();
|
||||||
chooser.setTitle("Load cheats");
|
chooser.setTitle("Load cheats");
|
||||||
chooser.setInitialFileName("cheat.txt");
|
chooser.setInitialFileName("cheat.txt");
|
||||||
File saveFile = chooser.showOpenDialog(null);
|
File saveFile = chooser.showOpenDialog(JaceApplication.getApplication().primaryStage);
|
||||||
if (saveFile != null) {
|
if (saveFile != null) {
|
||||||
cheatEngine.loadCheats(saveFile);
|
cheatEngine.loadCheats(saveFile);
|
||||||
}
|
}
|
||||||
|
if (resume) {
|
||||||
|
Emulator.computer.resume();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void saveCheats(ActionEvent event) {
|
void saveCheats(ActionEvent event) {
|
||||||
|
boolean resume = Emulator.computer.pause();
|
||||||
FileChooser chooser = new FileChooser();
|
FileChooser chooser = new FileChooser();
|
||||||
chooser.setTitle("Save current cheats");
|
chooser.setTitle("Save current cheats");
|
||||||
chooser.setInitialFileName("cheat.txt");
|
chooser.setInitialFileName("cheat.txt");
|
||||||
File saveFile = chooser.showSaveDialog(null);
|
File saveFile = chooser.showSaveDialog(JaceApplication.getApplication().primaryStage);
|
||||||
if (saveFile != null) {
|
if (saveFile != null) {
|
||||||
cheatEngine.saveCheats(saveFile);
|
cheatEngine.saveCheats(saveFile);
|
||||||
}
|
}
|
||||||
|
if (resume) {
|
||||||
|
Emulator.computer.resume();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@ -234,9 +255,16 @@ public class MetacheatUI {
|
|||||||
assert searchResultsListView != null : "fx:id=\"searchResultsListView\" was not injected: check your FXML file 'Metacheat.fxml'.";
|
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 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 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'.";
|
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(0).setUserData(SearchType.VALUE);
|
||||||
searchTypesTabPane.getTabs().get(1).setUserData(SearchType.CHANGE);
|
searchTypesTabPane.getTabs().get(1).setUserData(SearchType.CHANGE);
|
||||||
@ -281,7 +309,9 @@ public class MetacheatUI {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
memoryViewPane.boundsInParentProperty().addListener((prop, oldVal, newVal) -> redrawMemoryView());
|
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.setHgap(5);
|
||||||
watchesPane.setVgap(5);
|
watchesPane.setVgap(5);
|
||||||
@ -316,6 +346,18 @@ public class MetacheatUI {
|
|||||||
TableColumn<DynamicCheat, String> exprColumn = (TableColumn<DynamicCheat, String>) cheatsTableView.getColumns().get(3);
|
TableColumn<DynamicCheat, String> exprColumn = (TableColumn<DynamicCheat, String>) cheatsTableView.getColumns().get(3);
|
||||||
exprColumn.setCellValueFactory(new PropertyValueFactory<>("expression"));
|
exprColumn.setCellValueFactory(new PropertyValueFactory<>("expression"));
|
||||||
exprColumn.setCellFactory((TableColumn<DynamicCheat, String> param) -> new TextFieldTableCell<>(new DefaultStringConverter()));
|
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;
|
MetaCheat cheatEngine = null;
|
||||||
|
|
||||||
@ -354,8 +396,8 @@ public class MetacheatUI {
|
|||||||
currentWatch.disconnect();
|
currentWatch.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
double x = e.getX();
|
double x = e.getX() / drawScale;
|
||||||
double y = e.getY();
|
double y = e.getY() / drawScale;
|
||||||
int col = (int) (x / MEMORY_BOX_TOTAL_SIZE);
|
int col = (int) (x / MEMORY_BOX_TOTAL_SIZE);
|
||||||
int row = (int) (y / MEMORY_BOX_TOTAL_SIZE);
|
int row = (int) (y / MEMORY_BOX_TOTAL_SIZE);
|
||||||
int addr = cheatEngine.getStartAddress() + row * memoryViewColumns + col;
|
int addr = cheatEngine.getStartAddress() + row * memoryViewColumns + col;
|
||||||
@ -388,10 +430,13 @@ public class MetacheatUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processMemoryViewUpdates() {
|
private void processMemoryViewUpdates() {
|
||||||
Application.invokeLater(() -> {
|
if (!Emulator.computer.getRunningProperty().get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
|
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
|
||||||
Set<MemoryCell> draw = new HashSet<>(redrawNodes);
|
Set<MemoryCell> draw = new HashSet<>(redrawNodes);
|
||||||
redrawNodes.clear();
|
redrawNodes.clear();
|
||||||
|
Application.invokeLater(() -> {
|
||||||
draw.stream().forEach((jace.cheat.MemoryCell cell) -> {
|
draw.stream().forEach((jace.cheat.MemoryCell cell) -> {
|
||||||
if (showValuesCheckbox.isSelected()) {
|
if (showValuesCheckbox.isSelected()) {
|
||||||
int val = cell.value.get() & 0x0ff;
|
int val = cell.value.get() & 0x0ff;
|
||||||
@ -416,7 +461,7 @@ public class MetacheatUI {
|
|||||||
boolean resume = Emulator.computer.pause();
|
boolean resume = Emulator.computer.pause();
|
||||||
|
|
||||||
if (animationTimer == null) {
|
if (animationTimer == null) {
|
||||||
animationTimer = new ScheduledThreadPoolExecutor(10);
|
animationTimer = new ScheduledThreadPoolExecutor(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (animationFuture != null) {
|
if (animationFuture != null) {
|
||||||
@ -427,9 +472,18 @@ public class MetacheatUI {
|
|||||||
|
|
||||||
cheatEngine.initMemoryView();
|
cheatEngine.initMemoryView();
|
||||||
int pixelsPerBlock = 16 * MEMORY_BOX_TOTAL_SIZE;
|
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;
|
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);
|
memoryViewContents.setPrefHeight(canvasHeight);
|
||||||
memoryViewCanvas.setHeight(canvasHeight);
|
memoryViewCanvas.setHeight(canvasHeight);
|
||||||
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
|
GraphicsContext context = memoryViewCanvas.getGraphicsContext2D();
|
||||||
@ -439,13 +493,19 @@ public class MetacheatUI {
|
|||||||
int col = (addr - cheatEngine.getStartAddress()) % memoryViewColumns;
|
int col = (addr - cheatEngine.getStartAddress()) % memoryViewColumns;
|
||||||
int row = (addr - cheatEngine.getStartAddress()) / memoryViewColumns;
|
int row = (addr - cheatEngine.getStartAddress()) / memoryViewColumns;
|
||||||
MemoryCell cell = cheatEngine.getMemoryCell(addr);
|
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);
|
redrawNodes.add(cell);
|
||||||
}
|
}
|
||||||
MemoryCell.setListener((javafx.beans.value.ObservableValue<? extends jace.cheat.MemoryCell> prop, jace.cheat.MemoryCell oldCell, jace.cheat.MemoryCell newCell) -> {
|
MemoryCell.setListener((javafx.beans.value.ObservableValue<? extends jace.cheat.MemoryCell> prop, jace.cheat.MemoryCell oldCell, jace.cheat.MemoryCell newCell) -> {
|
||||||
redrawNodes.add(newCell);
|
redrawNodes.add(newCell);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setZoom(1/drawScale);
|
||||||
|
|
||||||
if (resume) {
|
if (resume) {
|
||||||
Emulator.computer.resume();
|
Emulator.computer.resume();
|
||||||
}
|
}
|
||||||
@ -455,6 +515,12 @@ public class MetacheatUI {
|
|||||||
if (memoryViewCanvas != null) {
|
if (memoryViewCanvas != null) {
|
||||||
double zoom = memoryViewCanvas.getScaleX();
|
double zoom = memoryViewCanvas.getScaleX();
|
||||||
zoom += amount;
|
zoom += amount;
|
||||||
|
setZoom(zoom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setZoom(double zoom) {
|
||||||
|
if (memoryViewCanvas != null) {
|
||||||
memoryViewCanvas.setScaleX(zoom);
|
memoryViewCanvas.setScaleX(zoom);
|
||||||
memoryViewCanvas.setScaleY(zoom);
|
memoryViewCanvas.setScaleY(zoom);
|
||||||
StackPane scrollArea = (StackPane) memoryViewCanvas.getParent();
|
StackPane scrollArea = (StackPane) memoryViewCanvas.getParent();
|
||||||
@ -509,4 +575,14 @@ public class MetacheatUI {
|
|||||||
private void addCheat(int addr, int val) {
|
private void addCheat(int addr, int val) {
|
||||||
cheatEngine.addCheat(new DynamicCheat(addr, String.valueOf(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
package jace.ui;
|
package jace.ui;
|
||||||
|
|
||||||
import jace.Emulator;
|
import jace.Emulator;
|
||||||
|
import jace.cheat.MemoryCell;
|
||||||
import jace.core.RAMListener;
|
import jace.core.RAMListener;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -19,6 +20,7 @@ import javafx.scene.canvas.Canvas;
|
|||||||
import javafx.scene.canvas.GraphicsContext;
|
import javafx.scene.canvas.GraphicsContext;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.Background;
|
import javafx.scene.layout.Background;
|
||||||
import javafx.scene.layout.BackgroundFill;
|
import javafx.scene.layout.BackgroundFill;
|
||||||
import javafx.scene.layout.CornerRadii;
|
import javafx.scene.layout.CornerRadii;
|
||||||
@ -32,6 +34,7 @@ import javafx.scene.text.TextAlignment;
|
|||||||
* @author blurry
|
* @author blurry
|
||||||
*/
|
*/
|
||||||
class Watch extends VBox {
|
class Watch extends VBox {
|
||||||
|
|
||||||
private static final int GRAPH_WIDTH = 50;
|
private static final int GRAPH_WIDTH = 50;
|
||||||
private static final double GRAPH_HEIGHT = 50;
|
private static final double GRAPH_HEIGHT = 50;
|
||||||
int address;
|
int address;
|
||||||
@ -41,11 +44,13 @@ class Watch extends VBox {
|
|||||||
int value = 0;
|
int value = 0;
|
||||||
BooleanProperty holding = null;
|
BooleanProperty holding = null;
|
||||||
private final MetacheatUI outer;
|
private final MetacheatUI outer;
|
||||||
|
MemoryCell cell;
|
||||||
|
|
||||||
public Watch(int address, final MetacheatUI outer) {
|
public Watch(int address, final MetacheatUI outer) {
|
||||||
super();
|
super();
|
||||||
this.outer = outer;
|
this.outer = outer;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
|
cell = outer.cheatEngine.getMemoryCell(address);
|
||||||
redraw = outer.animationTimer.scheduleAtFixedRate(this::redraw, MetacheatUI.FRAME_RATE, MetacheatUI.FRAME_RATE, TimeUnit.MILLISECONDS);
|
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)));
|
setBackground(new Background(new BackgroundFill(Color.NAVY, CornerRadii.EMPTY, Insets.EMPTY)));
|
||||||
Label addrLabel = new Label("$" + Integer.toHexString(address));
|
Label addrLabel = new Label("$" + Integer.toHexString(address));
|
||||||
@ -53,6 +58,12 @@ class Watch extends VBox {
|
|||||||
addrLabel.setMinWidth(GRAPH_WIDTH);
|
addrLabel.setMinWidth(GRAPH_WIDTH);
|
||||||
addrLabel.setFont(new Font(Font.getDefault().getFamily(), 14));
|
addrLabel.setFont(new Font(Font.getDefault().getFamily(), 14));
|
||||||
addrLabel.setTextFill(Color.WHITE);
|
addrLabel.setTextFill(Color.WHITE);
|
||||||
|
addrLabel.setMouseTransparent(false);
|
||||||
|
addrLabel.setOnMouseClicked((MouseEvent evt)-> {
|
||||||
|
if (evt.isPrimaryButtonDown()) {
|
||||||
|
outer.inspectAddress(address);
|
||||||
|
}
|
||||||
|
});
|
||||||
graph = new Canvas(GRAPH_WIDTH, GRAPH_HEIGHT);
|
graph = new Canvas(GRAPH_WIDTH, GRAPH_HEIGHT);
|
||||||
getChildren().add(addrLabel);
|
getChildren().add(addrLabel);
|
||||||
getChildren().add(graph);
|
getChildren().add(graph);
|
||||||
@ -68,7 +79,10 @@ class Watch extends VBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void redraw() {
|
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()) {
|
if (!holding.get()) {
|
||||||
value = val;
|
value = val;
|
||||||
}
|
}
|
||||||
|
@ -194,6 +194,41 @@
|
|||||||
</BorderPane>
|
</BorderPane>
|
||||||
</content>
|
</content>
|
||||||
</TitledPane>
|
</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">
|
<TitledPane expanded="false" text="Snapshots" VBox.vgrow="NEVER">
|
||||||
<content>
|
<content>
|
||||||
<BorderPane minWidth="-Infinity">
|
<BorderPane minWidth="-Infinity">
|
||||||
|
Loading…
Reference in New Issue
Block a user