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