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 
63 namespace openmsx {
64 
65 // MSXDevice
66 
68  : MSXDevice(config)
69  , i8255(*this, getCurrentTime(), config.getGlobalSettings().getInvalidPpiModeSetting())
70  , sram(getName() + " SRAM", 0x1000, config)
71 {
73 }
74 
75 void MSXHBI55::reset(EmuTime::param time)
76 {
77  lastC = 255; // hack
78  i8255.reset(time);
79 }
80 
81 byte MSXHBI55::readIO(word port, EmuTime::param time)
82 {
83  return i8255.read(port & 0x03, time);
84 }
85 
86 byte MSXHBI55::peekIO(word port, EmuTime::param time) const
87 {
88  return i8255.peek(port & 0x03, time);
89 }
90 
91 void MSXHBI55::writeIO(word port, byte value, EmuTime::param time)
92 {
93  i8255.write(port & 0x03, value, time);
94 }
95 
96 
97 // I8255Interface
98 
99 byte MSXHBI55::readA(EmuTime::param time)
100 {
101  return peekA(time);
102 }
103 byte MSXHBI55::peekA(EmuTime::param /*time*/) const
104 {
105  return 255; // TODO check this
106 }
107 byte MSXHBI55::readB(EmuTime::param time)
108 {
109  return peekB(time);
110 }
111 byte MSXHBI55::peekB(EmuTime::param /*time*/) const
112 {
113  return 255; // TODO check this
114 }
115 nibble MSXHBI55::readC0(EmuTime::param time)
116 {
117  return peekC0(time);
118 }
119 nibble MSXHBI55::peekC0(EmuTime::param /*time*/) const
120 {
121  return readStuff() & 0x0F;
122 }
123 nibble MSXHBI55::readC1(EmuTime::param time)
124 {
125  return peekC1(time);
126 }
127 nibble MSXHBI55::peekC1(EmuTime::param /*time*/) const
128 {
129  return readStuff() >> 4;
130 }
131 
132 void MSXHBI55::writeA(byte /*value*/, EmuTime::param /*time*/)
133 {
134  writeStuff();
135 }
136 void MSXHBI55::writeB(byte /*value*/, EmuTime::param /*time*/)
137 {
138  writeStuff();
139 }
140 void MSXHBI55::writeC0(nibble value, EmuTime::param /*time*/)
141 {
142  lastC = (lastC & 0xf0) | value; // hack
143  writeStuff();
144 }
145 void MSXHBI55::writeC1(nibble value, EmuTime::param /*time*/)
146 {
147  lastC = (lastC & 0x0f) | (value << 4); // hack
148  writeStuff();
149 }
150 
151 void 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 
177 byte 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.
204 template<typename Archive>
205 void 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
void reset(EmuTime::param time)
Definition: I8255.cc:33
byte getPortA() const
Definition: I8255.cc:365
byte getPortB() const
Definition: I8255.cc:375
byte peek(byte port, EmuTime::param time) const
Definition: I8255.cc:59
byte read(byte port, EmuTime::param time)
Definition: I8255.cc:42
void write(byte port, byte value, EmuTime::param time)
Definition: I8255.cc:76
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:33
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:126
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(unsigned addr, byte value)
Definition: SRAM.cc:64
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:727
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint8_t nibble
4 bit integer
Definition: openmsx.hh:23
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009