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 "MSXCPU.hh"
11 #include "MSXException.hh"
12 #include "enumerate.hh"
13 #include "likely.hh"
14 #include "outer.hh"
15 #include "ranges.hh"
16 #include "serialize.hh"
17 #include <array>
18 
19 namespace openmsx {
20 
21 // protection uses a simple rotation on databus, some lines inverted:
22 // out0 = ~in3 out1 = in7 out2 = ~in5 out3 = ~in1
23 // out4 = in0 out5 = in4 out6 = ~in2 out7 = in6
24 static constexpr auto decryptLUT = [] {
25  std::array<byte, 256> result = {};
26  //for (auto [i, r] : enumerate(result)) { msvc bug
27  for (int i = 0; i < 256; ++i) {
28  result[i] = (((i << 4) & 0x50) |
29  ((i >> 3) & 0x05) |
30  ((i << 1) & 0xa0) |
31  ((i << 2) & 0x08) |
32  ((i >> 6) & 0x02)) ^ 0x4d;
33  }
34  return result;
35 }();
36 
38  : MSXRom(config, std::move(rom_))
39  , romBlocks(*this)
40 {
41  if (rom.getSize() != 0x100000) { // 1MB
42  throw MSXException("Holy Quaran ROM should be exactly 1MB in size");
43  }
44  reset(EmuTime::dummy());
45 }
46 
47 void RomHolyQuran2::reset(EmuTime::param /*time*/)
48 {
49  ranges::fill(bank, &rom[0]);
50  decrypt = false;
51 }
52 
53 byte RomHolyQuran2::readMem(word address, EmuTime::param time)
54 {
55  byte result = RomHolyQuran2::peekMem(address, time);
56  if (unlikely(!decrypt)) {
57  if (getCPU().isM1Cycle(address)) {
58  // start decryption when we start executing the rom
59  decrypt = true;
60  }
61  }
62  return result;
63 }
64 
65 byte RomHolyQuran2::peekMem(word address, EmuTime::param /*time*/) const
66 {
67  if ((0x4000 <= address) && (address < 0xc000)) {
68  unsigned b = (address - 0x4000) >> 13;
69  byte raw = bank[b][address & 0x1fff];
70  return decrypt ? decryptLUT[raw] : raw;
71  } else {
72  return 0xff;
73  }
74 }
75 
76 void RomHolyQuran2::writeMem(word address, byte value, EmuTime::param /*time*/)
77 {
78  // TODO are switch addresses mirrored?
79  if ((0x5000 <= address) && (address < 0x6000)) {
80  byte region = (address >> 10) & 3;
81  bank[region] = &rom[(value & 127) * 0x2000];
82  }
83 }
84 
85 const byte* RomHolyQuran2::getReadCacheLine(word address) const
86 {
87  if ((0x4000 <= address) && (address < 0xc000)) {
88  return nullptr;
89  } else {
90  return unmappedRead;
91  }
92 }
93 
95 {
96  if ((0x5000 <= address) && (address < 0x6000)) {
97  return nullptr;
98  } else {
99  return unmappedWrite;
100  }
101 }
102 
103 template<typename Archive>
104 void RomHolyQuran2::serialize(Archive& ar, unsigned /*version*/)
105 {
106  // skip MSXRom base class
107  ar.template serializeBase<MSXDevice>(*this);
108 
109  unsigned bb[4];
110  if constexpr (Archive::IS_LOADER) {
111  ar.serialize("banks", bb);
112  for (auto [i, b] : enumerate(bb)) {
113  bank[i] = &rom[(b & 127) * 0x2000];
114  }
115  } else {
116  for (auto [i, b] : enumerate(bb)) {
117  b = (bank[i] - &rom[0]) / 0x2000;
118  }
119  ar.serialize("banks", bb);
120  }
121 
122  ar.serialize("decrypt", decrypt);
123 }
126 
127 
128 RomHolyQuran2::Blocks::Blocks(RomHolyQuran2& device_)
129  : RomBlockDebuggableBase(device_)
130 {
131 }
132 
133 byte RomHolyQuran2::Blocks::read(unsigned address)
134 {
135  if ((address < 0x4000) || (address >= 0xc000)) return 255;
136  unsigned page = (address - 0x4000) / 0x2000;
137  auto& device = OUTER(RomHolyQuran2, romBlocks);
138  return (device.bank[page] - &device.rom[0]) / 0x2000;
139 }
140 
141 } // namespace openmsx
MSXCPU & getCPU() const
Definition: MSXDevice.cc:130
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:301
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:302
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
RomHolyQuran2(const DeviceConfig &config, Rom &&rom)
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
void serialize(Archive &ar, unsigned version)
void writeMem(word address, byte value, EmuTime::param time) override
Write a given byte to a given location at a certain time to this device.
void reset(EmuTime::param time) override
This method is called on reset.
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
unsigned getSize() const
Definition: Rom.hh:34
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition: enumerate.hh:28
#define unlikely(x)
Definition: likely.hh:15
This file implemented 3 utility functions:
Definition: Autofire.cc:9
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:226
#define OUTER(type, member)
Definition: outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998