2012-03-14 00:43:29 +00:00
|
|
|
/+
|
|
|
|
+ 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;
|
|
|
|
|
2012-03-14 12:24:35 +00:00
|
|
|
import std.conv;
|
|
|
|
|
2012-03-14 00:43:29 +00:00
|
|
|
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 ~ ";";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-14 12:24:35 +00:00
|
|
|
enum langcardSwitches = [
|
2012-03-14 00:43:29 +00:00
|
|
|
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)
|
2012-03-14 12:24:35 +00:00
|
|
|
names[b] = name ~ " " ~ to!string(b);
|
2012-03-14 00:43:29 +00:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|