#include namespace SuperFamicom { MSU1 msu1; #include "serialization.cpp" auto MSU1::Enter() -> void { while(true) scheduler.synchronize(), msu1.main(); } auto MSU1::main() -> void { double left = 0.0; double right = 0.0; if(io.audioPlay) { if(audioFile) { if(audioFile->end()) { if(!io.audioRepeat) { io.audioPlay = false; audioFile->seek(io.audioPlayOffset = 8); } else { audioFile->seek(io.audioPlayOffset = io.audioLoopOffset); } } else { io.audioPlayOffset += 4; left = (double)(int16)audioFile->readl(2) / 32768.0 * (double)io.audioVolume / 255.0; right = (double)(int16)audioFile->readl(2) / 32768.0 * (double)io.audioVolume / 255.0; if(dsp.mute()) left = 0, right = 0; } } else { io.audioPlay = false; } } stream->sample(left, right); step(1); synchronizeCPU(); } auto MSU1::init() -> void { } auto MSU1::load() -> void { } auto MSU1::unload() -> void { dataFile.reset(); audioFile.reset(); } auto MSU1::power() -> void { } auto MSU1::reset() -> void { create(MSU1::Enter, 44100); stream = Emulator::audio.createStream(2, 44100.0); io.dataSeekOffset = 0; io.dataReadOffset = 0; io.audioPlayOffset = 0; io.audioLoopOffset = 0; io.audioTrack = 0; io.audioVolume = 0; io.audioResumeTrack = ~0; //no resume io.audioResumeOffset = 0; io.audioError = false; io.audioPlay = false; io.audioRepeat = false; io.audioBusy = false; io.dataBusy = false; dataOpen(); audioOpen(); } auto MSU1::dataOpen() -> void { dataFile.reset(); auto document = BML::unserialize(cartridge.information.manifest.cartridge); string name = document["board/msu1/rom/name"].text(); if(!name) name = "msu1.rom"; if(dataFile = interface->open(ID::SuperFamicom, name, File::Read)) { dataFile->seek(io.dataReadOffset); } } auto MSU1::audioOpen() -> void { audioFile.reset(); auto document = BML::unserialize(cartridge.information.manifest.cartridge); string name = {"track-", io.audioTrack, ".pcm"}; for(auto track : document.find("board/msu1/track")) { if(track["number"].natural() != io.audioTrack) continue; name = track["name"].text(); break; } if(audioFile = interface->open(ID::SuperFamicom, name, File::Read)) { if(audioFile->size() >= 8) { uint32 header = audioFile->readm(4); if(header == 0x4d535531) { //"MSU1" io.audioLoopOffset = 8 + audioFile->readl(4) * 4; if(io.audioLoopOffset > audioFile->size()) io.audioLoopOffset = 8; io.audioError = false; audioFile->seek(io.audioPlayOffset); return; } } audioFile.reset(); } io.audioError = true; } auto MSU1::readIO(uint24 addr, uint8) -> uint8 { cpu.synchronizeCoprocessors(); addr = 0x2000 | (addr & 7); switch(addr) { case 0x2000: return ( Revision << 0 | io.audioError << 3 | io.audioPlay << 4 | io.audioRepeat << 5 | io.audioBusy << 6 | io.dataBusy << 7 ); case 0x2001: if(io.dataBusy) return 0x00; if(!dataFile) return 0x00; if(dataFile->end()) return 0x00; io.dataReadOffset++; return dataFile->read(); case 0x2002: return 'S'; case 0x2003: return '-'; case 0x2004: return 'M'; case 0x2005: return 'S'; case 0x2006: return 'U'; case 0x2007: return '1'; } } auto MSU1::writeIO(uint24 addr, uint8 data) -> void { cpu.synchronizeCoprocessors(); addr = 0x2000 | (addr & 7); switch(addr) { case 0x2000: io.dataSeekOffset.byte(0) = data; break; case 0x2001: io.dataSeekOffset.byte(1) = data; break; case 0x2002: io.dataSeekOffset.byte(2) = data; break; case 0x2003: io.dataSeekOffset.byte(3) = data; io.dataReadOffset = io.dataSeekOffset; if(dataFile) dataFile->seek(io.dataReadOffset); break; case 0x2004: io.audioTrack.byte(0) = data; break; case 0x2005: io.audioTrack.byte(1) = data; io.audioPlayOffset = 8; if(io.audioTrack == io.audioResumeTrack) { io.audioPlayOffset = io.audioResumeOffset; io.audioResumeTrack = ~0; //erase resume track io.audioResumeOffset = 0; } audioOpen(); break; case 0x2006: io.audioVolume = data; break; case 0x2007: if(io.audioBusy) break; if(io.audioError) break; io.audioPlay = data.bit(0); io.audioRepeat = data.bit(1); bool audioResume = data.bit(2); if(!io.audioPlay && audioResume) { io.audioResumeTrack = io.audioTrack; io.audioResumeOffset = io.audioPlayOffset; } break; } } }