/* * 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.apple2e.SoftSwitches; import jace.config.Reconfigurable; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Vector; /** * RAM is a 64K address space of paged memory. It also manages sets of memory * listeners, used by I/O as well as emulator add-ons (and cheats). RAM also * manages cards in the emulator because they are tied into the MMU memory * bankswitch logic. * * @author Brendan Robert (BLuRry) brendan.robert@gmail.com */ public abstract class RAM implements Reconfigurable { public PagedMemory activeRead; public PagedMemory activeWrite; public List listeners; public List[] listenerMap; public List[] ioListenerMap; protected Card[] cards; // card 0 = 80 column card firmware / system rom public int activeSlot = 0; /** * Creates a new instance of RAM */ public RAM() { listeners = new Vector(); cards = new Card[8]; refreshListenerMap(); } public void setActiveCard(int slot) { if (activeSlot != slot) { activeSlot = slot; configureActiveMemory(); } else if (!SoftSwitches.CXROM.getState()) { configureActiveMemory(); } } public int getActiveSlot() { return activeSlot; } public Card[] getAllCards() { return cards; } public Card getCard(int slot) { if (slot >= 1 && slot <= 7) { return cards[slot]; } return null; } public void addCard(Card c, int slot) { cards[slot] = c; c.setSlot(slot); c.attach(); } public void removeCard(Card c) { c.suspend(); c.detach(); removeCard(c.getSlot()); } public void removeCard(int slot) { if (cards[slot] != null) { cards[slot].suspend(); cards[slot].detach(); } cards[slot] = null; } abstract public void configureActiveMemory(); public byte write(int address, byte b, boolean generateEvent, boolean requireSynchronization) { byte[] page = activeWrite.getMemoryPage(address); byte old = 0; if (page == null) { if (generateEvent) { callListener(RAMEvent.TYPE.WRITE, address, old, b, requireSynchronization); } } else { int offset = address & 0x0FF; old = page[offset]; if (generateEvent) { b = callListener(RAMEvent.TYPE.WRITE, address, old, b, requireSynchronization); } page[offset] = b; } return old; } public void writeWord(int address, int w, boolean generateEvent, boolean requireSynchronization) { int lsb = write(address, (byte) (w & 0x0ff), generateEvent, requireSynchronization); int msb = write(address + 1, (byte) (w >> 8), generateEvent, requireSynchronization); // int oldValue = msb << 8 + lsb; } public byte readRaw(int address) { // if (address >= 65536) return 0; return activeRead.getMemoryPage(address)[address & 0x0FF]; } public byte read(int address, RAMEvent.TYPE eventType, boolean triggerEvent, boolean requireSyncronization) { // if (address >= 65536) return 0; byte value = activeRead.getMemoryPage(address)[address & 0x0FF]; // if (triggerEvent || ((address & 0x0FF00) == 0x0C000)) { if (triggerEvent || (address & 0x0FFF0) == 0x0c030) { value = callListener(eventType, address, value, value, requireSyncronization); } return value; } public int readWordRaw(int address) { int lsb = 0x00ff & readRaw(address); int msb = (0x00ff & readRaw(address + 1)) << 8; return msb + lsb; } public int readWord(int address, RAMEvent.TYPE eventType, boolean triggerEvent, boolean requireSynchronization) { int lsb = 0x00ff & read(address, eventType, triggerEvent, requireSynchronization); int msb = (0x00ff & read(address + 1, eventType, triggerEvent, requireSynchronization)) << 8; int value = msb + lsb; // if (generateEvent) { // callListener(RAMEvent.TYPE.READ, address, value, value); // } return value; } private void mapListener(RAMListener l, int address) { if ((address & 0x0FF00) == 0x0C000) { int index = address & 0x0FF; List ioListeners = ioListenerMap[index]; if (ioListeners == null) { ioListeners = new ArrayList<>(); ioListenerMap[index] = ioListeners; } if (!ioListeners.contains(l)) { ioListeners.add(l); } } else { int index = address >> 8; List otherListeners = listenerMap[index]; if (otherListeners == null) { otherListeners = new ArrayList<>(); listenerMap[index] = otherListeners; } if (!otherListeners.contains(l)) { otherListeners.add(l); } } } private void addListenerRange(RAMListener l) { if (l.getScope() == RAMEvent.SCOPE.ADDRESS) { mapListener(l, l.getScopeStart()); } else { int start = 0; int end = 0x0ffff; if (l.getScope() == RAMEvent.SCOPE.RANGE) { start = l.getScopeStart(); end = l.getScopeEnd(); } for (int i = start; i <= end; i++) { mapListener(l, i); } } } private void refreshListenerMap() { listenerMap = new ArrayList[256]; ioListenerMap = new ArrayList[256]; for (RAMListener l : listeners) { addListenerRange(l); } } public void addListener(final RAMListener l) { boolean restart = Computer.pause(); if (listeners.contains(l)) { return; } listeners.add(l); addListenerRange(l); if (restart) { Computer.resume(); } } public void removeListener(final RAMListener l) { boolean restart = Computer.pause(); listeners.remove(l); refreshListenerMap(); if (restart) { Computer.resume(); } } public byte callListener(RAMEvent.TYPE t, int address, int oldValue, int newValue, boolean requireSyncronization) { List activeListeners = null; if (requireSyncronization) { Computer.getComputer().getCpu().suspend(); } if ((address & 0x0FF00) == 0x0C000) { activeListeners = ioListenerMap[address & 0x0FF]; if (activeListeners == null && t.isRead()) { if (requireSyncronization) { Computer.getComputer().getCpu().resume(); } return Computer.getComputer().getVideo().getFloatingBus(); } } else { activeListeners = listenerMap[(address >> 8) & 0x0ff]; } if (activeListeners != null) { RAMEvent e = new RAMEvent(t, RAMEvent.SCOPE.ADDRESS, RAMEvent.VALUE.ANY, address, oldValue, newValue); for (RAMListener l : activeListeners) { l.handleEvent(e); } if (requireSyncronization) { Computer.getComputer().getCpu().resume(); } return (byte) e.getNewValue(); } if (requireSyncronization) { Computer.getComputer().getCpu().resume(); } return (byte) newValue; } abstract protected void loadRom(String path) throws IOException; public void dump() { for (int i = 0; i < 0x0FFFF; i += 16) { System.out.print(Integer.toString(i, 16)); System.out.print(":"); String part1 = ""; String part2 = ""; for (int j = 0; j < 16; j++) { int a = i + j; int br = 0x0FF & activeRead.getMemory()[i >> 8][i & 0x0ff]; String s1 = Integer.toString(br, 16); System.out.print(' '); if (s1.length() == 1) { System.out.print('0'); } System.out.print(s1); /* try { int bw = 0; bw = 0x0FF & activeWrite.getMemory().get(a/256)[a%256]; String s2 = (br == bw) ? "**" : Integer.toString(bw,16); System.out.print(' '); if (s2.length()==1) System.out.print('0'); System.out.print(s2); } catch (NullPointerException ex) { System.out.print(" --"); } */ } System.out.println(); // System.out.println(Integer.toString(i, 16)+":"+part1+" -> "+part2); } } abstract public void attach(); abstract public void detach(); }