openMSX
ROMHunterMk2.cc
Go to the documentation of this file.
1 #include "ROMHunterMk2.hh"
2 #include "ranges.hh"
3 #include "serialize.hh"
4 #include <cassert>
5 
6 /*
7 
8 As reverse engineered by BiFi:
9 
10 At 0x3FFF is a configuration register.
11 bit 1: page 1 control:
12  0: ROM hunter ROM in page 1 for READs, writes ignored (default)
13  1: writes go to RAM in page 1, mapper switching disabled
14 bits 2 and 0 is a mapper selector:
15  00 = ASCII16 (default),
16  01 = ASCII8,
17  11 = Konami,
18  10 = unknown (seems to be some Konami 16K variant)
19 bit 3: RAM write protect:
20  0: write enable (default)
21  1: write protect; also sets reads in page 1 to RAM (and not ROM)
22 
23 When the ROM loads a MegaROM, bit 1 is left on 0. But for plain ROMs, it is set
24 to 1.
25 */
26 
27 namespace openmsx {
28 
30  : MSXRom(config, std::move(rom_))
31  , ram(config, getName() + " RAM", "ROM Hunter Mk 2 RAM", 0x40000)
32 {
34 }
35 
36 void ROMHunterMk2::reset(EmuTime::param /*time*/)
37 {
38  configReg = 0;
39  ranges::fill(bankRegs, 0);
40  invalidateDeviceRCache(); // flush all to be sure
41 }
42 
43 unsigned ROMHunterMk2::getRamAddr(unsigned addr) const
44 {
45  unsigned page = (addr >> 13) - 2;
46  assert(page < 4);
47  unsigned bank = bankRegs[page];
48  return (bank * 0x2000) + (addr & 0x1FFF);
49 }
50 
51 const byte* ROMHunterMk2::getReadCacheLine(word addr) const
52 {
53  // reads outside [0x4000, 0xC000) return 0xFF
54  if ((addr < 0x4000) || (0xC000 <= addr)) {
55  return unmappedRead;
56  }
57 
58  // if bit 3 is 0, reads from page 1 come from the ROM, else from the RAM
59  if (((configReg & 0b1000) == 0) && (addr < 0x8000)) {
60  return &rom[addr & 0x1FFF];
61  } else {
62  return &ram[getRamAddr(addr)];
63  }
64 }
65 
66 byte ROMHunterMk2::peekMem(word addr, EmuTime::param /*time*/) const
67 {
68  return *getReadCacheLine(addr);
69 }
70 
71 byte ROMHunterMk2::readMem(word addr, EmuTime::param time)
72 {
73  return peekMem(addr, time); // reads have no side effects
74 }
75 
76 void ROMHunterMk2::writeMem(word addr, byte value, EmuTime::param /*time*/)
77 {
78  // config register at address 0x3FFF
79  if (addr == 0x3FFF) {
80  configReg = value;
81  invalidateDeviceRCache(0x4000, 0x8000);
82  return;
83  }
84 
85  // ignore (other) writes outside [0x4000, 0xC000)
86  if ((addr < 0x4000) || (0xC000 <= addr)) {
87  return;
88  }
89 
90  // address is calculated before writes to other regions take effect
91  unsigned ramAddr = getRamAddr(addr);
92 
93  // only write mapper registers if bit 1 is not set
94  if ((configReg & 0b10) == 0) {
95  // (possibly) write to bank registers
96  switch (configReg & 0b101) {
97  case 0b000: {
98  // ASCII-16
99  // Implemented similarly as the (much later) MFR SCC+,
100  // TODO did we verify that ROMHunterMk2 behaves the same?
101  //
102  // ASCII-16 uses all 4 bank registers and one bank
103  // switch changes 2 registers at once. This matters
104  // when switching mapper mode, because the content of
105  // the bank registers is unchanged after a switch.
106  const byte maskedValue = value & 0xF;
107  if ((0x6000 <= addr) && (addr < 0x6800)) {
108  bankRegs[0] = 2 * maskedValue + 0;
109  bankRegs[1] = 2 * maskedValue + 1;
110  invalidateDeviceRCache(0x4000, 0x4000);
111  }
112  if ((0x7000 <= addr) && (addr < 0x7800)) {
113  bankRegs[2] = 2 * maskedValue + 0;
114  bankRegs[3] = 2 * maskedValue + 1;
115  invalidateDeviceRCache(0x8000, 0x4000);
116  }
117  break;
118  }
119  case 0b001:
120  // ASCII-8
121  if ((0x6000 <= addr) && (addr < 0x8000)) {
122  byte bank = (addr >> 11) & 0x03;
123  bankRegs[bank] = value & 0x1F;
124  invalidateDeviceRCache(0x4000 + 0x2000 * bank, 0x2000);
125  }
126  break;
127  case 0b101:
128  // Konami
129  if ((0x6000 <= addr) && (addr < 0xC000)) {
130  unsigned bank = (addr >> 13) - 2;
131  bankRegs[bank] = value & 0x1F;
132  invalidateDeviceRCache(0x4000 + 0x2000 * bank, 0x2000);
133  }
134  break;
135  case 0b100:
136  // TODO how does this configuration behave?
137  break;
138  }
139  }
140 
141  // write to RAM, if not write-protected
142  if ((configReg & 0b1000) == 0) {
143  // if write to [0x8000, 0xC000), just do it
144  // if write to [0x4000, 0x8000), only do it if bit 1 is set
145  if ((addr >= 0x8000) || ((configReg & 0b10) == 0b10)) {
146  ram[ramAddr] = value;
147  }
148  }
149 }
150 
152 {
153  return nullptr;
154 }
155 
156 template<typename Archive>
157 void ROMHunterMk2::serialize(Archive& ar, unsigned /*version*/)
158 {
159  // skip MSXRom base class
160  ar.template serializeBase<MSXDevice>(*this);
161  ar.serialize("ram", ram,
162  "configReg", configReg,
163  "bankRegs", bankRegs);
164 }
167 
168 } // namespace openmsx
void invalidateDeviceRCache()
Definition: MSXDevice.hh:210
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:301
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:126
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing.
ROMHunterMk2(const DeviceConfig &config, Rom &&rom)
Definition: ROMHunterMk2.cc:29
const byte * getReadCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading.
Definition: ROMHunterMk2.cc:51
void serialize(Archive &ar, unsigned version)
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
Definition: ROMHunterMk2.cc:66
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: ROMHunterMk2.cc:76
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition: ROMHunterMk2.cc:71
void reset(EmuTime::param time) override
This method is called on reset.
Definition: ROMHunterMk2.cc:36
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:741
This file implemented 3 utility functions:
Definition: Autofire.cc:9
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:226
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998