/** ** Supermodel ** A Sega Model 3 Arcade Emulator. ** Copyright 2011 Bart Trzynadlowski ** ** This file is part of Supermodel. ** ** Supermodel 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 3 of the License, or (at your option) ** any later version. ** ** Supermodel 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 Supermodel. If not, see . **/ /* * SoundBoard.cpp * * Model 3 sound board. Implementation of the CSoundBoard class. This class can * only be instantiated once because it relies on global variables (the non-OOP * 68K core). */ #include "Supermodel.h" //TEMP: these need to be dynamically allocated in the memory pool static INT16 leftBuffer[44100/60],rightBuffer[44100/60]; static FILE *soundFP; /****************************************************************************** Global 68K Access Handlers The 68K must interface with globally-accessible memory maps and access handlers. Turbo68K uses the __cdecl calling convention. Note that this is not explicitly defined for the Turbo68K functions themselves but in case Turbo68K crashes, this may be the culprit. ******************************************************************************/ // Prototypes for access handlers #ifdef SUPERMODEL_SOUND static UINT8 __cdecl UnknownRead8(UINT32); static UINT16 __cdecl UnknownRead16(UINT32); static UINT32 __cdecl UnknownRead32(UINT32); static void __cdecl UnknownWrite8(UINT32, UINT8); static void __cdecl UnknownWrite16(UINT32, UINT16); static void __cdecl UnknownWrite32(UINT32, UINT32); // Memory maps for 68K struct TURBO68K_FETCHREGION mapFetch[] = { { 0x000000, 0x0FFFFF, NULL }, // SCSP1 RAM { 0x200000, 0x2FFFFF, NULL }, // SCSP2 RAM { 0x600000, 0x67FFFF, NULL }, // program ROM { -1, -1, NULL } }; struct TURBO68K_DATAREGION mapRead8[] = { { 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM { 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM { 0x600000, 0x67FFFF, NULL, NULL }, // program ROM { 0x800000, 0x9FFFFF, NULL, NULL }, // sample ROM (low 2 MB) { 0xA00000, 0xDFFFFF, NULL, NULL }, // sample ROM (bank) { 0xE00000, 0xFFFFFF, NULL, NULL }, // sample ROM (bank) { 0x100000, 0x10FFFF, NULL, SCSP_Master_r8 }, { 0x300000, 0x30FFFF, NULL, SCSP_Slave_r8 }, { 0x000000, 0xFFFFFF, NULL, UnknownRead8 }, { -1, -1, NULL, NULL } }; struct TURBO68K_DATAREGION mapRead16[] = { { 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM { 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM { 0x600000, 0x67FFFF, NULL, NULL }, // program ROM { 0x800000, 0x9FFFFF, NULL, NULL }, // sample ROM (low 2 MB) { 0xA00000, 0xDFFFFF, NULL, NULL }, // sample ROM (bank) { 0xE00000, 0xFFFFFF, NULL, NULL }, // sample ROM (bank) { 0x100000, 0x10FFFF, NULL, SCSP_Master_r16 }, { 0x300000, 0x30FFFF, NULL, SCSP_Slave_r16 }, { 0x000000, 0xFFFFFF, NULL, UnknownRead16 }, { -1, -1, NULL, NULL } }; struct TURBO68K_DATAREGION mapRead32[] = { { 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM { 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM { 0x600000, 0x67FFFF, NULL, NULL }, // program ROM { 0x800000, 0x9FFFFF, NULL, NULL }, // sample ROM (low 2 MB) { 0xA00000, 0xDFFFFF, NULL, NULL }, // sample ROM (bank) { 0xE00000, 0xFFFFFF, NULL, NULL }, // sample ROM (bank) { 0x100000, 0x10FFFF, NULL, SCSP_Master_r32 }, { 0x300000, 0x30FFFF, NULL, SCSP_Slave_r32 }, { 0x000000, 0xFFFFFF, NULL, UnknownRead32 }, { -1, -1, NULL, NULL } }; struct TURBO68K_DATAREGION mapWrite8[] = { { 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM { 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM { 0x100000, 0x10FFFF, NULL, SCSP_Master_w8 }, { 0x300000, 0x30FFFF, NULL, SCSP_Slave_w8 }, { 0x000000, 0xFFFFFF, NULL, UnknownWrite8 }, { -1, -1, NULL, NULL } }; struct TURBO68K_DATAREGION mapWrite16[] = { { 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM { 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM { 0x100000, 0x10FFFF, NULL, SCSP_Master_w16 }, { 0x300000, 0x30FFFF, NULL, SCSP_Slave_w16 }, { 0x000000, 0xFFFFFF, NULL, UnknownWrite16 }, { -1, -1, NULL, NULL } }; struct TURBO68K_DATAREGION mapWrite32[] = { { 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM { 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM { 0x100000, 0x10FFFF, NULL, SCSP_Master_w32 }, { 0x300000, 0x30FFFF, NULL, SCSP_Slave_w32 }, { 0x000000, 0xFFFFFF, NULL, UnknownWrite32 }, { -1, -1, NULL, NULL } }; static UINT8 __cdecl UnknownRead8(UINT32 addr) { printf("68K read from %06X (byte)\n", addr); return 0; } static UINT16 __cdecl UnknownRead16(UINT32 addr) { printf("68K read from %06X (word)\n", addr); return 0; } static UINT32 __cdecl UnknownRead32(UINT32 addr) { printf("68K read from %06X (longword)\n", addr); return 0; } static void __cdecl UnknownWrite8(UINT32 addr, UINT8 data) { printf("68K wrote %06X=%02X\n", addr, data); } static void __cdecl UnknownWrite16(UINT32 addr, UINT16 data) { printf("68K wrote %06X=%04X\n", addr, data); } static void __cdecl UnknownWrite32(UINT32 addr, UINT32 data) { printf("68K wrote %06X=%08X\n", addr, data); } #endif /****************************************************************************** Emulation Functions ******************************************************************************/ int irqLine = 0; // SCSP callback for generating IRQs void SCSP68KIRQCallback(int irqNum) { #ifdef SUPERMODEL_SOUND //printf("IRQ: %d\n", irqNum); irqLine = irqNum; //Turbo68KInterrupt(irqNum, TURBO68K_AUTOVECTOR); #endif } // SCSP callback for running the 68K int SCSP68KRunCallback(int numCycles) { #ifdef SUPERMODEL_SOUND for (int i = 0; i < numCycles; i++) { if (irqLine) Turbo68KInterrupt(irqLine,TURBO68K_AUTOVECTOR); Turbo68KRun(1); } return numCycles; //Turbo68KRun(numCycles); //return Turbo68KGetElapsedCycles(); #else return numCycles; #endif } void CSoundBoard::WriteMIDIPort(UINT8 data) { #ifdef SUPERMODEL_SOUND SCSP_MidiIn(data); #endif } void CSoundBoard::RunFrame(void) { #ifdef SUPERMODEL_SOUND SCSP_Update(); // Output to binary file INT16 s; for (int i = 0; i < 44100/60; i++) { s = ((UINT16)leftBuffer[i]>>8) | ((leftBuffer[i]&0xFF)<<8); fwrite(&s, sizeof(INT16), 1, soundFP); // left channel s = ((UINT16)rightBuffer[i]>>8) | ((rightBuffer[i]&0xFF)<<8); fwrite(&s, sizeof(INT16), 1, soundFP); // right channel } #endif } void CSoundBoard::Reset(void) { #ifdef SUPERMODEL_SOUND memcpy(ram1, soundROM, 16); // copy 68K vector table Turbo68KReset(); /* printf("68K PC=%06X\n", Turbo68KReadPC()); for (int i = 0; i < 1000000; i++) { //printf("%06X\n", Turbo68KReadPC()); Turbo68KRun(1); } */ #endif DebugLog("Sound Board Reset\n"); } /****************************************************************************** Configuration, Initialization, and Shutdown ******************************************************************************/ // Offsets of memory regions within sound board's pool #define OFFSET_RAM1 0 // 1 MB SCSP1 RAM #define OFFSET_RAM2 0x100000 // 1 MB SCSP2 RAM #define MEMORY_POOL_SIZE (0x100000+0x100000) BOOL CSoundBoard::Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr, CIRQ *ppcIRQObjectPtr, unsigned soundIRQBit) { #ifdef SUPERMODEL_SOUND float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000; // Attach IRQ controller ppcIRQ = ppcIRQObjectPtr; ppcSoundIRQBit = soundIRQBit; // Receive sound ROMs soundROM = soundROMPtr; sampleROM = sampleROMPtr; // Allocate all memory for RAM memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE]; if (NULL == memoryPool) return ErrorLog("Insufficient memory for sound board (needs %1.1f MB).", memSizeMB); memset(memoryPool, 0, MEMORY_POOL_SIZE); // Set up memory pointers ram1 = &memoryPool[OFFSET_RAM1]; ram2 = &memoryPool[OFFSET_RAM2]; // Initialize 68K core mapFetch[0].ptr = mapRead8[0].ptr = mapRead16[0].ptr = mapRead32[0].ptr = mapWrite8[0].ptr = mapWrite16[0].ptr = mapWrite32[0].ptr = (UINT32)ram1 - mapFetch[0].base;; mapFetch[1].ptr = mapRead8[1].ptr = mapRead16[1].ptr = mapRead32[1].ptr = mapWrite8[1].ptr = mapWrite16[1].ptr = mapWrite32[1].ptr = (UINT32)ram2 - mapFetch[1].base; mapFetch[2].ptr = mapRead8[2].ptr = mapRead16[2].ptr = mapRead32[2].ptr = (UINT32)soundROM - mapFetch[2].base; mapRead8[3].ptr = mapRead16[3].ptr = mapRead32[3].ptr = (UINT32)&sampleROM[0x000000] - mapRead8[3].base; mapRead8[4].ptr = mapRead16[4].ptr = mapRead32[4].ptr = (UINT32)&sampleROM[0x200000] - mapRead8[4].base; mapRead8[5].ptr = mapRead16[5].ptr = mapRead32[5].ptr = (UINT32)&sampleROM[0x600000] - mapRead8[5].base; Turbo68KInit(); Turbo68KSetFetch(mapFetch, NULL); Turbo68KSetReadByte(mapRead8, NULL); Turbo68KSetReadWord(mapRead16, NULL); Turbo68KSetReadLong(mapRead32, NULL); Turbo68KSetWriteByte(mapWrite8, NULL); Turbo68KSetWriteWord(mapWrite16, NULL); Turbo68KSetWriteLong(mapWrite32, NULL); // Initialize SCSPs SCSP_SetBuffers(leftBuffer, rightBuffer, 44100/60); SCSP_SetCB(SCSP68KRunCallback, SCSP68KIRQCallback, ppcIRQ, ppcSoundIRQBit); SCSP_Init(2); SCSP_SetRAM(0, ram1); SCSP_SetRAM(1, ram2); // Binary logging soundFP = fopen("sound.bin","wb"); // delete existing file fclose(soundFP); soundFP = fopen("sound.bin","ab"); // append mode #endif return OKAY; } CSoundBoard::CSoundBoard(void) { memoryPool = NULL; ram1 = NULL; ram2 = NULL; DebugLog("Built Sound Board\n"); } static void Reverse16(UINT8 *buf, unsigned size) { unsigned i; UINT8 tmp; for (i = 0; i < size; i += 2) { tmp = buf[i+0]; buf[i+0] = buf[i+1]; buf[i+1] = tmp; } } CSoundBoard::~CSoundBoard(void) { #ifdef SUPERMODEL_SOUND // close binary log file fclose(soundFP); //#if 0 FILE *fp; Reverse16(ram1, 0x100000); Reverse16(ram2, 0x100000); fp = fopen("scspRAM1", "wb"); if (NULL != fp) { fwrite(ram1, sizeof(UINT8), 0x100000, fp); fclose(fp); printf("dumped %s\n", "scspRAM1"); } fp = fopen("scspRAM2", "wb"); if (NULL != fp) { fwrite(ram2, sizeof(UINT8), 0x100000, fp); fclose(fp); printf("dumped %s\n", "scspRAM2"); } //#endif SCSP_Deinit(); if (memoryPool != NULL) { delete [] memoryPool; memoryPool = NULL; } ram1 = NULL; ram2 = NULL; #endif DebugLog("Destroyed Sound Board\n"); }