| #include <iostream> |
| #include <fstream> |
| #include <sstream> |
| #include <string> |
| #include <cstdio> |
| #include "Audio.h" |
| #include "psid.h" |
| #include "CbmTune.h" |
| #include "Tedmem.h" |
| #include "Cpu.h" |
| #include "Sid.h" |
| |
| #define MAX_BUFFER_SIZE 0x10000 |
| |
| static uint8_t inputBuffer[MAX_BUFFER_SIZE]; |
| static const unsigned int playerStartAddress = 0xfe00; |
| static unsigned char psidPlayer[] = { |
| 0xA9, 0x00 // song nr (offset 2) |
| ,0x20 , |
| 0x33 , 0xfe, // song init (offset 4) |
| 0x78 , 0xA9 , 0x00 , 0x8D , |
| 0x0B , 0xFF , 0xA9 , 0x38 , |
| 0xCD , 0x1D , 0xFF , 0xD0 , |
| 0xFB , 0xCE , 0x19 , 0xFF , |
| 0x20 , 0x33 , 0xfe // song play (offset 23) |
| , 0xEE , 0x19 , 0xFF , 0x4C , 0x0B , 0xfe, |
| 0x00 , 0x60 // fake RTS at $FE33 |
| }; |
| |
| static unsigned char rsidPlayer[] = { |
| 0xA9, 0x00, // song nr (offset 2) |
| 0x20, |
| 0xea, 0xe2, // song init (offset 3) |
| 0x18, 0x90, 0xfe // song init (offset 5) |
| }; |
| |
| static unsigned char prgPlayer[] = { |
| 0x20, 0xbe, 0x8b, |
| 0x4c, 0xdc, 0x8b |
| }; |
| |
| static TED *ted; |
| static CPU *cpu; |
| static Audio *player; |
| static PsidHeader psidHdr; |
| static int playState = 0; |
| |
| PsidHeader &getPsidHeader() |
| { |
| return psidHdr; |
| } |
| |
| unsigned int parsePsid(uint8_t *buf, PsidHeader &psidHdr_) |
| { |
| char *buffer = (char *) buf; |
| |
| if (buf) { |
| memset(psidHdr_.title, 0, sizeof(psidHdr_.title)); |
| strncpy(psidHdr_.title, buffer + PSID_NAME, 32); |
| psidHdr_.title[PSID_NAME + 32] = 0; |
| strncpy(psidHdr_.author, buffer + PSID_AUTHOR, 32); |
| psidHdr_.author[PSID_AUTHOR + 32] = 0; |
| strncpy(psidHdr_.copyright, buffer + PSID_COPYRIGHT, 32); |
| psidHdr_.copyright[PSID_COPYRIGHT + 32] = 0; |
| } else { |
| memset(psidHdr_.title, 0, sizeof(psidHdr_.title)); |
| memset(psidHdr_.author, 0, 32); |
| memset(psidHdr_.copyright, 0, 32); |
| memset(psidHdr_.copyright, 0, 32); |
| } |
| return 0; |
| } |
| |
| void getPsidProperties(PsidHeader &psidHdr_, char *os) |
| { |
| char temp[256]; |
| |
| if (!psidHdr_.tracks) { |
| strcat(os, ""); |
| return; |
| } |
| std::string mType; |
| switch (psidHdr_.type) { |
| default: |
| mType = "Other"; |
| break; |
| case 0: |
| mType = "PSID"; |
| break; |
| case 1: |
| mType = "RSID"; |
| break; |
| case 2: |
| mType = "CBM8M"; |
| break; |
| } |
| sprintf(os, "Type: %s\r\n", mType.c_str()); |
| sprintf(temp, "Chip: %s\r\n", psidHdr_.model); |
| strcat(os, temp); |
| sprintf(temp, "Module: %s\r\n", psidHdr_.title); |
| strcat(os, temp); |
| sprintf(temp, "Author: %s\r\n", psidHdr_.author); |
| strcat(os, temp); |
| sprintf(temp, "Released: %s\r\n", psidHdr_.copyright); |
| strcat(os, temp); |
| sprintf(temp, "Total tunes: %u\r\n", psidHdr_.tracks); |
| strcat(os, temp); |
| sprintf(temp, "Default tune: %u\r\n", psidHdr_.defaultTune); |
| strcat(os, temp); |
| sprintf(temp, "Init : $%04X\r\n", psidHdr_.initAddress); |
| strcat(os, temp); |
| sprintf(temp, "Play address: $%04X\r\n", psidHdr_.replayAddress); |
| strcat(os, temp); |
| } |
| |
| void printPsidInfo(PsidHeader &psidHdr_) |
| { |
| char output[1024]; |
| getPsidProperties(psidHdr_, output); |
| std::cout << std::string(output) << std::endl; |
| } |
| |
| unsigned int readFile(char *fName, uint8_t **bufferPtr, size_t *bLen) |
| { |
| std::FILE *file = (std::FILE *) 0; |
| |
| if (!fName) |
| return 2; |
| |
| try { |
| size_t flen; |
| file = std::fopen(fName, "rb"); |
| if (!file) |
| return 3; |
| std::fseek(file, 0, SEEK_END); |
| flen = std::ftell(file); |
| if (flen > MAX_BUFFER_SIZE) flen = MAX_BUFFER_SIZE; |
| std::fseek(file, 0, SEEK_SET); |
| *bufferPtr = inputBuffer; |
| size_t r = std::fread(*bufferPtr, flen, 1, file); |
| *bLen = flen; |
| fclose(file); |
| } catch(char *str) { |
| std::cerr << "Error opening " << fName << std::endl; |
| std::cerr << " exception: " << str << std::endl; |
| return 1; |
| } |
| return 0; |
| } |
| |
| bool psidChangeTrack(int direction) |
| { |
| if (direction > 0) { |
| if (psidHdr.tracks > psidHdr.current) { |
| psidPlayer[1] += direction; |
| rsidPlayer[1] += direction; |
| psidHdr.current += direction; |
| } else { |
| std::cerr << "No more tracks." << std::endl; |
| return false; |
| } |
| } else { |
| if (1 < psidHdr.current) { |
| psidPlayer[1] += direction; |
| rsidPlayer[1] += direction; |
| psidHdr.current += direction; |
| } else { |
| std::cerr << "No more tracks." << std::endl; |
| return false; |
| } |
| } |
| if (psidHdr.type == 1 || (psidHdr.type == 2 && !psidHdr.replayAddress)) { |
| ted->writeProtectedPlayerMemory(playerStartAddress, rsidPlayer, sizeof(rsidPlayer)); |
| } else { |
| ted->writeProtectedPlayerMemory(playerStartAddress, psidPlayer, sizeof(psidPlayer)); |
| } |
| cpu->setPC(playerStartAddress); |
| return true; |
| } |
| |
| void machineReset() |
| { |
| ted->forcedReset(); |
| cpu->Reset(); |
| } |
| |
| TED *machineInit(unsigned int sampleRate, unsigned int filterOrder) |
| { |
| ted = new TED; |
| ted->initFilter(sampleRate, filterOrder); |
| cpu = new CPU(ted, ted->Ram + 0xff09, ted->Ram + 0x100); |
| ted->cpuptr = cpu; |
| ted->loadroms(); |
| machineReset(); |
| return ted; |
| } |
| |
| void tedPlaySetFilterOrder(unsigned int filterOrder) |
| { |
| if (ted) { |
| ted->setFilterOrder(filterOrder); |
| } |
| } |
| |
| void dumpMem(std::string name) |
| { |
| FILE *fp = fopen(name.c_str(), "wb"); |
| if (!fp) |
| return; |
| for(unsigned int i = 0; i <= 0xffff; i++) { |
| fputc(ted->Read(i), fp); |
| } |
| fclose(fp); |
| } |
| |
| void machineDoSomeFrames(unsigned int count) |
| { |
| if (player) |
| player->sleep(count * 20); |
| } |
| |
| void machineShutDown() |
| { |
| if(ted) { |
| delete ted; |
| ted = 0; |
| } |
| if (cpu) { |
| delete cpu; |
| cpu = 0; |
| } |
| } |
| |
| void tedplayPause() |
| { |
| if (player && playState) { |
| player->pause(); |
| player->lock(); |
| } |
| playState = 0; |
| } |
| |
| void tedplayPlay() |
| { |
| if (player) { |
| player->unlock(); |
| player->play(); |
| playState = 1; |
| } |
| } |
| |
| void tedplayStop() |
| { |
| if (player && playState) { |
| player->pause(); |
| player->lock(); |
| } |
| cpu->setPC(playerStartAddress); |
| playState = 0; |
| } |
| |
| unsigned int tedplayGetSecondsPlayed() |
| { |
| unsigned int sec = ted->getTimeSinceLastReset(); |
| return sec; |
| } |
| |
| void tedPlayResetCycleCounter() |
| { |
| ted->resetCycleCounter(); |
| } |
| |
| int tedPlayGetState() |
| { |
| return playState; |
| } |
| |
| unsigned int tedPlayGetWaveform(unsigned int channel) |
| { |
| if (ted) { |
| return ted->getWaveForm(channel); |
| } |
| return 0; |
| } |
| |
| void tedPlaySetWaveform(unsigned int channel, unsigned int wave) |
| { |
| bool wasPlaying = (playState == 1); |
| |
| if (wasPlaying) |
| tedplayPause(); |
| if (ted) { |
| ted->selectWaveForm(channel, wave); |
| } |
| if (wasPlaying) |
| tedplayPlay(); |
| } |
| |
| void tedplayClose() |
| { |
| if (player) { |
| player->stop(); |
| delete player; |
| player = 0; |
| } |
| machineShutDown(); |
| } |
| |
| void tedPlayGetInfo(void *file, PsidHeader &hdr) |
| { |
| unsigned char buf[256]; |
| FILE *fp = reinterpret_cast<FILE *>(file); |
| |
| memset(hdr.title, 0, sizeof(hdr.title)); |
| strcpy(hdr.title, "Unknown"); |
| memset(hdr.author, 0, sizeof(hdr.author)); |
| strcpy(hdr.author, "Unknown"); |
| memset(hdr.copyright, 0, sizeof(hdr.copyright)); |
| strcpy(hdr.copyright, "Unknown"); |
| |
| if (fread(buf, 1, 256, fp) >= 64) { |
| if (!strncmp((const char *) buf + 1, "SID", 3)) { |
| parsePsid(buf, hdr); |
| hdr.loadAddress = buf[PSID_START + 1] + (buf[PSID_START] << 8); |
| if (buf[0] == 'P') { |
| hdr.typeName = "PSID"; |
| } else { |
| hdr.typeName = "RSID"; |
| } |
| } else if (!strncmp((const char *) buf, "CBM8M", 5)) { |
| CbmTune tune; |
| //tune.parse( |
| hdr.typeName = "CBM8M"; |
| } else { |
| hdr.typeName = "PRG"; |
| hdr.loadAddress = buf[0] + (buf[1] << 8); |
| } |
| } |
| } |
| |
| int tedplayMain(char *fileName, Audio *player_) |
| { |
| size_t bufLength; |
| unsigned char *buf = 0; |
| |
| if (!player) { |
| player = player_; |
| if (!player) |
| return 1; |
| } |
| |
| if (!readFile(fileName, &buf, &bufLength)) { |
| |
| tedplayPause(); |
| machineReset(); |
| tedplayPlay(); |
| // if we do not flush the buffer, the reset may not get executed |
| player->flush(); |
| // let it run so that the reset sequence can finish |
| player->sleep(150); |
| tedplayStop(); |
| |
| psidHdr.fileName = fileName; |
| |
| if (!strncmp((const char *) buf + 1, "SID", 3)) { |
| psidHdr.loadAddress = buf[PSID_START + 1] + (buf[PSID_START] << 8); |
| unsigned int corr = 0; |
| // zero load address means PRG module |
| if (!psidHdr.loadAddress) { |
| psidHdr.loadAddress = buf[PSID_MAX_HEADER_LENGTH] + (buf[PSID_MAX_HEADER_LENGTH + 1] << 8); |
| corr = 2; |
| } |
| unsigned short initAddr = buf[PSID_INIT + 1] + (buf[PSID_INIT] << 8); |
| unsigned short replayAddr = buf[PSID_MAIN + 1] + (buf[PSID_MAIN] << 8); |
| // zero init address means equal to load address |
| psidHdr.initAddress = initAddr ? initAddr : psidHdr.loadAddress; |
| // 0 means an IRQ handler will be installed in the init routine |
| // (most likely nothing happens) |
| psidHdr.replayAddress = replayAddr ? replayAddr : 0xfe33; |
| psidHdr.defaultTune = readPsid16(buf, PSID_DEFSONG); |
| psidHdr.current = psidHdr.defaultTune; |
| psidHdr.version = readPsid16(buf, PSID_VERSION); |
| psidHdr.tracks = readPsid16(buf, PSID_NUMBER); |
| parsePsid(buf, psidHdr); |
| |
| // setup the SID card |
| SIDsound *sid = ted->getSidCard(); |
| if (sid) { |
| // Disable the ROM & IRQ's, so almost the entire memory is RAM |
| // more SID's are likely to play |
| ted->cpuptr->setST(ted->cpuptr->getST() | 4); |
| ted->ChangeMemBankSetup(true); |
| // v2 SID files have flags |
| if (psidHdr.version == 2 && (readPsid16(buf, PSID_FLAGS) & 0x20)) { |
| sid->setModel(SID8580); |
| strcpy(psidHdr.model, "SID8580"); |
| } else { |
| sid->setModel(SID6581); |
| strcpy(psidHdr.model, "SID6581"); |
| } |
| } else { |
| strcpy(psidHdr.model, "TED8360?"); |
| } |
| if (buf[0] == 'P') { // PSID |
| psidHdr.type = 0; |
| psidPlayer[1] = psidHdr.defaultTune - 1; |
| psidPlayer[3] = psidHdr.initAddress & 0xff; |
| psidPlayer[4] = psidHdr.initAddress >> 8; |
| psidPlayer[22] = psidHdr.replayAddress & 0xff; |
| psidPlayer[23] = psidHdr.replayAddress >> 8; |
| ted->writeProtectedPlayerMemory(playerStartAddress, psidPlayer, sizeof(psidPlayer)); |
| } else if (buf[0] == 'R') { // RSID |
| psidHdr.type = 1; |
| rsidPlayer[1] = psidHdr.defaultTune - 1; |
| rsidPlayer[3] = psidHdr.initAddress & 0xff; |
| rsidPlayer[4] = psidHdr.initAddress >> 8; |
| if (replayAddr != 0) { |
| rsidPlayer[5] = 0x4C; // JMP |
| rsidPlayer[6] = psidHdr.replayAddress & 0xff; |
| rsidPlayer[7] = psidHdr.replayAddress >> 8; |
| } else { |
| rsidPlayer[5] = 0x18; // CLC |
| rsidPlayer[6] = 0x90; // BCC *-2 |
| rsidPlayer[7] = 0xfe; |
| } |
| ted->writeProtectedPlayerMemory(playerStartAddress, rsidPlayer, sizeof(rsidPlayer)); |
| } |
| ted->injectCodeToRAM(psidHdr.loadAddress, buf + PSID_MAX_HEADER_LENGTH + corr, |
| bufLength - PSID_MAX_HEADER_LENGTH - corr); |
| |
| } else if (!strncmp((const char *) buf, "CBM8M", 5)) { |
| CbmTune tune; |
| tune.parse(fileName); |
| psidHdr.type = 2; |
| strcpy(psidHdr.title, tune.getName()); |
| strcpy(psidHdr.author, tune.getAuthor()); |
| strcpy(psidHdr.copyright, tune.getReleaseDate()); |
| unsigned int dataLen = tune.getDumpLength(0); |
| psidHdr.loadAddress = tune.getLoadAddress(0); |
| unsigned short initAddr = tune.getInitAddress(0); |
| unsigned short replayAddr = tune.getPlayAddress(0); |
| unsigned char *playerData; |
| unsigned int playerLength; |
| psidHdr.version = 1; |
| psidHdr.tracks = tune.getNrOfSubtunes() + 1; |
| psidHdr.defaultTune = tune.getDefaultSubtune() + 1; |
| psidHdr.current = psidHdr.defaultTune; |
| psidHdr.initAddress = initAddr; |
| psidHdr.replayAddress = replayAddr; |
| if (replayAddr) { |
| psidPlayer[1] = psidHdr.defaultTune; |
| psidPlayer[3] = psidHdr.loadAddress & 0xff; |
| psidPlayer[4] = psidHdr.loadAddress >> 8; |
| psidPlayer[22] = psidHdr.replayAddress & 0xff; |
| psidPlayer[23] = psidHdr.replayAddress >> 8; |
| playerLength = 33; |
| playerData = psidPlayer; |
| } else { |
| rsidPlayer[1] = psidHdr.defaultTune; |
| rsidPlayer[2] = 0x4C; // JMP |
| rsidPlayer[3] = psidHdr.loadAddress & 0xff; |
| rsidPlayer[4] = psidHdr.loadAddress >> 8; |
| playerLength = 8; |
| playerData = rsidPlayer; |
| } |
| strcpy(psidHdr.model, "TED8360"); |
| |
| ted->injectCodeToRAM(psidHdr.loadAddress, tune.getBinaryData(0), dataLen - 2); |
| ted->writeProtectedPlayerMemory(playerStartAddress, playerData, playerLength); |
| } else { // PRG |
| psidHdr.type = -1; |
| psidHdr.version = 0; |
| psidHdr.tracks = psidHdr.defaultTune = 1; |
| psidHdr.current = 1; |
| psidHdr.replayAddress = playerStartAddress; |
| strcpy(psidHdr.title, fileName); |
| strcpy(psidHdr.author, "Unknown"); |
| strcpy(psidHdr.copyright, "Unknown"); |
| strcpy(psidHdr.model, "Unknown"); |
| psidHdr.loadAddress = buf[0] + (buf[1] << 8); |
| |
| ted->injectCodeToRAM(psidHdr.loadAddress, buf + 2, bufLength - 2); |
| ted->writeProtectedPlayerMemory(playerStartAddress, prgPlayer, sizeof(prgPlayer)); |
| SIDsound *sid = ted->getSidCard(); |
| if (sid) |
| sid->setModel(SID8580); |
| } |
| #if 1 |
| cpu->setPC(playerStartAddress); |
| #else |
| char start[64]; |
| ted->copyToKbBuffer("M\317:\r"); |
| player->sleep(200); |
| sprintf(start, "G%04X\r", playerStartAddress); |
| ted->copyToKbBuffer(start); |
| #endif |
| try { |
| tedplayPlay(); |
| } catch (char *txt) { |
| std::cerr << "Exception: " << txt << std::endl; |
| exit(2); |
| } |
| #if 0 //_DEBUG |
| //Sleep(3000); |
| if (fileName) { |
| std::string fn = fileName; |
| fn += ".bin"; |
| dumpMem(fn); |
| } |
| #endif |
| } |
| return 0; |
| } |
| |
| void tedPlaySetVolume(unsigned int masterVolume) |
| { |
| if (player) |
| player->pause(); |
| if (ted) |
| ted->setMasterVolume(masterVolume); |
| if (player && tedPlayGetState() == 1) |
| player->play(); |
| } |
| |
| void tedPlaySetSpeed(unsigned int speedPct) |
| { |
| if (ted) |
| ted->setplaybackSpeed(speedPct); |
| } |
| |
| void tedPlayGetSongs(unsigned int ¤t, unsigned int &total) |
| { |
| current = psidHdr.current; |
| total = psidHdr.tracks; |
| } |
| |
| bool tedPlayIsChannelEnabled(unsigned int channel) |
| { |
| return ted->isChannelEnabled(channel); |
| } |
| |
| void tedPlayChannelEnable(unsigned int channel, unsigned int enable) |
| { |
| ted->enableChannel(channel, enable); |
| } |
| |
| void tedPlaySidEnable(bool enable) |
| { |
| ted->enableSidCard(enable); |
| } |
| |
| bool tedPlayCreateWav(const char *fileName) |
| { |
| return player->createWav(fileName); |
| } |
| |
| void tedPlayCloseWav() |
| { |
| if (player) |
| player->closeWav(); |
| } |