openMSX
RomHolyQuran2.cc
Go to the documentation of this file.
1 // Holy Qu'ran cartridge
2 // It is like an ASCII 8KB, but using the 5000h, 5400h, 5800h and 5C00h
3 // addresses.
4 //
5 // This is very similar to RomHolyQuran, but this mapper type works with the
6 // encrypted ROM content. Thanks to n_n for implementing it in meisei and
7 // sharing his implementation with us (and pointing us to it).
8 
9 #include "RomHolyQuran2.hh"
10 #include "RomBlockDebuggable.hh"
11 #include "Rom.hh"
12 #include "MSXCPU.hh"
13 #include "MSXException.hh"
14 #include "serialize.hh"
15 #include "likely.hh"
16 #include "memory.hh"
17 
18 namespace openmsx {
19 
21 {
22 public:
24  virtual byte read(unsigned address);
25 private:
26  RomHolyQuran2& device;
27 };
28 
29 
30 static byte decryptLUT[256];
31 
32 RomHolyQuran2::RomHolyQuran2(const DeviceConfig& config, std::unique_ptr<Rom> rom_)
33  : MSXRom(config, std::move(rom_))
34  , romBlocks(make_unique<Quran2RomBlocks>(*this))
35 {
36  // protection uses a simple rotation on databus, some lines inverted:
37  // out0 = ~in3 out1 = in7 out2 = ~in5 out3 = ~in1
38  // out4 = in0 out5 = in4 out6 = ~in2 out7 = in6
39  for (int i = 0; i < 256; ++i) {
40  decryptLUT[i] = (((i << 4) & 0x50) |
41  ((i >> 3) & 0x05) |
42  ((i << 1) & 0xa0) |
43  ((i << 2) & 0x08) |
44  ((i >> 6) & 0x02)) ^ 0x4d;
45  }
46 
47  if (rom->getSize() != 0x100000) { // 1MB
48  throw MSXException("Holy Quaran ROM should be exactly 1MB in size");
49  }
51 }
52 
54 {
55 }
56 
58 {
59  for (int i = 0; i < 4; ++i) {
60  bank[i] = &(*rom)[0];
61  }
62  decrypt = false;
63 }
64 
66 {
67  byte result = RomHolyQuran2::peekMem(address, time);
68  if (unlikely(!decrypt)) {
69  if (getCPU().isM1Cycle(address)) {
70  // start decryption when we start executing the rom
71  decrypt = true;
72  }
73  }
74  return result;
75 }
76 
78 {
79  if ((0x4000 <= address) && (address < 0xc000)) {
80  unsigned b = (address - 0x4000) >> 13;
81  byte raw = bank[b][address & 0x1fff];
82  return decrypt ? decryptLUT[raw] : raw;
83  } else {
84  return 0xff;
85  }
86 }
87 
88 void RomHolyQuran2::writeMem(word address, byte value, EmuTime::param /*time*/)
89 {
90  // TODO are switch addresses mirrored?
91  if ((0x5000 <= address) && (address < 0x6000)) {
92  byte region = (address >> 10) & 3;
93  bank[region] = &(*rom)[(value & 127) * 0x2000];
94  }
95 }
96 
98 {
99  if ((0x4000 <= address) && (address < 0xc000)) {
100  return nullptr;
101  } else {
102  return unmappedRead;
103  }
104 }
105 
107 {
108  if ((0x5000 <= address) && (address < 0x6000)) {
109  return nullptr;
110  } else {
111  return unmappedWrite;
112  }
113 }
114 
115 template<typename Archive>
116 void RomHolyQuran2::serialize(Archive& ar, unsigned /*version*/)
117 {
118  // skip MSXRom base class
119  ar.template serializeBase<MSXDevice>(*this);
120 
121  unsigned b[4];
122  if (ar.isLoader()) {
123  ar.serialize("banks", b);
124  for (unsigned i = 0; i < 4; ++i) {
125  bank[i] = &(*rom)[(b[i] & 127) * 0x2000];
126  }
127  } else {
128  for (unsigned i = 0; i < 4; ++i) {
129  b[i] = (bank[i] - &(*rom)[0]) / 0x2000;
130  }
131  ar.serialize("banks", b);
132  }
133 
134  ar.serialize("decrypt", decrypt);
135 }
137 REGISTER_MSXDEVICE(RomHolyQuran2, "RomHolyQuran2");
138 
139 
141  : RomBlockDebuggableBase(device_)
142  , device(device_)
143 {
144 }
145 
146 byte Quran2RomBlocks::read(unsigned address)
147 {
148  if ((address < 0x4000) || (address >= 0xc000)) return 255;
149  unsigned page = (address - 0x4000) / 0x2000;
150  return (device.bank[page] - &(*device.rom)[0]) / 0x2000;
151 }
152 
153 } // namespace openmsx