OXYGENHUB
◆ main openmsx / src / fdc / NowindInterface.cc
4.2 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "NowindInterface.hh"
2
3
#include "DiskChanger.hh"
4
5
#include "Clock.hh"
6
#include "MSXException.hh"
7
#include "MSXMotherBoard.hh"
8
#include "serialize.hh"
9
#include "serialize_stl.hh"
10
11
#include "narrow.hh"
12
13
#include <cassert>
14
#include <functional>
15
#include <memory>
16
17
namespace openmsx {
18
19
NowindInterface::NowindInterface(DeviceConfig& config)
20
	: MSXDevice(config)
21
	, rom(getName() + " ROM", "rom", config)
22
	, flash(rom, AmdFlashChip::AM29F040B, {}, config)
23
	, host(drives)
24
	, basename("nowindX")
25
{
26
	if ((rom.size() % 0x4000) != 0) {
27
		throw MSXException("NowindInterface ROM size must be a multiple of 16kB");
28
	}
29
	if (rom.size() == 0) {
30
		throw MSXException("NowindInterface ROM size cannot be zero");
31
	}
32
	if (rom.size() > size_t(256 * 0x4000)) {
33
		throw MSXException("NowindInterface ROM size cannot be larger than 4MB");
34
	}
35
36
	nowindsInUse = getMotherBoard().getSharedStuff<NowindsInUse>("nowindsInUse");
37
38
	unsigned i = 0;
39
	while ((*nowindsInUse)[i]) {
40
		if (++i == MAX_NOWINDS) {
41
			throw MSXException("Too many nowind interfaces.");
42
		}
43
	}
44
	(*nowindsInUse)[i] = true;
45
	basename[6] = char('a' + i);
46
47
	command.emplace(basename, getCommandController(), *this);
48
49
	// start with one (empty) drive
50
	auto drive = command->createDiskChanger(basename, 0, getMotherBoard());
51
	drive->createCommand();
52
	drives.push_back(std::move(drive));
53
54
	reset(EmuTime::dummy());
55
}
56
57
NowindInterface::~NowindInterface()
58
{
59
	unsigned i = basename[6] - 'a';
60
	assert((*nowindsInUse)[i]);
61
	(*nowindsInUse)[i] = false;
62
}
63
64
void NowindInterface::reset(EmuTime /*time*/)
65
{
66
	// version 1 didn't change the bank number
67
	// version 2 (produced by Sunrise) does reset the bank number
68
	bank = 0;
69
70
	// Flash state is NOT changed on reset
71
	//flash.reset();
72
}
73
74
byte NowindInterface::peekMem(uint16_t address, EmuTime time) const
75
{
76
	if (((0x2000 <= address) && (address < 0x4000)) ||
77
	    ((0x8000 <= address) && (address < 0xA000))) {
78
		return host.peek();
79
	} else if ((0x4000 <= address) && (address < 0xC000)) {
80
		// note: range 0x8000-0xA000 is already handled above
81
		return flash.peek(bank * 0x4000 + (address & 0x3FFF), time);
82
	} else {
83
		return 0xFF;
84
	}
85
}
86
87
byte NowindInterface::readMem(uint16_t address, EmuTime time)
88
{
89
	if (((0x2000 <= address) && (address < 0x4000)) ||
90
	    ((0x8000 <= address) && (address < 0xA000))) {
91
		return host.read();
92
	} else if ((0x4000 <= address) && (address < 0xC000)) {
93
		// note: range 0x8000-0xA000 is already handled above
94
		return flash.read(bank * 0x4000 + (address & 0x3FFF), time);
95
	} else {
96
		return 0xFF;
97
	}
98
}
99
100
const byte* NowindInterface::getReadCacheLine(uint16_t address) const
101
{
102
	if (((0x2000 <= address) && (address < 0x4000)) ||
103
	    ((0x8000 <= address) && (address < 0xA000))) {
104
		// nowind region, not cacheable
105
		return nullptr;
106
	} else if ((0x4000 <= address) && (address < 0xC000)) {
107
		// note: range 0x8000-0xA000 is already handled above
108
		return flash.getReadCacheLine(bank * 0x4000 + (address & 0x3FFF));
109
	} else {
110
		return unmappedRead.data();
111
	}
112
}
113
114
void NowindInterface::writeMem(uint16_t address, byte value, EmuTime time)
115
{
116
	if (address < 0x4000) {
117
		flash.write(bank * 0x4000 + address, value, time);
118
	} else if (((0x4000 <= address) && (address < 0x6000)) ||
119
	           ((0x8000 <= address) && (address < 0xA000))) {
120
		constexpr Clock<1000> clock(EmuTime::zero());
121
		host.write(value, clock.getTicksTill(time));
122
	} else if (((0x6000 <= address) && (address < 0x8000)) ||
123
	           ((0xA000 <= address) && (address < 0xC000))) {
124
		auto max = narrow<uint8_t>((rom.size() / 0x4000) - 1);
125
		bank = (value <= max) ? value : (value & max);
126
		invalidateDeviceRCache(0x4000, 0x4000);
127
		invalidateDeviceRCache(0xA000, 0x2000);
128
	}
129
}
130
131
byte* NowindInterface::getWriteCacheLine(uint16_t address)
132
{
133
	if (address < 0xC000) {
134
		// not cacheable
135
		return nullptr;
136
	} else {
137
		return unmappedWrite.data();
138
	}
139
}
140
141
142
template<typename Archive>
143
void NowindInterface::serialize(Archive& ar, unsigned /*version*/)
144
{
145
	ar.template serializeBase<MSXDevice>(*this);
146
	ar.serialize("flash", flash);
147
	ar.serializeWithID("drives", drives, std::ref(getMotherBoard()));
148
	ar.serialize("nowindhost", host,
149
	             "bank",       bank);
150
151
	// don't serialize command, rom, basename
152
}
153
INSTANTIATE_SERIALIZE_METHODS(NowindInterface);
154
REGISTER_MSXDEVICE(NowindInterface, "NowindInterface");
155
156
} // namespace openmsx