openMSX
RomAlAlamiah30in1.cc
Go to the documentation of this file.
1// Al Alamiah 30-in-1 cartridge series
2//
3// Wouter's analysis on the schematic that we drew up with the PCB:
4// * pin /SLTSL of the MSX cartridge slot is used (not /CS1, /CS2 or
5// /CS12). This means the device reacts to reads on the whole memory
6// region 0x0000..0xffff.
7// * pin A15 from the MSX cartridge slot does not seem to be connected.
8// That means memory region 0x0000-0x7ffff is mirrored in range
9// 0x8000..0xffff.
10// * Only pins A7..A3 (not A2..A0) seem to be used in the decoding of the
11// IO-port number. That means the cartridge will react to port range
12// 0..7.
13// * The 74LS174 can only store 6 bits (not 8). Only data bits D0..D5 of the
14// MSX cartridge slot are connected to the 74LS174. That means that the upper
15// two bits that are written via an OUT 0-7 operation are ignored.
16// * The 74LS174 /RESET input is connected to the /RESET signal of the MSX
17// cartridge slot. That means that on reset, the output pins Q0..Q5 of the
18// 74LS174 are set to zero. Thus bank 0 gets selected and (see later) we again
19// allow to select a new bank.
20// * The 74LS174 latches the input on a rising clock-edge. That mean a change
21// from 0 to 1 on the CLOCK input pin. See later for how this CLOCK input
22// signal gets calculated
23// * The 74LS02 chips offers 4 NOR gates, it seems only 2 of these are used.
24// * For one of these two NOR gates both inputs are connected to the same
25// signal (A7 of the MSX cartridge slot). This effectively turns this NOR gate
26// into a NOT gate.
27// * The 74LS138 chip is a 3-to-8-decoder. The datasheet(s) I found use a
28// different naming convention for the pin names than what is used in this
29// schematic. So I'm making some assumptions here:
30// * I'm assuming the A0, A1, A2 pins are the address-input pins. These 3
31// pins are interpreted as a value (between 0 and 7) and select one of the
32// 8 output pins.
33// * In this schematic only O0 is connected. This output pin becomes active
34// (= low) when the inputs A2,A1,A0 are 0,0,0.
35// * In addition the 74LS138 has several enable pins, I'm assuming those are
36// the E1, E2, E3 pins in the schematic.
37// * The decoder is only 'enabled' when two of these enable-inputs are 0 and
38// the 3rd is 1. Unfortunately in this schematic it's not clear which of
39// E1,E2,E3 plays which role (but I can make an educated guess).
40// * The inputs of the decoder are connected to the A7, A6, A5, A4, A3 and
41// /IORQ signals of the MSX cartridge slot. We want this device to react on
42// "OUT 0-7".
43// Thus all these signals (A7..A3 and /IORQ) must be zero. That's indeed the
44// case in this schematic: A7 gets inverted (via a NOT gate) and routed to
45// the E3 input (so we can assume this is the enable-input that must be 1),
46// and all other inputs must be 0 for the output O0 to become 0 (=active)
47// (in all other cases the output O0 will be 1).
48// * The output O0 of the 74LS138 (active on a "OUT 0-7") is routed to the
49// input of a NOR gate.
50// * The other input of this NOR gate is the Q5 output of the 74LS174 (this
51// chip remembers the last written value to port 0).
52// * A NOR gate outputs a 1 when both inputs are 0 (and 0 in all other cases).
53// * This means the output can only be 1 when we're processing a "OUT 0-7"
54// operation (good). And in addition Q5 must (still) be 0.
55// * The output of the above NOR gate is routed to the CLOCK input of the
56// 74LS174. We should examine when this signal changes from 0 to 1. Lets do
57// this in two steps:
58// * First: assume the Q5 output is 0 (this is the case after a reset).
59// * Then the signal changes from 0 to 1 on an "OUT 0-7" operation. That's
60// indeed what we want.
61// * In other words: on an "OUT 0-7" instruction we latch (=remember) the
62// bits D5..D0 (and ignore D7..D6) that were written in the OUT operation.
63// * Second: assume the Q5 output is 1. This can only happen when a prior
64// write has set this bit to 1.
65// * In this case the output of the NOR gate will always be 0. In other
66// words, it cannot change from 0 to 1 anymore. And then it's impossible
67// to change the latched value in the 74LS174.
68// * The only way to "recover" is via a RESET (this sets the Q5 output back
69// to 0).
70//
71// Summary:
72// * Writing a value 0..31, thus with bit 5 = 0 (and ignore bit 6 and 7) to
73// port 0..7 will select the corresponding bank.
74// * Writing a different value 0..31 selects a different bank.
75// * Writing a value 32..63, thus bit5 = 1 (ignore bits 6,7) will also select a
76// bank, but in addition this will block any further writes (writes to port
77// 0..7 will have no effect).
78// * The only way to recover from this is via a RESET. This reset will also
79// select bank 0.
80//
81// (End analysis by Wouter.)
82//
83// Also important to note: the /WR pin is not connected, so the mapper doesn't
84// know whether we're reading from or writing to it (either I/O or memory)
85//
86// Note that on real hardware, there seem to be many cases that block 31 of the
87// mapper is selected at random. I guess this is because of some noise on the
88// bus or something... but that must have been one of the reasons to implement
89// this block locking mechanism. Another reason could be protection against the
90// software in the blocks writing to ports 0-7. But that should not be very
91// common.
92//
93// Many thanks to tsjakoe for helping to reverse engineer the mapper and
94// dumping a ROM to test. And of course to Hashem for sending me the Group B
95// cartridge so we could reverse engineer the mapper in the first place.
96
97#include "RomAlAlamiah30in1.hh"
98#include "MSXCPUInterface.hh"
99#include "serialize.hh"
100#include "xrange.hh"
101
102namespace openmsx {
103
105 : Rom16kBBlocks(config, std::move(rom_))
106{
107 reset(EmuTime::dummy());
108 for (auto port : xrange(byte(8))) {
109 getCPUInterface().register_IO_Out(port, this);
110 getCPUInterface().register_IO_In(port, this);
111 }
112}
113
115{
116 for (auto port : xrange(byte(8))) {
118 getCPUInterface().unregister_IO_In(port, this);
119 }
120}
121
122void RomAlAlamiah30in1::reset(EmuTime::param time)
123{
124 mapperLocked = false;
125 writeIO(0, 0, time);
126}
127
128void RomAlAlamiah30in1::writeIO(word /*port*/, byte value, EmuTime::param /*time*/)
129{
130 if (!mapperLocked) {
131 // setRom works with 16kB pages as we're using Rom16kBBlocks,
132 // but we deal with 32kB blocks in the ROM image
133 byte page = 2 * (value & 0b1'1111);
134 setRom(0, page + 0);
135 setRom(1, page + 1);
136 setRom(2, page + 0);
137 setRom(3, page + 1);
138 }
139 // bit 5 of the value sets the mapper to a locked position
140 mapperLocked = mapperLocked || ((value & 0b10'0000) != 0);
141}
142
143byte RomAlAlamiah30in1::readIO(word port, EmuTime::param time)
144{
145 // as the cartridge doesn't look at the WR signal, assume a write
146 // of 0xFF (as if there are pull-up resistors on the bus).
147 writeIO(port, 0xFF, time);
148 return 0xFF;
149}
150
151template<typename Archive>
152void RomAlAlamiah30in1::serialize(Archive& ar, unsigned /*version*/)
153{
154 ar.template serializeBase<Rom16kBBlocks>(*this);
155 ar.serialize("mapperLocked", mapperLocked);
156}
158
160
161} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
void register_IO_Out(byte port, MSXDevice *device)
Devices can register their Out ports.
void register_IO_In(byte port, MSXDevice *device)
Devices can register their In ports.
void unregister_IO_In(byte port, MSXDevice *device)
void unregister_IO_Out(byte port, MSXDevice *device)
MSXCPUInterface & getCPUInterface() const
Definition MSXDevice.cc:133
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.
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
RomAlAlamiah30in1(const DeviceConfig &config, Rom &&rom)
void reset(EmuTime::param time) override
This method is called on reset.
void serialize(Archive &ar, unsigned version)
void setRom(unsigned region, unsigned block)
Selects a block of the ROM image for reading in a certain region.
Definition RomBlocks.cc:104
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)
Definition xrange.hh:132