OXYGENHUB
◆ main openmsx / src / serial / MSXModem.cc
4.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "MSXModem.hh"
2
3
#include "CacheLine.hh"
4
#include "MSXException.hh"
5
#include "serialize.hh"
6
7
// See also
8
// https://sourceforge.net/p/msxsyssrc/git/ci/master/tree/modem100/t600/t600-0.mac
9
// for a disassembly of the code in the ROM. That disassembly source by A.
10
// Zeilemaker is the technical source for this implementation.
11
//
12
// MSX-Modem seems to be specified here:
13
// http://ngs.no.coocan.jp/doc/wiki.cgi/datapack?page=2%BE%CF+MSX+MODEM
14
15
namespace openmsx {
16
17
MSXModem::MSXModem(DeviceConfig& config)
18
	: MSXDevice(config)
19
	, rom(getName() + " ROM", "rom", config)
20
	, sram(getName() + " SRAM", 0x2000, config)
21
{
22
	if (rom.size() != 0x10000) {
23
		throw MSXException("ROM must be exactly 64kB in size!");
24
	}
25
	reset(EmuTime::dummy());
26
}
27
28
void MSXModem::reset(EmuTime /*time*/)
29
{
30
	bank = 0; // TODO: verify
31
	selectedNCUreg = 0; // TODO: verify
32
}
33
34
void MSXModem::writeMem(uint16_t address, uint8_t value, EmuTime /*time*/)
35
{
36
	if (address == 0x7FC0) {
37
		//   b2-b0 = ROM segment (4000H-5FFFH)
38
		//   b3    = off hook
39
		//   b4    = connect to modem
40
		//   b5    = speaker
41
		if (uint8_t newBank = value & 0b111; bank != newBank) {
42
			bank = newBank;
43
			invalidateDeviceRCache();
44
			//std::cerr << "Changed bank to " << int(bank) << "\n";
45
		}
46
		 // other bits not implemented
47
	} else if (address == 0x7F00) {
48
		// TODO: write NCU data
49
		// These registers are actually connected to the modem part... but faked here.
50
		// NCU register 0, write
51
		// NCU register 1, write
52
		//   b7 = send break disabled
53
		//   b3 = disable receiver
54
		// NCU register 2, write
55
		// NCU register 3, write
56
		//   transmitter data
57
		//std::cerr << "Wrote to NCU reg " << int(selectedNCUreg & 0b11) << ": " << int(value) << "\n";
58
	} else if (address == 0x7F40) {
59
		// select NCU register
60
		selectedNCUreg = value;
61
		// std::cerr << "Changed selected NCU reg to " << int(selectedNCUreg) << "\n";
62
	} else if ((0x6000 <= address) && (address < 0x8000)) {
63
		sram.write(address & 0x1FFF, value);
64
	}
65
}
66
67
uint8_t* MSXModem::getWriteCacheLine(uint16_t address)
68
{
69
	if (address == (0x7FC0 & CacheLine::HIGH)) return nullptr;
70
	if (address == (0x7F40 & CacheLine::HIGH)) return nullptr;
71
	if (address == (0x7F00 & CacheLine::HIGH)) return nullptr;
72
	if ((0x6000 <= address) && (address < 0x8000)) {
73
		return nullptr;
74
	}
75
	return unmappedWrite.data();
76
}
77
78
uint8_t MSXModem::readMem(uint16_t address, EmuTime time)
79
{
80
	return peekMem(address, time);
81
}
82
83
uint8_t MSXModem::peekMem(uint16_t address, EmuTime /*time*/) const
84
{
85
	if (address == 0x7FC0) {
86
		//  b0    = line polarity
87
		//  b1    = line polarity
88
		//  b2    = pulse/tone dail
89
		return 0; // not implemented
90
	} else if (address == 0x7F00) {
91
		// read NCU register data
92
		// These registers are actually connected to the modem part... but faked here.
93
		// NCU register 0, read
94
		//   b0 =
95
		//   b1 =
96
		//   b2 =
97
		//   b3 = break detected
98
		//   b4 = long loop detected
99
		//   b6 =
100
		//   b7 = dial tone detected
101
		// NCU register 1, read
102
		//   b3 = empty transmitter
103
		//   b2 = data in receiver
104
		//   b1 =
105
		//   b0 =
106
		// NCU register 2, read
107
		//   not used
108
		// NCU register 3, read
109
		//   receiver data
110
		static constexpr std::array<uint8_t, 4> ncuRegsRead = {0x00, 0x08, 0x00, 0x00}; // set "empty transmitter" so software starts up...
111
		//std::cerr << "Reading from NCU reg " << int(selectedNCUreg & 0b11) << "\n";
112
		return ncuRegsRead[selectedNCUreg & 0b11];
113
	} else if ((0x4000 <= address) && (address < 0x6000)) {
114
		return rom[bank * 0x2000 + (address & 0x1FFF)];
115
	} else if ((0x6000 <= address) && (address < 0x8000)) {
116
		return sram[address & 0x1FFF];
117
	} else {
118
		return 0xFF;
119
	}
120
}
121
122
const uint8_t* MSXModem::getReadCacheLine(uint16_t address) const
123
{
124
	if (address == (0x7FC0 & CacheLine::HIGH)) return nullptr;
125
	if (address == (0x7F00 & CacheLine::HIGH)) return nullptr;
126
	if ((0x4000 <= address) && (address < 0x6000)) {
127
		return &rom[bank * 0x2000 + (address & 0x1FFF)];
128
	}
129
	if ((0x6000 <= address) && (address < 0x8000)) {
130
		return &sram[address & 0x1FFF];
131
	}
132
	return unmappedRead.data();
133
}
134
135
template<typename Archive>
136
void MSXModem::serialize(Archive& ar, unsigned /*version*/)
137
{
138
	ar.template serializeBase<MSXDevice>(*this);
139
	ar.serialize("SRAM", sram,
140
		     "bank", bank,
141
		     "selectedNCUreg", selectedNCUreg);
142
}
143
INSTANTIATE_SERIALIZE_METHODS(MSXModem);
144
REGISTER_MSXDEVICE(MSXModem, "MSXModem");
145
146
} // namespace openmsx