/***************************************************************************** ** $Source: /cygdrive/d/Private/_SVNROOT/bluemsx/blueMSX/Src/Memory/romMapperMsxAudio.c,v $ ** ** $Revision: 1.20 $ ** ** $Date: 2009-07-18 14:35:59 $ ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2006 Daniel Vik ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ** ****************************************************************************** */ #include "romMapperMsxAudio.h" #include "MediaDb.h" #include "Switches.h" #include "SlotManager.h" #include "DeviceManager.h" #include "DebugDeviceManager.h" #include "IoPort.h" #include "Board.h" #include "Y8950.h" #include "MSXMidi.h" #include "MidiIO.h" #include "SaveState.h" #include "Language.h" #include #include #include #include "ArchEvent.h" #define RX_QUEUE_SIZE 256 typedef struct { MidiIO* midiIo; UInt8 command; UInt8 rxData; UInt8 status; UInt8 txBuffer; int txPending; UInt8 rxQueue[RX_QUEUE_SIZE]; int rxPending; int rxHead; void* semaphore; UInt32 charTime; BoardTimer* timerRecv; UInt32 timeRecv; BoardTimer* timerTrans; UInt32 timeTrans; } PhilipsMidi; #define STAT_RXRDY 0x01 #define STAT_TXEMPTY 0x02 #define STAT_OE 0x20 #define ST_INT 0x80 #define CMD_WRINT 0x20 #define CMD_RDINT 0x80 static void midiInCallback(PhilipsMidi* midi, UInt8* buffer, UInt32 length) { archSemaphoreWait(midi->semaphore, -1); if (midi->rxPending + length < RX_QUEUE_SIZE) { while (length--) { midi->rxQueue[midi->rxHead & (RX_QUEUE_SIZE - 1)] = *buffer++; midi->rxHead++; midi->rxPending++; } } archSemaphoreSignal(midi->semaphore); } static void onRecv(PhilipsMidi* midi, UInt32 time) { midi->timeRecv = 0; if (midi->status & STAT_RXRDY) { midi->status |= STAT_OE; } else if (midi->rxPending != 0) { archSemaphoreWait(midi->semaphore, -1); midi->rxData = midi->rxQueue[(midi->rxHead - midi->rxPending) & (RX_QUEUE_SIZE - 1)]; midi->rxPending--; archSemaphoreSignal(midi->semaphore); midi->status |= STAT_RXRDY; if (midi->command & CMD_RDINT) { boardSetInt(0x400); midi->status |= ST_INT; } } midi->timeRecv = boardSystemTime() + midi->charTime; boardTimerAdd(midi->timerRecv, midi->timeRecv); } static void onTrans(PhilipsMidi* midi, UInt32 time) { midi->timeTrans = 0; if (midi->status & STAT_TXEMPTY) { midi->txPending = 0; } else { midiIoTransmit(midi->midiIo, midi->txBuffer); midi->timeTrans = boardSystemTime() + midi->charTime; boardTimerAdd(midi->timerTrans, midi->timeTrans); midi->status |= STAT_TXEMPTY; if (midi->command & CMD_WRINT) { boardSetInt(0x400); midi->status |= ST_INT; } } } void philipsMidiReset(PhilipsMidi* midi) { midi->status = STAT_TXEMPTY; midi->txPending = 0; midi->rxPending = 0; midi->command = 0; midi->timeRecv = 0; midi->timeTrans = 0; midi->charTime = 10 * boardFrequency() / 31250; boardTimerRemove(midi->timerRecv); boardTimerRemove(midi->timerTrans); midi->timeRecv = boardSystemTime() + midi->charTime; boardTimerAdd(midi->timerRecv, midi->timeRecv); } PhilipsMidi* philipsMidiCreate() { PhilipsMidi* midi = (PhilipsMidi*)calloc(1, sizeof(PhilipsMidi)); midi->midiIo = midiIoCreate(midiInCallback, midi); midi->semaphore = archSemaphoreCreate(1); midi->timerRecv = boardTimerCreate(onRecv, midi); midi->timerTrans = boardTimerCreate(onTrans, midi); return midi; } void philipsMidiDestroy(PhilipsMidi* midi) { boardTimerDestroy(midi->timerTrans); boardTimerDestroy(midi->timerRecv); midiIoDestroy(midi->midiIo); archSemaphoreDestroy(midi->semaphore); free(midi); } UInt8 philipsMidiReadStatus(PhilipsMidi* midi) { UInt8 val = midi->status; boardClearInt(0x400); midi->status &= ~ST_INT; return val; } UInt8 philipsMidiReadData(PhilipsMidi* midi) { midi->status &= ~(STAT_RXRDY | STAT_OE); return midi->rxData; } void philipsMidiWriteCommand(PhilipsMidi* midi, UInt8 value) { int baudrate = 1; int dataBits = 8; int parityEnable = 0; int stopBits = 1; UInt64 charLength; midi->command = value; switch (value & 0x03) { case 0: baudrate = 1; break; case 1: baudrate = 16; break; case 2: baudrate = 64; break; case 3: philipsMidiReset(midi); break; } switch (value & 0x1c) { case 0: dataBits = 7; parityEnable = 1; stopBits = 2; break; case 1: dataBits = 7; parityEnable = 1; stopBits = 2; break; case 2: dataBits = 7; parityEnable = 1; stopBits = 1; break; case 3: dataBits = 7; parityEnable = 1; stopBits = 1; break; case 4: dataBits = 8; parityEnable = 0; stopBits = 2; break; case 5: dataBits = 8; parityEnable = 0; stopBits = 1; break; case 6: dataBits = 8; parityEnable = 0; stopBits = 1; break; case 7: dataBits = 8; parityEnable = 1; stopBits = 1; break; } charLength = (dataBits + parityEnable + stopBits) * baudrate; midi->charTime = (UInt32)(charLength * boardFrequency() / 500000); midi->timeRecv = boardSystemTime() + midi->charTime; boardTimerAdd(midi->timerRecv, midi->timeRecv); } void philipsMidiWriteData(PhilipsMidi* midi, UInt8 value) { if (!(midi->status & STAT_TXEMPTY)) { return; } if (midi->txPending == 0) { midiIoTransmit(midi->midiIo, value); midi->timeTrans = boardSystemTime() + midi->charTime; boardTimerAdd(midi->timerTrans, midi->timeTrans); midi->txPending = 1; } else { midi->status &= ~STAT_TXEMPTY; midi->txBuffer = value; } } typedef struct { int deviceHandle; int debugHandle; Y8950* y8950; int ioBase; UInt8* romData; UInt8 ram[0x1000]; int bankSelect; int sizeMask; PhilipsMidi* midi; int slot; int sslot; int startPage; int is_fs_ca1; } RomMapperMsxAudio; static int deviceCount = 0; static void saveState(RomMapperMsxAudio* rm) { SaveState* state = saveStateOpenForWrite("mapperMsxAudio"); saveStateSet(state, "bankSelect", rm->bankSelect); saveStateSetBuffer(state, "ram", rm->ram, sizeof(rm->ram)); saveStateClose(state); if (rm->y8950 != NULL) { y8950SaveState(rm->y8950); } } static void loadState(RomMapperMsxAudio* rm) { SaveState* state = saveStateOpenForRead("mapperMsxAudio"); rm->bankSelect = saveStateGet(state, "bankSelect", 0); saveStateGetBuffer(state, "ram", rm->ram, sizeof(rm->ram)); saveStateClose(state); if (rm->y8950 != NULL) { y8950LoadState(rm->y8950); } } static void destroy(RomMapperMsxAudio* rm) { if (rm->midi) { philipsMidiDestroy(rm->midi); } ioPortUnregister(0x00); ioPortUnregister(0x01); ioPortUnregister(0x04); ioPortUnregister(0x05); ioPortUnregister(rm->ioBase + 0); ioPortUnregister(rm->ioBase + 1); if (rm->y8950) { if (ioPortGetRef(0xc0)==rm->y8950&&ioPortGetRef(0xc1)==rm->y8950) { ioPortUnregister(0xc0); ioPortUnregister(0xc1); } if (ioPortGetRef(0xc2)==rm->y8950&&ioPortGetRef(0xc3)==rm->y8950) { ioPortUnregister(0xc2); ioPortUnregister(0xc3); } } deviceCount--; if (rm->y8950 != NULL) { y8950Destroy(rm->y8950); } if (rm->sizeMask != -1) { slotUnregister(rm->slot, rm->sslot, rm->startPage); } debugDeviceUnregister(rm->debugHandle); deviceManagerUnregister(rm->deviceHandle); if (rm->romData != NULL) { free(rm->romData); } free(rm); } static UInt8 read(RomMapperMsxAudio* rm, UInt16 address) { if (rm->bankSelect == 0 && (address & 0x3fff) >= 0x3000) { return rm->ram[(address & 0x3fff) - 0x3000]; } return rm->romData[(0x8000 * rm->bankSelect + (address & 0x7fff)) & rm->sizeMask]; } static void write(RomMapperMsxAudio* rm, UInt16 address, UInt8 value) { address &= 0x7fff; #if 0 // FS-CA1 port select, bit 0:c0/c1, bit 1:c2/c3 if (rm->is_fs_ca1&&address==0x7fff&&rm->y8950) { if (value&1) { ioPortRegister(0xc0, y8950Read, y8950Write, rm->y8950); ioPortRegister(0xc1, y8950Read, y8950Write, rm->y8950); } else if (ioPortGetRef(0xc0)==rm->y8950&&ioPortGetRef(0xc1)==rm->y8950) { ioPortUnregister(0xc0); ioPortUnregister(0xc1); } if (value&2) { ioPortRegister(0xc2, y8950Read, y8950Write, rm->y8950); ioPortRegister(0xc3, y8950Read, y8950Write, rm->y8950); } else if (ioPortGetRef(0xc2)==rm->y8950&&ioPortGetRef(0xc3)==rm->y8950) { ioPortUnregister(0xc2); ioPortUnregister(0xc3); } } #endif // bankswitch if (address==0x7ffe) rm->bankSelect = value & 3; address &= 0x3fff; if (rm->bankSelect == 0 && address >= 0x3000) { rm->ram[address - 0x3000] = value; } } static void reset(RomMapperMsxAudio* rm) { if (rm->y8950 != NULL) { y8950Reset(rm->y8950); } if (rm->midi) { philipsMidiReset(rm->midi); } // FS-CA1 write (rm,0x7ffe,0); write (rm,0x7fff,0); } static void midiWrite(RomMapperMsxAudio* rm, UInt16 ioPort, UInt8 value) { if (!rm->midi) { return; } switch (ioPort & 1) { case 0x00: philipsMidiWriteCommand(rm->midi, value); break; case 0x01: philipsMidiWriteData(rm->midi, value); break; } } static UInt8 midiRead(RomMapperMsxAudio* rm, UInt16 ioPort) { if (!rm->midi) { return 0xff; } switch (ioPort & 1) { case 0x00: return philipsMidiReadStatus(rm->midi); case 0x01: return philipsMidiReadData(rm->midi); } return 0xff; } static void getDebugInfo(RomMapperMsxAudio* rm, DbgDevice* dbgDevice) { DbgIoPorts* ioPorts; if (rm->y8950 == NULL) { return; } ioPorts = dbgDeviceAddIoPorts(dbgDevice, langDbgDevMsxAudio(), 2); dbgIoPortsAddPort(ioPorts, 0, rm->ioBase + 0, DBG_IO_READWRITE, y8950Peek(rm->y8950, 0)); dbgIoPortsAddPort(ioPorts, 1, rm->ioBase + 1, DBG_IO_READWRITE, y8950Peek(rm->y8950, 1)); ioPorts = dbgDeviceAddIoPorts(dbgDevice, langDbgDevMsxAudioMidi(), 4); dbgIoPortsAddPort(ioPorts, 0, 0x00, DBG_IO_WRITE, 0); dbgIoPortsAddPort(ioPorts, 1, 0x01, DBG_IO_WRITE, 0); dbgIoPortsAddPort(ioPorts, 2, 0x04, DBG_IO_READ, midiRead(rm, 0x04)); dbgIoPortsAddPort(ioPorts, 3, 0x05, DBG_IO_READ, midiRead(rm, 0x05)); y8950GetDebugInfo(rm->y8950, dbgDevice); } int romMapperMsxAudioCreate(const char* filename, UInt8* romData, int size, int slot, int sslot, int startPage) { DeviceCallbacks callbacks = { destroy, reset, saveState, loadState }; DebugCallbacks dbgCallbacks = { getDebugInfo, NULL, NULL, NULL }; RomMapperMsxAudio* rm; int i; rm = malloc(sizeof(RomMapperMsxAudio)); rm->deviceHandle = deviceManagerRegister(ROM_MSXAUDIO, &callbacks, rm); rm->debugHandle = debugDeviceRegister(DBGTYPE_AUDIO, langDbgDevMsxAudio(), &dbgCallbacks, rm); rm->ioBase = 0xc0 + deviceCount++ * 2; rm->romData = NULL; if (size > 0) { int pages=8; rm->is_fs_ca1 = (size == 0x20000); // meh // pages=rm->is_fs_ca1?4:8; // For FS-CA1, $8000-$FFFF is unmapped // firmware locks up, needs more testing slotRegister(slot, sslot, startPage, pages, read, read, write, destroy, rm); rm->romData = malloc(size); memcpy(rm->romData, romData, size); memset(rm->ram, 0, 0x1000); rm->bankSelect = 0; rm->sizeMask = size - 1; rm->slot = slot; rm->sslot = sslot; rm->startPage = startPage; rm->midi = NULL; if (!switchGetAudio()) { // FS-CA1 BIOS hack, ret z -> nop // not needed if port select register is emulated rm->romData[0x408e] = 0; } for (i = 0; i < pages; i++) { slotMapPage(rm->slot, rm->sslot, rm->startPage + i, NULL, 0, 0); } } rm->y8950 = NULL; if (boardGetY8950Enable()) { rm->y8950 = y8950Create(boardGetMixer()); ioPortRegister(rm->ioBase + 0, y8950Read, y8950Write, rm->y8950); ioPortRegister(rm->ioBase + 1, y8950Read, y8950Write, rm->y8950); ioPortRegister(0x00, NULL, midiWrite, rm); ioPortRegister(0x01, NULL, midiWrite, rm); ioPortRegister(0x04, midiRead, NULL, rm); ioPortRegister(0x05, midiRead, NULL, rm); } if (deviceCount == 1) { rm->midi = philipsMidiCreate(); } reset(rm); return 1; }