Committing upstream changes from Lawless Legends to address mockingboard init issues.

This commit is contained in:
Brendan Robert 2018-05-22 22:28:13 -05:00
parent ad9da99cb8
commit eb776d44af
4 changed files with 909 additions and 825 deletions

View File

@ -1,445 +1,445 @@
package jace.cheat; package jace.cheat;
import jace.Emulator; import jace.Emulator;
import jace.JaceApplication; import jace.JaceApplication;
import jace.core.CPU; 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;
import jace.core.RAMListener; import jace.core.RAMListener;
import jace.state.State; import jace.state.State;
import jace.ui.MetacheatUI; import jace.ui.MetacheatUI;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter; 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.atomic.AtomicInteger; 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.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;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javax.script.Invocable; import javax.script.Invocable;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager; import javax.script.ScriptEngineManager;
public class MetaCheat extends Cheats { public class MetaCheat extends Cheats {
static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn"); static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn");
static Invocable NASHORN_INVOCABLE = (Invocable) NASHORN_ENGINE; static Invocable NASHORN_INVOCABLE = (Invocable) NASHORN_ENGINE;
public static enum SearchType { public static enum SearchType {
VALUE, TEXT, CHANGE VALUE, TEXT, CHANGE
} }
public static enum SearchChangeType { public static enum SearchChangeType {
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
} }
public static class SearchResult { public static class SearchResult {
int address; int address;
int lastObservedValue = 0; int lastObservedValue = 0;
private SearchResult(int address, int val) { private SearchResult(int address, int val) {
this.address = address; this.address = address;
lastObservedValue = val; lastObservedValue = val;
} }
@Override @Override
public String toString() { public String toString() {
return Integer.toHexString(address) + ": " + lastObservedValue + " (" + Integer.toHexString(lastObservedValue) + ")"; return Integer.toHexString(address) + ": " + lastObservedValue + " (" + Integer.toHexString(lastObservedValue) + ")";
} }
public int getAddress() { public int getAddress() {
return address; return address;
} }
} }
MetacheatUI ui; MetacheatUI ui;
public int fadeRate = 1; public int fadeRate = 1;
public int lightRate = 30; public int lightRate = 30;
public int historyLength = 10; public int historyLength = 10;
private int startAddress = 0; private int startAddress = 0;
private int endAddress = 0x0ffff; private int endAddress = 0x0ffff;
private final StringProperty startAddressProperty = new SimpleStringProperty(Integer.toHexString(startAddress)); private final StringProperty startAddressProperty = new SimpleStringProperty(Integer.toHexString(startAddress));
private final StringProperty endAddressProperty = new SimpleStringProperty(Integer.toHexString(endAddress)); private final StringProperty endAddressProperty = new SimpleStringProperty(Integer.toHexString(endAddress));
private boolean byteSized = true; private boolean byteSized = true;
private SearchType searchType = SearchType.VALUE; private SearchType searchType = SearchType.VALUE;
private SearchChangeType searchChangeType = SearchChangeType.NO_CHANGE; private SearchChangeType searchChangeType = SearchChangeType.NO_CHANGE;
private final BooleanProperty signedProperty = new SimpleBooleanProperty(false); private final BooleanProperty signedProperty = new SimpleBooleanProperty(false);
private final StringProperty searchValueProperty = new SimpleStringProperty("0"); private final StringProperty searchValueProperty = new SimpleStringProperty("0");
private final StringProperty changeByProperty = new SimpleStringProperty("0"); private final StringProperty changeByProperty = new SimpleStringProperty("0");
private final ObservableList<DynamicCheat> cheatList = FXCollections.observableArrayList(); private final ObservableList<DynamicCheat> cheatList = FXCollections.observableArrayList();
private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList(); private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList();
private final ObservableList<State> snapshotList = FXCollections.observableArrayList(); private final ObservableList<State> snapshotList = FXCollections.observableArrayList();
public MetaCheat(Computer computer) { public MetaCheat(Computer computer) {
super(computer); super(computer);
addNumericValidator(startAddressProperty); addNumericValidator(startAddressProperty);
addNumericValidator(endAddressProperty); addNumericValidator(endAddressProperty);
addNumericValidator(searchValueProperty); addNumericValidator(searchValueProperty);
addNumericValidator(changeByProperty); addNumericValidator(changeByProperty);
startAddressProperty.addListener((prop, oldVal, newVal) -> { startAddressProperty.addListener((prop, oldVal, newVal) -> {
startAddress = Math.max(0, Math.min(65535, parseInt(newVal))); startAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
}); });
endAddressProperty.addListener((prop, oldVal, newVal) -> { endAddressProperty.addListener((prop, oldVal, newVal) -> {
endAddress = Math.max(0, Math.min(65535, parseInt(newVal))); endAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
}); });
} }
private void addNumericValidator(StringProperty stringProperty) { private void addNumericValidator(StringProperty stringProperty) {
stringProperty.addListener((ObservableValue<? extends String> prop, String oldVal, String newVal) -> { stringProperty.addListener((ObservableValue<? extends String> prop, String oldVal, String newVal) -> {
if (newVal == null || newVal.isEmpty()) { if (newVal == null || newVal.isEmpty()) {
return; return;
} }
if (!newVal.matches("(\\+|-)?(x|$)?[0-9a-fA-F]*")) { if (!newVal.matches("(\\+|-)?(x|$)?[0-9a-fA-F]*")) {
stringProperty.set(""); stringProperty.set("");
} }
}); });
} }
public int parseInt(String s) throws NumberFormatException { public int parseInt(String s) throws NumberFormatException {
if (s == null || s.isEmpty()) { if (s == null || s.isEmpty()) {
return 0; return 0;
} }
if (s.matches("(\\+|-)?[0-9]+")) { if (s.matches("(\\+|-)?[0-9]+")) {
return Integer.parseInt(s); return Integer.parseInt(s);
} else { } else {
String upper = s.toUpperCase(); String upper = s.toUpperCase();
boolean positive = !upper.startsWith("-"); boolean positive = !upper.startsWith("-");
for (int i = 0; i < upper.length(); i++) { for (int i = 0; i < upper.length(); i++) {
char c = upper.charAt(i); char c = upper.charAt(i);
if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) { if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) {
int value = Integer.parseInt(s.substring(i), 16); int value = Integer.parseInt(s.substring(i), 16);
if (!positive) { if (!positive) {
value *= -1; value *= -1;
} }
return value; return value;
} }
} }
} }
throw new NumberFormatException("Could not interpret int value " + s); throw new NumberFormatException("Could not interpret int value " + s);
} }
@Override @Override
public void registerListeners() { public void registerListeners() {
} }
public void addCheat(DynamicCheat cheat) { public void addCheat(DynamicCheat cheat) {
cheatList.add(cheat); cheatList.add(cheat);
computer.getMemory().addListener(cheat); computer.getMemory().addListener(cheat);
cheat.addressProperty().addListener((prop, oldVal, newVal) -> { cheat.addressProperty().addListener((prop, oldVal, newVal) -> {
computer.getMemory().removeListener(cheat); computer.getMemory().removeListener(cheat);
cheat.doConfig(); cheat.doConfig();
computer.getMemory().addListener(cheat); computer.getMemory().addListener(cheat);
}); });
} }
public void removeCheat(DynamicCheat cheat) { public void removeCheat(DynamicCheat cheat) {
cheat.active.set(false); cheat.active.set(false);
computer.getMemory().removeListener(cheat); computer.getMemory().removeListener(cheat);
cheatList.remove(cheat); cheatList.remove(cheat);
} }
@Override @Override
protected void unregisterListeners() { protected void unregisterListeners() {
super.unregisterListeners(); super.unregisterListeners();
cheatList.stream().forEach(computer.getMemory()::removeListener); cheatList.stream().forEach(computer.getMemory()::removeListener);
} }
@Override @Override
protected String getDeviceName() { protected String getDeviceName() {
return "MetaCheat"; return "MetaCheat";
} }
@Override @Override
public void detach() { public void detach() {
super.detach(); super.detach();
ui.detach(); ui.detach();
} }
@Override @Override
public void attach() { public void attach() {
ui = JaceApplication.getApplication().showMetacheat(); ui = JaceApplication.getApplication().showMetacheat();
ui.registerMetacheatEngine(this); ui.registerMetacheatEngine(this);
super.attach(); super.attach();
} }
public int getStartAddress() { public int getStartAddress() {
return startAddress; return startAddress;
} }
public int getEndAddress() { public int getEndAddress() {
return endAddress; return endAddress;
} }
public void setByteSized(boolean b) { public void setByteSized(boolean b) {
byteSized = b; byteSized = b;
} }
public void setSearchType(SearchType searchType) { public void setSearchType(SearchType searchType) {
this.searchType = searchType; this.searchType = searchType;
} }
public void setSearchChangeType(SearchChangeType searchChangeType) { public void setSearchChangeType(SearchChangeType searchChangeType) {
this.searchChangeType = searchChangeType; this.searchChangeType = searchChangeType;
} }
public Property<Boolean> signedProperty() { public Property<Boolean> signedProperty() {
return signedProperty; return signedProperty;
} }
public Property<String> searchValueProperty() { public Property<String> searchValueProperty() {
return searchValueProperty; return searchValueProperty;
} }
public Property<String> searchChangeByProperty() { public Property<String> searchChangeByProperty() {
return changeByProperty; return changeByProperty;
} }
public ObservableList<DynamicCheat> getCheats() { public ObservableList<DynamicCheat> getCheats() {
return cheatList; return cheatList;
} }
public ObservableList<SearchResult> getSearchResults() { public ObservableList<SearchResult> getSearchResults() {
return resultList; return resultList;
} }
public ObservableList<State> getSnapshots() { public ObservableList<State> getSnapshots() {
return snapshotList; return snapshotList;
} }
public Property<String> startAddressProperty() { public Property<String> startAddressProperty() {
return startAddressProperty; return startAddressProperty;
} }
public Property<String> endAddressProperty() { public Property<String> endAddressProperty() {
return endAddressProperty; return endAddressProperty;
} }
public void newSearch() { public void newSearch() {
RAM memory = Emulator.computer.getMemory(); RAM memory = Emulator.computer.getMemory();
resultList.clear(); resultList.clear();
int compare = parseInt(searchValueProperty.get()); int compare = parseInt(searchValueProperty.get());
for (int i = 0; i < 0x10000; i++) { for (int i = 0; i < 0x10000; i++) {
boolean signed = signedProperty.get(); boolean signed = signedProperty.get();
int val int val
= byteSized = byteSized
? signed ? memory.readRaw(i) : memory.readRaw(i) & 0x0ff ? signed ? memory.readRaw(i) : memory.readRaw(i) & 0x0ff
: signed ? memory.readWordRaw(i) : memory.readWordRaw(i) & 0x0ffff; : signed ? memory.readWordRaw(i) : memory.readWordRaw(i) & 0x0ffff;
if (!searchType.equals(SearchType.VALUE) || val == compare) { if (!searchType.equals(SearchType.VALUE) || val == compare) {
SearchResult result = new SearchResult(i, val); SearchResult result = new SearchResult(i, val);
resultList.add(result); resultList.add(result);
} }
} }
} }
public void performSearch() { public void performSearch() {
RAM memory = Emulator.computer.getMemory(); RAM memory = Emulator.computer.getMemory();
boolean signed = signedProperty.get(); boolean signed = signedProperty.get();
resultList.removeIf((SearchResult result) -> { resultList.removeIf((SearchResult result) -> {
int val = byteSized int val = byteSized
? signed ? memory.readRaw(result.address) : memory.readRaw(result.address) & 0x0ff ? signed ? memory.readRaw(result.address) : memory.readRaw(result.address) & 0x0ff
: signed ? memory.readWordRaw(result.address) : memory.readWordRaw(result.address) & 0x0ffff; : signed ? memory.readWordRaw(result.address) : memory.readWordRaw(result.address) & 0x0ffff;
int last = result.lastObservedValue; int last = result.lastObservedValue;
result.lastObservedValue = val; result.lastObservedValue = val;
switch (searchType) { switch (searchType) {
case VALUE: case VALUE:
int compare = parseInt(searchValueProperty.get()); int compare = parseInt(searchValueProperty.get());
return compare != val; return compare != val;
case CHANGE: case CHANGE:
switch (searchChangeType) { switch (searchChangeType) {
case AMOUNT: case AMOUNT:
int amount = parseInt(searchChangeByProperty().getValue()); int amount = parseInt(searchChangeByProperty().getValue());
return (val - last) != amount; return (val - last) != amount;
case GREATER: case GREATER:
return val <= last; return val <= last;
case ANY_CHANGE: case ANY_CHANGE:
return val == last; return val == last;
case LESS: case LESS:
return val >= last; return val >= last;
case NO_CHANGE: case NO_CHANGE:
return val != last; return val != last;
} }
break; break;
case TEXT: case TEXT:
break; break;
} }
return false; return false;
}); });
} }
RAMListener memoryViewListener = null; RAMListener memoryViewListener = null;
private final Map<Integer, MemoryCell> memoryCells = new ConcurrentHashMap<>(); private final Map<Integer, MemoryCell> memoryCells = new ConcurrentHashMap<>();
public MemoryCell getMemoryCell(int address) { public MemoryCell getMemoryCell(int address) {
return memoryCells.get(address); return memoryCells.get(address);
} }
public void initMemoryView() { public void initMemoryView() {
RAM memory = Emulator.computer.getMemory(); RAM memory = Emulator.computer.getMemory();
for (int addr = getStartAddress(); addr <= getEndAddress(); addr++) { for (int addr = getStartAddress(); addr <= getEndAddress(); addr++) {
if (getMemoryCell(addr) == null) { if (getMemoryCell(addr) == null) {
MemoryCell cell = new MemoryCell(); MemoryCell cell = new MemoryCell();
cell.address = addr; cell.address = addr;
cell.value.set(memory.readRaw(addr)); cell.value.set(memory.readRaw(addr));
memoryCells.put(addr, cell); memoryCells.put(addr, cell);
} }
} }
if (memoryViewListener == null) { if (memoryViewListener == null) {
memoryViewListener = memory.observe(RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent); memoryViewListener = memory.observe(RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent);
listeners.add(memoryViewListener); listeners.add(memoryViewListener);
} }
} }
int fadeCounter = 0; int fadeCounter = 0;
int FADE_TIMER_VALUE = (int) (Emulator.computer.getMotherboard().cyclesPerSecond / 60); int FADE_TIMER_VALUE = (int) (Emulator.computer.getMotherboard().getSpeedInHz() / 60);
@Override @Override
public void tick() { public void tick() {
computer.cpu.performSingleTrace(); computer.cpu.performSingleTrace();
if (fadeCounter-- <= 0) { if (fadeCounter-- <= 0) {
fadeCounter = FADE_TIMER_VALUE; fadeCounter = FADE_TIMER_VALUE;
memoryCells.values().stream() memoryCells.values().stream()
.filter((cell) -> cell.hasCounts()) .filter((cell) -> cell.hasCounts())
.forEach((cell) -> { .forEach((cell) -> {
if (cell.execCount.get() > 0) { if (cell.execCount.get() > 0) {
cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate)); cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate));
} }
if (cell.readCount.get() > 0) { if (cell.readCount.get() > 0) {
cell.readCount.set(Math.max(0, cell.readCount.get() - fadeRate)); cell.readCount.set(Math.max(0, cell.readCount.get() - fadeRate));
} }
if (cell.writeCount.get() > 0) { if (cell.writeCount.get() > 0) {
cell.writeCount.set(Math.max(0, cell.writeCount.get() - fadeRate)); cell.writeCount.set(Math.max(0, cell.writeCount.get() - fadeRate));
} }
if (MemoryCell.listener != null) { if (MemoryCell.listener != null) {
MemoryCell.listener.changed(null, cell, cell); MemoryCell.listener.changed(null, cell, cell);
} }
}); });
} }
} }
AtomicInteger pendingInspectorUpdates = new AtomicInteger(0); AtomicInteger pendingInspectorUpdates = new AtomicInteger(0);
public void onInspectorChanged() { public void onInspectorChanged() {
pendingInspectorUpdates.set(0); 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) {
CPU cpu = Emulator.computer.getCpu(); CPU cpu = Emulator.computer.getCpu();
int pc = cpu.getProgramCounter(); int pc = cpu.getProgramCounter();
String trace = cpu.getLastTrace(); String trace = cpu.getLastTrace();
switch (e.getType()) { switch (e.getType()) {
case EXECUTE: case EXECUTE:
cell.execInstructionsDisassembly.add(trace); cell.execInstructionsDisassembly.add(trace);
if (cell.execInstructionsDisassembly.size() > historyLength) { if (cell.execInstructionsDisassembly.size() > historyLength) {
cell.execInstructionsDisassembly.remove(0); cell.execInstructionsDisassembly.remove(0);
} }
case READ_OPERAND: case READ_OPERAND:
cell.execCount.set(Math.min(255, cell.execCount.get() + lightRate)); cell.execCount.set(Math.min(255, cell.execCount.get() + lightRate));
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));
if (ui.isInspecting(cell.address)) { if (ui.isInspecting(cell.address)) {
if (pendingInspectorUpdates.incrementAndGet() < 5) { if (pendingInspectorUpdates.incrementAndGet() < 5) {
Platform.runLater(() -> { Platform.runLater(() -> {
pendingInspectorUpdates.decrementAndGet(); pendingInspectorUpdates.decrementAndGet();
cell.writeInstructions.add(pc); cell.writeInstructions.add(pc);
cell.writeInstructionsDisassembly.add(trace); 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); cell.writeInstructionsDisassembly.remove(0);
} }
}); });
} }
} else { } else {
cell.writeInstructions.add(cpu.getProgramCounter()); cell.writeInstructions.add(cpu.getProgramCounter());
cell.writeInstructionsDisassembly.add(cpu.getLastTrace()); cell.writeInstructionsDisassembly.add(cpu.getLastTrace());
if (cell.writeInstructions.size() > historyLength) { if (cell.writeInstructions.size() > historyLength) {
cell.writeInstructions.remove(0); cell.writeInstructions.remove(0);
cell.writeInstructionsDisassembly.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));
if (ui.isInspecting(cell.address)) { if (ui.isInspecting(cell.address)) {
if (pendingInspectorUpdates.incrementAndGet() < 5) { if (pendingInspectorUpdates.incrementAndGet() < 5) {
Platform.runLater(() -> { Platform.runLater(() -> {
pendingInspectorUpdates.decrementAndGet(); pendingInspectorUpdates.decrementAndGet();
cell.readInstructions.add(pc); cell.readInstructions.add(pc);
cell.readInstructionsDisassembly.add(trace); 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); cell.readInstructionsDisassembly.remove(0);
} }
}); });
} }
} else { } else {
cell.readInstructions.add(cpu.getProgramCounter()); cell.readInstructions.add(cpu.getProgramCounter());
cell.readInstructionsDisassembly.add(cpu.getLastTrace()); cell.readInstructionsDisassembly.add(cpu.getLastTrace());
if (cell.readInstructions.size() > historyLength) { if (cell.readInstructions.size() > historyLength) {
cell.readInstructions.remove(0); cell.readInstructions.remove(0);
cell.readInstructionsDisassembly.remove(0); cell.readInstructionsDisassembly.remove(0);
} }
} }
} }
cell.value.set(e.getNewValue()); cell.value.set(e.getNewValue());
} }
} }
public void saveCheats(File saveFile) { public void saveCheats(File saveFile) {
FileWriter writer = null; FileWriter writer = null;
try { try {
writer = new FileWriter(saveFile); writer = new FileWriter(saveFile);
for (DynamicCheat cheat : cheatList) { for (DynamicCheat cheat : cheatList) {
writer.write(cheat.serialize()); writer.write(cheat.serialize());
writer.write("\n"); writer.write("\n");
} }
writer.close(); writer.close();
} catch (IOException ex) { } catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
} finally { } finally {
try { try {
writer.close(); writer.close();
} catch (IOException ex) { } catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
} }
} }
} }
public void loadCheats(File saveFile) { public void loadCheats(File saveFile) {
BufferedReader in = null; BufferedReader in = null;
try { try {
in = new BufferedReader(new FileReader(saveFile)); in = new BufferedReader(new FileReader(saveFile));
StringBuilder guts = new StringBuilder(); StringBuilder guts = new StringBuilder();
String line; String line;
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
DynamicCheat cheat = DynamicCheat.deserialize(line); DynamicCheat cheat = DynamicCheat.deserialize(line);
addCheat(cheat); addCheat(cheat);
} }
in.close(); in.close();
} catch (IOException ex) { } catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
} finally { } finally {
try { try {
in.close(); in.close();
} catch (IOException ex) { } catch (IOException ex) {
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
} }
} }
} }
} }

View File

@ -21,24 +21,25 @@ package jace.core;
import jace.config.ConfigurableField; import jace.config.ConfigurableField;
/** /**
* A timed device is a device which executes so many ticks in a given time * A timed device is a device which executes so many ticks in a given time interval. This is the core of the emulator
* interval. This is the core of the emulator timing mechanics. * timing mechanics.
* *
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com * @author Brendan Robert (BLuRry) brendan.robert@gmail.com
*/ */
public abstract class TimedDevice extends Device { public abstract class TimedDevice extends Device {
/** /**
* Creates a new instance of TimedDevice * Creates a new instance of TimedDevice
*
* @param computer * @param computer
*/ */
public TimedDevice(Computer computer) { public TimedDevice(Computer computer) {
super(computer); super(computer);
setSpeed(cyclesPerSecond); setSpeedInHz(cyclesPerSecond);
} }
@ConfigurableField(name = "Speed", description = "(Percentage)") @ConfigurableField(name = "Speed", description = "(Percentage)")
public int speedRatio = 100; public int speedRatio = 100;
public long cyclesPerSecond = defaultCyclesPerSecond(); private long cyclesPerSecond = defaultCyclesPerSecond();
@ConfigurableField(name = "Max speed") @ConfigurableField(name = "Max speed")
public boolean maxspeed = false; public boolean maxspeed = false;
@ -57,10 +58,11 @@ public abstract class TimedDevice extends Device {
public boolean suspend() { public boolean suspend() {
disableTempMaxSpeed(); disableTempMaxSpeed();
boolean result = super.suspend(); boolean result = super.suspend();
if (worker != null && worker.isAlive()) { Thread w = worker;
if (w != null && w.isAlive()) {
try { try {
worker.interrupt(); w.interrupt();
worker.join(1000); w.join(1000);
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
} }
} }
@ -114,13 +116,44 @@ public abstract class TimedDevice extends Device {
long cyclesPerInterval; // How many cycles to wait until a pause interval long cyclesPerInterval; // How many cycles to wait until a pause interval
long nextSync; // When was the last pause? long nextSync; // When was the last pause?
public final void setSpeed(long cyclesPerSecond) { public final int getSpeedRatio() {
return speedRatio;
}
public final void setMaxSpeed(boolean enabled) {
maxspeed = enabled;
if (!enabled) {
disableTempMaxSpeed();
}
}
public final boolean isMaxSpeed() {
return maxspeed;
}
public final long getSpeedInHz() {
return cyclesPerInterval * 100L;
}
public final void setSpeedInHz(long cyclesPerSecond) {
// System.out.println("Raw set speed for " + getName() + " to " + cyclesPerSecond + "hz");
speedRatio = (int) Math.round(cyclesPerSecond * 100.0 / defaultCyclesPerSecond());
cyclesPerInterval = cyclesPerSecond / 100L; cyclesPerInterval = cyclesPerSecond / 100L;
nanosPerInterval = (long) (cyclesPerInterval * NANOS_PER_SECOND / cyclesPerSecond); nanosPerInterval = (long) (cyclesPerInterval * NANOS_PER_SECOND / cyclesPerSecond);
// System.out.println("Will pause " + nanosPerInterval + " nanos every " + cyclesPerInterval + " cycles"); // System.out.println("Will pause " + nanosPerInterval + " nanos every " + cyclesPerInterval + " cycles");
cycleTimer = 0; cycleTimer = 0;
resetSyncTimer(); resetSyncTimer();
} }
public final void setSpeedInPercentage(int ratio) {
// System.out.println("Setting " + getName() + " speed ratio to " + speedRatio);
cyclesPerSecond = defaultCyclesPerSecond() * ratio / 100;
if (cyclesPerSecond == 0) {
cyclesPerSecond = defaultCyclesPerSecond();
}
setSpeedInHz(cyclesPerSecond);
}
long skip = 0; long skip = 0;
long wait = 0; long wait = 0;
@ -171,11 +204,6 @@ public abstract class TimedDevice extends Device {
@Override @Override
public void reconfigure() { public void reconfigure() {
cyclesPerSecond = defaultCyclesPerSecond() * speedRatio / 100;
if (cyclesPerSecond == 0) {
cyclesPerSecond = defaultCyclesPerSecond();
}
setSpeed(cyclesPerSecond);
} }
public abstract long defaultCyclesPerSecond(); public abstract long defaultCyclesPerSecond();

View File

@ -27,7 +27,6 @@ import jace.core.RAMEvent;
import jace.core.RAMEvent.TYPE; import jace.core.RAMEvent.TYPE;
import jace.core.RAMListener; import jace.core.RAMListener;
import jace.core.SoundMixer; import jace.core.SoundMixer;
import static jace.core.Utility.*;
import jace.hardware.mockingboard.PSG; import jace.hardware.mockingboard.PSG;
import jace.hardware.mockingboard.R6522; import jace.hardware.mockingboard.R6522;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -80,7 +79,7 @@ public class CardMockingboard extends Card implements Runnable {
Condition playbackFinished = timerSync.newCondition(); Condition playbackFinished = timerSync.newCondition();
@ConfigurableField(name = "Idle sample threshold", description = "Number of samples to wait before suspending sound") @ConfigurableField(name = "Idle sample threshold", description = "Number of samples to wait before suspending sound")
private int MAX_IDLE_SAMPLES = SAMPLE_RATE; private int MAX_IDLE_SAMPLES = SAMPLE_RATE;
@Override @Override
public String getDeviceName() { public String getDeviceName() {
return "Mockingboard"; return "Mockingboard";
@ -90,7 +89,7 @@ public class CardMockingboard extends Card implements Runnable {
super(computer); super(computer);
controllers = new R6522[2]; controllers = new R6522[2];
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
//don't ask... // has to be final to be used inside of anonymous class below
final int j = i; final int j = i;
controllers[i] = new R6522(computer) { controllers[i] = new R6522(computer) {
int controller = j; int controller = j;
@ -131,6 +130,13 @@ public class CardMockingboard extends Card implements Runnable {
public String getShortName() { public String getShortName() {
return "timer" + j; return "timer" + j;
} }
public void doTick() {
super.doTick();
if (controller == 0) {
doSoundTick();
}
}
}; };
} }
} }
@ -140,6 +146,18 @@ public class CardMockingboard extends Card implements Runnable {
suspend(); suspend();
} }
RAMListener mainListener = null; RAMListener mainListener = null;
boolean heatbeatUnclocked = false;
long heartbeatReclockTime = 0L;
long unclockTime = 5000L;
private void setUnclocked(boolean unclocked) {
heatbeatUnclocked = unclocked;
for (R6522 controller : controllers) {
controller.setUnclocked(unclocked);
}
heartbeatReclockTime = System.currentTimeMillis() + unclockTime;
}
@Override @Override
protected void handleFirmwareAccess(int register, TYPE type, int value, RAMEvent e) { protected void handleFirmwareAccess(int register, TYPE type, int value, RAMEvent e) {
@ -152,7 +170,7 @@ public class CardMockingboard extends Card implements Runnable {
chip++; chip++;
} }
if (chip >= 2) { if (chip >= 2) {
System.err.println("Could not determine which PSG to communicate to"); System.err.println("Could not determine which PSG to communicate to for access to regsiter + " + Integer.toHexString(register));
e.setNewValue(computer.getVideo().getFloatingBus()); e.setNewValue(computer.getVideo().getFloatingBus());
return; return;
} }
@ -177,13 +195,25 @@ public class CardMockingboard extends Card implements Runnable {
@Override @Override
public void tick() { public void tick() {
for (R6522 c : controllers) { if (heatbeatUnclocked) {
if (c == null || !c.isRunning()) { if (System.currentTimeMillis() - heartbeatReclockTime >= unclockTime) {
continue; setUnclocked(false);
} else {
for (R6522 c : controllers) {
if (c == null || !c.isRunning()) {
continue;
}
c.doTick();
}
} }
c.tick();
} }
}
public boolean isRunning() {
return super.isRunning() && playbackThread != null && playbackThread.isAlive();
}
private void doSoundTick() {
if (isRunning() && !pause) { if (isRunning() && !pause) {
// buildMixerTable(); // buildMixerTable();
timerSync.lock(); timerSync.lock();
@ -194,7 +224,7 @@ public class CardMockingboard extends Card implements Runnable {
while (isRunning() && ticksSinceLastPlayback >= ticksBetweenPlayback) { while (isRunning() && ticksSinceLastPlayback >= ticksBetweenPlayback) {
if (!playbackFinished.await(1, TimeUnit.SECONDS)) { if (!playbackFinished.await(1, TimeUnit.SECONDS)) {
// gripe("The mockingboard playback thread has stalled. Disabling mockingboard."); // gripe("The mockingboard playback thread has stalled. Disabling mockingboard.");
suspend(); suspendSound();
} }
} }
} }
@ -263,14 +293,15 @@ public class CardMockingboard extends Card implements Runnable {
@Override @Override
public void resume() { public void resume() {
pause = false; pause = false;
if (!isRunning()) { if (chips == null) {
if (chips == null) { initPSG();
initPSG(); for (PSG psg : chips) {
for (PSG psg : chips) { psg.setRate(phasorMode ? CLOCK_SPEED * 2 : CLOCK_SPEED, SAMPLE_RATE);
psg.setRate(phasorMode ? CLOCK_SPEED * 2 : CLOCK_SPEED, SAMPLE_RATE); psg.reset();
psg.reset();
}
} }
}
if (!isRunning()) {
setUnclocked(true);
for (R6522 controller : controllers) { for (R6522 controller : controllers) {
controller.attach(); controller.attach();
controller.resume(); controller.resume();
@ -290,6 +321,10 @@ public class CardMockingboard extends Card implements Runnable {
controller.suspend(); controller.suspend();
controller.detach(); controller.detach();
} }
return suspendSound();
}
public boolean suspendSound() {
if (playbackThread == null || !playbackThread.isAlive()) { if (playbackThread == null || !playbackThread.isAlive()) {
return false; return false;
} }
@ -325,11 +360,11 @@ public class CardMockingboard extends Card implements Runnable {
int zeroSamples = 0; int zeroSamples = 0;
setRun(true); setRun(true);
LockSupport.parkNanos(5000); LockSupport.parkNanos(5000);
while (isRunning()) { while (isRunning() && !Thread.interrupted()) {
while (isRunning() && !computer.isRunning()) { while (isRunning() && !computer.isRunning()) {
Thread.currentThread().yield(); Thread.sleep(1000);
} }
if (isRunning()) { if (isRunning() && !Thread.interrupted()) {
playSound(leftBuffer, rightBuffer); playSound(leftBuffer, rightBuffer);
int p = 0; int p = 0;
for (int idx = 0; idx < BUFFER_LENGTH; idx++) { for (int idx = 0; idx < BUFFER_LENGTH; idx++) {
@ -394,6 +429,8 @@ public class CardMockingboard extends Card implements Runnable {
} catch (LineUnavailableException ex) { } catch (LineUnavailableException ex) {
Logger.getLogger(CardMockingboard.class Logger.getLogger(CardMockingboard.class
.getName()).log(Level.SEVERE, null, ex); .getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(CardMockingboard.class.getName()).log(Level.SEVERE, null, ex);
} finally { } finally {
computer.getMotherboard().cancelSpeedRequest(this); computer.getMotherboard().cancelSpeedRequest(this);
System.out.println("Mockingboard playback stopped"); System.out.println("Mockingboard playback stopped");

View File

@ -1,346 +1,365 @@
/* /*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com. * Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version. * version 2.1 of the License, or (at your option) any later version.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
package jace.hardware.mockingboard; package jace.hardware.mockingboard;
import jace.core.Computer; import jace.core.Computer;
import jace.core.Device; import jace.core.Device;
import jace.core.TimedDevice;
/**
* Implementation of 6522 VIA chip /**
* * Implementation of 6522 VIA chip
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com *
*/ * @author Brendan Robert (BLuRry) brendan.robert@gmail.com
public abstract class R6522 extends Device { */
public abstract class R6522 extends TimedDevice {
public R6522(Computer computer) { public static long SPEED = 1020484L; // (NTSC)
super(computer);
timer1freerun = true; public R6522(Computer computer) {
timer1running = true; super(computer);
timer1latch = 0x1fff; timer1freerun = true;
timer1interruptEnabled = false; timer1running = true;
setRun(true); timer1latch = 0x1fff;
} timer1interruptEnabled = false;
setSpeedInHz(SPEED);
// 6522 VIA setRun(true);
// http://www.applevault.com/twiki/Main/Mockingboard/6522.pdf }
// I/O registers
public static enum Register { @Override
ORB(0), // Output Register B public long defaultCyclesPerSecond() {
ORA(1), // Output Register A return SPEED;
DDRB(2),// Data direction reg B }
DDRA(3),// Data direction reg A
T1CL(4),// T1 low-order latches (low-order counter for read operations) // 6522 VIA
T1CH(5),// T1 high-order counter // http://www.applevault.com/twiki/Main/Mockingboard/6522.pdf
T1LL(6),// T1 low-order latches // I/O registers
T1LH(7),// T1 high-order latches public static enum Register {
T2CL(8),// T2 low-order latches (low-order counter for read operations) ORB(0), // Output Register B
T2CH(9),// T2 high-order counter ORA(1), // Output Register A
SR(10),// Shift register DDRB(2),// Data direction reg B
ACR(11),// Aux control register DDRA(3),// Data direction reg A
PCR(12),// Perripheral control register T1CL(4),// T1 low-order latches (low-order counter for read operations)
IFR(13),// Interrupt flag register T1CH(5),// T1 high-order counter
IER(14),// Interrupt enable register T1LL(6),// T1 low-order latches
ORAH(15);// Output Register A (no handshake) T1LH(7),// T1 high-order latches
T2CL(8),// T2 low-order latches (low-order counter for read operations)
int val; T2CH(9),// T2 high-order counter
SR(10),// Shift register
Register(int v) { ACR(11),// Aux control register
val = v; PCR(12),// Perripheral control register
} IFR(13),// Interrupt flag register
IER(14),// Interrupt enable register
static public Register fromInt(int i) { ORAH(15);// Output Register A (no handshake)
for (Register r : Register.values()) {
if (r.val == i) { int val;
return r;
} Register(int v) {
} val = v;
return null; }
}
} static public Register fromInt(int i) {
// state variables for (Register r : Register.values()) {
public int oraReg = 0; if (r.val == i) {
public int iraReg = 0; return r;
public int orbReg = 0; }
public int irbReg = 0; }
// DDRA and DDRB must be set to output for mockingboard to do anything return null;
// Common values for this are FF for DDRA and 7 for DDRB }
// DDRB bits 0-2 are used to control AY chips but bits 3-7 are not connected. }
// that's why it is common to see mockingboard drivers init the port with a 7 // state variables
public int dataDirectionA = 0; public int oraReg = 0;
public int dataDirectionB = 0; public int iraReg = 0;
public int orbReg = 0;
// Though this is necessary for a complete emulation of the 6522, it isn't needed by the mockingboard public int irbReg = 0;
// set by bit 0 of ACR // DDRA and DDRB must be set to output for mockingboard to do anything
// public boolean latchEnabledA = false; // Common values for this are FF for DDRA and 7 for DDRB
// set by bit 1 of ACR // DDRB bits 0-2 are used to control AY chips but bits 3-7 are not connected.
// public boolean latchEnabledB = false; // that's why it is common to see mockingboard drivers init the port with a 7
//Bits 2,3,4 of ACR public int dataDirectionA = 0;
// static public enum ShiftRegisterControl { public int dataDirectionB = 0;
// interruptDisabled(0),
// shiftInT2(4), // Though this is necessary for a complete emulation of the 6522, it isn't needed by the mockingboard
// shiftIn02(8), // set by bit 0 of ACR
// shiftInExt(12), // public boolean latchEnabledA = false;
// shiftOutFree(16), // set by bit 1 of ACR
// shiftOutT2(20), // public boolean latchEnabledB = false;
// shiftOut02(24), //Bits 2,3,4 of ACR
// shiftOutExt(28); // static public enum ShiftRegisterControl {
// // interruptDisabled(0),
// int val; // shiftInT2(4),
// private ShiftRegisterControl(int v) { // shiftIn02(8),
// val = v; // shiftInExt(12),
// } // shiftOutFree(16),
// // shiftOutT2(20),
// public static ShiftRegisterControl fromBits(int b) { // shiftOut02(24),
// b=b&28; // shiftOutExt(28);
// for (ShiftRegisterControl s : values()) { //
// if (s.val == b) return s; // int val;
// } // private ShiftRegisterControl(int v) {
// return null; // val = v;
// } // }
// } //
// public ShiftRegisterControl shiftMode = ShiftRegisterControl.interruptDisabled; // public static ShiftRegisterControl fromBits(int b) {
// //Bit 5 of ACR (false = timed interrupt, true = count down pulses on PB6) // b=b&28;
// public boolean t2countPulses = false; // for (ShiftRegisterControl s : values()) {
// //Bit 6 of ACR (true = continuous, false = one-shot) // if (s.val == b) return s;
// public boolean t1continuous = false; // }
// //Bit 7 of ACR (true = enable PB7, false = interruptDisabled) // return null;
// public boolean t1enablePB7 = false; // }
// // NOTE: Mockingboard did not use PB6 or PB7, they are not connected to anything // }
public boolean timer1interruptEnabled = true; // public ShiftRegisterControl shiftMode = ShiftRegisterControl.interruptDisabled;
public boolean timer1IRQ = false; // True if timer interrupt flag is set // //Bit 5 of ACR (false = timed interrupt, true = count down pulses on PB6)
public int timer1latch = 0; // public boolean t2countPulses = false;
public int timer1counter = 0; // //Bit 6 of ACR (true = continuous, false = one-shot)
public boolean timer1freerun = false; // public boolean t1continuous = false;
public boolean timer1running = false; // //Bit 7 of ACR (true = enable PB7, false = interruptDisabled)
public boolean timer2interruptEnabled = true; // public boolean t1enablePB7 = false;
public boolean timer2IRQ = false; // True if timer interrupt flag is set // // NOTE: Mockingboard did not use PB6 or PB7, they are not connected to anything
public int timer2latch = 0; public boolean timer1interruptEnabled = true;
public int timer2counter = 0; public boolean timer1IRQ = false; // True if timer interrupt flag is set
public boolean timer2running = false; public int timer1latch = 0;
public int timer1counter = 0;
@Override public boolean timer1freerun = false;
protected String getDeviceName() { public boolean timer1running = false;
return "6522 VIA Chip"; public boolean timer2interruptEnabled = true;
} public boolean timer2IRQ = false; // True if timer interrupt flag is set
public int timer2latch = 0;
@Override public int timer2counter = 0;
public void tick() { public boolean timer2running = false;
if (timer1running) { public boolean unclocked = false;
timer1counter--;
if (timer1counter < 0) { @Override
timer1counter = timer1latch; protected String getDeviceName() {
if (!timer1freerun) { return "6522 VIA Chip";
timer1running = false; }
}
if (timer1interruptEnabled) { @Override
// System.out.println("Timer 1 generated interrupt"); public void tick() {
timer1IRQ = true; if (!unclocked) {
computer.getCpu().generateInterrupt(); doTick();
} }
} }
}
if (timer2running) { public void setUnclocked(boolean unclocked) {
timer2counter--; this.unclocked = unclocked;
if (timer2counter < 0) { }
timer2running = false;
timer2counter = timer2latch; public void doTick() {
if (timer2interruptEnabled) { if (timer1running) {
timer2IRQ = true; timer1counter--;
computer.getCpu().generateInterrupt(); if (timer1counter < 0) {
} timer1counter = timer1latch;
} if (!timer1freerun) {
} timer1running = false;
if (!timer1running && !timer2running) { }
setRun(false); if (timer1interruptEnabled) {
} // System.out.println("Timer 1 generated interrupt");
} timer1IRQ = true;
computer.getCpu().generateInterrupt();
@Override }
public void attach() { }
// Start chip }
} if (timer2running) {
timer2counter--;
@Override if (timer2counter < 0) {
public void reconfigure() { timer2running = false;
// Reset timer2counter = timer2latch;
} if (timer2interruptEnabled) {
timer2IRQ = true;
public void writeRegister(int reg, int val) { computer.getCpu().generateInterrupt();
int value = val & 0x0ff; }
Register r = Register.fromInt(reg); }
// System.out.println("Writing "+(value&0x0ff)+" to register "+r.toString()); }
switch (r) { if (!timer1running && !timer2running) {
case ORB: setRun(false);
if (dataDirectionB == 0) { }
break; }
}
sendOutputB(value & dataDirectionB); @Override
break; public void attach() {
case ORA: // Start chip
// case ORAH: }
if (dataDirectionA == 0) {
break; @Override
} public void reconfigure() {
sendOutputA(value & dataDirectionA); // Reset
break; }
case DDRB:
dataDirectionB = value; public void writeRegister(int reg, int val) {
break; int value = val & 0x0ff;
case DDRA: Register r = Register.fromInt(reg);
dataDirectionA = value; // System.out.println("Writing "+(value&0x0ff)+" to register "+r.toString());
break; switch (r) {
case T1CL: case ORB:
case T1LL: if (dataDirectionB == 0) {
timer1latch = (timer1latch & 0x0ff00) | value; break;
break; }
case T1CH: sendOutputB(value & dataDirectionB);
timer1latch = (timer1latch & 0x0ff) | (value << 8); break;
timer1IRQ = false; case ORA:
timer1counter = timer1latch; // case ORAH:
timer1running = true; if (dataDirectionA == 0) {
setRun(true); break;
break; }
case T1LH: sendOutputA(value & dataDirectionA);
timer1latch = (timer1latch & 0x0ff) | (value << 8); break;
timer1IRQ = false; case DDRB:
break; dataDirectionB = value;
case T2CL: break;
timer2latch = (timer2latch & 0x0ff00) | value; case DDRA:
break; dataDirectionA = value;
case T2CH: break;
timer2latch = (timer2latch & 0x0ff) | (value << 8); case T1CL:
timer2IRQ = false; case T1LL:
timer2counter = timer2latch; timer1latch = (timer1latch & 0x0ff00) | value;
timer2running = true; break;
setRun(true); case T1CH:
break; timer1latch = (timer1latch & 0x0ff) | (value << 8);
case SR: timer1IRQ = false;
// SHIFT REGISTER NOT IMPLEMENTED timer1counter = timer1latch;
break; timer1running = true;
case ACR: setRun(true);
// SHIFT REGISTER NOT IMPLEMENTED break;
timer1freerun = (value & 64) != 0; case T1LH:
if (timer1freerun) { timer1latch = (timer1latch & 0x0ff) | (value << 8);
timer1running = true; timer1IRQ = false;
setRun(true); break;
} case T2CL:
break; timer2latch = (timer2latch & 0x0ff00) | value;
case PCR: break;
// TODO: Implement if Votrax (SSI) is to be supported case T2CH:
break; timer2latch = (timer2latch & 0x0ff) | (value << 8);
case IFR: timer2IRQ = false;
if ((value & 64) != 0) { timer2counter = timer2latch;
timer1IRQ = false; timer2running = true;
} setRun(true);
if ((value & 32) != 0) { break;
timer2IRQ = false; case SR:
} // SHIFT REGISTER NOT IMPLEMENTED
break; break;
case IER: case ACR:
boolean enable = (value & 128) != 0; // SHIFT REGISTER NOT IMPLEMENTED
if ((value & 64) != 0) { timer1freerun = (value & 64) != 0;
timer1interruptEnabled = enable; if (timer1freerun) {
} timer1running = true;
if ((value & 32) != 0) { setRun(true);
timer2interruptEnabled = enable; }
} break;
break; case PCR:
default: // TODO: Implement if Votrax (SSI) is to be supported
} break;
} case IFR:
if ((value & 64) != 0) {
// Whatever uses 6522 will want to know when it is outputting values timer1IRQ = false;
// So to hook that in, these abstract methods will be defined as appropriate }
public abstract void sendOutputA(int value); if ((value & 32) != 0) {
timer2IRQ = false;
public abstract void sendOutputB(int value); }
break;
public int readRegister(int reg) { case IER:
Register r = Register.fromInt(reg); boolean enable = (value & 128) != 0;
// System.out.println("Reading register "+r.toString()); if ((value & 64) != 0) {
switch (r) { timer1interruptEnabled = enable;
case ORB: }
if (dataDirectionB == 0x0ff) { if ((value & 32) != 0) {
break; timer2interruptEnabled = enable;
} }
return receiveOutputB() & (dataDirectionB ^ 0x0ff); break;
case ORA: default:
case ORAH: }
if (dataDirectionA == 0x0ff) { }
break;
} // Whatever uses 6522 will want to know when it is outputting values
return receiveOutputA() & (dataDirectionA ^ 0x0ff); // So to hook that in, these abstract methods will be defined as appropriate
case DDRB: public abstract void sendOutputA(int value);
return dataDirectionB;
case DDRA: public abstract void sendOutputB(int value);
return dataDirectionA;
case T1CL: public int readRegister(int reg) {
timer1IRQ = false; Register r = Register.fromInt(reg);
return timer1counter & 0x0ff; // System.out.println("Reading register "+r.toString());
case T1CH: switch (r) {
return (timer1counter & 0x0ff00) >> 8; case ORB:
case T1LL: if (dataDirectionB == 0x0ff) {
return timer1latch & 0x0ff; break;
case T1LH: }
return (timer1latch & 0x0ff00) >> 8; return receiveOutputB() & (dataDirectionB ^ 0x0ff);
case T2CL: case ORA:
timer2IRQ = false; case ORAH:
return timer2counter & 0x0ff; if (dataDirectionA == 0x0ff) {
case T2CH: break;
return (timer2counter & 0x0ff00) >> 8; }
case SR: return receiveOutputA() & (dataDirectionA ^ 0x0ff);
// SHIFT REGISTER NOT IMPLEMENTED case DDRB:
return 0; return dataDirectionB;
case ACR: case DDRA:
// SHIFT REGISTER NOT IMPLEMENTED return dataDirectionA;
if (timer1freerun) { case T1CL:
return 64; timer1IRQ = false;
} return timer1counter & 0x0ff;
return 0; case T1CH:
case PCR: return (timer1counter & 0x0ff00) >> 8;
break; case T1LL:
case IFR: return timer1latch & 0x0ff;
int val = 0; case T1LH:
if (timer1IRQ) { return (timer1latch & 0x0ff00) >> 8;
val |= 64; case T2CL:
} timer2IRQ = false;
if (timer2IRQ) { return timer2counter & 0x0ff;
val |= 32; case T2CH:
} return (timer2counter & 0x0ff00) >> 8;
if (val != 0) { case SR:
val |= 128; // SHIFT REGISTER NOT IMPLEMENTED
} return 0;
return val; case ACR:
case IER: // SHIFT REGISTER NOT IMPLEMENTED
val = 128; if (timer1freerun) {
if (timer1interruptEnabled) { return 64;
val |= 64; }
} return 0;
if (timer2interruptEnabled) { case PCR:
val |= 32; break;
} case IFR:
return val; int val = 0;
} if (timer1IRQ) {
return 0; val |= 64;
} }
if (timer2IRQ) {
public abstract int receiveOutputA(); val |= 32;
}
public abstract int receiveOutputB(); if (val != 0) {
} val |= 128;
}
return val;
case IER:
val = 128;
if (timer1interruptEnabled) {
val |= 64;
}
if (timer2interruptEnabled) {
val |= 32;
}
return val;
}
return 0;
}
public abstract int receiveOutputA();
public abstract int receiveOutputB();
}