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 Chakkara Copy
41 // from detecting the correct parameters Chakkara 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 
82 namespace 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 {
103  modeSetting.attach(*this);
104 }
105 
107 {
108  modeSetting.detach(*this);
109 }
110 
111 void ChakkariCopy::reset(EmuTime::param time)
112 {
113  writeIO(0, 0xFF, time);
114 }
115 
116 void 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 
137 byte ChakkariCopy::readIO(word port, EmuTime::param time)
138 {
139  return peekIO(port, time);
140 }
141 
142 byte 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 
150 byte ChakkariCopy::readMem(word address, EmuTime::param time)
151 {
152  return peekMem(address, time);
153 }
154 
155 byte ChakkariCopy::peekMem(word address, EmuTime::param /*time*/) const
156 {
157  return *getReadCacheLine(address);
158 }
159 
160 const 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 
184 void ChakkariCopy::writeMem(word address, byte value, EmuTime::param /*time*/)
185 {
186  *getWriteCacheLine(address) = value;
187 }
188 
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 
210 void ChakkariCopy::update(const Setting& /*setting*/)
211 {
212  // switch COPY <-> RAM mode, memory layout changes
214 }
215 
216 template<typename Archive>
217 void 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 (ar.isLoader()) {
224  writeIO(0, reg, getCurrentTime());
225  }
226 
227 }
229 REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy");
230 
231 } // namespace openmsx
openmsx::EnumSetting
Definition: Reactor.hh:54
openmsx::MSXDevice
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:31
openmsx::ChakkariCopy::RAM
@ RAM
Definition: ChakkariCopy.hh:15
serialize.hh
openmsx::DeviceConfig
Definition: DeviceConfig.hh:19
openmsx::YM2413NukeYKT::rom
constexpr Rom rom
Definition: YM2413NukeYKT.cc:71
openmsx::MSXDevice::unmappedWrite
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:301
openmsx::ChakkariCopy::peekIO
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition: ChakkariCopy.cc:142
openmsx::EnumSetting::getEnum
T getEnum() const noexcept
Definition: EnumSetting.hh:92
openmsx::Setting
Definition: Setting.hh:119
openmsx::CliComm::printInfo
void printInfo(std::string_view message)
Definition: CliComm.cc:5
openmsx::REGISTER_MSXDEVICE
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
openmsx::ChakkariCopy::readIO
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition: ChakkariCopy.cc:137
openmsx::ChakkariCopy::peekMem
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: ChakkariCopy.cc:155
openmsx::Keys::getName
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:740
openmsx::ChakkariCopy::getReadCacheLine
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
Definition: ChakkariCopy.cc:160
openmsx::ChakkariCopy::COPY
@ COPY
Definition: ChakkariCopy.hh:15
openmsx::ChakkariCopy::~ChakkariCopy
~ChakkariCopy() override
Definition: ChakkariCopy.cc:106
openmsx::MSXDevice::getCurrentTime
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:131
INSTANTIATE_SERIALIZE_METHODS
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
openmsx::ChakkariCopy::readMem
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition: ChakkariCopy.cc:150
openmsx::ChakkariCopy
Definition: ChakkariCopy.hh:12
openmsx::MSXDevice::getCliComm
CliComm & getCliComm() const
Definition: MSXDevice.cc:147
openmsx::ChakkariCopy::reset
void reset(EmuTime::param time) override
This method is called on reset.
Definition: ChakkariCopy.cc:111
openmsx::MSXDevice::invalidateDeviceRWCache
void invalidateDeviceRWCache()
Calls MSXCPUInterface::invalidateXXCache() for the specific (part of) the slot that this device is lo...
Definition: MSXDevice.hh:208
openmsx::ChakkariCopy::writeIO
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
Definition: ChakkariCopy.cc:116
openmsx::MSXDevice::getName
virtual std::string getName() const
Returns a human-readable name for this device.
Definition: MSXDevice.cc:381
openmsx::MSXDevice::unmappedRead
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:300
openmsx::ChakkariCopy::Mode
Mode
Definition: ChakkariCopy.hh:15
openmsx::word
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
openmsx::ChakkariCopy::serialize
void serialize(Archive &ar, unsigned version)
Definition: ChakkariCopy.cc:217
openmsx::ChakkariCopy::ChakkariCopy
ChakkariCopy(const DeviceConfig &config)
Definition: ChakkariCopy.cc:84
CliComm.hh
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
openmsx::BooleanSetting::getBoolean
bool getBoolean() const noexcept
Definition: BooleanSetting.hh:17
ChakkariCopy.hh
openmsx::ChakkariCopy::getWriteCacheLine
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
Definition: ChakkariCopy.cc:189
openmsx::ChakkariCopy::writeMem
void writeMem(word address, byte value, EmuTime::param time) override
Write a given byte to a given location at a certain time to this device.
Definition: ChakkariCopy.cc:184