openMSX
RomRamFile.cc
Go to the documentation of this file.
1 /**
2  * Tecall MSX Ramfile TM220
3  *
4  * This is a cartridge with 16kB ROM and 16kB RAM (powered by a battery). It
5  * offers BASIC commands to load/save files to this RAM. See manual for more
6  * details:
7  * http://www.msxarchive.nl/pub/msx/graphics/jpg/hardware/manuals/
8  *
9  * The ROM is visible in region 0x4000-0x8000.
10  * The RAM is visible in region 0x8000-0xC000.
11  * Reading ROM/RAM works as expected, but writing to RAM is special.
12  *
13  * The /WE signal of the RAM chip (actually implemented as 2x8kB RAM chips) is
14  * calculated from the /WE signal on the MSX cartridge slot plus 3 output bits
15  * from a 74LS164 shift-register. The input of this shift-register is 'D2 & D6'
16  * of the cartridge slot. The clock input of the shift-register is a
17  * combination of the /CS1 and /M1 cartridge signals. This means that on each
18  * (first byte of an) instruction fetch from the ROM, 2 bits of the fetched
19  * opcode are combined and shifted in an 8-bit value. 3 of those shifted bits
20  * need to have a specific value to allow write. See implementation below for
21  * more details.
22  *
23  * So in summary, the TM220 monitors which instructions are fetched from ROM
24  * and only allows to write to RAM after certain sequences of instructions.
25  */
26 
27 #include "RomRamFile.hh"
28 #include "MSXCPU.hh"
29 #include "serialize.hh"
30 #include <memory>
31 
32 namespace openmsx {
33 
35  : MSXRom(config, std::move(rom_))
36  , cpu(getCPU())
37 {
38  sram = std::make_unique<SRAM>(getName() + " SRAM", 0x4000, config);
39  reset(EmuTime::dummy());
40 }
41 
42 void RomRamFile::reset(EmuTime::param /*time*/)
43 {
44  shiftValue = 0;
45 }
46 
48 {
49  if ((0x4000 <= address) && (address < 0x8000)) {
50  byte result = rom[address & 0x1fff];
51  if (cpu.isM1Cycle(address)) {
52  bool tmp = (result & 0x44) == 0x44;
53  shiftValue = (shiftValue << 1) | byte(tmp);
54  }
55  return result;
56  } else if ((0x8000 <= address) && (address < 0xC000)) {
57  return (*sram)[address & 0x3fff];
58  } else {
59  return 0xff;
60  }
61 }
62 
64 {
65  if ((0x4000 <= address) && (address < 0x8000)) {
66  return rom[address & 0x1fff];
67  } else if ((0x8000 <= address) && (address < 0xC000)) {
68  return (*sram)[address & 0x3fff];
69  } else {
70  return 0xff;
71  }
72 }
73 
75 {
76  if ((0x4000 <= address) && (address < 0x8000)) {
77  // reads from ROM are not cacheable because of the M1 stuff
78  return nullptr;
79  } else if ((0x8000 <= address) && (address < 0xC000)) {
80  return &(*sram)[address & 0x3fff];
81  } else {
82  return unmappedRead;
83  }
84 }
85 
87 {
88  if ((0x8000 <= address) && (address < 0xC000) &&
89  ((shiftValue & 0x31) == 0x11)) {
90  sram->write(address & 0x3fff, value);
91  }
92 }
93 
95 {
96  if ((0x8000 <= address) && (address < 0xC000)) {
97  // writes to SRAM are not cacheable because of sync-to-disk
98  return nullptr;
99  } else {
100  return unmappedWrite;
101  }
102 }
103 
104 template<typename Archive>
105 void RomRamFile::serialize(Archive& ar, unsigned /*version*/)
106 {
107  // skip MSXRom base class
108  ar.template serializeBase<MSXDevice>(*this);
109  ar.serialize("shiftValue", shiftValue);
110 }
112 REGISTER_MSXDEVICE(RomRamFile, "RomRamFile");
113 
114 } // namespace openmsx
bool getEnum() const noexcept
Definition: EnumSetting.hh:96
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition: MSXDevice.hh:307
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981