mirror of
https://github.com/badvision/jace.git
synced 2025-01-03 19:30:44 +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;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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");
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user