twoapple-reboot/src/memory.d

414 lines
9.2 KiB
D

/+
+ memory.d
+
+ Copyright: 2007 Gerald Stocker
+
+ This file is part of twoapple-reboot.
+
+ twoapple-reboot is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ twoapple-reboot 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with twoapple-reboot; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
import std.conv;
class Memory
{
ushort baseAddress;
uint blockSize;
string debugName;
this(ushort baseAddr, uint size)
{
assert(baseAddr + size <= 0x10000,
"Memory block larger than 64K");
assert((baseAddr % 0x0100) == 0,
"Memory block does not start on page boundary");
assert((size % 0x0100) == 0,
"Memory block does not end on page boundary");
baseAddress = baseAddr;
blockSize = size;
}
abstract ubyte read(ushort addr);
abstract void write(ushort addr, ubyte val);
void reboot() {}
}
class ZeroMem : Memory
{
this(ushort baseAddr, uint size)
{
super(baseAddr, size);
}
ubyte read(ushort addr) { return 0; }
void write(ushort addr, ubyte val) {}
}
class DataMem : Memory
{
ubyte* data;
ubyte data_[];
this(ushort baseAddr, uint size)
{
super(baseAddr, size);
}
ubyte read(ushort addr)
{
return data[addr - baseAddress];
}
void write(ushort addr, ubyte val)
{
data[addr - baseAddress] = val;
}
}
class PrimaryMem : DataMem
{
this(ushort baseAddr, uint size)
{
super(baseAddr, size);
}
void reboot()
{
data_ = new ubyte[blockSize];
data = data_.ptr;
}
}
class Rom : DataMem
{
this(ushort baseAddr, uint size, ubyte[] rom)
{
super(baseAddr, size);
data_ = rom;
data = data_.ptr;
}
void write(ushort addr, ubyte val) {}
}
class SliceMem : DataMem
{
DataMem otherMem;
this(ushort baseAddr, uint size, DataMem other)
{
super(baseAddr, size);
otherMem = other;
debugName = otherMem.debugName;
}
void reboot()
{
int otherStart = baseAddress - otherMem.baseAddress;
int otherEnd = otherStart + blockSize;
assert((otherStart >= 0) && (otherEnd <= otherMem.blockSize),
"Memory slice out of range");
data_ = otherMem.data_[otherStart..otherEnd];
data = data_.ptr;
}
}
class BankMem : DataMem
{
ubyte[][] banks;
string[] debugNames;
this(ushort baseAddr, uint size, uint numBanks)
{
super(baseAddr, size);
banks.length = numBanks;
debugNames.length = numBanks;
}
void setDebugNames(string name)
{
if (debugNames.length > 1)
{
for (int n = 0; n < debugNames.length; ++n)
{
debugNames[n] = name ~ " bank " ~ to!string(n);
}
}
else
{
debugNames[0] = name;
}
}
void reboot()
{
for (int b = 0; b < banks.length; ++b)
{
banks[b] = new ubyte[blockSize];
}
setBank(0);
}
void setBank(int bankNum)
{
data_ = banks[bankNum];
data = data_.ptr;
debugName = debugNames[bankNum];
}
}
class SubBankMem : DataMem
{
ubyte[][][] banks;
string[][] debugNames;
int primaryBank, subBank;
int numBanks, numSubBanks;
this(ushort baseAddr, uint size, uint numBanks_, uint numSubBanks_)
{
super(baseAddr, size);
banks.length = numBanks = numBanks_;
debugNames.length = numBanks;
numSubBanks = numSubBanks_;
for (int b = 0; b < numBanks; ++b)
{
banks[b].length = numSubBanks_;
debugNames[b].length = numSubBanks_;
}
}
void setDebugNames(string[] names)
{
for (int b = 0; b < numBanks; ++b)
{
for (int n = 0; n < numSubBanks; ++n)
{
debugNames[b][n] = names[b] ~ " bank " ~ to!string(n);
}
}
}
void reboot()
{
for (int b = 0; b < banks.length; ++b)
{
for (int s = 0; s < banks[b].length; ++s)
{
banks[b][s] = new ubyte[blockSize];
}
}
primaryBank = subBank = 0;
setSubBank(0);
setPrimaryBank(0);
}
void setPrimaryBank(uint bank)
{
primaryBank = bank;
data_ = banks[bank][subBank];
data = data_.ptr;
debugName = debugNames[bank][subBank];
}
void setSubBank(uint bank)
{
subBank = bank;
data_ = banks[primaryBank][bank];
data = data_.ptr;
debugName = debugNames[primaryBank][bank];
}
}
alias ubyte delegate(ushort) ReadFunc;
alias void delegate(ushort, ubyte) WriteFunc;
class AddressDecoder
{
ReadFunc readPages[256];
WriteFunc writePages[256];
Memory readResponders[256];
Memory writeResponders[256];
void nullWrite(ushort addr, ubyte val) {}
public:
ubyte delegate(ushort) nullRead;
void installSwitches(SoftSwitchPage switches)
{
readPages[0xC0] = &switches.read;
writePages[0xC0] = &switches.write;
}
ubyte opIndex(ushort addr)
{
return readPages[addr >> 8](addr);
}
ubyte opIndexAssign(ubyte val, ushort addr)
{
writePages[addr >> 8](addr, val);
return val;
}
// XXX address read only/write only code
void install(Memory block, bool forRead = true, bool forWrite = true)
{
uint base = block.baseAddress >> 8;
uint size = block.blockSize >> 8;
for (uint pg = base; pg < base + size; ++pg)
{
if (pg == 0xC0) continue;
if (forRead)
{
readPages[pg] = &block.read;
readResponders[pg] = block;
}
if (forWrite)
{
writePages[pg] = &block.write;
writeResponders[pg] = block;
}
}
}
void installNull(uint baseAddress, uint blockSize, bool forRead = true,
bool forWrite = true)
{
uint base = baseAddress >> 8;
uint size = blockSize >> 8;
for (uint pg = base; pg < base + size; ++pg)
{
if (pg == 0xC0) continue;
if (forRead)
{
readPages[pg] = nullRead;
readResponders[pg] = null;
}
if (forWrite)
{
writePages[pg] = &nullWrite;
writeResponders[pg] = null;
}
}
}
void installRead(Memory block)
{
install(block, true, false);
}
void installWrite(Memory block)
{
install(block, false, true);
}
string memoryReadName(ushort addr)
{
int page = addr >> 8;
if (readResponders[page] is null) return null;
return readResponders[page].debugName;
}
Memory readResponse(int page)
{
return readResponders[page];
}
Memory writeResponse(int page)
{
return writeResponders[page];
}
}
class SoftSwitchPage : Memory
{
private:
ReadFunc[256] readSwitches;
ubyte[256] bitsReturned;
WriteFunc[256] writeSwitches;
ubyte nullRead(ushort addr) { return 0; }
void nullWrite(ushort addr, ubyte val) {}
public:
ReadFunc floatingBus;
this()
{
super(0xC000, 0x0100);
for (int addr = 0xC000; addr < 0xC100; ++addr)
{
writeSwitches[addr & 0xFF] = &nullWrite;
}
}
void setFloatingBus(ReadFunc floatingBus_)
{
floatingBus = floatingBus_;
for (int addr = 0xC000; addr < 0xC100; ++addr)
{
if (readSwitches[addr & 0xFF] is null)
readSwitches[addr & 0xFF] = floatingBus;
}
}
void setReadSwitch(ushort addr, ReadFunc read_, ubyte bitsReturned_)
{
readSwitches[addr - 0xC000] = read_;
bitsReturned[addr - 0xC000] = bitsReturned_;
}
void setR0Switch(ushort addr, ReadFunc read_)
{
setReadSwitch(addr, read_, 0);
}
void setR7Switch(ushort addr, ReadFunc read_)
{
setReadSwitch(addr, read_, 0x80);
}
void setRSwitch(ushort addr, ReadFunc read_)
{
setReadSwitch(addr, read_, 0xFF);
}
void setWSwitch(ushort addr, WriteFunc write_)
{
writeSwitches[addr - 0xC000] = write_;
}
final ubyte read(ushort addr)
{
ubyte ret = readSwitches[addr - 0xC000](addr);
ubyte mask = bitsReturned[addr - 0xC000];
if (mask < 0xFF)
{
ret = (ret & mask) | (floatingBus(addr) & (mask ^ 0xFF));
}
return ret;
}
final void write(ushort addr, ubyte val)
{
writeSwitches[addr - 0xC000](addr, val);
}
}