OXYGENHUB
◆ main openmsx / src / ChakkariCopy.cc
7.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "ChakkariCopy.hh"
2
3
#include "MSXCliComm.hh"
4
#include "serialize.hh"
5
6
// The cartridge has 2 buttons with 2 LEDs, marked PAUSE and COPY. It also has
7
// a toggle switch with the options COPY and RAM. Setting this toggle to RAM
8
// makes the cartridge a 16kB RAM expansion.
9
//
10
// The following information is provided by Rudolf Lechleitner, who devised it
11
// together with Enrico Barbisan.
12
//
13
// ----------------------------------------------------------------------------
14
//
15
// The memory layout:
16
//
17
// +--------------------------------------------+
18
// | 0000-3FFF: 16 KB RAM in COPY mode          |
19
// | Page 0: available in COPY mode only        |
20
// | can be toggled to read-only                |
21
// +--------------------------------------------+
22
// | 4000-5FFF: 8KB ROM code                    |
23
// | Page 1: +                                  |
24
// | 6000-67FF: 2KB internal RAM                |
25
// +--------------------------------------------+
26
//
27
// The shown 16KB RAM in page 0 are only available in COPY mode, but can be
28
// toggled to read-only. The location of the RAM in RAM mode is unknown.
29
//
30
// The memory at 6800-7FFF is unused by the cartridge.
31
//
32
// Internal handling: during cartridge initialisation Chakkari Copy copies the
33
// contents of BIOS page 0 into its own RAM and patches some entries to
34
// capture the input of PAUSE and COPY buttons of the cartridge. Afterwards the
35
// patched memory is set to read-only.
36
// PAUSE and COPY itself are processed by the routines in the ROM. In case the
37
// page 0 is (for any reason) switched to the original MSX-BIOS the cartridge
38
// buttons are no longer working. In this case CALL SCHANGE switches the BIOS
39
// to the patched version again.
40
//
41
// In case a program switches screen modes in a way that prevents Chakkari Copy
42
// from detecting the correct parameters Chakkari Copy can be started by
43
// keeping the COPY button pressed. In this case the screen-mode autodetect
44
// will be skipped and the parameters can be entered manually.
45
// This appears to be an undocumented feature, confirmed by typos and a very
46
// rudimentary screen handling.
47
//
48
// In case the printer is not ready or not available the printing can be
49
// aborted by pressing Ctrl+Stop.
50
//
51
// The Chakkari Copy cartridge has one register, which controls its complete
52
// behavior. This register is available at MSX port &H7F.
53
//
54
// READ only:
55
//
56
// +-----------------------------------------+
57
// |  1  |  1  |  1  |  1  |  1  | PSE | CPY |
58
// +-----+-----+-----+-----+-----+-----+-----+
59
// CPY: 0 = Copy button pressed, 1 = Copy button released
60
// PSE: 0 = Pause button pressed, 1 = Pause button released
61
//
62
// WRITE only:
63
//
64
// +-----+-----+-----+-----+-----+-----+-----+
65
// |  1  |  1  |  1  |  1  | RAM | PSE | CPY |
66
// +-----+-----+-----+-----+-----+-----+-----+
67
// CPY: 0 = Copy LED on, 1 = Copy LED off
68
// PSE: 0 = Pause LED on, 1 = Pause LED off
69
// RAM: 0 = Page 0 in RAM mode, 1 = Page 0 in ROM mode (applies to COPY mode only)
70
//
71
// ----------------------------------------------------------------------------
72
73
// The RAM mode has now been implemented to switch the RAM to page 1. This
74
// doesn't seem to be the most logical, but it's closest to the output of
75
// MSXMEM2. It could still be useful for 32kB machines.
76
//
77
// The work RAM has been mirrored in the 0x6000-0x8000 area to match the output
78
// of MSXMEM2.BAS which was run on a machine with the real cartridge inserted.
79
// It showed 'R' on page 1 for both COPY and RAM mode. The only thing that
80
// doesn't match yet is that it also showed 'R' in page 0 for COPY mode for
81
// subslots 0 and 1. The latter is unexplained so far.
82
83
namespace openmsx {
84
85
using namespace std::literals;
86
87
ChakkariCopy::ChakkariCopy(DeviceConfig& config)
88
	: MSXDevice(config)
89
	, biosRam(config, getName() + " BIOS RAM", "Chakkari Copy BIOS RAM", 0x4000)
90
	, workRam(config, getName() + " work RAM", "Chakkari Copy work RAM", 0x0800)
91
	, rom(getName() + " ROM", "rom", config)
92
	, pauseButtonPressedSetting(getCommandController(),
93
		getName() + " PAUSE button pressed",
94
		"controls the PAUSE button state", false, Setting::Save::NO)
95
	, copyButtonPressedSetting(getCommandController(),
96
		getName() + " COPY button pressed",
97
		"controls the COPY button state", false, Setting::Save::NO)
98
	, modeSetting(getCommandController(), getName() + " mode",
99
		"Sets mode of the cartridge: in COPY mode you can hardcopy MSX1 screens, "
100
		"in RAM mode you just have a 16kB RAM expansion", ChakkariCopy::Mode::COPY,
101
		EnumSetting<ChakkariCopy::Mode>::Map{
102
			{"COPY", ChakkariCopy::Mode::COPY}, {"RAM", ChakkariCopy::Mode::RAM}})
103
{
104
	reset(getCurrentTime());
105
	modeSetting.attach(*this);
106
}
107
108
ChakkariCopy::~ChakkariCopy()
109
{
110
	modeSetting.detach(*this);
111
}
112
113
void ChakkariCopy::reset(EmuTime time)
114
{
115
	writeIO(0, 0xFF, time);
116
}
117
118
void ChakkariCopy::writeIO(uint16_t /*port*/, byte value, EmuTime /*time*/)
119
{
120
	byte diff = reg ^ value;
121
	reg = value;
122
123
	if (diff & 0x01) {
124
		getCliComm().printInfo(getName(), " COPY LED ",
125
			(((value & 1) == 0x01) ? "OFF"sv : "ON"sv));
126
	}
127
	if (diff & 0x02) {
128
		getCliComm().printInfo(getName(), " PAUSE LED ",
129
			(((value & 2) == 0x02) ? "OFF"sv : "ON"sv));
130
	}
131
	if (diff & 0x04) {
132
		if (modeSetting.getEnum() == Mode::COPY) {
133
			// page 0 toggles writable/read-only
134
			invalidateDeviceRWCache(0x0000, 0x4000);
135
		}
136
	}
137
}
138
139
byte ChakkariCopy::readIO(uint16_t port, EmuTime time)
140
{
141
	return peekIO(port, time);
142
}
143
144
byte ChakkariCopy::peekIO(uint16_t /*port*/, EmuTime /*time*/) const
145
{
146
	byte retVal = 0xFF;
147
	if (copyButtonPressedSetting .getBoolean()) retVal &= ~0x01;
148
	if (pauseButtonPressedSetting.getBoolean()) retVal &= ~0x02;
149
	return retVal;
150
}
151
152
byte ChakkariCopy::readMem(uint16_t address, EmuTime time)
153
{
154
	return peekMem(address, time);
155
}
156
157
byte ChakkariCopy::peekMem(uint16_t address, EmuTime /*time*/) const
158
{
159
	return *getReadCacheLine(address);
160
}
161
162
const byte* ChakkariCopy::getReadCacheLine(uint16_t address) const
163
{
164
	if (modeSetting.getEnum() == Mode::COPY) {
165
		// page 0
166
		if (address < 0x4000) {
167
			return &biosRam[address];
168
		}
169
		// page 1
170
		if ((0x4000 <= address) && (address < 0x6000)) {
171
			return &rom[address & 0x1FFF];
172
		}
173
		// the work RAM is mirrored in 0x6000-0x8000, see above
174
		if ((0x6000 <= address) && (address < 0x8000)) {
175
			return &workRam[address & 0x07FF];
176
		}
177
	} else {
178
		// page 1 RAM mode
179
		if ((0x4000 <= address) && (address < 0x8000)) {
180
			return &biosRam[address & 0x3FFF];
181
		}
182
	}
183
	return unmappedRead.data();
184
}
185
186
void ChakkariCopy::writeMem(uint16_t address, byte value, EmuTime /*time*/)
187
{
188
	*getWriteCacheLine(address) = value;
189
}
190
191
byte* ChakkariCopy::getWriteCacheLine(uint16_t address)
192
{
193
	if (modeSetting.getEnum() == Mode::COPY) {
194
		// page 0
195
		if ((address < 0x4000) && ((reg & 0x04) == 0)) {
196
			return &biosRam[address & 0x3FFF];
197
		}
198
		// page 1
199
		// the work RAM is mirrored in 0x6000-0x8000, see above
200
		if ((0x6000 <= address) && (address < 0x8000)) {
201
			return &workRam[address & 0x07FF];
202
		}
203
	} else {
204
		// page 1 RAM mode
205
		if ((0x4000 <= address) && (address < 0x8000)) {
206
			return &biosRam[address & 0x3FFF];
207
		}
208
	}
209
	return unmappedWrite.data();
210
}
211
212
void ChakkariCopy::update(const Setting& /*setting*/) noexcept
213
{
214
	// switch COPY <-> RAM mode, memory layout changes
215
	invalidateDeviceRWCache();
216
}
217
218
template<typename Archive>
219
void ChakkariCopy::serialize(Archive& ar, unsigned /*version*/)
220
{
221
	ar.template serializeBase<MSXDevice>(*this);
222
	ar.serialize("biosRam", biosRam,
223
	             "workRam", workRam,
224
	             "reg", reg);
225
	if constexpr (Archive::IS_LOADER) {
226
		writeIO(0, reg, getCurrentTime());
227
	}
228
229
}
230
INSTANTIATE_SERIALIZE_METHODS(ChakkariCopy);
231
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy");
232
233
} // namespace openmsx