forked from Apple-2-Tools/jace
285 lines
10 KiB
Java
285 lines
10 KiB
Java
/*
|
|
* 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.core;
|
|
|
|
import jace.state.Stateful;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* A softswitch is a hidden bit that lives in the MMU, it can be activated or
|
|
* deactivated to change operating characteristics of the computer such as video
|
|
* display mode or memory paging model. Other special softswitches access
|
|
* keyboard and speaker ports. The underlying mechanic of softswitches is
|
|
* managed by the RamListener/Ram model and, in the case of video modes, the
|
|
* Video classes.
|
|
*
|
|
* The implementation of softswitches is in jace.apple2e.SoftSwitches
|
|
*
|
|
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
|
* @see jace.apple2e.SoftSwitches
|
|
*/
|
|
public abstract class SoftSwitch {
|
|
|
|
@Stateful
|
|
public Boolean state;
|
|
private Boolean initalState;
|
|
private List<RAMListener> listeners;
|
|
private final List<Integer> exclusionActivate = new ArrayList<>();
|
|
private final List<Integer> exclusionDeactivate = new ArrayList<>();
|
|
private final List<Integer> exclusionQuery = new ArrayList<>();
|
|
private String name;
|
|
private boolean toggleType = false;
|
|
|
|
/**
|
|
* Creates a new instance of SoftSwitch
|
|
*
|
|
* @param name
|
|
* @param initalState
|
|
*/
|
|
public SoftSwitch(String name, Boolean initalState) {
|
|
this.initalState = initalState;
|
|
this.state = initalState;
|
|
this.listeners = new ArrayList<>();
|
|
this.name = name;
|
|
}
|
|
|
|
public SoftSwitch(String name, int offAddress, int onAddress, int queryAddress, RAMEvent.TYPE changeType, Boolean initalState) {
|
|
if (onAddress == offAddress && onAddress != -1) {
|
|
toggleType = true;
|
|
// System.out.println("Switch " + name + " is a toggle type switch!");
|
|
}
|
|
this.initalState = initalState;
|
|
this.state = initalState;
|
|
this.listeners = new ArrayList<>();
|
|
this.name = name;
|
|
int[] onAddresses = null;
|
|
int[] offAddresses = null;
|
|
int[] queryAddressList = null;
|
|
if (onAddress >= 0) {
|
|
onAddresses = new int[]{onAddress};
|
|
}
|
|
if (offAddress >= 0) {
|
|
offAddresses = new int[]{offAddress};
|
|
}
|
|
if (queryAddress >= 0) {
|
|
queryAddressList = new int[]{queryAddress};
|
|
}
|
|
init(offAddresses, onAddresses, queryAddressList, changeType);
|
|
}
|
|
|
|
public SoftSwitch(String name, int[] offAddrs, int[] onAddrs, int[] queryAddrs, RAMEvent.TYPE changeType, Boolean initalState) {
|
|
this(name, initalState);
|
|
init(offAddrs, onAddrs, queryAddrs, changeType);
|
|
}
|
|
|
|
private void init(int[] offAddrs, int[] onAddrs, int[] queryAddrs, RAMEvent.TYPE changeType) {
|
|
if (toggleType) {
|
|
List<Integer> addrs = new ArrayList<>();
|
|
for (int i : onAddrs) {
|
|
addrs.add(i);
|
|
}
|
|
Collections.sort(addrs);
|
|
final int beginAddr = addrs.get(0);
|
|
final int endAddr = addrs.get(addrs.size() - 1);
|
|
for (int i = beginAddr; i < endAddr; i++) {
|
|
if (!addrs.contains(i)) {
|
|
exclusionActivate.add(i);
|
|
}
|
|
}
|
|
RAMListener l = new RAMListener(changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
|
|
@Override
|
|
protected void doConfig() {
|
|
setScopeStart(beginAddr);
|
|
setScopeEnd(endAddr);
|
|
}
|
|
|
|
@Override
|
|
protected void doEvent(RAMEvent e) {
|
|
if (!exclusionActivate.contains(e.getAddress())) {
|
|
// System.out.println("Access to "+Integer.toHexString(e.getAddress())+" ENABLES switch "+getName());
|
|
setState(!getState());
|
|
}
|
|
}
|
|
};
|
|
addListener(l);
|
|
} else {
|
|
if (onAddrs != null) {
|
|
List<Integer> addrs = new ArrayList<>();
|
|
for (int i : onAddrs) {
|
|
addrs.add(i);
|
|
}
|
|
Collections.sort(addrs);
|
|
final int beginAddr = addrs.get(0);
|
|
final int endAddr = addrs.get(addrs.size() - 1);
|
|
for (int i = beginAddr; i < endAddr; i++) {
|
|
if (!addrs.contains(i)) {
|
|
exclusionActivate.add(i);
|
|
}
|
|
}
|
|
RAMListener l = new RAMListener(changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
|
|
@Override
|
|
protected void doConfig() {
|
|
setScopeStart(beginAddr);
|
|
setScopeEnd(endAddr);
|
|
}
|
|
|
|
@Override
|
|
protected void doEvent(RAMEvent e) {
|
|
if (!exclusionActivate.contains(e.getAddress())) {
|
|
// System.out.println("Access to "+Integer.toHexString(e.getAddress())+" ENABLES switch "+getName());
|
|
setState(true);
|
|
}
|
|
}
|
|
};
|
|
addListener(l);
|
|
}
|
|
|
|
if (offAddrs != null) {
|
|
List<Integer> addrs = new ArrayList<>();
|
|
for (int i : offAddrs) {
|
|
addrs.add(i);
|
|
}
|
|
final int beginAddr = addrs.get(0);
|
|
final int endAddr = addrs.get(addrs.size() - 1);
|
|
for (int i = beginAddr; i < endAddr; i++) {
|
|
if (!addrs.contains(i)) {
|
|
exclusionDeactivate.add(i);
|
|
}
|
|
}
|
|
RAMListener l = new RAMListener(changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
|
|
@Override
|
|
protected void doConfig() {
|
|
setScopeStart(beginAddr);
|
|
setScopeEnd(endAddr);
|
|
}
|
|
|
|
@Override
|
|
protected void doEvent(RAMEvent e) {
|
|
if (!exclusionDeactivate.contains(e.getAddress())) {
|
|
setState(false);
|
|
// System.out.println("Access to "+Integer.toHexString(e.getAddress())+" disables switch "+getName());
|
|
}
|
|
}
|
|
};
|
|
addListener(l);
|
|
}
|
|
}
|
|
|
|
if (queryAddrs != null) {
|
|
List<Integer> addrs = new ArrayList<>();
|
|
for (int i : queryAddrs) {
|
|
addrs.add(i);
|
|
}
|
|
final int beginAddr = addrs.get(0);
|
|
final int endAddr = addrs.get(addrs.size() - 1);
|
|
for (int i = beginAddr; i < endAddr; i++) {
|
|
if (!addrs.contains(i)) {
|
|
exclusionQuery.add(i);
|
|
}
|
|
}
|
|
// RAMListener l = new RAMListener(changeType, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
|
|
RAMListener l = new RAMListener(RAMEvent.TYPE.READ, RAMEvent.SCOPE.RANGE, RAMEvent.VALUE.ANY) {
|
|
@Override
|
|
protected void doConfig() {
|
|
setScopeStart(beginAddr);
|
|
setScopeEnd(endAddr);
|
|
}
|
|
|
|
@Override
|
|
protected void doEvent(RAMEvent e) {
|
|
if (!exclusionQuery.contains(e.getAddress())) {
|
|
e.setNewValue(0x0ff & readSwitch());
|
|
// System.out.println("Read from "+Integer.toHexString(e.getAddress())+" returns "+Integer.toHexString(e.getNewValue()));
|
|
}
|
|
}
|
|
};
|
|
addListener(l);
|
|
}
|
|
}
|
|
|
|
public boolean inhibit() {
|
|
return false;
|
|
}
|
|
|
|
abstract protected byte readSwitch();
|
|
|
|
protected void addListener(RAMListener l) {
|
|
listeners.add(l);
|
|
}
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public void reset() {
|
|
if (initalState != null) {
|
|
setState(initalState);
|
|
}
|
|
}
|
|
|
|
public void register() {
|
|
RAM m = Computer.getComputer().getMemory();
|
|
listeners.stream().forEach((l) -> {
|
|
m.addListener(l);
|
|
});
|
|
}
|
|
|
|
public void unregister() {
|
|
RAM m = Computer.getComputer().getMemory();
|
|
listeners.stream().forEach((l) -> {
|
|
m.removeListener(l);
|
|
});
|
|
}
|
|
|
|
public void setState(boolean newState) {
|
|
if (inhibit()) {
|
|
return;
|
|
}
|
|
// if (this != SoftSwitches.VBL.getSwitch() &&
|
|
// this != SoftSwitches.KEYBOARD.getSwitch())
|
|
// System.out.println("Switch "+name+" set to "+newState);
|
|
state = newState;
|
|
/*
|
|
if (queryAddresses != null) {
|
|
RAM m = Computer.getComputer().getMemory();
|
|
for (int i:queryAddresses) {
|
|
byte old = m.read(i, false);
|
|
m.write(i, (byte) (old & 0x7f | (state ? 0x080:0x000)), false);
|
|
}
|
|
}
|
|
*/
|
|
stateChanged();
|
|
}
|
|
|
|
public final boolean getState() {
|
|
if (state == null) {
|
|
return false;
|
|
}
|
|
return state;
|
|
}
|
|
|
|
abstract public void stateChanged();
|
|
|
|
@Override
|
|
public String toString() {
|
|
return getName() + (getState() ? ":1" : ":0");
|
|
}
|
|
} |