mirror of
https://github.com/badvision/jace.git
synced 2025-01-01 05:29:49 +00:00
Committing upstream changes from Lawless Legends to address mockingboard init issues.
This commit is contained in:
parent
ad9da99cb8
commit
eb776d44af
@ -1,445 +1,445 @@
|
||||
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;
|
||||
import jace.core.RAMListener;
|
||||
import jace.state.State;
|
||||
import jace.ui.MetacheatUI;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
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;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
public class MetaCheat extends Cheats {
|
||||
|
||||
static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn");
|
||||
static Invocable NASHORN_INVOCABLE = (Invocable) NASHORN_ENGINE;
|
||||
|
||||
public static enum SearchType {
|
||||
VALUE, TEXT, CHANGE
|
||||
}
|
||||
|
||||
public static enum SearchChangeType {
|
||||
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
|
||||
}
|
||||
|
||||
public static class SearchResult {
|
||||
|
||||
int address;
|
||||
int lastObservedValue = 0;
|
||||
|
||||
private SearchResult(int address, int val) {
|
||||
this.address = address;
|
||||
lastObservedValue = val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toHexString(address) + ": " + lastObservedValue + " (" + Integer.toHexString(lastObservedValue) + ")";
|
||||
}
|
||||
|
||||
public int getAddress() {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
MetacheatUI ui;
|
||||
|
||||
public int fadeRate = 1;
|
||||
public int lightRate = 30;
|
||||
public int historyLength = 10;
|
||||
|
||||
private int startAddress = 0;
|
||||
private int endAddress = 0x0ffff;
|
||||
private final StringProperty startAddressProperty = new SimpleStringProperty(Integer.toHexString(startAddress));
|
||||
private final StringProperty endAddressProperty = new SimpleStringProperty(Integer.toHexString(endAddress));
|
||||
private boolean byteSized = true;
|
||||
private SearchType searchType = SearchType.VALUE;
|
||||
private SearchChangeType searchChangeType = SearchChangeType.NO_CHANGE;
|
||||
private final BooleanProperty signedProperty = new SimpleBooleanProperty(false);
|
||||
private final StringProperty searchValueProperty = new SimpleStringProperty("0");
|
||||
private final StringProperty changeByProperty = new SimpleStringProperty("0");
|
||||
private final ObservableList<DynamicCheat> cheatList = FXCollections.observableArrayList();
|
||||
private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList();
|
||||
private final ObservableList<State> snapshotList = FXCollections.observableArrayList();
|
||||
|
||||
public MetaCheat(Computer computer) {
|
||||
super(computer);
|
||||
addNumericValidator(startAddressProperty);
|
||||
addNumericValidator(endAddressProperty);
|
||||
addNumericValidator(searchValueProperty);
|
||||
addNumericValidator(changeByProperty);
|
||||
startAddressProperty.addListener((prop, oldVal, newVal) -> {
|
||||
startAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
|
||||
});
|
||||
endAddressProperty.addListener((prop, oldVal, newVal) -> {
|
||||
endAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
|
||||
});
|
||||
}
|
||||
|
||||
private void addNumericValidator(StringProperty stringProperty) {
|
||||
stringProperty.addListener((ObservableValue<? extends String> prop, String oldVal, String newVal) -> {
|
||||
if (newVal == null || newVal.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!newVal.matches("(\\+|-)?(x|$)?[0-9a-fA-F]*")) {
|
||||
stringProperty.set("");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int parseInt(String s) throws NumberFormatException {
|
||||
if (s == null || s.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
if (s.matches("(\\+|-)?[0-9]+")) {
|
||||
return Integer.parseInt(s);
|
||||
} else {
|
||||
String upper = s.toUpperCase();
|
||||
boolean positive = !upper.startsWith("-");
|
||||
for (int i = 0; i < upper.length(); i++) {
|
||||
char c = upper.charAt(i);
|
||||
if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) {
|
||||
int value = Integer.parseInt(s.substring(i), 16);
|
||||
if (!positive) {
|
||||
value *= -1;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NumberFormatException("Could not interpret int value " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
}
|
||||
|
||||
public void addCheat(DynamicCheat cheat) {
|
||||
cheatList.add(cheat);
|
||||
computer.getMemory().addListener(cheat);
|
||||
cheat.addressProperty().addListener((prop, oldVal, newVal) -> {
|
||||
computer.getMemory().removeListener(cheat);
|
||||
cheat.doConfig();
|
||||
computer.getMemory().addListener(cheat);
|
||||
});
|
||||
}
|
||||
|
||||
public void removeCheat(DynamicCheat cheat) {
|
||||
cheat.active.set(false);
|
||||
computer.getMemory().removeListener(cheat);
|
||||
cheatList.remove(cheat);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterListeners() {
|
||||
super.unregisterListeners();
|
||||
cheatList.stream().forEach(computer.getMemory()::removeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDeviceName() {
|
||||
return "MetaCheat";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
super.detach();
|
||||
ui.detach();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach() {
|
||||
ui = JaceApplication.getApplication().showMetacheat();
|
||||
ui.registerMetacheatEngine(this);
|
||||
super.attach();
|
||||
}
|
||||
|
||||
public int getStartAddress() {
|
||||
return startAddress;
|
||||
}
|
||||
|
||||
public int getEndAddress() {
|
||||
return endAddress;
|
||||
}
|
||||
|
||||
public void setByteSized(boolean b) {
|
||||
byteSized = b;
|
||||
}
|
||||
|
||||
public void setSearchType(SearchType searchType) {
|
||||
this.searchType = searchType;
|
||||
}
|
||||
|
||||
public void setSearchChangeType(SearchChangeType searchChangeType) {
|
||||
this.searchChangeType = searchChangeType;
|
||||
}
|
||||
|
||||
public Property<Boolean> signedProperty() {
|
||||
return signedProperty;
|
||||
}
|
||||
|
||||
public Property<String> searchValueProperty() {
|
||||
return searchValueProperty;
|
||||
}
|
||||
|
||||
public Property<String> searchChangeByProperty() {
|
||||
return changeByProperty;
|
||||
}
|
||||
|
||||
public ObservableList<DynamicCheat> getCheats() {
|
||||
return cheatList;
|
||||
}
|
||||
|
||||
public ObservableList<SearchResult> getSearchResults() {
|
||||
return resultList;
|
||||
}
|
||||
|
||||
public ObservableList<State> getSnapshots() {
|
||||
return snapshotList;
|
||||
}
|
||||
|
||||
public Property<String> startAddressProperty() {
|
||||
return startAddressProperty;
|
||||
}
|
||||
|
||||
public Property<String> endAddressProperty() {
|
||||
return endAddressProperty;
|
||||
}
|
||||
|
||||
public void newSearch() {
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
resultList.clear();
|
||||
int compare = parseInt(searchValueProperty.get());
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
boolean signed = signedProperty.get();
|
||||
int val
|
||||
= byteSized
|
||||
? signed ? memory.readRaw(i) : memory.readRaw(i) & 0x0ff
|
||||
: signed ? memory.readWordRaw(i) : memory.readWordRaw(i) & 0x0ffff;
|
||||
if (!searchType.equals(SearchType.VALUE) || val == compare) {
|
||||
SearchResult result = new SearchResult(i, val);
|
||||
resultList.add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void performSearch() {
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
boolean signed = signedProperty.get();
|
||||
resultList.removeIf((SearchResult result) -> {
|
||||
int val = byteSized
|
||||
? signed ? memory.readRaw(result.address) : memory.readRaw(result.address) & 0x0ff
|
||||
: signed ? memory.readWordRaw(result.address) : memory.readWordRaw(result.address) & 0x0ffff;
|
||||
int last = result.lastObservedValue;
|
||||
result.lastObservedValue = val;
|
||||
switch (searchType) {
|
||||
case VALUE:
|
||||
int compare = parseInt(searchValueProperty.get());
|
||||
return compare != val;
|
||||
case CHANGE:
|
||||
switch (searchChangeType) {
|
||||
case AMOUNT:
|
||||
int amount = parseInt(searchChangeByProperty().getValue());
|
||||
return (val - last) != amount;
|
||||
case GREATER:
|
||||
return val <= last;
|
||||
case ANY_CHANGE:
|
||||
return val == last;
|
||||
case LESS:
|
||||
return val >= last;
|
||||
case NO_CHANGE:
|
||||
return val != last;
|
||||
}
|
||||
break;
|
||||
case TEXT:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
RAMListener memoryViewListener = null;
|
||||
private final Map<Integer, MemoryCell> memoryCells = new ConcurrentHashMap<>();
|
||||
|
||||
public MemoryCell getMemoryCell(int address) {
|
||||
return memoryCells.get(address);
|
||||
}
|
||||
|
||||
public void initMemoryView() {
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
for (int addr = getStartAddress(); addr <= getEndAddress(); addr++) {
|
||||
if (getMemoryCell(addr) == null) {
|
||||
MemoryCell cell = new MemoryCell();
|
||||
cell.address = addr;
|
||||
cell.value.set(memory.readRaw(addr));
|
||||
memoryCells.put(addr, cell);
|
||||
}
|
||||
}
|
||||
if (memoryViewListener == null) {
|
||||
memoryViewListener = memory.observe(RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent);
|
||||
listeners.add(memoryViewListener);
|
||||
}
|
||||
}
|
||||
|
||||
int fadeCounter = 0;
|
||||
int FADE_TIMER_VALUE = (int) (Emulator.computer.getMotherboard().cyclesPerSecond / 60);
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
computer.cpu.performSingleTrace();
|
||||
if (fadeCounter-- <= 0) {
|
||||
fadeCounter = FADE_TIMER_VALUE;
|
||||
memoryCells.values().stream()
|
||||
.filter((cell) -> cell.hasCounts())
|
||||
.forEach((cell) -> {
|
||||
if (cell.execCount.get() > 0) {
|
||||
cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate));
|
||||
}
|
||||
if (cell.readCount.get() > 0) {
|
||||
cell.readCount.set(Math.max(0, cell.readCount.get() - fadeRate));
|
||||
}
|
||||
if (cell.writeCount.get() > 0) {
|
||||
cell.writeCount.set(Math.max(0, cell.writeCount.get() - fadeRate));
|
||||
}
|
||||
if (MemoryCell.listener != null) {
|
||||
MemoryCell.listener.changed(null, cell, cell);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
AtomicInteger pendingInspectorUpdates = new AtomicInteger(0);
|
||||
public void onInspectorChanged() {
|
||||
pendingInspectorUpdates.set(0);
|
||||
}
|
||||
|
||||
private void processMemoryEvent(RAMEvent e) {
|
||||
MemoryCell cell = getMemoryCell(e.getAddress());
|
||||
if (cell != null) {
|
||||
CPU cpu = Emulator.computer.getCpu();
|
||||
int pc = cpu.getProgramCounter();
|
||||
String trace = cpu.getLastTrace();
|
||||
switch (e.getType()) {
|
||||
case EXECUTE:
|
||||
cell.execInstructionsDisassembly.add(trace);
|
||||
if (cell.execInstructionsDisassembly.size() > historyLength) {
|
||||
cell.execInstructionsDisassembly.remove(0);
|
||||
}
|
||||
case READ_OPERAND:
|
||||
cell.execCount.set(Math.min(255, cell.execCount.get() + lightRate));
|
||||
break;
|
||||
case WRITE:
|
||||
cell.writeCount.set(Math.min(255, cell.writeCount.get() + lightRate));
|
||||
if (ui.isInspecting(cell.address)) {
|
||||
if (pendingInspectorUpdates.incrementAndGet() < 5) {
|
||||
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));
|
||||
if (ui.isInspecting(cell.address)) {
|
||||
if (pendingInspectorUpdates.incrementAndGet() < 5) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
public void saveCheats(File saveFile) {
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
writer = new FileWriter(saveFile);
|
||||
for (DynamicCheat cheat : cheatList) {
|
||||
writer.write(cheat.serialize());
|
||||
writer.write("\n");
|
||||
}
|
||||
writer.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loadCheats(File saveFile) {
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
in = new BufferedReader(new FileReader(saveFile));
|
||||
StringBuilder guts = new StringBuilder();
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
DynamicCheat cheat = DynamicCheat.deserialize(line);
|
||||
addCheat(cheat);
|
||||
}
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
import jace.core.RAMListener;
|
||||
import jace.state.State;
|
||||
import jace.ui.MetacheatUI;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
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;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
public class MetaCheat extends Cheats {
|
||||
|
||||
static final ScriptEngine NASHORN_ENGINE = new ScriptEngineManager().getEngineByName("nashorn");
|
||||
static Invocable NASHORN_INVOCABLE = (Invocable) NASHORN_ENGINE;
|
||||
|
||||
public static enum SearchType {
|
||||
VALUE, TEXT, CHANGE
|
||||
}
|
||||
|
||||
public static enum SearchChangeType {
|
||||
NO_CHANGE, ANY_CHANGE, LESS, GREATER, AMOUNT
|
||||
}
|
||||
|
||||
public static class SearchResult {
|
||||
|
||||
int address;
|
||||
int lastObservedValue = 0;
|
||||
|
||||
private SearchResult(int address, int val) {
|
||||
this.address = address;
|
||||
lastObservedValue = val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toHexString(address) + ": " + lastObservedValue + " (" + Integer.toHexString(lastObservedValue) + ")";
|
||||
}
|
||||
|
||||
public int getAddress() {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
MetacheatUI ui;
|
||||
|
||||
public int fadeRate = 1;
|
||||
public int lightRate = 30;
|
||||
public int historyLength = 10;
|
||||
|
||||
private int startAddress = 0;
|
||||
private int endAddress = 0x0ffff;
|
||||
private final StringProperty startAddressProperty = new SimpleStringProperty(Integer.toHexString(startAddress));
|
||||
private final StringProperty endAddressProperty = new SimpleStringProperty(Integer.toHexString(endAddress));
|
||||
private boolean byteSized = true;
|
||||
private SearchType searchType = SearchType.VALUE;
|
||||
private SearchChangeType searchChangeType = SearchChangeType.NO_CHANGE;
|
||||
private final BooleanProperty signedProperty = new SimpleBooleanProperty(false);
|
||||
private final StringProperty searchValueProperty = new SimpleStringProperty("0");
|
||||
private final StringProperty changeByProperty = new SimpleStringProperty("0");
|
||||
private final ObservableList<DynamicCheat> cheatList = FXCollections.observableArrayList();
|
||||
private final ObservableList<SearchResult> resultList = FXCollections.observableArrayList();
|
||||
private final ObservableList<State> snapshotList = FXCollections.observableArrayList();
|
||||
|
||||
public MetaCheat(Computer computer) {
|
||||
super(computer);
|
||||
addNumericValidator(startAddressProperty);
|
||||
addNumericValidator(endAddressProperty);
|
||||
addNumericValidator(searchValueProperty);
|
||||
addNumericValidator(changeByProperty);
|
||||
startAddressProperty.addListener((prop, oldVal, newVal) -> {
|
||||
startAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
|
||||
});
|
||||
endAddressProperty.addListener((prop, oldVal, newVal) -> {
|
||||
endAddress = Math.max(0, Math.min(65535, parseInt(newVal)));
|
||||
});
|
||||
}
|
||||
|
||||
private void addNumericValidator(StringProperty stringProperty) {
|
||||
stringProperty.addListener((ObservableValue<? extends String> prop, String oldVal, String newVal) -> {
|
||||
if (newVal == null || newVal.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!newVal.matches("(\\+|-)?(x|$)?[0-9a-fA-F]*")) {
|
||||
stringProperty.set("");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public int parseInt(String s) throws NumberFormatException {
|
||||
if (s == null || s.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
if (s.matches("(\\+|-)?[0-9]+")) {
|
||||
return Integer.parseInt(s);
|
||||
} else {
|
||||
String upper = s.toUpperCase();
|
||||
boolean positive = !upper.startsWith("-");
|
||||
for (int i = 0; i < upper.length(); i++) {
|
||||
char c = upper.charAt(i);
|
||||
if ((c >= '0' && c <= '9') || (c >= 'A' & c <= 'F')) {
|
||||
int value = Integer.parseInt(s.substring(i), 16);
|
||||
if (!positive) {
|
||||
value *= -1;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NumberFormatException("Could not interpret int value " + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
}
|
||||
|
||||
public void addCheat(DynamicCheat cheat) {
|
||||
cheatList.add(cheat);
|
||||
computer.getMemory().addListener(cheat);
|
||||
cheat.addressProperty().addListener((prop, oldVal, newVal) -> {
|
||||
computer.getMemory().removeListener(cheat);
|
||||
cheat.doConfig();
|
||||
computer.getMemory().addListener(cheat);
|
||||
});
|
||||
}
|
||||
|
||||
public void removeCheat(DynamicCheat cheat) {
|
||||
cheat.active.set(false);
|
||||
computer.getMemory().removeListener(cheat);
|
||||
cheatList.remove(cheat);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterListeners() {
|
||||
super.unregisterListeners();
|
||||
cheatList.stream().forEach(computer.getMemory()::removeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDeviceName() {
|
||||
return "MetaCheat";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
super.detach();
|
||||
ui.detach();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach() {
|
||||
ui = JaceApplication.getApplication().showMetacheat();
|
||||
ui.registerMetacheatEngine(this);
|
||||
super.attach();
|
||||
}
|
||||
|
||||
public int getStartAddress() {
|
||||
return startAddress;
|
||||
}
|
||||
|
||||
public int getEndAddress() {
|
||||
return endAddress;
|
||||
}
|
||||
|
||||
public void setByteSized(boolean b) {
|
||||
byteSized = b;
|
||||
}
|
||||
|
||||
public void setSearchType(SearchType searchType) {
|
||||
this.searchType = searchType;
|
||||
}
|
||||
|
||||
public void setSearchChangeType(SearchChangeType searchChangeType) {
|
||||
this.searchChangeType = searchChangeType;
|
||||
}
|
||||
|
||||
public Property<Boolean> signedProperty() {
|
||||
return signedProperty;
|
||||
}
|
||||
|
||||
public Property<String> searchValueProperty() {
|
||||
return searchValueProperty;
|
||||
}
|
||||
|
||||
public Property<String> searchChangeByProperty() {
|
||||
return changeByProperty;
|
||||
}
|
||||
|
||||
public ObservableList<DynamicCheat> getCheats() {
|
||||
return cheatList;
|
||||
}
|
||||
|
||||
public ObservableList<SearchResult> getSearchResults() {
|
||||
return resultList;
|
||||
}
|
||||
|
||||
public ObservableList<State> getSnapshots() {
|
||||
return snapshotList;
|
||||
}
|
||||
|
||||
public Property<String> startAddressProperty() {
|
||||
return startAddressProperty;
|
||||
}
|
||||
|
||||
public Property<String> endAddressProperty() {
|
||||
return endAddressProperty;
|
||||
}
|
||||
|
||||
public void newSearch() {
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
resultList.clear();
|
||||
int compare = parseInt(searchValueProperty.get());
|
||||
for (int i = 0; i < 0x10000; i++) {
|
||||
boolean signed = signedProperty.get();
|
||||
int val
|
||||
= byteSized
|
||||
? signed ? memory.readRaw(i) : memory.readRaw(i) & 0x0ff
|
||||
: signed ? memory.readWordRaw(i) : memory.readWordRaw(i) & 0x0ffff;
|
||||
if (!searchType.equals(SearchType.VALUE) || val == compare) {
|
||||
SearchResult result = new SearchResult(i, val);
|
||||
resultList.add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void performSearch() {
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
boolean signed = signedProperty.get();
|
||||
resultList.removeIf((SearchResult result) -> {
|
||||
int val = byteSized
|
||||
? signed ? memory.readRaw(result.address) : memory.readRaw(result.address) & 0x0ff
|
||||
: signed ? memory.readWordRaw(result.address) : memory.readWordRaw(result.address) & 0x0ffff;
|
||||
int last = result.lastObservedValue;
|
||||
result.lastObservedValue = val;
|
||||
switch (searchType) {
|
||||
case VALUE:
|
||||
int compare = parseInt(searchValueProperty.get());
|
||||
return compare != val;
|
||||
case CHANGE:
|
||||
switch (searchChangeType) {
|
||||
case AMOUNT:
|
||||
int amount = parseInt(searchChangeByProperty().getValue());
|
||||
return (val - last) != amount;
|
||||
case GREATER:
|
||||
return val <= last;
|
||||
case ANY_CHANGE:
|
||||
return val == last;
|
||||
case LESS:
|
||||
return val >= last;
|
||||
case NO_CHANGE:
|
||||
return val != last;
|
||||
}
|
||||
break;
|
||||
case TEXT:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
RAMListener memoryViewListener = null;
|
||||
private final Map<Integer, MemoryCell> memoryCells = new ConcurrentHashMap<>();
|
||||
|
||||
public MemoryCell getMemoryCell(int address) {
|
||||
return memoryCells.get(address);
|
||||
}
|
||||
|
||||
public void initMemoryView() {
|
||||
RAM memory = Emulator.computer.getMemory();
|
||||
for (int addr = getStartAddress(); addr <= getEndAddress(); addr++) {
|
||||
if (getMemoryCell(addr) == null) {
|
||||
MemoryCell cell = new MemoryCell();
|
||||
cell.address = addr;
|
||||
cell.value.set(memory.readRaw(addr));
|
||||
memoryCells.put(addr, cell);
|
||||
}
|
||||
}
|
||||
if (memoryViewListener == null) {
|
||||
memoryViewListener = memory.observe(RAMEvent.TYPE.ANY, startAddress, endAddress, this::processMemoryEvent);
|
||||
listeners.add(memoryViewListener);
|
||||
}
|
||||
}
|
||||
|
||||
int fadeCounter = 0;
|
||||
int FADE_TIMER_VALUE = (int) (Emulator.computer.getMotherboard().getSpeedInHz() / 60);
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
computer.cpu.performSingleTrace();
|
||||
if (fadeCounter-- <= 0) {
|
||||
fadeCounter = FADE_TIMER_VALUE;
|
||||
memoryCells.values().stream()
|
||||
.filter((cell) -> cell.hasCounts())
|
||||
.forEach((cell) -> {
|
||||
if (cell.execCount.get() > 0) {
|
||||
cell.execCount.set(Math.max(0, cell.execCount.get() - fadeRate));
|
||||
}
|
||||
if (cell.readCount.get() > 0) {
|
||||
cell.readCount.set(Math.max(0, cell.readCount.get() - fadeRate));
|
||||
}
|
||||
if (cell.writeCount.get() > 0) {
|
||||
cell.writeCount.set(Math.max(0, cell.writeCount.get() - fadeRate));
|
||||
}
|
||||
if (MemoryCell.listener != null) {
|
||||
MemoryCell.listener.changed(null, cell, cell);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
AtomicInteger pendingInspectorUpdates = new AtomicInteger(0);
|
||||
public void onInspectorChanged() {
|
||||
pendingInspectorUpdates.set(0);
|
||||
}
|
||||
|
||||
private void processMemoryEvent(RAMEvent e) {
|
||||
MemoryCell cell = getMemoryCell(e.getAddress());
|
||||
if (cell != null) {
|
||||
CPU cpu = Emulator.computer.getCpu();
|
||||
int pc = cpu.getProgramCounter();
|
||||
String trace = cpu.getLastTrace();
|
||||
switch (e.getType()) {
|
||||
case EXECUTE:
|
||||
cell.execInstructionsDisassembly.add(trace);
|
||||
if (cell.execInstructionsDisassembly.size() > historyLength) {
|
||||
cell.execInstructionsDisassembly.remove(0);
|
||||
}
|
||||
case READ_OPERAND:
|
||||
cell.execCount.set(Math.min(255, cell.execCount.get() + lightRate));
|
||||
break;
|
||||
case WRITE:
|
||||
cell.writeCount.set(Math.min(255, cell.writeCount.get() + lightRate));
|
||||
if (ui.isInspecting(cell.address)) {
|
||||
if (pendingInspectorUpdates.incrementAndGet() < 5) {
|
||||
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));
|
||||
if (ui.isInspecting(cell.address)) {
|
||||
if (pendingInspectorUpdates.incrementAndGet() < 5) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
public void saveCheats(File saveFile) {
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
writer = new FileWriter(saveFile);
|
||||
for (DynamicCheat cheat : cheatList) {
|
||||
writer.write(cheat.serialize());
|
||||
writer.write("\n");
|
||||
}
|
||||
writer.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loadCheats(File saveFile) {
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
in = new BufferedReader(new FileReader(saveFile));
|
||||
StringBuilder guts = new StringBuilder();
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
DynamicCheat cheat = DynamicCheat.deserialize(line);
|
||||
addCheat(cheat);
|
||||
}
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(MetaCheat.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,24 +21,25 @@ package jace.core;
|
||||
import jace.config.ConfigurableField;
|
||||
|
||||
/**
|
||||
* A timed device is a device which executes so many ticks in a given time
|
||||
* interval. This is the core of the emulator timing mechanics.
|
||||
* A timed device is a device which executes so many ticks in a given time interval. This is the core of the emulator
|
||||
* timing mechanics.
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public abstract class TimedDevice extends Device {
|
||||
|
||||
/**
|
||||
* Creates a new instance of TimedDevice
|
||||
*
|
||||
* @param computer
|
||||
*/
|
||||
public TimedDevice(Computer computer) {
|
||||
super(computer);
|
||||
setSpeed(cyclesPerSecond);
|
||||
setSpeedInHz(cyclesPerSecond);
|
||||
}
|
||||
@ConfigurableField(name = "Speed", description = "(Percentage)")
|
||||
public int speedRatio = 100;
|
||||
public long cyclesPerSecond = defaultCyclesPerSecond();
|
||||
private long cyclesPerSecond = defaultCyclesPerSecond();
|
||||
@ConfigurableField(name = "Max speed")
|
||||
public boolean maxspeed = false;
|
||||
|
||||
@ -57,10 +58,11 @@ public abstract class TimedDevice extends Device {
|
||||
public boolean suspend() {
|
||||
disableTempMaxSpeed();
|
||||
boolean result = super.suspend();
|
||||
if (worker != null && worker.isAlive()) {
|
||||
Thread w = worker;
|
||||
if (w != null && w.isAlive()) {
|
||||
try {
|
||||
worker.interrupt();
|
||||
worker.join(1000);
|
||||
w.interrupt();
|
||||
w.join(1000);
|
||||
} 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 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;
|
||||
nanosPerInterval = (long) (cyclesPerInterval * NANOS_PER_SECOND / cyclesPerSecond);
|
||||
// System.out.println("Will pause " + nanosPerInterval + " nanos every " + cyclesPerInterval + " cycles");
|
||||
cycleTimer = 0;
|
||||
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 wait = 0;
|
||||
|
||||
@ -171,11 +204,6 @@ public abstract class TimedDevice extends Device {
|
||||
|
||||
@Override
|
||||
public void reconfigure() {
|
||||
cyclesPerSecond = defaultCyclesPerSecond() * speedRatio / 100;
|
||||
if (cyclesPerSecond == 0) {
|
||||
cyclesPerSecond = defaultCyclesPerSecond();
|
||||
}
|
||||
setSpeed(cyclesPerSecond);
|
||||
}
|
||||
|
||||
public abstract long defaultCyclesPerSecond();
|
||||
|
@ -27,7 +27,6 @@ import jace.core.RAMEvent;
|
||||
import jace.core.RAMEvent.TYPE;
|
||||
import jace.core.RAMListener;
|
||||
import jace.core.SoundMixer;
|
||||
import static jace.core.Utility.*;
|
||||
import jace.hardware.mockingboard.PSG;
|
||||
import jace.hardware.mockingboard.R6522;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -80,7 +79,7 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
Condition playbackFinished = timerSync.newCondition();
|
||||
@ConfigurableField(name = "Idle sample threshold", description = "Number of samples to wait before suspending sound")
|
||||
private int MAX_IDLE_SAMPLES = SAMPLE_RATE;
|
||||
|
||||
|
||||
@Override
|
||||
public String getDeviceName() {
|
||||
return "Mockingboard";
|
||||
@ -90,7 +89,7 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
super(computer);
|
||||
controllers = new R6522[2];
|
||||
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;
|
||||
controllers[i] = new R6522(computer) {
|
||||
int controller = j;
|
||||
@ -131,6 +130,13 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
public String getShortName() {
|
||||
return "timer" + j;
|
||||
}
|
||||
|
||||
public void doTick() {
|
||||
super.doTick();
|
||||
if (controller == 0) {
|
||||
doSoundTick();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -140,6 +146,18 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
suspend();
|
||||
}
|
||||
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
|
||||
protected void handleFirmwareAccess(int register, TYPE type, int value, RAMEvent e) {
|
||||
@ -152,7 +170,7 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
chip++;
|
||||
}
|
||||
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());
|
||||
return;
|
||||
}
|
||||
@ -177,13 +195,25 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
for (R6522 c : controllers) {
|
||||
if (c == null || !c.isRunning()) {
|
||||
continue;
|
||||
if (heatbeatUnclocked) {
|
||||
if (System.currentTimeMillis() - heartbeatReclockTime >= unclockTime) {
|
||||
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) {
|
||||
// buildMixerTable();
|
||||
timerSync.lock();
|
||||
@ -194,7 +224,7 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
while (isRunning() && ticksSinceLastPlayback >= ticksBetweenPlayback) {
|
||||
if (!playbackFinished.await(1, TimeUnit.SECONDS)) {
|
||||
// gripe("The mockingboard playback thread has stalled. Disabling mockingboard.");
|
||||
suspend();
|
||||
suspendSound();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -263,14 +293,15 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
@Override
|
||||
public void resume() {
|
||||
pause = false;
|
||||
if (!isRunning()) {
|
||||
if (chips == null) {
|
||||
initPSG();
|
||||
for (PSG psg : chips) {
|
||||
psg.setRate(phasorMode ? CLOCK_SPEED * 2 : CLOCK_SPEED, SAMPLE_RATE);
|
||||
psg.reset();
|
||||
}
|
||||
if (chips == null) {
|
||||
initPSG();
|
||||
for (PSG psg : chips) {
|
||||
psg.setRate(phasorMode ? CLOCK_SPEED * 2 : CLOCK_SPEED, SAMPLE_RATE);
|
||||
psg.reset();
|
||||
}
|
||||
}
|
||||
if (!isRunning()) {
|
||||
setUnclocked(true);
|
||||
for (R6522 controller : controllers) {
|
||||
controller.attach();
|
||||
controller.resume();
|
||||
@ -290,6 +321,10 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
controller.suspend();
|
||||
controller.detach();
|
||||
}
|
||||
return suspendSound();
|
||||
}
|
||||
|
||||
public boolean suspendSound() {
|
||||
if (playbackThread == null || !playbackThread.isAlive()) {
|
||||
return false;
|
||||
}
|
||||
@ -325,11 +360,11 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
int zeroSamples = 0;
|
||||
setRun(true);
|
||||
LockSupport.parkNanos(5000);
|
||||
while (isRunning()) {
|
||||
while (isRunning() && !Thread.interrupted()) {
|
||||
while (isRunning() && !computer.isRunning()) {
|
||||
Thread.currentThread().yield();
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
if (isRunning()) {
|
||||
if (isRunning() && !Thread.interrupted()) {
|
||||
playSound(leftBuffer, rightBuffer);
|
||||
int p = 0;
|
||||
for (int idx = 0; idx < BUFFER_LENGTH; idx++) {
|
||||
@ -394,6 +429,8 @@ public class CardMockingboard extends Card implements Runnable {
|
||||
} catch (LineUnavailableException ex) {
|
||||
Logger.getLogger(CardMockingboard.class
|
||||
.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(CardMockingboard.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
computer.getMotherboard().cancelSpeedRequest(this);
|
||||
System.out.println("Mockingboard playback stopped");
|
||||
|
@ -1,346 +1,365 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* 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,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
package jace.hardware.mockingboard;
|
||||
|
||||
import jace.core.Computer;
|
||||
import jace.core.Device;
|
||||
|
||||
/**
|
||||
* Implementation of 6522 VIA chip
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public abstract class R6522 extends Device {
|
||||
|
||||
public R6522(Computer computer) {
|
||||
super(computer);
|
||||
timer1freerun = true;
|
||||
timer1running = true;
|
||||
timer1latch = 0x1fff;
|
||||
timer1interruptEnabled = false;
|
||||
setRun(true);
|
||||
}
|
||||
|
||||
// 6522 VIA
|
||||
// http://www.applevault.com/twiki/Main/Mockingboard/6522.pdf
|
||||
// I/O registers
|
||||
public static enum Register {
|
||||
ORB(0), // Output Register B
|
||||
ORA(1), // Output Register A
|
||||
DDRB(2),// Data direction reg B
|
||||
DDRA(3),// Data direction reg A
|
||||
T1CL(4),// T1 low-order latches (low-order counter for read operations)
|
||||
T1CH(5),// T1 high-order counter
|
||||
T1LL(6),// T1 low-order latches
|
||||
T1LH(7),// T1 high-order latches
|
||||
T2CL(8),// T2 low-order latches (low-order counter for read operations)
|
||||
T2CH(9),// T2 high-order counter
|
||||
SR(10),// Shift register
|
||||
ACR(11),// Aux control register
|
||||
PCR(12),// Perripheral control register
|
||||
IFR(13),// Interrupt flag register
|
||||
IER(14),// Interrupt enable register
|
||||
ORAH(15);// Output Register A (no handshake)
|
||||
|
||||
int val;
|
||||
|
||||
Register(int v) {
|
||||
val = v;
|
||||
}
|
||||
|
||||
static public Register fromInt(int i) {
|
||||
for (Register r : Register.values()) {
|
||||
if (r.val == i) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// state variables
|
||||
public int oraReg = 0;
|
||||
public int iraReg = 0;
|
||||
public int orbReg = 0;
|
||||
public int irbReg = 0;
|
||||
// DDRA and DDRB must be set to output for mockingboard to do anything
|
||||
// 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
|
||||
public int dataDirectionA = 0;
|
||||
public int dataDirectionB = 0;
|
||||
|
||||
// Though this is necessary for a complete emulation of the 6522, it isn't needed by the mockingboard
|
||||
// set by bit 0 of ACR
|
||||
// public boolean latchEnabledA = false;
|
||||
// set by bit 1 of ACR
|
||||
// public boolean latchEnabledB = false;
|
||||
//Bits 2,3,4 of ACR
|
||||
// static public enum ShiftRegisterControl {
|
||||
// interruptDisabled(0),
|
||||
// shiftInT2(4),
|
||||
// shiftIn02(8),
|
||||
// shiftInExt(12),
|
||||
// shiftOutFree(16),
|
||||
// shiftOutT2(20),
|
||||
// shiftOut02(24),
|
||||
// shiftOutExt(28);
|
||||
//
|
||||
// int val;
|
||||
// private ShiftRegisterControl(int v) {
|
||||
// val = v;
|
||||
// }
|
||||
//
|
||||
// public static ShiftRegisterControl fromBits(int b) {
|
||||
// b=b&28;
|
||||
// for (ShiftRegisterControl s : values()) {
|
||||
// if (s.val == b) return s;
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
// public ShiftRegisterControl shiftMode = ShiftRegisterControl.interruptDisabled;
|
||||
// //Bit 5 of ACR (false = timed interrupt, true = count down pulses on PB6)
|
||||
// public boolean t2countPulses = false;
|
||||
// //Bit 6 of ACR (true = continuous, false = one-shot)
|
||||
// public boolean t1continuous = false;
|
||||
// //Bit 7 of ACR (true = enable PB7, false = interruptDisabled)
|
||||
// public boolean t1enablePB7 = false;
|
||||
// // NOTE: Mockingboard did not use PB6 or PB7, they are not connected to anything
|
||||
public boolean timer1interruptEnabled = true;
|
||||
public boolean timer1IRQ = false; // True if timer interrupt flag is set
|
||||
public int timer1latch = 0;
|
||||
public int timer1counter = 0;
|
||||
public boolean timer1freerun = false;
|
||||
public boolean timer1running = false;
|
||||
public boolean timer2interruptEnabled = true;
|
||||
public boolean timer2IRQ = false; // True if timer interrupt flag is set
|
||||
public int timer2latch = 0;
|
||||
public int timer2counter = 0;
|
||||
public boolean timer2running = false;
|
||||
|
||||
@Override
|
||||
protected String getDeviceName() {
|
||||
return "6522 VIA Chip";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (timer1running) {
|
||||
timer1counter--;
|
||||
if (timer1counter < 0) {
|
||||
timer1counter = timer1latch;
|
||||
if (!timer1freerun) {
|
||||
timer1running = false;
|
||||
}
|
||||
if (timer1interruptEnabled) {
|
||||
// System.out.println("Timer 1 generated interrupt");
|
||||
timer1IRQ = true;
|
||||
computer.getCpu().generateInterrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timer2running) {
|
||||
timer2counter--;
|
||||
if (timer2counter < 0) {
|
||||
timer2running = false;
|
||||
timer2counter = timer2latch;
|
||||
if (timer2interruptEnabled) {
|
||||
timer2IRQ = true;
|
||||
computer.getCpu().generateInterrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!timer1running && !timer2running) {
|
||||
setRun(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach() {
|
||||
// Start chip
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconfigure() {
|
||||
// Reset
|
||||
}
|
||||
|
||||
public void writeRegister(int reg, int val) {
|
||||
int value = val & 0x0ff;
|
||||
Register r = Register.fromInt(reg);
|
||||
// System.out.println("Writing "+(value&0x0ff)+" to register "+r.toString());
|
||||
switch (r) {
|
||||
case ORB:
|
||||
if (dataDirectionB == 0) {
|
||||
break;
|
||||
}
|
||||
sendOutputB(value & dataDirectionB);
|
||||
break;
|
||||
case ORA:
|
||||
// case ORAH:
|
||||
if (dataDirectionA == 0) {
|
||||
break;
|
||||
}
|
||||
sendOutputA(value & dataDirectionA);
|
||||
break;
|
||||
case DDRB:
|
||||
dataDirectionB = value;
|
||||
break;
|
||||
case DDRA:
|
||||
dataDirectionA = value;
|
||||
break;
|
||||
case T1CL:
|
||||
case T1LL:
|
||||
timer1latch = (timer1latch & 0x0ff00) | value;
|
||||
break;
|
||||
case T1CH:
|
||||
timer1latch = (timer1latch & 0x0ff) | (value << 8);
|
||||
timer1IRQ = false;
|
||||
timer1counter = timer1latch;
|
||||
timer1running = true;
|
||||
setRun(true);
|
||||
break;
|
||||
case T1LH:
|
||||
timer1latch = (timer1latch & 0x0ff) | (value << 8);
|
||||
timer1IRQ = false;
|
||||
break;
|
||||
case T2CL:
|
||||
timer2latch = (timer2latch & 0x0ff00) | value;
|
||||
break;
|
||||
case T2CH:
|
||||
timer2latch = (timer2latch & 0x0ff) | (value << 8);
|
||||
timer2IRQ = false;
|
||||
timer2counter = timer2latch;
|
||||
timer2running = true;
|
||||
setRun(true);
|
||||
break;
|
||||
case SR:
|
||||
// SHIFT REGISTER NOT IMPLEMENTED
|
||||
break;
|
||||
case ACR:
|
||||
// SHIFT REGISTER NOT IMPLEMENTED
|
||||
timer1freerun = (value & 64) != 0;
|
||||
if (timer1freerun) {
|
||||
timer1running = true;
|
||||
setRun(true);
|
||||
}
|
||||
break;
|
||||
case PCR:
|
||||
// TODO: Implement if Votrax (SSI) is to be supported
|
||||
break;
|
||||
case IFR:
|
||||
if ((value & 64) != 0) {
|
||||
timer1IRQ = false;
|
||||
}
|
||||
if ((value & 32) != 0) {
|
||||
timer2IRQ = false;
|
||||
}
|
||||
break;
|
||||
case IER:
|
||||
boolean enable = (value & 128) != 0;
|
||||
if ((value & 64) != 0) {
|
||||
timer1interruptEnabled = enable;
|
||||
}
|
||||
if ((value & 32) != 0) {
|
||||
timer2interruptEnabled = enable;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Whatever uses 6522 will want to know when it is outputting values
|
||||
// So to hook that in, these abstract methods will be defined as appropriate
|
||||
public abstract void sendOutputA(int value);
|
||||
|
||||
public abstract void sendOutputB(int value);
|
||||
|
||||
public int readRegister(int reg) {
|
||||
Register r = Register.fromInt(reg);
|
||||
// System.out.println("Reading register "+r.toString());
|
||||
switch (r) {
|
||||
case ORB:
|
||||
if (dataDirectionB == 0x0ff) {
|
||||
break;
|
||||
}
|
||||
return receiveOutputB() & (dataDirectionB ^ 0x0ff);
|
||||
case ORA:
|
||||
case ORAH:
|
||||
if (dataDirectionA == 0x0ff) {
|
||||
break;
|
||||
}
|
||||
return receiveOutputA() & (dataDirectionA ^ 0x0ff);
|
||||
case DDRB:
|
||||
return dataDirectionB;
|
||||
case DDRA:
|
||||
return dataDirectionA;
|
||||
case T1CL:
|
||||
timer1IRQ = false;
|
||||
return timer1counter & 0x0ff;
|
||||
case T1CH:
|
||||
return (timer1counter & 0x0ff00) >> 8;
|
||||
case T1LL:
|
||||
return timer1latch & 0x0ff;
|
||||
case T1LH:
|
||||
return (timer1latch & 0x0ff00) >> 8;
|
||||
case T2CL:
|
||||
timer2IRQ = false;
|
||||
return timer2counter & 0x0ff;
|
||||
case T2CH:
|
||||
return (timer2counter & 0x0ff00) >> 8;
|
||||
case SR:
|
||||
// SHIFT REGISTER NOT IMPLEMENTED
|
||||
return 0;
|
||||
case ACR:
|
||||
// SHIFT REGISTER NOT IMPLEMENTED
|
||||
if (timer1freerun) {
|
||||
return 64;
|
||||
}
|
||||
return 0;
|
||||
case PCR:
|
||||
break;
|
||||
case IFR:
|
||||
int val = 0;
|
||||
if (timer1IRQ) {
|
||||
val |= 64;
|
||||
}
|
||||
if (timer2IRQ) {
|
||||
val |= 32;
|
||||
}
|
||||
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();
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* 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,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
package jace.hardware.mockingboard;
|
||||
|
||||
import jace.core.Computer;
|
||||
import jace.core.Device;
|
||||
import jace.core.TimedDevice;
|
||||
|
||||
/**
|
||||
* Implementation of 6522 VIA chip
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public abstract class R6522 extends TimedDevice {
|
||||
public static long SPEED = 1020484L; // (NTSC)
|
||||
|
||||
public R6522(Computer computer) {
|
||||
super(computer);
|
||||
timer1freerun = true;
|
||||
timer1running = true;
|
||||
timer1latch = 0x1fff;
|
||||
timer1interruptEnabled = false;
|
||||
setSpeedInHz(SPEED);
|
||||
setRun(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long defaultCyclesPerSecond() {
|
||||
return SPEED;
|
||||
}
|
||||
|
||||
// 6522 VIA
|
||||
// http://www.applevault.com/twiki/Main/Mockingboard/6522.pdf
|
||||
// I/O registers
|
||||
public static enum Register {
|
||||
ORB(0), // Output Register B
|
||||
ORA(1), // Output Register A
|
||||
DDRB(2),// Data direction reg B
|
||||
DDRA(3),// Data direction reg A
|
||||
T1CL(4),// T1 low-order latches (low-order counter for read operations)
|
||||
T1CH(5),// T1 high-order counter
|
||||
T1LL(6),// T1 low-order latches
|
||||
T1LH(7),// T1 high-order latches
|
||||
T2CL(8),// T2 low-order latches (low-order counter for read operations)
|
||||
T2CH(9),// T2 high-order counter
|
||||
SR(10),// Shift register
|
||||
ACR(11),// Aux control register
|
||||
PCR(12),// Perripheral control register
|
||||
IFR(13),// Interrupt flag register
|
||||
IER(14),// Interrupt enable register
|
||||
ORAH(15);// Output Register A (no handshake)
|
||||
|
||||
int val;
|
||||
|
||||
Register(int v) {
|
||||
val = v;
|
||||
}
|
||||
|
||||
static public Register fromInt(int i) {
|
||||
for (Register r : Register.values()) {
|
||||
if (r.val == i) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// state variables
|
||||
public int oraReg = 0;
|
||||
public int iraReg = 0;
|
||||
public int orbReg = 0;
|
||||
public int irbReg = 0;
|
||||
// DDRA and DDRB must be set to output for mockingboard to do anything
|
||||
// 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
|
||||
public int dataDirectionA = 0;
|
||||
public int dataDirectionB = 0;
|
||||
|
||||
// Though this is necessary for a complete emulation of the 6522, it isn't needed by the mockingboard
|
||||
// set by bit 0 of ACR
|
||||
// public boolean latchEnabledA = false;
|
||||
// set by bit 1 of ACR
|
||||
// public boolean latchEnabledB = false;
|
||||
//Bits 2,3,4 of ACR
|
||||
// static public enum ShiftRegisterControl {
|
||||
// interruptDisabled(0),
|
||||
// shiftInT2(4),
|
||||
// shiftIn02(8),
|
||||
// shiftInExt(12),
|
||||
// shiftOutFree(16),
|
||||
// shiftOutT2(20),
|
||||
// shiftOut02(24),
|
||||
// shiftOutExt(28);
|
||||
//
|
||||
// int val;
|
||||
// private ShiftRegisterControl(int v) {
|
||||
// val = v;
|
||||
// }
|
||||
//
|
||||
// public static ShiftRegisterControl fromBits(int b) {
|
||||
// b=b&28;
|
||||
// for (ShiftRegisterControl s : values()) {
|
||||
// if (s.val == b) return s;
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
// public ShiftRegisterControl shiftMode = ShiftRegisterControl.interruptDisabled;
|
||||
// //Bit 5 of ACR (false = timed interrupt, true = count down pulses on PB6)
|
||||
// public boolean t2countPulses = false;
|
||||
// //Bit 6 of ACR (true = continuous, false = one-shot)
|
||||
// public boolean t1continuous = false;
|
||||
// //Bit 7 of ACR (true = enable PB7, false = interruptDisabled)
|
||||
// public boolean t1enablePB7 = false;
|
||||
// // NOTE: Mockingboard did not use PB6 or PB7, they are not connected to anything
|
||||
public boolean timer1interruptEnabled = true;
|
||||
public boolean timer1IRQ = false; // True if timer interrupt flag is set
|
||||
public int timer1latch = 0;
|
||||
public int timer1counter = 0;
|
||||
public boolean timer1freerun = false;
|
||||
public boolean timer1running = false;
|
||||
public boolean timer2interruptEnabled = true;
|
||||
public boolean timer2IRQ = false; // True if timer interrupt flag is set
|
||||
public int timer2latch = 0;
|
||||
public int timer2counter = 0;
|
||||
public boolean timer2running = false;
|
||||
public boolean unclocked = false;
|
||||
|
||||
@Override
|
||||
protected String getDeviceName() {
|
||||
return "6522 VIA Chip";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (!unclocked) {
|
||||
doTick();
|
||||
}
|
||||
}
|
||||
|
||||
public void setUnclocked(boolean unclocked) {
|
||||
this.unclocked = unclocked;
|
||||
}
|
||||
|
||||
public void doTick() {
|
||||
if (timer1running) {
|
||||
timer1counter--;
|
||||
if (timer1counter < 0) {
|
||||
timer1counter = timer1latch;
|
||||
if (!timer1freerun) {
|
||||
timer1running = false;
|
||||
}
|
||||
if (timer1interruptEnabled) {
|
||||
// System.out.println("Timer 1 generated interrupt");
|
||||
timer1IRQ = true;
|
||||
computer.getCpu().generateInterrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timer2running) {
|
||||
timer2counter--;
|
||||
if (timer2counter < 0) {
|
||||
timer2running = false;
|
||||
timer2counter = timer2latch;
|
||||
if (timer2interruptEnabled) {
|
||||
timer2IRQ = true;
|
||||
computer.getCpu().generateInterrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!timer1running && !timer2running) {
|
||||
setRun(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attach() {
|
||||
// Start chip
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconfigure() {
|
||||
// Reset
|
||||
}
|
||||
|
||||
public void writeRegister(int reg, int val) {
|
||||
int value = val & 0x0ff;
|
||||
Register r = Register.fromInt(reg);
|
||||
// System.out.println("Writing "+(value&0x0ff)+" to register "+r.toString());
|
||||
switch (r) {
|
||||
case ORB:
|
||||
if (dataDirectionB == 0) {
|
||||
break;
|
||||
}
|
||||
sendOutputB(value & dataDirectionB);
|
||||
break;
|
||||
case ORA:
|
||||
// case ORAH:
|
||||
if (dataDirectionA == 0) {
|
||||
break;
|
||||
}
|
||||
sendOutputA(value & dataDirectionA);
|
||||
break;
|
||||
case DDRB:
|
||||
dataDirectionB = value;
|
||||
break;
|
||||
case DDRA:
|
||||
dataDirectionA = value;
|
||||
break;
|
||||
case T1CL:
|
||||
case T1LL:
|
||||
timer1latch = (timer1latch & 0x0ff00) | value;
|
||||
break;
|
||||
case T1CH:
|
||||
timer1latch = (timer1latch & 0x0ff) | (value << 8);
|
||||
timer1IRQ = false;
|
||||
timer1counter = timer1latch;
|
||||
timer1running = true;
|
||||
setRun(true);
|
||||
break;
|
||||
case T1LH:
|
||||
timer1latch = (timer1latch & 0x0ff) | (value << 8);
|
||||
timer1IRQ = false;
|
||||
break;
|
||||
case T2CL:
|
||||
timer2latch = (timer2latch & 0x0ff00) | value;
|
||||
break;
|
||||
case T2CH:
|
||||
timer2latch = (timer2latch & 0x0ff) | (value << 8);
|
||||
timer2IRQ = false;
|
||||
timer2counter = timer2latch;
|
||||
timer2running = true;
|
||||
setRun(true);
|
||||
break;
|
||||
case SR:
|
||||
// SHIFT REGISTER NOT IMPLEMENTED
|
||||
break;
|
||||
case ACR:
|
||||
// SHIFT REGISTER NOT IMPLEMENTED
|
||||
timer1freerun = (value & 64) != 0;
|
||||
if (timer1freerun) {
|
||||
timer1running = true;
|
||||
setRun(true);
|
||||
}
|
||||
break;
|
||||
case PCR:
|
||||
// TODO: Implement if Votrax (SSI) is to be supported
|
||||
break;
|
||||
case IFR:
|
||||
if ((value & 64) != 0) {
|
||||
timer1IRQ = false;
|
||||
}
|
||||
if ((value & 32) != 0) {
|
||||
timer2IRQ = false;
|
||||
}
|
||||
break;
|
||||
case IER:
|
||||
boolean enable = (value & 128) != 0;
|
||||
if ((value & 64) != 0) {
|
||||
timer1interruptEnabled = enable;
|
||||
}
|
||||
if ((value & 32) != 0) {
|
||||
timer2interruptEnabled = enable;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Whatever uses 6522 will want to know when it is outputting values
|
||||
// So to hook that in, these abstract methods will be defined as appropriate
|
||||
public abstract void sendOutputA(int value);
|
||||
|
||||
public abstract void sendOutputB(int value);
|
||||
|
||||
public int readRegister(int reg) {
|
||||
Register r = Register.fromInt(reg);
|
||||
// System.out.println("Reading register "+r.toString());
|
||||
switch (r) {
|
||||
case ORB:
|
||||
if (dataDirectionB == 0x0ff) {
|
||||
break;
|
||||
}
|
||||
return receiveOutputB() & (dataDirectionB ^ 0x0ff);
|
||||
case ORA:
|
||||
case ORAH:
|
||||
if (dataDirectionA == 0x0ff) {
|
||||
break;
|
||||
}
|
||||
return receiveOutputA() & (dataDirectionA ^ 0x0ff);
|
||||
case DDRB:
|
||||
return dataDirectionB;
|
||||
case DDRA:
|
||||
return dataDirectionA;
|
||||
case T1CL:
|
||||
timer1IRQ = false;
|
||||
return timer1counter & 0x0ff;
|
||||
case T1CH:
|
||||
return (timer1counter & 0x0ff00) >> 8;
|
||||
case T1LL:
|
||||
return timer1latch & 0x0ff;
|
||||
case T1LH:
|
||||
return (timer1latch & 0x0ff00) >> 8;
|
||||
case T2CL:
|
||||
timer2IRQ = false;
|
||||
return timer2counter & 0x0ff;
|
||||
case T2CH:
|
||||
return (timer2counter & 0x0ff00) >> 8;
|
||||
case SR:
|
||||
// SHIFT REGISTER NOT IMPLEMENTED
|
||||
return 0;
|
||||
case ACR:
|
||||
// SHIFT REGISTER NOT IMPLEMENTED
|
||||
if (timer1freerun) {
|
||||
return 64;
|
||||
}
|
||||
return 0;
|
||||
case PCR:
|
||||
break;
|
||||
case IFR:
|
||||
int val = 0;
|
||||
if (timer1IRQ) {
|
||||
val |= 64;
|
||||
}
|
||||
if (timer2IRQ) {
|
||||
val |= 32;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user