/* F99bMemoryModel.java (c) 2010-2015 Edward Swartz All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html */ package v9t9.machine.ti99.machine; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URL; import org.apache.log4j.Logger; import v9t9.common.client.ISettingsHandler; import v9t9.common.events.IEventNotifier; import v9t9.common.files.DataFiles; import v9t9.common.machine.IBaseMachine; import v9t9.common.machine.IMachine; import v9t9.common.memory.IMemoryDomain; import v9t9.common.memory.IMemoryEntry; import v9t9.common.memory.MemoryEntryInfo; import v9t9.engine.memory.DiskMemoryEntry; import v9t9.engine.memory.GplMmio; import v9t9.engine.memory.MemoryArea; import v9t9.engine.memory.MemoryEntry; import v9t9.engine.memory.MemoryEntryInfoBuilder; import v9t9.engine.memory.MultiBankedMemoryEntry; import v9t9.machine.EmulatorMachinesData; import v9t9.machine.ti99.memory.BaseTI994AMemoryModel; import v9t9.machine.ti99.memory.EnhancedRamByteArea; import v9t9.machine.ti99.memory.mmio.Forth9900ConsoleMmioArea; import ejs.base.properties.IProperty; import ejs.base.utils.FileUtils; import ejs.base.utils.Pair; /** * F99b console memory model. * @author ejs */ public class Forth9900MemoryModel extends BaseTI994AMemoryModel { public class Forth9900ConsoleMemoryEntry extends MemoryEntry { private Forth9900ConsoleMemoryEntry(String name, IMemoryDomain domain, int addr, int size, MemoryArea area) { super(name, domain, addr, size, area); } @Override public String lookupSymbol(short addr) { String symb = super.lookupSymbol(addr); if (symb == null) symb = bankedRomMemoryEntry.lookupSymbol(addr); return symb; } /* (non-Javadoc) * @see v9t9.engine.memory.MemoryEntry#findSymbol(java.lang.String) */ @Override public Integer findSymbol(String name) { Integer addr = super.findSymbol(name); if (addr == null) addr = bankedRomMemoryEntry.findSymbol(name); return addr; } @Override public Pair<String, Short> lookupSymbolNear(short addr, int range) { Pair<String, Short> info = super.lookupSymbolNear(addr, range); if (info == null) info = bankedRomMemoryEntry.lookupSymbolNear(addr, range); return info; } } private static final Logger log = Logger.getLogger(Forth9900MemoryModel.class); private MemoryEntry consoleEntry; private IMemoryEntry gromEntry; private IMemoryEntry gramDictEntry; private IMemoryEntry gramMemoryEntry; private IMemoryEntry cpuForthRomEntry; private IMemoryEntry cpuRomBankEntry; private MemoryEntry mmioEntry; private MultiBankedMemoryEntry bankedRomMemoryEntry; private Forth9900ConsoleMmioArea mmioArea; public Forth9900MemoryModel(IMachine machine) { super(machine); } @Override protected void initSettings(ISettingsHandler settings) { URL dataURL; dataURL = EmulatorMachinesData.getDataURL("../../../build/forth99"); DataFiles.addSearchPath(settings, dataURL.getPath()); IProperty shipPath = settings.get(DataFiles.settingShippingRomsPath); dataURL = EmulatorMachinesData.getDataURL("f9900/"); shipPath.getList().add(dataURL.toString()); } protected void defineConsoleMemory(IBaseMachine machine) { consoleEntry = new MemoryEntry("64K RAM", CPU, 0x0400, 0xFC00, new EnhancedRamByteArea(0, 0xFC00)); memory.addAndMap(consoleEntry); } protected void defineMmioMemory(IBaseMachine machine) { //this.memory.addAndMap(mmioEntry); mmioArea = new Forth9900ConsoleMmioArea((IMachine) machine); mmioEntry = new Forth9900ConsoleMemoryEntry("MMIO", CPU, 0x0000, 0x0400, mmioArea); } private static MemoryEntryInfo f9900ForthRomMemoryEntryInfo = MemoryEntryInfoBuilder .wordMemoryEntry() .withFilename("f9900rombank0.bin") .withSize(-0x10000) .create("Forth9900 Forth ROM"); private static MemoryEntryInfo f9900BankedRomMemoryEntryInfo = MemoryEntryInfoBuilder .wordMemoryEntry() .withFilename("f9900rombank1.bin") .withSize(-0x10000) .create("Forth9900 CPU ROM Bank"); private static String FORTH_GROM = "f9900grom.bin"; private static MemoryEntryInfo f9900GromMemoryEntryInfo = MemoryEntryInfoBuilder .standardConsoleGrom(FORTH_GROM) .withSize(-0x4000) .create("Forth9900 CPU GROM"); private static MemoryEntryInfo f9900GramMemoryEntryInfo = MemoryEntryInfoBuilder .byteMemoryEntry() .withDomain(IMemoryDomain.NAME_GRAPHICS) .withAddress(0x4000) .withSize(0x4000) .create("Forth9900 16K GRAM Dictionary"); private static MemoryEntryInfo f9900DiskGramMemoryEntryInfo = MemoryEntryInfoBuilder .byteMemoryEntry() .withFilename("f9900gram.bin") .withDomain(IMemoryDomain.NAME_GRAPHICS) .withAddress(0x8000) .withSize(0x8000) .storable(true) .create("Forth99 GRAM"); /* (non-Javadoc) * @see v9t9.emulator.hardware.memory.StandardConsoleMemoryModel#loadMemory() */ @Override public void loadMemory(IEventNotifier eventNotifier) { try { // get the FORTH ROM (bank #0) cpuForthRomEntry = memory.getMemoryEntryFactory().newMemoryEntry( f9900ForthRomMemoryEntryInfo); cpuForthRomEntry.load(); CPU.mapEntry(cpuForthRomEntry); cpuForthRomEntry.copySymbols(CPU); // get the CPU ROM (bank #1) cpuRomBankEntry = memory.getMemoryEntryFactory().newMemoryEntry( f9900BankedRomMemoryEntryInfo); cpuRomBankEntry.load(); CPU.mapEntry(cpuRomBankEntry); cpuRomBankEntry.copySymbols(CPU); // Get the meat of the ROM from the assembly bank Integer endShared = cpuRomBankEntry.findSymbol("ForthROM"); if (endShared == null) endShared = cpuRomBankEntry.findSymbol("_RESET"); // both symbols at same addr, one wins if (endShared != null) { boolean changed = false; short prevValue = 0; boolean invertSoc = false; for (int addr = 0; addr < endShared; addr+=2) { short value = cpuRomBankEntry.flatReadWord(addr); if (prevValue == 0x720) { // SETO @> //String sym= cpuRomBankEntry.lookupSymbol((short) (addr - 2)); if (value == 4) { // we're in bank 0 invertSoc = true; } } else if (invertSoc && value == (short) 0xe3e0) { // SOC @>...,R15 value = 0x43e0; // SZC @>...,R15 invertSoc = false; } else { invertSoc = false; } changed |= cpuForthRomEntry.patchWord(addr, value); prevValue = value; } if (changed) { DiskMemoryEntry ent = (DiskMemoryEntry) cpuForthRomEntry; ent.overwrite(); } } memory.removeAndUnmap(cpuRomBankEntry); memory.removeAndUnmap(cpuForthRomEntry); memory.removeAndUnmap(consoleEntry); // Make the RAM area for the Forth RAM dictionary/etc., // which lives on the area boundary past the Forth ROM int st = cpuForthRomEntry.getAddr() + 0x400 * ((cpuForthRomEntry.getSize() + 0x3ff) / 0x400); int sz = 0x10000 - st; consoleEntry = new MemoryEntry("64K RAM", CPU, st, sz, new EnhancedRamByteArea(0, sz)); memory.addAndMap(consoleEntry); // Make the bank-switching ROM bankedRomMemoryEntry = new MultiBankedMemoryEntry(settings, memory, "Forth/ROM Bank", new IMemoryEntry[] { cpuForthRomEntry, cpuRomBankEntry }); mmioArea.setUnderlyingRomEntry(bankedRomMemoryEntry); memory.addAndMap(bankedRomMemoryEntry); memory.addAndMap(mmioEntry); } catch (IOException e) { reportLoadError(eventNotifier, f9900ForthRomMemoryEntryInfo.getFilename(), e); } loadGromAndGram(eventNotifier); } /** * @param eventNotifier * */ private void loadGromAndGram(IEventNotifier eventNotifier) { // GROM consists of ROM up to 16k try { memory.removeAndUnmap(gromEntry); gromEntry = memory.getMemoryEntryFactory().newMemoryEntry( f9900GromMemoryEntryInfo); memory.addAndMap(gromEntry); } catch (IOException e) { reportLoadError(eventNotifier, FORTH_GROM, e); } // then 16k of GRAM for new dictionary try { memory.removeAndUnmap(gramDictEntry); gramDictEntry = memory.getMemoryEntryFactory().newMemoryEntry( f9900GramMemoryEntryInfo); gramDictEntry.getArea().setLatency(0); memory.addAndMap(gramDictEntry); } catch (IOException e1) { // should not happen reportLoadError(eventNotifier, f9900GramMemoryEntryInfo.getFilename(), e1); } // then 32k of GRAM storage IProperty shipPath = settings.get(DataFiles.settingShippingRomsPath); URI shippingDiskImage = URI.create(shipPath.getList().get(0) + f9900DiskGramMemoryEntryInfo.getFilename()); URI userDiskImage = machine.getRomPathFileLocator().getWriteURI(f9900DiskGramMemoryEntryInfo.getFilename()); File userDiskImageFile = new File(userDiskImage); if (shippingDiskImage != null && userDiskImage == null || !userDiskImageFile.exists()) { userDiskImageFile.getParentFile().mkdirs(); InputStream is = null; OutputStream os = null; try { is = machine.getRomPathFileLocator().createInputStream(shippingDiskImage); os = machine.getRomPathFileLocator().createOutputStream(userDiskImage); byte[] content = FileUtils.readInputStreamContentsAndClose(is); FileUtils.writeOutputStreamContentsAndClose(os, content, content.length); } catch (IOException e) { log.error("Failed to copy initial disk image from " + shippingDiskImage + " to " + userDiskImage, e); // eventNotifier.notifyEvent(this, Level.ERROR, // "Failed to copy initial disk image from " + shippingDiskImage + " to " + userDiskImage); } finally { try { if (is != null) is.close(); } catch (IOException e) { } try { if (os != null) os.close(); } catch (IOException e) { } } } try { memory.removeAndUnmap(gramMemoryEntry); gramMemoryEntry = memory.getMemoryEntryFactory().newMemoryEntry( f9900DiskGramMemoryEntryInfo); memory.addAndMap(gramMemoryEntry); } catch (IOException e) { reportLoadError(eventNotifier, f9900DiskGramMemoryEntryInfo.getFilename(), e); } } /* (non-Javadoc) * @see v9t9.emulator.hardware.memory.TI994AStandardConsoleMemoryModel#resetMemory() */ @Override public void resetMemory() { } /* (non-Javadoc) * @see v9t9.common.memory.IMemoryModel#getRequiredRomProperties() */ @Override public MemoryEntryInfo[] getRequiredRomMemoryEntries() { return new MemoryEntryInfo[] { f9900ForthRomMemoryEntryInfo, f9900GromMemoryEntryInfo, f9900DiskGramMemoryEntryInfo, }; } /* (non-Javadoc) * @see v9t9.common.memory.IMemoryModel#getOptionalRomProperties() */ @Override public MemoryEntryInfo[] getOptionalRomMemoryEntries() { return new MemoryEntryInfo[0]; } /* (non-Javadoc) * @see v9t9.machine.ti99.memory.BaseTI994AMemoryModel#initMemory(v9t9.common.machine.IBaseMachine) */ @Override public void initMemory(IBaseMachine machine) { gplMmio = new GplMmio(machine, GRAPHICS); super.initMemory(machine); } }