OXYGENHUB
◆ main openmsx / src / SensorKid.cc
4.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "SensorKid.hh"
2
3
#include "CommandController.hh"
4
#include "MSXCliComm.hh"
5
#include "MSXException.hh"
6
#include "StringSetting.hh"
7
#include "serialize.hh"
8
9
#include "narrow.hh"
10
11
namespace openmsx {
12
13
SensorKid::SensorKid(const DeviceConfig& config)
14
	: MSXDevice(config)
15
	, portStatusCallback(getCommandController(),
16
		getName() + "_port_status_callback",
17
		"Tcl proc to call when an Sensor Kid port status is changed",
18
		"", Setting::Save::NO)
19
	, acquireCallback(getCommandController(),
20
		getName() + "_acquire_callback",
21
		"Tcl proc called to acquire analog data. "
22
		"Input: port number (0-3). "
23
		"Output: the value for that port (0-255).",
24
		"", Setting::Save::NO)
25
{
26
	reset(getCurrentTime());
27
}
28
29
void SensorKid::reset(EmuTime /*time*/)
30
{
31
	prev = 255; // previously written value to port 0
32
	mb4052_ana = 0; // the analog value that's currently being read
33
	mb4052_count = 0; // keep track of which bit we're reading
34
}
35
36
void SensorKid::writeIO(uint16_t port, byte value, EmuTime /*time*/)
37
{
38
	if ((port & 1) != 0) return;
39
40
	byte diff = prev ^ value;
41
	prev = value;
42
43
	if (diff & 0xC0) {
44
		// upper two bits changed
45
		putPort(value, diff);
46
	}
47
48
	if (diff & 0x10) {
49
		// CS bit changed
50
		if (value & 0x10) {
51
			// 0 -> 1 : start of A/D convert
52
			mb4052_ana = getAnalog(value & 0x0C);
53
			mb4052_count = 12;
54
		} else {
55
			// 1 -> 0 : end of A/D convert
56
			mb4052_count = 0;
57
		}
58
	}
59
	if ((diff & 0x01) && (value & 0x01)) {
60
		// CLK changed 0 -> 1
61
		if (mb4052_count > 0) --mb4052_count;
62
	}
63
}
64
65
byte SensorKid::readIO(uint16_t port, EmuTime /* time */)
66
{
67
	if ((port & 1) == 0) {
68
		// port 0
69
		// mb4052_count can take on values [0, 12]
70
		//  for 12,11,1,0 we return 1
71
		//  for 10        we return 0
72
		//  for 9..2      we return one of the analog bits
73
		byte result = [&] {
74
			if (mb4052_count == 10) {
75
				return byte(0);
76
			} else if ((mb4052_count < 10) && (mb4052_count > 1)) {
77
				return byte((mb4052_ana >> (mb4052_count - 2)) & 1);
78
			} else {
79
				return byte(1);
80
			}
81
		}();
82
		return 0xFE | result; // other bits read as '1'.
83
	} else {
84
		// port 1
85
		// returns two of the previously written bits (shuffled in different positions)
86
		return 0xFC | ((prev >> 5) & 0x02) | ((prev >> 7) & 0x01);
87
	}
88
}
89
90
byte SensorKid::getAnalog(byte chi) const
91
{
92
	// bits 2 and 3 in the 'port-0' byte select between 4 possible channels
93
	// for some reason bits 2 and 3 are swapped and then shifted down
94
	byte port = ((chi >> 1) & 2) | ((chi >> 3) & 1);
95
96
	// Execute Tcl callback
97
	//   input: the port number (0-3)
98
	//   output: the analog value for that port (0-255)
99
	// On the real cartridge
100
	//  port 0 is connected to a light sensor         HIKARI
101
	//  port 1 is connector to a temperature sensor   ONDO
102
	//  port 2 is connected to a microphone           OTO
103
	//  port 3 is not connected, always return 255
104
	byte result = 255;
105
	try {
106
		auto obj = acquireCallback.execute(port);
107
		if (obj != TclObject()) {
108
			auto tmp = obj.getInt(getCommandController().getInterpreter());
109
			if ((tmp < 0) || (tmp > 255)) {
110
				throw MSXException("outside range 0..255");
111
			}
112
			result = narrow_cast<byte>(tmp);
113
		}
114
	} catch (MSXException& e) {
115
		getCliComm().printWarning(
116
			"Wrong result for callback function \"",
117
			acquireCallback.getSetting().getFullName(),
118
			"\": ", e.getMessage());
119
	}
120
	return result;
121
}
122
123
void SensorKid::putPort(byte data, byte diff) const
124
{
125
	// When the upper 2 bits (bit 6 and 7) change we send a message.
126
	// I assume the cartridge also has two digital output pins?
127
	if (diff & 0x80) {
128
		//std::cout << "Status Port 0: " << int((data & 0x80) == 0) << '\n';
129
		portStatusCallback.execute(0, (data & 0x80) == 0);
130
	}
131
	if (diff & 0x40) {
132
		//std::cout << "Status Port 1:  " << int((data & 0x40) == 0) << '\n';
133
		portStatusCallback.execute(1, (data & 0x40) == 0);
134
	}
135
}
136
137
template<typename Archive>
138
void SensorKid::serialize(Archive& ar, unsigned /*version*/)
139
{
140
	ar.template serializeBase<MSXDevice>(*this);
141
	ar.serialize("prev",         prev,
142
	             "mb4052_ana",   mb4052_ana,
143
	             "mb4052_count", mb4052_count);
144
}
145
INSTANTIATE_SERIALIZE_METHODS(SensorKid);
146
REGISTER_MSXDEVICE(SensorKid, "SensorKid");
147
148
} // namespace openmsx