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
8As reverse engineered by BiFi:
9
10At 0x3FFF is a configuration register.
11bit 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
14bits 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)
19bit 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
23When the ROM loads a MegaROM, bit 1 is left on 0. But for plain ROMs, it is set
24to 1.
25*/
26
27namespace openmsx {
28
30 : MSXRom(config, std::move(rom_))
31 , ram(config, getName() + " RAM", "ROM Hunter Mk 2 RAM", 0x40000)
32{
34}
35
36void ROMHunterMk2::reset(EmuTime::param /*time*/)
37{
38 configReg = 0;
39 ranges::fill(bankRegs, 0);
40 invalidateDeviceRCache(); // flush all to be sure
41}
42
43unsigned 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
51const byte* ROMHunterMk2::getReadCacheLine(word addr) const
52{
53 // reads outside [0x4000, 0xC000) return 0xFF
54 if ((addr < 0x4000) || (0xC000 <= addr)) {
55 return unmappedRead.data();
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
66byte ROMHunterMk2::peekMem(word addr, EmuTime::param /*time*/) const
67{
68 return *getReadCacheLine(addr);
69}
70
71byte ROMHunterMk2::readMem(word addr, EmuTime::param time)
72{
73 return peekMem(addr, time); // reads have no side effects
74}
75
76void 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
156template<typename Archive>
157void 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:211
static std::array< byte, 0x10000 > unmappedRead
Definition: MSXDevice.hh:302
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:125
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:730
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
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:287
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021