openMSX
MSXHBI55.cc
Go to the documentation of this file.
1/* Submitted By: Albert Beevendorp (bifimsx)
2 *
3 * Some Sony MSX machines have built-in firmware
4 * containing an address 'book', memo and scheduler and a
5 * few of these can store these data on a data cartridge.
6 *
7 * In Basic it's possible to use it with the "CAT:" device.
8 *
9 * I'm not sure if there are more types, though the HBI-55
10 * is a 4KB SRAM containing cartridge accessed by I/O
11 * ports connected to a 8255 chip:
12 *
13 * B0 = LSB address
14 * B1 = MSB address (D7-D6 = 01 write, 11 read)
15 * B2 = Data port
16 * B3 = Access control
17 *
18 * Sample basic program:
19 * 10 FOR I=0 TO 4095: OUT &HB3,128: OUT &HB0,I MOD 256:
20 * OUT &HB1,64 OR I\256: OUT &HB2,I MOD 256: NEXT I
21 * 20 FOR I=0 TO 4095:OUT &HB3,128-9*(I<>0): OUT &HB0,I MOD 256:
22 * OUT &HB1,192 OR I\256: IF I MOD 256=INP(&HB2) THEN NEXT
23 * ELSE PRINT "Error comparing byte:";I: END
24 * 30 PRINT "Done!"
25 * Update:
26 * This test-program is not correct, see the link below for improved
27 * version(s).
28 *
29 * -----
30 *
31 * Improved code mostly copied from blueMSX, many thanks to Daniel Vik.
32 * http://cvs.sourceforge.net/viewcvs.py/bluemsx/blueMSX/Src/Memory/romMapperSonyHBI55.c
33 * Update:
34 * Later the implementation was simplified quite a bit by replacing internal
35 * state with looking at the current PPI output values. This is closer to how
36 * the real hardware works.
37 *
38 * When only one of the nibbles is written (actually when one is set as
39 * input the other as output), the other nibble gets the value of the
40 * last time that nibble was written. Thanks to Laurens Holst for
41 * investigating this. See this bug report for more details:
42 * https://sourceforge.net/p/openmsx/bugs/536/
43 * Update:
44 * This behavior is actually undefined: it writes a 'floating value'.
45 * This value happens to (mostly) be the last written value, at least if
46 * there's not too much time between the writes. Nevertheless we currently
47 * emulate it like this.
48 *
49 * For more details see the bug report(s) at:
50 * https://github.com/openMSX/openMSX/issues/927
51 * Especially see grauw's (updated) test programs (2022/02/20).
52 *
53 * See also the (updated) documentation on:
54 * http://map.grauw.nl/resources/hbi55.php
55 * And the service manual (contains electrical schema):
56 * http://map.grauw.nl/resources/memory/sony_hbi-55_sm.pdf
57 */
58
59#include "MSXHBI55.hh"
60#include "GlobalSettings.hh"
61#include "serialize.hh"
62
63namespace openmsx {
64
65// MSXDevice
66
68 : MSXDevice(config)
69 , i8255(*this, getCurrentTime(), config.getGlobalSettings().getInvalidPpiModeSetting())
70 , sram(getName() + " SRAM", 0x1000, config)
71{
73}
74
75void MSXHBI55::reset(EmuTime::param time)
76{
77 lastC = 255; // hack
78 i8255.reset(time);
79}
80
81byte MSXHBI55::readIO(word port, EmuTime::param time)
82{
83 return i8255.read(port & 0x03, time);
84}
85
86byte MSXHBI55::peekIO(word port, EmuTime::param time) const
87{
88 return i8255.peek(port & 0x03, time);
89}
90
91void MSXHBI55::writeIO(word port, byte value, EmuTime::param time)
92{
93 i8255.write(port & 0x03, value, time);
94}
95
96
97// I8255Interface
98
99byte MSXHBI55::readA(EmuTime::param time)
100{
101 return peekA(time);
102}
103byte MSXHBI55::peekA(EmuTime::param /*time*/) const
104{
105 return 255; // TODO check this
106}
107byte MSXHBI55::readB(EmuTime::param time)
108{
109 return peekB(time);
110}
111byte MSXHBI55::peekB(EmuTime::param /*time*/) const
112{
113 return 255; // TODO check this
114}
115nibble MSXHBI55::readC0(EmuTime::param time)
116{
117 return peekC0(time);
118}
119nibble MSXHBI55::peekC0(EmuTime::param /*time*/) const
120{
121 return readStuff() & 0x0F;
122}
123nibble MSXHBI55::readC1(EmuTime::param time)
124{
125 return peekC1(time);
126}
127nibble MSXHBI55::peekC1(EmuTime::param /*time*/) const
128{
129 return readStuff() >> 4;
130}
131
132void MSXHBI55::writeA(byte /*value*/, EmuTime::param /*time*/)
133{
134 writeStuff();
135}
136void MSXHBI55::writeB(byte /*value*/, EmuTime::param /*time*/)
137{
138 writeStuff();
139}
140void MSXHBI55::writeC0(nibble value, EmuTime::param /*time*/)
141{
142 lastC = (lastC & 0xf0) | value; // hack
143 writeStuff();
144}
145void MSXHBI55::writeC1(nibble value, EmuTime::param /*time*/)
146{
147 lastC = byte((lastC & 0x0f) | (value << 4)); // hack
148 writeStuff();
149}
150
151void MSXHBI55::writeStuff()
152{
153 byte B = i8255.getPortB();
154 if ((B & 0x70) != 0x40) {
155 // /CE of RAM chip(s) not active
156 return;
157 }
158
159 if (B & 0x80) {
160 // read, do nothing
161 } else {
162 // write
163 byte A = i8255.getPortA();
164 unsigned addr = ((B & 0x0f) << 8) | A;
165 // In the normal case port C should be programmed as an output,
166 // and then we can get the value like this:
167 // byte C = i8255.getPortC();
168 // When port C is (partially) set as input, we technically use a
169 // 'floating value'. Hack: experiments have shown that we more
170 // closely _approximate_ this (invalid) case by using the last
171 // written (output-)value.
172 byte C = lastC;
173 sram.write(addr, C);
174 }
175}
176
177byte MSXHBI55::readStuff() const
178{
179 byte B = i8255.getPortB();
180 if ((B & 0x70) != 0x40) {
181 // /CE of RAM chip(s) not active
182 return lastC; // actually floating value
183 }
184
185 if (B & 0x80) {
186 // read
187 byte A = i8255.getPortA();
188 unsigned addr = ((B & 0x0f) << 8) | A;
189 return sram[addr];
190 } else {
191 // write
192 // Normally this shouldn't occur: when writing, port C should be
193 // programmed as an output, and then this method won't get called.
194 return lastC; // actually floating value
195 }
196}
197
198// version 1: initial version
199// version 2: removed 'readAddress', 'writeAddress', 'addressLatch', 'writeLatch', 'mode'
200// these are replaced by i8255.getPort{A,B,C}().
201// introduced 'lastC'
202// This is a hack: it's used instead of i8255.getPortC().
203// This more or less maps to the old 'writeLatch' variable.
204template<typename Archive>
205void MSXHBI55::serialize(Archive& ar, unsigned version)
206{
207 ar.template serializeBase<MSXDevice>(*this);
208 ar.serialize("i8255", i8255,
209 "SRAM", sram);
210 if (ar.versionAtLeast(version, 2)) {
211 ar.serialize("lastC", lastC);
212 } else {
213 assert(Archive::IS_LOADER);
214 byte writeLatch = 0;
215 ar.serialize("writeLatch", writeLatch);
216 lastC = writeLatch;
217 }
218}
221
222} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
void reset(EmuTime::param time)
Definition I8255.cc:33
byte getPortA() const
Definition I8255.cc:363
byte getPortB() const
Definition I8255.cc:373
byte peek(byte port, EmuTime::param time) const
Definition I8255.cc:58
byte read(byte port, EmuTime::param time)
Definition I8255.cc:42
void write(byte port, byte value, EmuTime::param time)
Definition I8255.cc:74
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
EmuTime::param getCurrentTime() const
Definition MSXDevice.cc:125
void reset(EmuTime::param time) override
This method is called on reset.
Definition MSXHBI55.cc:75
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition MSXHBI55.cc:86
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition MSXHBI55.cc:81
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 MSXHBI55.cc:91
void serialize(Archive &ar, unsigned version)
Definition MSXHBI55.cc:205
MSXHBI55(const DeviceConfig &config)
Definition MSXHBI55.cc:67
void write(size_t addr, byte value)
Definition SRAM.cc:64
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint8_t byte
8 bit unsigned integer
Definition openmsx.hh:26
uint8_t nibble
4 bit integer
Definition openmsx.hh:23
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
std::array< const EDStorage, 4 > A
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)