OXYGENHUB
◆ main openmsx / src / ide / MegaSCSI.cc
4.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
/*
2
 * MEGA-SCSI and ESE-RAM cartridge:
3
 *  The mapping does SRAM and MB89352A(MEGA-SCSI) to ASCII8 or
4
 *  an interchangeable bank controller.
5
 *
6
 * Specification:
7
 *  SRAM(MegaROM) controller: ASCII8 type
8
 *  SRAM capacity           : 128, 256, 512 and 1024KB
9
 *  SCSI Protocol Controller: Fujitsu MB89352A
10
 *
11
 * Bank changing address:
12
 *  bank 4(0x4000-0x5fff): 0x6000 - 0x67FF (0x6000 used)
13
 *  bank 6(0x6000-0x7fff): 0x6800 - 0x6FFF (0x6800 used)
14
 *  bank 8(0x8000-0x9fff): 0x7000 - 0x77FF (0x7000 used)
15
 *  bank A(0xa000-0xbfff): 0x7800 - 0x7FFF (0x7800 used)
16
 *
17
 * ESE-RAM Bank Map:
18
 *  BANK 0x00-0x7F (read only)
19
 *  BANK 0x80-0xFF (write and read. mirror of 0x00-0x7F)
20
 *
21
 * MEGA-SCSI Bank Map:
22
 *  BANK 0x00-0x3F (sram read only. mirror of 0x80-0xBF)
23
 *  BANK 0x40-0x7E (mirror of 0x7F. Use is prohibited)
24
 *  BANK 0x7F      (SPC)
25
 *  BANK 0x80-0xFF (sram write and read)
26
 *
27
 * SPC Bank:
28
 *  0x0000 - 0x0FFF :
29
 *      SPC Data register r/w (mirror of all 0x1FFA)
30
 *  0x1000 - 0x1FEF :
31
 *      mirror of 0x1FF0 - 0x1FFF
32
 *      Use is prohibited about the image
33
 *  0x1FF0 - 0x1FFE :
34
 *      SPC register
35
 *  0x1FFF :
36
 *      un mapped
37
 *
38
 * Note:
39
 *  It is possible to access it by putting it out to 0x8000 - 0xBFFF
40
 *  though the SPC bank is arranged in chiefly 0x4000-0x5FFF.
41
 */
42
43
#include "MegaSCSI.hh"
44
#include "MSXException.hh"
45
#include "narrow.hh"
46
#include "one_of.hh"
47
#include "serialize.hh"
48
#include "xrange.hh"
49
#include <cassert>
50
51
namespace openmsx {
52
53
static constexpr byte SPC = 0x7F;
54
55
size_t MegaSCSI::getSramSize() const
56
{
57
	size_t sramSize = getDeviceConfig().getChildDataAsInt("sramsize", 1024); // size in kb
58
	if (sramSize != one_of(1024u, 512u, 256u, 128u)) {
59
		throw MSXException(
60
			"SRAM size for ", getName(),
61
			" should be 128, 256, 512 or 1024kB and not ",
62
			sramSize, "kB!");
63
	}
64
	return sramSize * 1024; // in bytes
65
}
66
67
MegaSCSI::MegaSCSI(DeviceConfig& config)
68
	: MSXDevice(config)
69
	, mb89352(config)
70
	, sram(getName() + " SRAM", getSramSize(), config)
71
	, romBlockDebug(*this, mapped, 0x4000, 0x8000, 13)
72
	, blockMask(narrow<byte>((sram.size() / 0x2000) - 1))
73
{
74
	reset(EmuTime::dummy());
75
}
76
77
void MegaSCSI::reset(EmuTime /*time*/)
78
{
79
	for (auto i : xrange(4)) {
80
		setSRAM(i, 0);
81
	}
82
	mb89352.reset(true);
83
}
84
85
byte MegaSCSI::readMem(uint16_t address, EmuTime /*time*/)
86
{
87
	if ((0x4000 <= address) && (address < 0xC000)) {
88
		unsigned page = (address / 0x2000) - 2;
89
		uint16_t addr = address & 0x1FFF;
90
		if (mapped[page] == SPC) {
91
			// SPC read
92
			if (addr < 0x1000) {
93
				// Data Register
94
				return mb89352.readDREG();
95
			} else {
96
				return mb89352.readRegister(addr & 0x0F);
97
			}
98
		} else {
99
			return sram[0x2000 * mapped[page] + addr];
100
		}
101
	} else {
102
		return 0xFF;
103
	}
104
}
105
106
byte MegaSCSI::peekMem(uint16_t address, EmuTime /*time*/) const
107
{
108
	if (const byte* cacheline = MegaSCSI::getReadCacheLine(address)) {
109
		return *cacheline;
110
	} else {
111
		address &= 0x1FFF;
112
		if (address < 0x1000) {
113
			return mb89352.peekDREG();
114
		} else {
115
			return mb89352.peekRegister(address & 0x0F);
116
		}
117
	}
118
}
119
120
const byte* MegaSCSI::getReadCacheLine(uint16_t address) const
121
{
122
	if ((0x4000 <= address) && (address < 0xC000)) {
123
		unsigned page = (address / 0x2000) - 2;
124
		address &= 0x1FFF;
125
		if (mapped[page] == SPC) {
126
			return nullptr;
127
		} else {
128
			return &sram[0x2000 * mapped[page] + address];
129
		}
130
	} else {
131
		return unmappedRead.data();
132
	}
133
}
134
135
void MegaSCSI::writeMem(uint16_t address, byte value, EmuTime /*time*/)
136
{
137
	if ((0x6000 <= address) && (address < 0x8000)) {
138
		byte region = ((address >> 11) & 3);
139
		setSRAM(region, value);
140
	} else if ((0x4000 <= address) && (address < 0xC000)) {
141
		unsigned page = (address / 0x2000) - 2;
142
		address &= 0x1FFF;
143
		if (mapped[page] == SPC) {
144
			if (address < 0x1000) {
145
				mb89352.writeDREG(value);
146
			} else {
147
				mb89352.writeRegister(address & 0x0F, value);
148
			}
149
		} else if (isWriteable[page]) {
150
			sram.write(0x2000 * mapped[page] + address, value);
151
		}
152
	}
153
}
154
155
byte* MegaSCSI::getWriteCacheLine(uint16_t address)
156
{
157
	if ((0x6000 <= address) && (address < 0x8000)) {
158
		return nullptr;
159
	} else if ((0x4000 <= address) && (address < 0xC000)) {
160
		unsigned page = (address / 0x2000) - 2;
161
		if (mapped[page] == SPC) {
162
			return nullptr;
163
		} else if (isWriteable[page]) {
164
			return nullptr;
165
		}
166
	}
167
	return unmappedWrite.data();
168
}
169
170
void MegaSCSI::setSRAM(unsigned region, byte block)
171
{
172
	invalidateDeviceRWCache(region * 0x2000 + 0x4000, 0x2000);
173
	assert(region < 4);
174
	isWriteable[region] = (block & 0x80) != 0;
175
	mapped[region] = ((block & 0xC0) == 0x40) ? 0x7F : (block & blockMask);
176
}
177
178
template<typename Archive>
179
void MegaSCSI::serialize(Archive& ar, unsigned /*version*/)
180
{
181
	ar.serialize("SRAM",        sram,
182
	             "MB89352",     mb89352,
183
	             "isWriteable", isWriteable,
184
	             "mapped",      mapped);
185
}
186
INSTANTIATE_SERIALIZE_METHODS(MegaSCSI);
187
REGISTER_MSXDEVICE(MegaSCSI, "MegaSCSI");
188
189
} // namespace openmsx