OXYGENHUB
◆ main openmsx / src / memory / RomHolyQuran2.cc
3.6 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 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 "serialize.hh"
18
19
#include <algorithm>
20
#include <array>
21
22
namespace 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
27
static 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
40
RomHolyQuran2::RomHolyQuran2(const DeviceConfig& config, Rom&& rom_)
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
50
void RomHolyQuran2::reset(EmuTime /*time*/)
51
{
52
	std::ranges::fill(bank, &rom[0]);
53
	decrypt = false;
54
}
55
56
byte RomHolyQuran2::readMem(uint16_t address, EmuTime 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
68
byte RomHolyQuran2::peekMem(uint16_t address, EmuTime /*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
79
void RomHolyQuran2::writeMem(uint16_t address, byte value, EmuTime /*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
88
const byte* RomHolyQuran2::getReadCacheLine(uint16_t address) const
89
{
90
	if ((0x4000 <= address) && (address < 0xc000)) {
91
		return nullptr;
92
	} else {
93
		return unmappedRead.data();
94
	}
95
}
96
97
byte* RomHolyQuran2::getWriteCacheLine(uint16_t address)
98
{
99
	if ((0x5000 <= address) && (address < 0x6000)) {
100
		return nullptr;
101
	} else {
102
		return unmappedWrite.data();
103
	}
104
}
105
106
template<typename Archive>
107
void 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
}
127
INSTANTIATE_SERIALIZE_METHODS(RomHolyQuran2);
128
REGISTER_MSXDEVICE(RomHolyQuran2, "RomHolyQuran2");
129
130
131
RomHolyQuran2::Blocks::Blocks(const RomHolyQuran2& device_)
132
	: RomBlockDebuggableBase(device_)
133
{
134
}
135
136
unsigned 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