summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/msx/cart/holy_quran.cpp
blob: b04edb3a6b089e6a0824774545d0f104840c6f6a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/******************************************************************************

Al-Alamiah Al-Qur'an Al-Karim
Only works on Arabian MSXes.

GCMK-16X PCB, 2 ROM chips, Yamaha XE297A0 mapper chip.

******************************************************************************/

#include "emu.h"
#include "holy_quran.h"

namespace {

class msx_cart_holy_quran_device : public device_t, public msx_cart_interface
{
public:
	msx_cart_holy_quran_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
		: device_t(mconfig, MSX_CART_HOLY_QURAN, tag, owner, clock)
		, msx_cart_interface(mconfig, *this)
		, m_rombank(*this, "rombank%u", 0U)
		, m_view{ {*this, "view1"}, {*this, "view2"} }
	{ }

	virtual std::error_condition initialize_cartridge(std::string &message) override;

protected:
	// device_t implementation
	virtual void device_start() override { }
	virtual void device_reset() override ATTR_COLD;

private:
	static constexpr size_t BANK_SIZE = 0x2000;

	u8 read(offs_t offset);
	u8 read2(offs_t offset);
	template <int Bank> void bank_w(u8 data);

	memory_bank_array_creator<4> m_rombank;
	memory_view m_view[2];

	std::vector<u8> m_decrypted;
	u8 m_bank_mask;
};

std::error_condition msx_cart_holy_quran_device::initialize_cartridge(std::string &message)
{
	if (!cart_rom_region())
	{
		message = "msx_cart_holy_quran_device: Required region 'rom' was not found.";
		return image_error::INTERNAL;
	}

	const u32 size = cart_rom_region()->bytes();
	const u16 banks = size / BANK_SIZE;

	if (size > 256 * BANK_SIZE || size < 0x10000 || size != banks * BANK_SIZE || (~(banks - 1) % banks))
	{
		message = "msx_cart_holy_quran_device: Region 'rom' has unsupported size.";
		return image_error::INVALIDLENGTH;
	}

	m_bank_mask = banks - 1;

	m_decrypted.resize(size);

	// protection uses a simple rotation on databus, some lines inverted
	u8 *rom = cart_rom_region()->base();
	for (u32 i = 0; i < size; i++)
		m_decrypted[i] = bitswap<8>(rom[i],6,2,4,0,1,5,7,3) ^ 0x4d;

	for (int i = 0; i < 4; i++)
		m_rombank[i]->configure_entries(0, banks, m_decrypted.data(), BANK_SIZE);

	page(1)->install_view(0x4000, 0x7fff, m_view[0]);
	m_view[0][0].install_read_handler(0x4000, 0x7fff, emu::rw_delegate(*this, FUNC(msx_cart_holy_quran_device::read)));
	m_view[0][1].install_read_bank(0x4000, 0x5fff, m_rombank[0]);
	m_view[0][1].install_read_bank(0x6000, 0x7fff, m_rombank[1]);
	m_view[0][1].install_write_handler(0x5000, 0x5000, emu::rw_delegate(*this, FUNC(msx_cart_holy_quran_device::bank_w<0>)));
	m_view[0][1].install_write_handler(0x5400, 0x5400, emu::rw_delegate(*this, FUNC(msx_cart_holy_quran_device::bank_w<1>)));
	m_view[0][1].install_write_handler(0x5800, 0x5800, emu::rw_delegate(*this, FUNC(msx_cart_holy_quran_device::bank_w<2>)));
	m_view[0][1].install_write_handler(0x5c00, 0x5c00, emu::rw_delegate(*this, FUNC(msx_cart_holy_quran_device::bank_w<3>)));

	page(2)->install_view(0x8000, 0xbfff, m_view[1]);
	m_view[1][0].install_read_handler(0x8000, 0xbfff, emu::rw_delegate(*this, FUNC(msx_cart_holy_quran_device::read2)));
	m_view[1][1].install_read_bank(0x8000, 0x9fff, m_rombank[2]);
	m_view[1][1].install_read_bank(0xa000, 0xbfff, m_rombank[3]);

	return std::error_condition();
}

void msx_cart_holy_quran_device::device_reset()
{
	m_view[0].select(0);
	m_view[1].select(0);
	for (int i = 0; i < 4; i++)
		m_rombank[i]->set_entry(0);
}

u8 msx_cart_holy_quran_device::read(offs_t offset)
{
	u8 data = cart_rom_region()->base()[offset];
	// The decryption should actually start working after the first M1 cycle executing something from the cartridge.
	if (offset + 0x4000 == ((cart_rom_region()->base()[3] << 8) | cart_rom_region()->base()[2]) && !machine().side_effects_disabled())
	{
		// Switch to decrypted contents
		m_view[0].select(1);
		m_view[1].select(1);
	}
	return data;
}

u8 msx_cart_holy_quran_device::read2(offs_t offset)
{
	u8 data = cart_rom_region()->base()[offset + 0x4000];
	// The decryption should actually start working after the first M1 cycle executing something from the cartridge.
	if (offset + 0x8000 == ((cart_rom_region()->base()[3] << 8) | cart_rom_region()->base()[2]) && !machine().side_effects_disabled())
	{
		// Switch to decrypted contents
		m_view[0].select(1);
		m_view[1].select(1);
	}
	return data;
}

template <int Bank>
void msx_cart_holy_quran_device::bank_w(u8 data)
{
	m_rombank[Bank]->set_entry(data & m_bank_mask);
}

} // anonymous namespace

DEFINE_DEVICE_TYPE_PRIVATE(MSX_CART_HOLY_QURAN, msx_cart_interface, msx_cart_holy_quran_device, "msx_cart_holy_quran", "MSX Cartridge - Holy Quran")