gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

mqmc.cpp (8390B)


      1 #include "mqmc.h"
      2 
      3 #include <array>
      4 #include <fstream>
      5 
      6 #include "rom.h"
      7 
      8 #include <cstring>	// memcpy
      9 
     10 #include "mqbuildconfig.h"
     11 #include "mc68k/logging.h"
     12 
     13 #define MC68K_CLASS mqLib::MqMc
     14 #include "mc68k/musashiEntry.h"
     15 
     16 namespace mqLib
     17 {
     18 	constexpr uint32_t g_romAddress = 0x80000;
     19 	constexpr uint32_t g_pcInitial = 0x80130;
     20 	constexpr uint32_t g_memorySize = 0x40000;
     21 
     22 	static std::string logChar(char _val)
     23 	{
     24 		std::stringstream ss;
     25 		if(_val > 32 && _val < 127)
     26 			ss << _val;
     27 		else
     28 			ss << "[" << MCHEXN(static_cast<uint8_t>(_val), 2) << "]";
     29 		return ss.str();
     30 	}
     31 
     32 	MqMc::MqMc(const ROM& _rom) : m_rom(_rom)
     33 	{
     34 		if(!_rom.isValid())
     35 			return;
     36 		m_romRuntimeData.resize(ROM::size());
     37 		memcpy(m_romRuntimeData.data(), m_rom.getData().data(), ROM::size());
     38 
     39 		m_flash.reset(new hwLib::Am29f(m_romRuntimeData.data(), m_romRuntimeData.size(), false, true));
     40 
     41 		m_memory.resize(g_memorySize, 0);
     42 
     43 		reset();
     44 
     45 		setPC(g_pcInitial);
     46 
     47 		getPortGP().setWriteTXCallback([this](const mc68k::Port&)
     48 		{
     49 			onPortGPWritten();
     50 		});
     51 
     52 		getPortE().setWriteTXCallback([this](const mc68k::Port&)
     53 		{
     54 			onPortEWritten();
     55 		});
     56 
     57 		getPortF().setWriteTXCallback([this](const mc68k::Port&)
     58 		{
     59 			onPortFWritten();
     60 		});
     61 
     62 		getPortQS().setDirectionChangeCallback([this](const mc68k::Port&)
     63 		{
     64 			onPortQSWritten();
     65 		});
     66 
     67 		getPortQS().setWriteTXCallback([this](const mc68k::Port&)
     68 		{
     69 			onPortQSWritten();
     70 		});
     71 
     72 #if 0
     73 		dumpAssembly(g_romAddress, g_romSize);
     74 #endif
     75 	}
     76 
     77 	MqMc::~MqMc() = default;
     78 
     79 //	uint64_t g_instructionCounter = 0;
     80 //	std::deque<uint32_t> g_lastPCs;
     81 
     82 	uint32_t MqMc::exec()
     83 	{
     84 //		if(g_instructionCounter >= 17300000)
     85 //			MCLOG("Exec @ " << MCHEX(getPC()));
     86 /*
     87 		g_lastPCs.push_back(getPC());
     88 		if(g_lastPCs.size() > 100)
     89 			g_lastPCs.pop_front();
     90 */
     91 //		++g_instructionCounter;
     92 
     93 		const uint32_t deltaCycles = Mc68k::exec();
     94 
     95 		m_hdi08a.exec(deltaCycles);
     96 
     97 		if constexpr (g_useVoiceExpansion)
     98 		{
     99 			m_hdi08b.exec(deltaCycles);
    100 			m_hdi08c.exec(deltaCycles);
    101 		}
    102 
    103 		m_buttons.processButtons(getPortGP(), getPortE());
    104 
    105 		return deltaCycles;
    106 	}
    107 
    108 	void MqMc::notifyDSPBooted()
    109 	{
    110 		if(!m_dspResetCompleted)
    111 			MCLOG("DSP has booted");
    112 		m_dspResetCompleted = true;
    113 	}
    114 
    115 	uint16_t MqMc::readImm16(uint32_t addr)
    116 	{
    117 		if(addr < g_memorySize)
    118 		{
    119 			return mc68k::memoryOps::readU16(m_memory, addr);
    120 		}
    121 
    122 		if(addr >= g_romAddress && addr < g_romAddress + ROM::size())
    123 		{
    124 			const auto r = mc68k::memoryOps::readU16(m_romRuntimeData, addr - g_romAddress);
    125 //			LOG("read16 from ROM addr=" << HEXN(addr, 8) << " val=" << HEXN(r, 4));
    126 			return r;
    127 		}
    128 //		__debugbreak();
    129 		return 0;
    130 	}
    131 
    132 	uint16_t MqMc::read16(uint32_t addr)
    133 	{
    134 		if(addr < g_memorySize)
    135 		{
    136 			return mc68k::memoryOps::readU16(m_memory, addr);
    137 		}
    138 
    139 		if(addr >= g_romAddress && addr < g_romAddress + ROM::size())
    140 		{
    141 			const auto r = mc68k::memoryOps::readU16(m_romRuntimeData, addr - g_romAddress);
    142 //			LOG("read16 from ROM addr=" << HEXN(addr, 8) << " val=" << HEXN(r, 4));
    143 			return r;
    144 		}
    145 
    146 		const auto pa = static_cast<mc68k::PeriphAddress>(addr & mc68k::g_peripheralMask);
    147 
    148 		if (m_hdi08a.isInRange(pa))
    149 			return m_hdi08a.read16(pa);
    150 
    151 		if constexpr (g_useVoiceExpansion)
    152 		{
    153 			if (m_hdi08b.isInRange(pa))
    154 				return m_hdi08b.read16(pa);
    155 			if (m_hdi08c.isInRange(pa))
    156 				return m_hdi08c.read16(pa);
    157 		}
    158 
    159 //		LOG("read16 addr=" << HEXN(addr, 8) << ", pc=" << HEXN(getPC(), 8));
    160 
    161 		return Mc68k::read16(addr);
    162 	}
    163 
    164 	uint8_t MqMc::read8(uint32_t addr)
    165 	{
    166 		if(addr < g_memorySize)
    167 			return m_memory[addr];
    168 
    169 		if(addr >= g_romAddress && addr < g_romAddress + ROM::size())
    170 			return m_romRuntimeData[addr - g_romAddress];
    171 
    172 		const auto pa = static_cast<mc68k::PeriphAddress>(addr & mc68k::g_peripheralMask);
    173 
    174 		if (m_hdi08a.isInRange(pa))
    175 			return m_hdi08a.read8(pa);
    176 		if constexpr (g_useVoiceExpansion)
    177 		{
    178 			if (m_hdi08b.isInRange(pa))
    179 				return m_hdi08b.read8(pa);
    180 			if (m_hdi08c.isInRange(pa))
    181 				return m_hdi08c.read8(pa);
    182 		}
    183 
    184 //		LOG("read8 addr=" << HEXN(addr, 8) << ", pc=" << HEXN(getPC(), 8));
    185 
    186 		return Mc68k::read8(addr);
    187 	}
    188 
    189 	void MqMc::write16(uint32_t addr, uint16_t val)
    190 	{
    191 		// Dump memory if DSP test reaches error state
    192 		if(addr == 0x384A8)
    193 		{
    194 			if(val > 0 && val <= 0xff)
    195 				dumpMemory((std::string("DSPTest_Error_") + std::to_string(val)).c_str());
    196 		}
    197 
    198 		if(addr < g_memorySize)
    199 		{
    200 			mc68k::memoryOps::writeU16(m_memory, addr, val);
    201 			return;
    202 		}
    203 
    204 		if(addr >= g_romAddress && addr < g_romAddress + ROM::size())
    205 		{
    206 			MCLOG("write16 TO ROM addr=" << MCHEXN(addr, 8) << ", value=" << MCHEXN(val,4) << ", pc=" << MCHEXN(getPC(), 8));
    207 			m_flash->write(addr - g_romAddress, val);
    208 			return;
    209 		}
    210 
    211 		const auto pa = static_cast<mc68k::PeriphAddress>(addr & mc68k::g_peripheralMask);
    212 
    213 		if (m_hdi08a.isInRange(pa))
    214 		{
    215 			m_hdi08a.write16(pa, val);
    216 			return;
    217 		}
    218 
    219 		if constexpr (g_useVoiceExpansion)
    220 		{
    221 			if (m_hdi08b.isInRange(pa))
    222 			{
    223 				m_hdi08b.write16(pa, val);
    224 				return;
    225 			}
    226 			if (m_hdi08c.isInRange(pa))
    227 			{
    228 				m_hdi08c.write16(pa, val);
    229 				return;
    230 			}
    231 		}
    232 
    233 		Mc68k::write16(addr, val);
    234 	}
    235 
    236 	void MqMc::write8(uint32_t addr, uint8_t val)
    237 	{
    238 		// Dump memory if DSP test reaches error state
    239 		if(addr == 0x384A8)
    240 		{
    241 			if(val > 0)
    242 				dumpMemory((std::string("DSPTest_Error_") + std::to_string(val)).c_str());
    243 		}
    244 
    245 		if(addr < g_memorySize)
    246 		{
    247 			m_memory[addr] = val;
    248 			return;
    249 		}
    250 
    251 		if(addr >= g_romAddress && addr < g_romAddress + ROM::size())
    252 		{
    253 			MCLOG("write8 TO ROM addr=" << MCHEXN(addr, 8) << ", value=" << MCHEXN(val,2) << " char=" << logChar(val) << ", pc=" << MCHEXN(getPC(), 8));
    254 			m_flash->write(addr - g_romAddress, val);
    255 			return;
    256 		}
    257 
    258 //		LOG("write8 addr=" << HEXN(addr, 8) << ", value=" << HEXN(val,2) << " char=" << logChar(val) << ", pc=" << HEXN(getPC(), 8));
    259 
    260 		const auto pa = static_cast<mc68k::PeriphAddress>(addr & mc68k::g_peripheralMask);
    261 		if (m_hdi08a.isInRange(pa))
    262 		{
    263 			m_hdi08a.write8(pa, val);
    264 			return;
    265 		}
    266 
    267 		if constexpr (g_useVoiceExpansion)
    268 		{
    269 			if (m_hdi08b.isInRange(pa))
    270 			{
    271 				m_hdi08b.write8(pa, val);
    272 				return;
    273 			}
    274 			if (m_hdi08c.isInRange(pa))
    275 			{
    276 				m_hdi08c.write8(pa, val);
    277 				return;
    278 			}
    279 		}
    280 
    281 		Mc68k::write8(addr, val);
    282 	}
    283 
    284 	void MqMc::dumpMemory(const char* _filename) const
    285 	{
    286 		FILE* hFile = fopen((std::string(_filename) + ".bin").c_str(), "wb");
    287 		fwrite(m_memory.data(), 1, m_memory.size(), hFile);
    288 		fclose(hFile);
    289 	}
    290 
    291 	void MqMc::dumpROM(const char* _filename) const
    292 	{
    293 		FILE* hFile = fopen((std::string(_filename) + ".bin").c_str(), "wb");
    294 		fwrite(m_romRuntimeData.data(), 1, ROM::size(), hFile);
    295 		fclose(hFile);
    296 	}
    297 
    298 	void MqMc::dumpAssembly(const uint32_t _first, const uint32_t _count)
    299 	{
    300 		std::stringstream ss;
    301 		ss << "mq_68k_" << _first << '-' << (_first + _count) << ".asm";
    302 
    303 		std::ofstream f(ss.str(), std::ios::out);
    304 
    305 		for(uint32_t i=_first; i<_first + _count;)
    306 		{
    307 			char disasm[64];
    308 			const auto opSize = disassemble(i, disasm);
    309 			f << MCHEXN(i,5) << ": " << disasm << std::endl;
    310 			if(!opSize)
    311 				++i;
    312 			else
    313 				i += opSize;
    314 		}
    315 		f.close();
    316 	}
    317 
    318 	void MqMc::onReset()
    319 	{
    320 		dumpMemory("dump_reset");
    321 	}
    322 
    323 	uint32_t MqMc::onIllegalInstruction(uint32_t opcode)
    324 	{
    325 		std::stringstream ss;
    326 		ss << "illegalInstruction_" << MCHEXN(getPC(), 8) << "_op" << MCHEXN(opcode,8);
    327 		dumpMemory(ss.str().c_str());
    328 
    329 		return Mc68k::onIllegalInstruction(opcode);
    330 	}
    331 
    332 	void MqMc::onPortEWritten()
    333 	{
    334 		processLCDandLEDs();
    335 	}
    336 
    337 	void MqMc::onPortFWritten()
    338 	{
    339 		processLCDandLEDs();
    340 	}
    341 
    342 	void MqMc::onPortGPWritten()
    343 	{
    344 		processLCDandLEDs();
    345 	}
    346 
    347 	void MqMc::onPortQSWritten()
    348 	{
    349 		const bool resetIsOutput = getPortQS().getDirection() & (1<<3);
    350 		if(resetIsOutput)
    351 		{
    352 			if(!(getPortQS().read() & (1<<3)))
    353 			{
    354 				if(!m_dspResetRequest)
    355 				{
    356 #ifdef _DEBUG
    357 					MCLOG("Request DSP RESET");
    358 #endif
    359 					m_dspResetRequest = true;
    360 					m_dspResetCompleted = false;
    361 				}
    362 			}
    363 		}
    364 		else
    365 		{
    366 			if(m_dspResetCompleted)
    367 			{
    368 				m_dspResetRequest = false;
    369 				getPortQS().writeRX(1<<3);
    370 			}
    371 		}
    372 #if SUPPORT_NMI_INTERRUPT
    373 		if(getPortQS().getDirection() & (1<<6))
    374 		{
    375 			m_dspInjectNmiRequest = (getPortQS().read() >> 6) & 1;
    376 			if(m_dspInjectNmiRequest)
    377 				int debug=0;
    378 		}
    379 #endif
    380 	}
    381 
    382 	void MqMc::processLCDandLEDs()
    383 	{
    384 		if(m_lcd.exec(getPortGP(), getPortF()))
    385 		{
    386 //			const std::string s(&m_lcd.getDdRam().front());
    387 //			if(s.find("SIG") != std::string::npos)
    388 //				dumpMemory("SIG");
    389 		}
    390 		else
    391 		{
    392 			m_leds.exec(getPortF(), getPortGP(), getPortE());
    393 		}
    394 	}
    395 }