/* * 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; protected Computer computer; /** * 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 (e.getType().isRead()) { e.setNewValue(computer.getVideo().getFloatingBus()); } 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(Computer computer) { this.computer = computer; RAM m = computer.getMemory(); listeners.stream().forEach((l) -> { m.addListener(l); }); } public void unregister() { RAM m = computer.getMemory(); listeners.stream().forEach((l) -> { m.removeListener(l); }); this.computer = null; } 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.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"); } }