OXYGENHUB
◆ main openmsx / src / memory / RomRamFile.cc
3.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
/**
2
 * Tecall MSX RAMFile TM220
3
 *
4
 * This is a cartridge with 16kB ROM and 16kB RAM (powered by a battery). It
5
 * offers BASIC commands to load/save files to this RAM. See manual for more
6
 * details:
7
 *     http://www.msxarchive.nl/pub/msx/graphics/jpg/hardware/manuals/
8
 *
9
 * The ROM is visible in region 0x4000-0x8000.
10
 * The RAM is visible in region 0x8000-0xC000.
11
 * Reading ROM/RAM works as expected, but writing to RAM is special.
12
 *
13
 * The /WE signal of the RAM chip (actually implemented as 2x8kB RAM chips) is
14
 * calculated from the /WE signal on the MSX cartridge slot plus 3 output bits
15
 * from a 74LS164 shift-register. The input of this shift-register is 'D2 & D6'
16
 * of the cartridge slot. The clock input of the shift-register is a
17
 * combination of the /CS1 and /M1 cartridge signals. This means that on each
18
 * (first byte of an) instruction fetch from the ROM, 2 bits of the fetched
19
 * opcode are combined and shifted in an 8-bit value. 3 of those shifted bits
20
 * need to have a specific value to allow write. See implementation below for
21
 * more details.
22
 *
23
 * So in summary, the TM220 monitors which instructions are fetched from ROM
24
 * and only allows to write to RAM after certain sequences of instructions.
25
 */
26
27
#include "RomRamFile.hh"
28
#include "MSXCPU.hh"
29
#include "narrow.hh"
30
#include "serialize.hh"
31
#include <memory>
32
33
namespace openmsx {
34
35
RomRamFile::RomRamFile(const DeviceConfig& config, Rom&& rom_)
36
	: MSXRom(config, std::move(rom_))
37
	, cpu(getCPU())
38
{
39
	sram = std::make_unique<SRAM>(getName() + " SRAM", 0x4000, config);
40
	reset(EmuTime::dummy());
41
}
42
43
void RomRamFile::reset(EmuTime /*time*/)
44
{
45
	shiftValue = 0;
46
}
47
48
byte RomRamFile::readMem(uint16_t address, EmuTime /*time*/)
49
{
50
	if ((0x4000 <= address) && (address < 0x8000)) {
51
		byte result = rom[address & 0x1fff];
52
		if (cpu.isM1Cycle(address)) {
53
			bool tmp = (result & 0x44) == 0x44;
54
			shiftValue = narrow_cast<byte>((shiftValue << 1) | byte(tmp));
55
		}
56
		return result;
57
	} else if ((0x8000 <= address) && (address < 0xC000)) {
58
		return (*sram)[address & 0x3fff];
59
	} else {
60
		return 0xff;
61
	}
62
}
63
64
byte RomRamFile::peekMem(uint16_t address, EmuTime /*time*/) const
65
{
66
	if ((0x4000 <= address) && (address < 0x8000)) {
67
		return rom[address & 0x1fff];
68
	} else if ((0x8000 <= address) && (address < 0xC000)) {
69
		return (*sram)[address & 0x3fff];
70
	} else {
71
		return 0xff;
72
	}
73
}
74
75
const byte* RomRamFile::getReadCacheLine(uint16_t address) const
76
{
77
	if ((0x4000 <= address) && (address < 0x8000)) {
78
		// reads from ROM are not cacheable because of the M1 stuff
79
		return nullptr;
80
	} else if ((0x8000 <= address) && (address < 0xC000)) {
81
		return &(*sram)[address & 0x3fff];
82
	} else {
83
		return unmappedRead.data();
84
	}
85
}
86
87
void RomRamFile::writeMem(uint16_t address, byte value, EmuTime /*time*/)
88
{
89
	if ((0x8000 <= address) && (address < 0xC000) &&
90
	    ((shiftValue & 0x31) == 0x11)) {
91
		sram->write(address & 0x3fff, value);
92
	}
93
}
94
95
byte* RomRamFile::getWriteCacheLine(uint16_t address)
96
{
97
	if ((0x8000 <= address) && (address < 0xC000)) {
98
		// writes to SRAM are not cacheable because of sync-to-disk
99
		return nullptr;
100
	} else {
101
		return unmappedWrite.data();
102
	}
103
}
104
105
template<typename Archive>
106
void RomRamFile::serialize(Archive& ar, unsigned /*version*/)
107
{
108
	// skip MSXRom base class
109
	ar.template serializeBase<MSXDevice>(*this);
110
	ar.serialize("shiftValue", shiftValue);
111
}
112
INSTANTIATE_SERIALIZE_METHODS(RomRamFile);
113
REGISTER_MSXDEVICE(RomRamFile, "RomRamFile");
114
115
} // namespace openmsx