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