summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/dirom.ipp
blob: f6460e84d33d92f001c8c58f57a623b1357ae0c1 (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
137
138
139
140
141
142
143
144
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
#ifndef MAME_EMU_DIROM_IPP
#define MAME_EMU_DIROM_IPP

#pragma once

template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian>
device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::device_rom_interface(const machine_config &mconfig, device_t &device) :
	device_memory_interface(mconfig, device),
	m_rom_region(device, DEVICE_SELF),
	m_rom_config("rom", Endian, 8 << DataWidth, AddrWidth, AddrShift),
	m_bank(device, "bank"),
	m_cur_bank(-1)
{
}

template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian>
void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::override_address_width(u8 width)
{
	// cach does not need level match, only specific does at this point
	//  if(emu::detail::handler_entry_dispatch_level(AddrWidth) != emu::detail::handler_entry_dispatch_level(width))
	//      emu_fatalerror("%s: Widths %d and %d are incompatible", device().tag(), width, AddrWidth);

	m_rom_config.m_addr_width = width;
}

template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian>
device_memory_interface::space_config_vector device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(0, &m_rom_config)
	};
}

template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian>
void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::set_rom_bank(int bank)
{
	if(!m_bank)
		throw emu_fatalerror("%s: device_rom_interface::set_rom_bank called without banking setup", device().tag());

	if(bank >= m_bank_count) {
		device().logerror("Warning: requested bank %x higher than actual bank count %x\n", bank, m_bank_count);
		bank = bank % m_bank_count;
	}

	if (m_cur_bank != bank) {
		m_cur_bank = bank;
		m_bank->set_entry(bank);
		rom_bank_updated();
	}
}

template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian>
void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::interface_post_load()
{
	device_memory_interface::interface_post_load();

	if(m_bank)
		m_bank->set_entry(m_cur_bank);
}

template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian>
void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::set_rom(const void *base, u32 size)
{
	const u32 mend = make_bitmask<u32>(m_rom_config.addr_width());
	const u32 rend = size-1;
	m_bank_count = (mend == 0xffffffff) ? 1 : (rend+1) / (mend+1);
	if(m_bank_count < 1)
		m_bank_count = 1;

	if(rend >= mend) {
		space().install_read_bank(0, mend, m_bank);
		m_bank->configure_entries(0, m_bank_count, const_cast<void *>(base), mend+1);
		m_bank->set_entry(0);
		m_cur_bank = 0;

	} else {
		// Round up to the nearest power-of-two-minus-one
		u32 rmask = rend;
		rmask |= rmask >> 1;
		rmask |= rmask >> 2;
		rmask |= rmask >> 4;
		rmask |= rmask >> 8;
		rmask |= rmask >> 16;
		if(rmask != rend)
			space().unmap_read(0, mend);
		// Mirror over the high bits.  mend and rmask are both
		// powers-of-two-minus-one, so the xor works
		space().install_rom(0, rend, mend ^ rmask, const_cast<void *>(base));
	}
}

template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian>
void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::interface_validity_check(validity_checker &valid) const
{
	device_memory_interface::interface_validity_check(valid);

	if(has_configured_map(0)) {
		const auto rom_target = m_rom_region.finder_target();
		if((&rom_target.first != &device()) || strcmp(rom_target.second, DEVICE_SELF))
			osd_printf_error("Address map and ROM region both specified\n");
	}
}

template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian>
void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::interface_pre_start()
{
	device_memory_interface::interface_pre_start();

	if(!has_space(0))
		return;

	space().cache(m_rom_cache);

	if(!has_configured_map(0)) {
		if(m_rom_region.found())
			set_rom(m_rom_region->base(), m_rom_region->bytes());
		else {
			const auto rom_target = m_rom_region.finder_target();
			if((&rom_target.first != &device()) || strcmp(rom_target.second, DEVICE_SELF))
				throw emu_fatalerror("%s: device_rom_interface ROM region '%s' not found", device().tag(), rom_target.first.subtag(rom_target.second));

			device().logerror("ROM region '%s' not found\n", rom_target.first.subtag(rom_target.second));
			space().unmap_read(0, make_bitmask<offs_t>(m_rom_config.addr_width()));
		}
	} else {
		const auto rom_target = m_rom_region.finder_target();
		if((&rom_target.first != &device()) || strcmp(rom_target.second, DEVICE_SELF))
			throw emu_fatalerror("%s: device_rom_interface has configured address map and ROM region", device().tag());
	}
}

template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian>
void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::interface_register_save(save_registrar &save)
{
	device_memory_interface::interface_register_save(save);

	save_registrar(save, "rom")
		.reg(NAME(m_cur_bank))
		.reg(NAME(m_bank_count));
}

#endif // MAME_EMU_DIROM_IPP