twoapple-reboot/src/peripheral/langcard.d

431 lines
9.7 KiB
D

/+
+ peripheral/langcard.d
+
+ Copyright: 2007 Gerald Stocker
+
+ This file is part of Twoapple.
+
+ Twoapple 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 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; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
module peripheral.langcard;
import peripheral.base;
import device.base;
import memory;
private:
string MakeRAMSwitches(bool isIIe)
{
string ramSwitches;
for (int sw = 0xC080; sw <= 0xC08F; ++sw)
{
ramSwitches ~= "void read" ~ hex4Digits(sw) ~ "()\n{\n" ~
MakeRAMSwitch(sw, false, isIIe) ~ "}\n";
ramSwitches ~= "void write" ~ hex4Digits(sw) ~ "()\n{\n" ~
MakeRAMSwitch(sw, true, isIIe) ~ "}\n";
}
return ramSwitches;
}
string MakeRAMSwitch(int sw, bool write, bool isIIe)
{
string ramSwitch;
ramSwitch ~= CallRAMFunction(isIIe, "setBank(" ~
hex4Digits((sw & 0b1000) >> 3 ) ~ ")") ~ "\n";
if (((sw & 0b11) == 0b11) || ((sw & 0b11) == 0b00))
ramSwitch ~= CallRAMFunction(isIIe, "enableRead()") ~ "\n";
else
ramSwitch ~= CallRAMFunction(isIIe, "disableRead()") ~ "\n";
if ((sw & 0b1) == 0b0)
ramSwitch ~= CallRAMFunction(isIIe, "clearPreWrite()") ~ "\n" ~
CallRAMFunction(isIIe, "disableWrite()") ~ "\n";
else
{
if (write)
ramSwitch ~= CallRAMFunction(isIIe, "clearPreWrite()") ~ "\n";
else
ramSwitch ~= CallRAMFunction(isIIe, "checkEnableWrite()") ~ "\n";
}
return ramSwitch;
}
string CallRAMFunction(bool isIIe, string func)
{
if (!isIIe)
{
return "implementation." ~ func ~ ";";
}
else
{
return "onboard." ~ func ~ ";\n" ~
"if (auxslot !is null) auxslot." ~ func ~ ";";
}
}
static const string[][] langcardSwitches = [
mixin(MakeSwitch([0xC080], "R0", "readC080")),
mixin(MakeSwitch([0xC081], "R0", "readC081")),
mixin(MakeSwitch([0xC082], "R0", "readC082")),
mixin(MakeSwitch([0xC083], "R0", "readC083")),
mixin(MakeSwitch([0xC084], "R0", "readC084")),
mixin(MakeSwitch([0xC085], "R0", "readC085")),
mixin(MakeSwitch([0xC086], "R0", "readC086")),
mixin(MakeSwitch([0xC087], "R0", "readC087")),
mixin(MakeSwitch([0xC088], "R0", "readC088")),
mixin(MakeSwitch([0xC089], "R0", "readC089")),
mixin(MakeSwitch([0xC08A], "R0", "readC08A")),
mixin(MakeSwitch([0xC08B], "R0", "readC08B")),
mixin(MakeSwitch([0xC08C], "R0", "readC08C")),
mixin(MakeSwitch([0xC08D], "R0", "readC08D")),
mixin(MakeSwitch([0xC08E], "R0", "readC08E")),
mixin(MakeSwitch([0xC08F], "R0", "readC08F")),
mixin(MakeSwitch([0xC080], "W", "writeC080")),
mixin(MakeSwitch([0xC081], "W", "writeC081")),
mixin(MakeSwitch([0xC082], "W", "writeC082")),
mixin(MakeSwitch([0xC083], "W", "writeC083")),
mixin(MakeSwitch([0xC084], "W", "writeC084")),
mixin(MakeSwitch([0xC085], "W", "writeC085")),
mixin(MakeSwitch([0xC086], "W", "writeC086")),
mixin(MakeSwitch([0xC087], "W", "writeC087")),
mixin(MakeSwitch([0xC088], "W", "writeC088")),
mixin(MakeSwitch([0xC089], "W", "writeC089")),
mixin(MakeSwitch([0xC08A], "W", "writeC08A")),
mixin(MakeSwitch([0xC08B], "W", "writeC08B")),
mixin(MakeSwitch([0xC08C], "W", "writeC08C")),
mixin(MakeSwitch([0xC08D], "W", "writeC08D")),
mixin(MakeSwitch([0xC08E], "W", "writeC08E")),
mixin(MakeSwitch([0xC08F], "W", "writeC08F"))
];
public:
class HighRam
{
AddressDecoder decoder;
ReadFunc origRead;
WriteFunc origWrite;
Memory lowerChunk, upperChunk;
bool preWrite;
bool readEn, writeEn;
bool enabled;
int current4KBank;
this()
{
initMemory();
}
abstract void initMemory();
void init(AddressDecoder addrDecode, ReadFunc read, WriteFunc write)
{
decoder = addrDecode;
origRead = read;
origWrite = write;
}
void reboot()
{
preWrite = false;
lowerChunk.reboot();
upperChunk.reboot();
current4KBank = 0;
forceReadDisable();
forceWriteEnable();
}
void clearPreWrite()
{
preWrite = false;
}
void forceReadDisable()
{
readEn = true;
disableRead();
}
void forceReadEnable()
{
readEn = false;
enableRead();
}
void forceWriteEnable()
{
writeEn = false;
enableWrite();
}
void enableRead()
{
if ((!readEn) && enabled)
{
decoder.installRead(lowerChunk);
decoder.installRead(upperChunk);
}
readEn = true;
}
void disableRead()
{
if (readEn && enabled)
{
decoder.readPages[0xD0..0x100] = origRead;
}
readEn = false;
}
void checkEnableWrite()
{
if (preWrite) enableWrite();
preWrite = true;
}
void enableWrite()
{
if ((!writeEn) && enabled)
{
decoder.installWrite(lowerChunk);
decoder.installWrite(upperChunk);
}
writeEn = true;
}
void disableWrite()
{
if (writeEn && enabled)
{
decoder.writePages[0xD0..0x100] = origWrite;
}
writeEn = false;
}
abstract void setLowerBank(int bank);
void setBank(int bank)
{
setLowerBank(bank);
current4KBank = bank;
}
}
class HighRam_II : HighRam
{
PrimaryMem e000ffff;
BankMem d000dfff;
this()
{
super();
enabled = true;
}
void initMemory()
{
d000dfff = new BankMem(0xD000, 0x1000, 2);
e000ffff = new PrimaryMem(0xE000, 0x2000);
lowerChunk = d000dfff;
upperChunk = e000ffff;
}
void setDebugName(string name)
{
e000ffff.debugName = name;
d000dfff.setDebugNames(name);
}
void setLowerBank(int bank)
{
d000dfff.setBank(bank);
}
}
class HighRam_IIe : HighRam
{
bool isOnboard;
int numBanks;
SubBankMem d000dfff;
BankMem e000ffff;
this(bool isOnboard_, int banks)
{
numBanks = banks;
super();
isOnboard = isOnboard_;
}
void initMemory()
{
d000dfff = new SubBankMem(0xD000, 0x1000, numBanks, 2);
e000ffff = new BankMem(0xE000, 0x2000, numBanks);
lowerChunk = d000dfff;
upperChunk = e000ffff;
}
void setLowerBank(int bank)
{
d000dfff.setSubBank(bank);
}
void resetBanks()
{
e000ffff.setBank(0);
d000dfff.setPrimaryBank(0);
d000dfff.setSubBank(0);
}
void enable(bool newState)
{
if (enabled == newState) return;
enabled = newState;
if (newState)
{
if (writeEn) forceWriteEnable();
if (readEn) forceReadEnable();
}
}
void setDebugName(string name)
{
string[] names;
names.length = numBanks;
if (numBanks > 1)
{
for (int b = 0; b < numBanks; ++b)
names[b] = name ~ " " ~ std.string.toString(b);
}
else
{
names[0] = name;
}
e000ffff.setDebugNames(name);
d000dfff.setDebugNames(names);
}
void reboot()
{
enabled = isOnboard;
super.reboot();
}
void reset()
{
enabled = isOnboard;
preWrite = false;
resetBanks();
current4KBank = 0;
disableRead();
enableWrite();
}
}
class HighRam_Null : HighRam_IIe
{
this()
{
super(false, 0);
}
void initMemory()
{
lowerChunk = new ZeroMem(0xD000, 0x1000);
upperChunk = new ZeroMem(0xE000, 0x2000);
}
void setLowerBank(int bank) {}
void resetBanks() {}
}
class LanguageCard : Peripheral
{
HighRam_II implementation;
mixin(MakeRAMSwitches(false));
mixin(InitSwitches("", langcardSwitches));
this()
{
implementation = new HighRam_II();
implementation.setDebugName("Language card");
}
void reboot()
{
implementation.reboot();
}
}
class LanguageCard_IIe
{
HighRam_IIe onboard, auxslot;
ubyte delegate() kbdLatch;
mixin(MakeRAMSwitches(true));
mixin(InitSwitches("", langcardSwitches ~ [
mixin(MakeSwitch([0xC011], "R", "readBank2Switch")),
mixin(MakeSwitch([0xC012], "R", "readReadSwitch"))
]));
this()
{
onboard = new HighRam_IIe(true, 1);
onboard.setDebugName("High RAM");
}
void init(AddressDecoder addrDecode, ReadFunc read, WriteFunc write)
{
onboard.init(addrDecode, read, write);
auxslot.init(addrDecode, read, write);
}
void reboot()
{
onboard.reboot();
auxslot.reboot();
}
void reset()
{
onboard.reset();
auxslot.reset();
}
void enableAuxSlot(bool isAuxSlot)
{
onboard.enable(!isAuxSlot);
auxslot.enable(isAuxSlot);
}
ubyte readBank2Switch()
{
return kbdLatch() | ((onboard.current4KBank == 0) ? 0x80 : 0x00);
}
ubyte readReadSwitch()
{
return kbdLatch() | (onboard.readEn ? 0x80 : 0x00);
}
}