OXYGENHUB
◆ main openmsx / src / memory / CheckedRam.cc
3.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "CheckedRam.hh"
2
3
#include "DeviceConfig.hh"
4
#include "GlobalSettings.hh"
5
#include "MSXCPU.hh"
6
#include "MSXMotherBoard.hh"
7
#include "StringSetting.hh"
8
9
#include "narrow.hh"
10
#include "xrange.hh"
11
12
#include <cassert>
13
14
namespace openmsx {
15
16
CheckedRam::CheckedRam(const DeviceConfig& config, const std::string& name,
17
                       static_string_view description, size_t size)
18
	: ram(config, name, description, size)
19
	, msxcpu(config.getMotherBoard().getCPU())
20
	, umrCallback(config.getGlobalSettings().getUMRCallBackSetting())
21
{
22
	umrCallback.getSetting().attach(*this);
23
	init();
24
}
25
26
CheckedRam::~CheckedRam()
27
{
28
	umrCallback.getSetting().detach(*this);
29
}
30
31
uint8_t CheckedRam::read(size_t addr)
32
{
33
	if (size_t line = addr >> CacheLine::BITS;
34
	    !completely_initialized_cacheline[line]) [[unlikely]] {
35
		if (uninitialized[line][addr &  CacheLine::LOW]) [[unlikely]] {
36
			umrCallback.execute(narrow<int>(addr), ram.getName());
37
		}
38
	}
39
	return ram[addr];
40
}
41
42
const uint8_t* CheckedRam::getReadCacheLine(size_t addr) const
43
{
44
	return (completely_initialized_cacheline[addr >> CacheLine::BITS])
45
	     ? &ram[addr] : nullptr;
46
}
47
48
uint8_t* CheckedRam::getWriteCacheLine(size_t addr)
49
{
50
	return (completely_initialized_cacheline[addr >> CacheLine::BITS])
51
	     ? &ram[addr] : nullptr;
52
}
53
54
uint8_t* CheckedRam::getRWCacheLines(size_t addr, size_t size)
55
{
56
	// TODO optimize
57
	size_t num = size >> CacheLine::BITS;
58
	size_t first = addr >> CacheLine::BITS;
59
	for (auto i : xrange(num)) {
60
		if (!completely_initialized_cacheline[first + i]) {
61
			return nullptr;
62
		}
63
	}
64
	return &ram[addr];
65
}
66
67
void CheckedRam::write(size_t addr, const uint8_t value)
68
{
69
	if (size_t line = addr >> CacheLine::BITS;
70
	    !completely_initialized_cacheline[line]) [[unlikely]] {
71
		uninitialized[line][addr & CacheLine::LOW] = false;
72
		if (uninitialized[line].none()) [[unlikely]] {
73
			completely_initialized_cacheline[line] = true;
74
			// This invalidates way too much stuff. But because
75
			// (locally) we don't know exactly how this class ie
76
			// being used in the MSXDevice, there's no easy way to
77
			// figure out what exactly should be invalidated.
78
			//
79
			// This is anyway only a debug feature (usually it's
80
			// not in use), therefor it's OK to implement this in a
81
			// easy/slow way rather than complex/fast.
82
			msxcpu.invalidateAllSlotsRWCache(0, 0x10000);
83
		}
84
	}
85
	ram[addr] = value;
86
}
87
88
void CheckedRam::clear()
89
{
90
	ram.clear();
91
	init();
92
}
93
94
void CheckedRam::init()
95
{
96
	auto lines = ram.size() / CacheLine::SIZE;
97
	if (umrCallback.getValue().empty()) {
98
		// there is no callback function,
99
		// do as if everything is initialized
100
		completely_initialized_cacheline.assign(lines, true);
101
		// 'uninitialized' won't be accessed, so don't even allocate
102
	} else {
103
		// new callback function, forget about initialized areas
104
		completely_initialized_cacheline.assign(lines, false);
105
106
		std::bitset<CacheLine::SIZE> allTrue;
107
		allTrue.set();
108
		uninitialized.assign(lines, allTrue);
109
	}
110
	msxcpu.invalidateAllSlotsRWCache(0, 0x10000);
111
}
112
113
void CheckedRam::update(const Setting& setting) noexcept
114
{
115
	assert(&setting == &umrCallback.getSetting());
116
	(void)setting;
117
	init();
118
}
119
120
} // namespace openmsx