From 096f760361ebf86812a3c284f0fa9ea67b9801a2 Mon Sep 17 00:00:00 2001 From: FIX94 Date: Mon, 15 May 2017 02:37:48 +0200 Subject: [PATCH] more apu work and added gbs music support --- README.md | 1 + apu.c | 65 +++++++++-------- cpu.c | 72 ++++++++++++++++-- cpu.h | 2 + main.c | 160 ++++++++++++++++++++++++---------------- mbc.c | 9 +++ mbc.h | 1 + mem.c | 213 ++++++++++++++++++++++++++++++++++-------------------- mem.h | 3 +- ppu.c | 186 ++++++++++++++++++++++++++++------------------- 10 files changed, 456 insertions(+), 256 deletions(-) diff --git a/README.md b/README.md index 22e9a0b..7db096c 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ This is still quite early in development so it cant do all that much, its essent If you want to check it out for some reason I do include a windows binary in the "Releases" tab, if you want to compile it go check out the "build" files. You will need freeglut as well as openal-soft to compile the project, it should run on most systems since it is fairly generic C code. Right now most standard GB titles using MBC1, 3 and 5 should work just fine and also save into standard .sav files. +You can also listen to .gbs files by dragging them in, changing tracks works by pressing left/right. Controls right now are keyboard only and do the following: Y/Z is A diff --git a/apu.c b/apu.c index eacc750..54a0dbe 100644 --- a/apu.c +++ b/apu.c @@ -52,9 +52,11 @@ static envelope_t p1Env, p2Env, noiseEnv; typedef struct _sweep_t { bool enabled; bool negative; + bool inNegative; uint8_t period; uint8_t divider; uint8_t shift; + uint16_t pfreq; } sweep_t; static sweep_t p1Sweep; @@ -133,6 +135,7 @@ void apuInit() p1dacenable = false; p2dacenable = false; wavdacenable = false; noisedacenable = false; noiseMode1 = false; + soundEnabled = true; } extern uint32_t cpu_oam_dma; @@ -283,30 +286,36 @@ void doEnvelopeLogic(envelope_t *env) env->vol--; } } - env->divider = env->period; + //period 0 is actually period 8! + env->divider = (env->period-1)&7; } else env->divider--; - //too slow on its own? - //env->envelope = (env->constant ? env->vol : env->decay); } -void sweepUpdateFreq(sweep_t *sw, uint16_t *freq) +void sweepUpdateFreq(sweep_t *sw, uint16_t *freq, bool update) { + if(!sw->enabled) + return; //printf("%i\n", *freq); - uint16_t inFreq = *freq; + uint16_t inFreq = sw->pfreq; uint16_t shiftVal = (inFreq >> sw->shift); - //if(sw->shift > 0) + + if(sw->negative) { - if(sw->negative) - inFreq -= shiftVal; - else - inFreq += shiftVal; + sw->inNegative = true; + inFreq -= shiftVal; } + else + inFreq += shiftVal; + if(inFreq <= 0x7FF) { - if(sw->enabled && sw->shift && sw->period) + if(sw->enabled && sw->shift && sw->period && update) + { *freq = inFreq; + sw->pfreq = inFreq; + } } else { @@ -319,11 +328,12 @@ void doSweepLogic(sweep_t *sw, uint16_t *freq) { if(sw->divider == 0) { + //printf("Divider 0\n"); if(sw->period) { - sweepUpdateFreq(sw, freq); + sweepUpdateFreq(sw, freq, true); //gameboy checks a SECOND time after updating... - uint16_t inFreq = *freq; + uint16_t inFreq = sw->pfreq; uint16_t shiftVal = (inFreq >> sw->shift); if(sw->negative) inFreq -= shiftVal; @@ -335,7 +345,8 @@ void doSweepLogic(sweep_t *sw, uint16_t *freq) p1enable = false; } } - sw->divider = sw->period; + //period 0 is actually period 8! + sw->divider = (sw->period-1)&7; } else sw->divider--; @@ -442,12 +453,8 @@ void apuSet8(uint8_t reg, uint8_t val) p1Sweep.shift = val&7; p1Sweep.period = (val>>4)&7; p1Sweep.negative = ((val&0x8) != 0); - //enabled by trigger - if(p1Sweep.enabled) - { - p1Sweep.divider = 0; - sweepUpdateFreq(&p1Sweep, &freq1); - } + if(p1Sweep.inNegative && !p1Sweep.negative) + p1enable = false; } else if(reg == 0x11) { @@ -462,8 +469,6 @@ void apuSet8(uint8_t reg, uint8_t val) if(!p1dacenable) p1enable = false; p1Env.period = val&7; - //if(p1Env.period==0) - // p1Env.period=8; p1Env.divider = p1Env.period; } else if(reg == 0x13) @@ -483,15 +488,17 @@ void apuSet8(uint8_t reg, uint8_t val) p1Cycle = 0; //trigger used to enable/disable sweep if(p1Sweep.period || p1Sweep.shift) - { - - p1Sweep.divider = 0; p1Sweep.enabled = true; - } else p1Sweep.enabled = false; + //trigger also resets divider, neg mode and frequency + p1Sweep.inNegative = false; + p1Sweep.pfreq = freq1; + //period 0 is actually period 8! + p1Sweep.divider = (p1Sweep.period-1)&7; + //if sweep shift>0, pre-calc frequency if(p1Sweep.shift) - sweepUpdateFreq(&p1Sweep, &freq1); + sweepUpdateFreq(&p1Sweep, &freq1, false); } //printf("P1 new freq %04x\n", freq1); } @@ -508,8 +515,6 @@ void apuSet8(uint8_t reg, uint8_t val) if(!p2dacenable) p2enable = false; p2Env.period = val&7; - //if(p2Env.period==0) - // p2Env.period=8; p2Env.divider = p2Env.period; } else if(reg == 0x18) @@ -588,8 +593,6 @@ void apuSet8(uint8_t reg, uint8_t val) if(!noisedacenable) noiseenable = false; noiseEnv.period=val&7; - //if(noiseEnv.period==0) - // noiseEnv.period=8; noiseEnv.divider = noiseEnv.period; } else if(reg == 0x22) diff --git a/cpu.c b/cpu.c index 8eefa4f..6ee8d6d 100644 --- a/cpu.c +++ b/cpu.c @@ -9,6 +9,8 @@ #include #include #include "cpu.h" +#include "apu.h" +#include "ppu.h" #include "mem.h" #include "input.h" @@ -17,6 +19,13 @@ #define P_FLAG_N (1<<6) #define P_FLAG_Z (1<<7) +//from main.c +extern bool gbEmuGBSPlayback; +extern uint16_t gbsLoadAddr; +extern uint16_t gbsInitAddr; +extern uint16_t gbsPlayAddr; +extern uint16_t gbsSP; + void cpuSetupActionArr(); static uint16_t sp, pc, cpuTmp16; @@ -984,6 +993,7 @@ bool firstIrq = false, secondIrq = false; bool cpuHandleIrqUpdates() { + if(gbEmuGBSPlayback) return false; if(!irqEnable) return false; uint8_t irqList = (memGetCurIrqList()); if(irqList & 1) @@ -1039,6 +1049,12 @@ void cpuGetInstruction() cpu_arr_pos = 0; return; } + if(gbEmuGBSPlayback && pc == 0x8765) + { + cpu_action_arr = cpu_nop_arr; + cpu_arr_pos = 0; + return; + } if(cpuHaltLoop) { //happens when IME=0 @@ -1377,42 +1393,42 @@ bool cpuCycle() case CPU_SP_WRITE8_PCL_DEC_PC_FROM_00: sp--; memSet8(sp, pc&0xFF); - pc = 0x00; + pc = 0x00+gbsLoadAddr; break; case CPU_SP_WRITE8_PCL_DEC_PC_FROM_08: sp--; memSet8(sp, pc&0xFF); - pc = 0x08; + pc = 0x08+gbsLoadAddr; break; case CPU_SP_WRITE8_PCL_DEC_PC_FROM_10: sp--; memSet8(sp, pc&0xFF); - pc = 0x10; + pc = 0x10+gbsLoadAddr; break; case CPU_SP_WRITE8_PCL_DEC_PC_FROM_18: sp--; memSet8(sp, pc&0xFF); - pc = 0x18; + pc = 0x18+gbsLoadAddr; break; case CPU_SP_WRITE8_PCL_DEC_PC_FROM_20: sp--; memSet8(sp, pc&0xFF); - pc = 0x20; + pc = 0x20+gbsLoadAddr; break; case CPU_SP_WRITE8_PCL_DEC_PC_FROM_28: sp--; memSet8(sp, pc&0xFF); - pc = 0x28; + pc = 0x28+gbsLoadAddr; break; case CPU_SP_WRITE8_PCL_DEC_PC_FROM_30: sp--; memSet8(sp, pc&0xFF); - pc = 0x30; + pc = 0x30+gbsLoadAddr; break; case CPU_SP_WRITE8_PCL_DEC_PC_FROM_38: sp--; memSet8(sp, pc&0xFF); - pc = 0x38; + pc = 0x38+gbsLoadAddr; break; case CPU_SP_WRITE8_PCL_DEC_PC_FROM_40: sp--; @@ -1566,3 +1582,43 @@ uint16_t cpuCurPC() { return pc; } + +void cpuPlayGBS() +{ + //push back detect pc + sp--; + memSet8(sp, 0x87); + sp--; + memSet8(sp, 0x65); + //jump to play + pc = gbsPlayAddr; + cpu_action_arr = cpu_nop_arr; + cpu_arr_pos = 0; + //printf("Playback Start at %04x\n", pc); +} +extern uint8_t gbsTMA, gbsTAC; +void cpuLoadGBS(uint8_t song) +{ + //full reset + cpuInit(); + ppuInit(); + apuInit(); + inputInit(); + memInit(false,false); + memSet8(0xFF06,gbsTMA); + memSet8(0xFF07,gbsTAC); + //set requested sp + sp = gbsSP; + //push back detect pc + sp--; + memSet8(sp, 0x87); + sp--; + memSet8(sp, 0x65); + //set song and init routine + a = song; + pc = gbsInitAddr; + //start getting instructions + cpu_action_arr = cpu_nop_arr; + cpu_arr_pos = 0; + //printf("Init Start at %04x\n", pc); +} diff --git a/cpu.h b/cpu.h index e07b4ff..3fda99a 100644 --- a/cpu.h +++ b/cpu.h @@ -11,5 +11,7 @@ void cpuInit(); bool cpuCycle(); uint16_t cpuCurPC(); +void cpuLoadGBS(uint8_t song); +void cpuPlayGBS(); #endif diff --git a/main.c b/main.c index a417963..9499cf2 100644 --- a/main.c +++ b/main.c @@ -26,7 +26,7 @@ #define DEBUG_KEY 0 #define DEBUG_LOAD_INFO 1 -static const char *VERSION_STRING = "fixGB Alpha v0.1"; +static const char *VERSION_STRING = "fixGB Alpha v0.2"; static void gbEmuDisplayFrame(void); static void gbEmuMainLoop(void); @@ -43,11 +43,15 @@ char *emuSaveName = NULL; uint8_t *textureImage = NULL; bool nesPause = false; bool ppuDebugPauseFrame = false; -bool doOverscan = true; bool gbEmuGBSPlayback = false; +bool gbsTimerMode = false; +uint16_t gbsLoadAddr = 0; +uint16_t gbsInitAddr = 0; +uint16_t gbsPlayAddr = 0; +uint16_t gbsSP = 0; +uint8_t gbsTracksTotal = 0, gbsTMA = 0, gbsTAC = 0; static bool inPause = false; -static bool inOverscanToggle = false; static bool inResize = false; #if WINDOWS_BUILD @@ -74,14 +78,66 @@ static const uint32_t visibleImg = VISIBLE_DOTS*VISIBLE_LINES*4; static uint8_t scaleFactor = 3; static uint32_t mainLoopRuns; static uint16_t mainLoopPos; +static uint8_t cpuTimer; //from input.c extern uint8_t inValReads[8]; int main(int argc, char** argv) { puts(VERSION_STRING); - if(argc >= 2 && (strstr(argv[1],".gb") != NULL || strstr(argv[1],".GB") != NULL - || strstr(argv[1],".gbc") != NULL || strstr(argv[1],".GBC") != NULL)) + if(argc >= 2 && (strstr(argv[1],".gbs") != NULL || strstr(argv[1],".GBS") != NULL)) + { + FILE *gbF = fopen(argv[1],"rb"); + if(!gbF) return EXIT_SUCCESS; + fseek(gbF,0,SEEK_END); + size_t fsize = ftell(gbF); + rewind(gbF); + uint8_t *tmpROM = malloc(fsize); + fread(tmpROM,1,fsize,gbF); + fclose(gbF); + gbsTracksTotal = tmpROM[4]; + gbsLoadAddr = (tmpROM[6])|(tmpROM[7]<<8); + gbsInitAddr = (tmpROM[8])|(tmpROM[9]<<8); + gbsPlayAddr = (tmpROM[0xA])|(tmpROM[0xB]<<8); + gbsSP = (tmpROM[0xC])|(tmpROM[0xD]<<8); + //should give more than enough room for everything + uint32_t totalROMsize = (fsize-0x70+gbsLoadAddr+0x7FFF)&(~0x7FFF); + emuGBROM = malloc(totalROMsize); + memset(emuGBROM,0xFF,totalROMsize); + memcpy(emuGBROM+gbsLoadAddr,tmpROM+0x70,fsize-0x70); + memInit(true,true); + gbsTMA = tmpROM[0xE]; + gbsTAC = tmpROM[0xF]; + if(gbsTAC&4) + { + printf("Play Timing: Timer\n"); + gbsTimerMode = true; + } + else + { + printf("Play Timing: VSync\n"); + gbsTimerMode = false; + } + if(gbsTAC&0x80) + { + printf("CPU: CGB Speed\n"); + cpuTimer = 2; + } + else + { + printf("CPU: DMG Speed\n"); + cpuTimer = 4; + } + if(tmpROM[0x10] != 0) + printf("Game: %.32s\n",(char*)(tmpROM+0x10)); + free(tmpROM); + apuInitBufs(); + //does all inits for us + memStartGBS(); + gbEmuGBSPlayback = true; + } + else if(argc >= 2 && (strstr(argv[1],".gbc") != NULL || strstr(argv[1],".GBC") != NULL + || strstr(argv[1],".gb") != NULL || strstr(argv[1],".GB") != NULL)) { FILE *gbF = fopen(argv[1],"rb"); if(!gbF) return EXIT_SUCCESS; @@ -108,12 +164,14 @@ int main(int argc, char** argv) memcpy(emuSaveName,argv[1],strlen(argv[1])+1); memcpy(emuSaveName+strlen(argv[1])-2,"sav",4); } - if(!memInit()) + if(!memInit(true,false)) { free(emuGBROM); printf("Exit...\n"); exit(EXIT_SUCCESS); } + //DMG Mode + cpuTimer = 4; apuInitBufs(); cpuInit(); ppuInit(); @@ -125,26 +183,6 @@ int main(int argc, char** argv) //printf("PRG: 0x%x bytes PRG RAM: 0x%x bytes CHR: 0x%x bytes\n", prgROMsize, emuPrgRAMsize, chrROMsize); #endif } - /*else if(argc >= 2 && (strstr(argv[1],".gbs") != NULL || strstr(argv[1],".GBS") != NULL)) - { - FILE *gbF = fopen(argv[1],"rb"); - if(!gbF) return EXIT_SUCCESS; - fseek(gbF,0,SEEK_END); - size_t fsize = ftell(gbF); - rewind(gbF); - emuGBROM = malloc(fsize); - fread(emuGBROM,1,fsize,gbF); - fclose(gbF); - emuPrgRAMsize = 0x2000; - emuPrgRAM = malloc(emuPrgRAMsize); - if(!mapperInitGBS(emuGBROM, fsize, emuPrgRAM, emuPrgRAMsize)) - { - printf("GBS init failed!\n"); - free(emuGBROM); - return EXIT_SUCCESS; - } - gbEmuGBSPlayback = true; - }*/ if(emuGBROM == NULL) return EXIT_SUCCESS; #if WINDOWS_BUILD @@ -229,6 +267,7 @@ bool emuSkipFrame = false; //static bool emuApuDoCycle = false; static uint16_t mainClock = 1; +static uint16_t cpuClock = 1; static uint16_t memClock = 1; //static uint16_t vrc7Clock = 1; @@ -244,6 +283,18 @@ static void gbEmuMainLoop(void) audioSleep(); return; } + if(cpuClock == cpuTimer) + { + //main CPU clock + if(!cpuCycle()) + { + //memDumpMainMem(); + exit(EXIT_SUCCESS); + } + cpuClock = 1; + } + else + cpuClock++; if(mainClock == 4) { if(!apuCycle()) @@ -254,12 +305,6 @@ static void gbEmuMainLoop(void) audioSleep(); return; } - //main CPU clock - if(!cpuCycle()) - { - //memDumpMainMem(); - exit(EXIT_SUCCESS); - } if(memClock == 4) { memClockTimers(); @@ -280,26 +325,31 @@ static void gbEmuMainLoop(void) apuClockTimers(); if(!ppuCycle()) exit(EXIT_SUCCESS); - if(!gbEmuGBSPlayback && ppuDrawDone()) + if(ppuDrawDone()) { - //printf("%i\n",mCycles); - //mCycles = 0; - emuRenderFrame = true; - #if (WINDOWS_BUILD && DEBUG_HZ) - emuTimesCalled++; - DWORD end = GetTickCount(); - emuTotalElapsed += end - emuFrameStart; - if(emuTotalElapsed >= 1000) + if(!gbEmuGBSPlayback) { - printf("\r%iHz ", emuTimesCalled); - emuTimesCalled = 0; - emuTotalElapsed = 0; + //printf("%i\n",mCycles); + //mCycles = 0; + emuRenderFrame = true; + #if (WINDOWS_BUILD && DEBUG_HZ) + emuTimesCalled++; + DWORD end = GetTickCount(); + emuTotalElapsed += end - emuFrameStart; + if(emuTotalElapsed >= 1000) + { + printf("\r%iHz ", emuTimesCalled); + emuTimesCalled = 0; + emuTotalElapsed = 0; + } + emuFrameStart = end; + #endif + glutPostRedisplay(); + if(ppuDebugPauseFrame) + nesPause = true; } - emuFrameStart = end; - #endif - glutPostRedisplay(); - if(ppuDebugPauseFrame) - nesPause = true; + else if(!gbsTimerMode) + cpuPlayGBS(); } } while(mainLoopPos--); @@ -437,14 +487,6 @@ static void gbEmuHandleKeyDown(unsigned char key, int x, int y) glutReshapeWindow(VISIBLE_DOTS*9, VISIBLE_LINES*9); } break; - case 'o': - case 'O': - if(!inOverscanToggle) - { - inOverscanToggle = true; - doOverscan ^= true; - } - break; default: break; } @@ -498,10 +540,6 @@ static void gbEmuHandleKeyUp(unsigned char key, int x, int y) case '7': case '8': case '9': inResize = false; break; - case 'o': - case 'O': - inOverscanToggle = false; - break; default: break; } diff --git a/mbc.c b/mbc.c index 019bbf3..7ca4407 100644 --- a/mbc.c +++ b/mbc.c @@ -27,6 +27,7 @@ static void noSet8(uint16_t addr, uint8_t val); static void mbc1Set8(uint16_t addr, uint8_t val); static void mbc3Set8(uint16_t addr, uint8_t val); static void mbc5Set8(uint16_t addr, uint8_t val); +static void gbsSet8(uint16_t addr, uint8_t val); void mbcInit(uint8_t type) { @@ -36,6 +37,8 @@ void mbcInit(uint8_t type) mbcSet8 = mbc3Set8; else if(type == MBC_TYPE_5) mbcSet8 = mbc5Set8; + else if(type == MBC_TYPE_GBS) + mbcSet8 = gbsSet8; else mbcSet8 = noSet8; } @@ -142,3 +145,9 @@ static void mbc5Set8(uint16_t addr, uint8_t val) } } } + +static void gbsSet8(uint16_t addr, uint8_t val) +{ + if(addr >= 0x2000 && addr < 0x3000) + cBank = val; +} diff --git a/mbc.h b/mbc.h index ac76353..5717e33 100644 --- a/mbc.h +++ b/mbc.h @@ -17,6 +17,7 @@ enum { MBC_TYPE_5, MBC_TYPE_6, MBC_TYPE_7, + MBC_TYPE_GBS, }; typedef void (*set8FuncT)(uint16_t, uint8_t); diff --git a/mem.c b/mem.c index 482d90d..8fc9f18 100644 --- a/mem.c +++ b/mem.c @@ -17,6 +17,7 @@ static uint8_t Ext_Mem[0x20000]; static uint8_t Main_Mem[0x2000]; static uint8_t High_Mem[0x80]; +static uint8_t gbs_prevValReads[8]; static uint8_t memLastVal; static uint8_t irqEnableReg; static uint8_t irqFlagsReg; @@ -154,88 +155,99 @@ static void memSetExtVal() break; } } - -bool memInit() +static uint8_t curGBS = 0; +extern uint8_t gbsTracksTotal; +bool memInit(bool romcheck, bool gbs) { - switch(emuGBROM[0x147]) + if(romcheck) { - case 0x00: - printf("ROM Only\n"); - mbcInit(MBC_TYPE_NONE); - bankUsed = false; - extMemUsed = false; - break; - case 0x01: - printf("ROM Only (MBC1)\n"); - mbcInit(MBC_TYPE_1); - memSetBankVal(); - extMemUsed = false; - break; - case 0x02: - printf("ROM and RAM (without save) (MBC1)\n"); - mbcInit(MBC_TYPE_1); - memSetBankVal(); - memSetExtVal(); - break; - case 0x03: - printf("ROM and RAM (with save) (MBC1)\n"); - mbcInit(MBC_TYPE_1); - memSetBankVal(); - memSetExtVal(); - memLoadSave(); - break; - case 0x11: - printf("ROM Only (MBC3)\n"); - mbcInit(MBC_TYPE_3); - memSetBankVal(); - extMemUsed = false; - break; - case 0x12: - printf("ROM and RAM (without save) (MBC3)\n"); - mbcInit(MBC_TYPE_3); - memSetBankVal(); - memSetExtVal(); - break; - case 0x13: - printf("ROM and RAM (with save) (MBC3)\n"); - mbcInit(MBC_TYPE_3); - memSetBankVal(); - memSetExtVal(); - memLoadSave(); - break; - case 0x19: - case 0x1C: - printf("ROM Only (MBC5)\n"); - mbcInit(MBC_TYPE_5); - memSetBankVal(); - extMemUsed = false; - break; - case 0x1A: - case 0x1D: - printf("ROM and RAM (without save) (MBC5)\n"); - mbcInit(MBC_TYPE_5); - memSetBankVal(); - memSetExtVal(); - break; - case 0x1B: - case 0x1E: - printf("ROM and RAM (with save) (MBC5)\n"); - mbcInit(MBC_TYPE_5); - memSetBankVal(); - memSetExtVal(); - memLoadSave(); - break; - default: - printf("Unsupported Type %02x!\n", emuGBROM[0x147]); - return false; - } - extMemUsed = (emuGBROM[0x149] > 0); - if(extMemUsed) - { - if(emuGBROM[0x149] == 1) - extMask = 0x7FF; + if(gbs) + { + printf("GBS Mode\n"); + mbcInit(MBC_TYPE_GBS); + bankUsed = true; + extMemUsed = true; + printf("8KB RAM allowed\n"); + extTotalSize = 0x2000; + extTotalMask = 0x1FFF; + extMask = 1; + memset(gbs_prevValReads,0,8); + } else - extMask = 0x1FFF; + { + switch(emuGBROM[0x147]) + { + case 0x00: + printf("ROM Only\n"); + mbcInit(MBC_TYPE_NONE); + bankUsed = false; + extMemUsed = false; + break; + case 0x01: + printf("ROM Only (MBC1)\n"); + mbcInit(MBC_TYPE_1); + memSetBankVal(); + extMemUsed = false; + break; + case 0x02: + printf("ROM and RAM (without save) (MBC1)\n"); + mbcInit(MBC_TYPE_1); + memSetBankVal(); + memSetExtVal(); + break; + case 0x03: + printf("ROM and RAM (with save) (MBC1)\n"); + mbcInit(MBC_TYPE_1); + memSetBankVal(); + memSetExtVal(); + memLoadSave(); + break; + case 0x11: + printf("ROM Only (MBC3)\n"); + mbcInit(MBC_TYPE_3); + memSetBankVal(); + extMemUsed = false; + break; + case 0x12: + printf("ROM and RAM (without save) (MBC3)\n"); + mbcInit(MBC_TYPE_3); + memSetBankVal(); + memSetExtVal(); + break; + case 0x13: + printf("ROM and RAM (with save) (MBC3)\n"); + mbcInit(MBC_TYPE_3); + memSetBankVal(); + memSetExtVal(); + memLoadSave(); + break; + case 0x19: + case 0x1C: + printf("ROM Only (MBC5)\n"); + mbcInit(MBC_TYPE_5); + memSetBankVal(); + extMemUsed = false; + break; + case 0x1A: + case 0x1D: + printf("ROM and RAM (without save) (MBC5)\n"); + mbcInit(MBC_TYPE_5); + memSetBankVal(); + memSetExtVal(); + break; + case 0x1B: + case 0x1E: + printf("ROM and RAM (with save) (MBC5)\n"); + mbcInit(MBC_TYPE_5); + memSetBankVal(); + memSetExtVal(); + memLoadSave(); + break; + default: + printf("Unsupported Type %02x!\n", emuGBROM[0x147]); + return false; + } + } } memset(Main_Mem,0,0x2000); memset(High_Mem,0,0x80); @@ -253,6 +265,13 @@ bool memInit() return true; } +void memStartGBS() +{ + curGBS = 1; + printf("Track %i/%i ", curGBS, gbsTracksTotal); + cpuLoadGBS(curGBS-1); +} + uint8_t memGet8(uint16_t addr) { uint8_t val = memLastVal; @@ -428,9 +447,40 @@ void memSaveGame() } } +extern bool gbEmuGBSPlayback; +extern bool gbsTimerMode; +extern uint8_t inValReads[8]; + //clocked at 262144 Hz void memClockTimers() { + if(gbEmuGBSPlayback) + { + if(inValReads[BUTTON_RIGHT] && !gbs_prevValReads[BUTTON_RIGHT]) + { + gbs_prevValReads[BUTTON_RIGHT] = inValReads[BUTTON_RIGHT]; + curGBS++; + if(curGBS > gbsTracksTotal) + curGBS = 1; + printf("\rTrack %i/%i ", curGBS, gbsTracksTotal); + cpuLoadGBS(curGBS-1); + } + else if(!inValReads[BUTTON_RIGHT]) + gbs_prevValReads[BUTTON_RIGHT] = 0; + + if(inValReads[BUTTON_LEFT] && !gbs_prevValReads[BUTTON_LEFT]) + { + gbs_prevValReads[BUTTON_LEFT] = inValReads[BUTTON_LEFT]; + curGBS--; + if(curGBS < 1) + curGBS = gbsTracksTotal; + printf("\rTrack %i/%i ", curGBS, gbsTracksTotal); + cpuLoadGBS(curGBS-1); + } + else if(!inValReads[BUTTON_LEFT]) + gbs_prevValReads[BUTTON_LEFT] = 0; + } + //clocked at 16384 Hz (262144 / 16 = 16384) if(divRegClock == 16) { @@ -451,7 +501,10 @@ void memClockTimers() { //printf("Timer interrupt\n"); timerRegVal = timerResetVal; - irqFlagsReg |= 4; + if(!gbEmuGBSPlayback) + irqFlagsReg |= 4; + else if(gbsTimerMode) + cpuPlayGBS(); } timerRegClock = 1; } diff --git a/mem.h b/mem.h index 8279ef6..43e3aac 100644 --- a/mem.h +++ b/mem.h @@ -8,7 +8,8 @@ #ifndef _mem_h_ #define _mem_h_ -bool memInit(); +bool memInit(bool romcheck, bool gbs); +void memStartGBS(); uint8_t memGet8(uint16_t addr); void memSet8(uint16_t addr, uint8_t val); void memSet16(uint16_t addr, uint16_t val); diff --git a/ppu.c b/ppu.c index 923e9da..5dff401 100644 --- a/ppu.c +++ b/ppu.c @@ -35,6 +35,8 @@ #define PPU_SPRITE_FLIP_Y (1<<6) #define PPU_SPRITE_PRIO (1<<7) +static uint8_t ppuDoSprites(uint8_t color, uint8_t tCol); + extern uint8_t *textureImage; static uint32_t ppuClock; @@ -183,76 +185,7 @@ bool ppuCycle() tCol = (~(PPU_Reg[7]>>(color*2)))&3; } if(PPU_Reg[0]&PPU_SPRITE_ENABLE) - { - uint8_t i; - uint8_t cSpriteAnd = (PPU_Reg[0] & PPU_SPRITE_8_16) ? 15 : 7; - for(i = 0; i < ppuOAM2pos; i++) - { - uint8_t OAMcXpos = PPU_OAM2[(i<<2)+1]; - if(OAMcXpos >= 168) - continue; - int16_t cmpPos = ((int16_t)OAMcXpos)-8; - if(cmpPos <= ppuDots && (cmpPos+8) > ppuDots) - { - uint8_t cSpriteByte3 = PPU_OAM2[(i<<2)+3]; - uint8_t tVal = PPU_OAM2[(i<<2)+2]; - uint16_t tPos = tVal*16; - - uint8_t OAMcYpos = PPU_OAM2[(i<<2)]; - uint8_t cmpYPos = OAMcYpos-16; - uint8_t cSpriteY = (PPU_Reg[4] - cmpYPos)&cSpriteAnd; - uint8_t cSpriteAdd = 0; //used to select which 8 by 16 tile - if(cSpriteY > 7) //8 by 16 select - { - cSpriteAdd = 16; - cSpriteY &= 7; - } - if(cSpriteByte3 & PPU_SPRITE_FLIP_Y) - { - cSpriteY ^= 7; - if(PPU_Reg[0] & PPU_SPRITE_8_16) - cSpriteAdd ^= 16; //8 by 16 select - } - tPos+=(cSpriteY)*2; - - ChrRegA = PPU_VRAM[(tPos+cSpriteAdd)&0x1FFF]; - ChrRegB = PPU_VRAM[(tPos+cSpriteAdd+1)&0x1FFF]; - - uint8_t cSpriteX = (ppuDots - OAMcXpos)&7; - if(cSpriteByte3 & PPU_SPRITE_FLIP_X) - cSpriteX ^= 7; - uint8_t sprCol = 0; - if(ChrRegA & (0x80>>cSpriteX)) - sprCol |= 1; - if(ChrRegB & (0x80>>cSpriteX)) - sprCol |= 2; - - //done looking at sprites, we have to - //always return the first one we find - if(sprCol != 0) - { - //sprite has highest priority, return sprite - if((cSpriteByte3 & PPU_SPRITE_PRIO) == 0) - { - if(cSpriteByte3 & PPU_SPRITE_PAL) - tCol = (~(PPU_Reg[9]>>(sprCol*2)))&3; - else - tCol = (~(PPU_Reg[8]>>(sprCol*2)))&3; - break; - } //sprite has low priority and BG is not 0, return BG - else if((color&3) != 0) - break; - //background is 0 so return sprite - if(cSpriteByte3 & PPU_SPRITE_PAL) - tCol = (~(PPU_Reg[9]>>(sprCol*2)))&3; - else - tCol = (~(PPU_Reg[8]>>(sprCol*2)))&3; - break; - } - //Sprite is 0, keep looking for sprites - } - } - } + tCol = ppuDoSprites(color, tCol); uint8_t draw = (tCol == 0) ? 0 : (tCol == 1) ? 0x55 : (tCol == 2) ? 0xAA : 0xFF; { size_t drawPos = (ppuDots*4)+(PPU_Reg[4]*160*4); @@ -319,9 +252,19 @@ uint8_t ppuGet8(uint16_t addr) { uint8_t val = 0; if(addr >= 0x8000 && addr < 0xA000) - val = PPU_VRAM[addr&0x1FFF]; + { + if(!(PPU_Reg[0] & PPU_ENABLE) || (ppuMode != 3)) + val = PPU_VRAM[addr&0x1FFF]; + else + val = 0xFF; + } else if(addr >= 0xFE00 && addr < 0xFEA0) - val = PPU_OAM[addr&0xFF]; + { + if(!(PPU_Reg[0] & PPU_ENABLE) || (ppuMode == 0) || (ppuMode == 1)) + val = PPU_OAM[addr&0xFF]; + else + val = 0xFF; + } else if(addr >= 0xFF40 && addr < 0xFF4C) { if(addr == 0xFF41) @@ -342,9 +285,15 @@ uint8_t ppuGet8(uint16_t addr) void ppuSet8(uint16_t addr, uint8_t val) { if(addr >= 0x8000 && addr < 0xA000) - PPU_VRAM[addr&0x1FFF] = val; + { + if(!(PPU_Reg[0] & PPU_ENABLE) || (ppuMode != 3)) + PPU_VRAM[addr&0x1FFF] = val; + } else if(addr >= 0xFE00 && addr < 0xFEA0) - PPU_OAM[addr&0xFF] = val; + { + if(!(PPU_Reg[0] & PPU_ENABLE) || (ppuMode == 0) || (ppuMode == 1)) + PPU_OAM[addr&0xFF] = val; + } else if(addr >= 0xFF40 && addr < 0xFF4C) { if(addr == 0xFF46) //OAM DMA @@ -362,7 +311,12 @@ void ppuSet8(uint16_t addr, uint8_t val) PPU_Reg[addr&0xF] = (val&(~7)); else //other R/W regs PPU_Reg[addr&0xF] = val; - //if(addr == 0xFF40) + if(addr == 0xFF40 && !(val&PPU_ENABLE)) + { + PPU_Reg[4] = 0; + ppuClock = 0; + ppuMode = 2; + } // printf("ppuSet8(%04x, %02x)\n",addr,val); } } @@ -405,3 +359,85 @@ bool ppuInVBlank() ppuVBlankTriggered = false; return false; } + +static uint8_t ppuDoSprites(uint8_t color, uint8_t tCol) +{ + uint8_t i; + uint8_t cSpriteAnd = (PPU_Reg[0] & PPU_SPRITE_8_16) ? 15 : 7; + uint8_t cPrioSpriteX = 0xFF; + uint8_t ChrRegA = 0, ChrRegB = 0; + for(i = 0; i < ppuOAM2pos; i++) + { + uint8_t OAMcXpos = PPU_OAM2[(i<<2)+1]; + if(OAMcXpos >= 168) + continue; + int16_t cmpPos = ((int16_t)OAMcXpos)-8; + if(cmpPos <= ppuDots && (cmpPos+8) > ppuDots) + { + uint8_t cSpriteByte3 = PPU_OAM2[(i<<2)+3]; + uint8_t tVal = PPU_OAM2[(i<<2)+2]; + uint16_t tPos = tVal*16; + + uint8_t OAMcYpos = PPU_OAM2[(i<<2)]; + uint8_t cmpYPos = OAMcYpos-16; + uint8_t cSpriteY = (PPU_Reg[4] - cmpYPos)&cSpriteAnd; + uint8_t cSpriteAdd = 0; //used to select which 8 by 16 tile + if(cSpriteY > 7) //8 by 16 select + { + cSpriteAdd = 16; + cSpriteY &= 7; + } + if(cSpriteByte3 & PPU_SPRITE_FLIP_Y) + { + cSpriteY ^= 7; + if(PPU_Reg[0] & PPU_SPRITE_8_16) + cSpriteAdd ^= 16; //8 by 16 select + } + tPos+=(cSpriteY)*2; + + ChrRegA = PPU_VRAM[(tPos+cSpriteAdd)&0x1FFF]; + ChrRegB = PPU_VRAM[(tPos+cSpriteAdd+1)&0x1FFF]; + + uint8_t cSpriteX = (ppuDots - OAMcXpos)&7; + if(cSpriteByte3 & PPU_SPRITE_FLIP_X) + cSpriteX ^= 7; + uint8_t sprCol = 0; + if(ChrRegA & (0x80>>cSpriteX)) + sprCol |= 1; + if(ChrRegB & (0x80>>cSpriteX)) + sprCol |= 2; + + //found possible candidate to display + if(sprCol != 0) + { + //there already was a sprite set with lower X + if(cPrioSpriteX < OAMcXpos) + continue; + //sprite has highest priority, return sprite + if((cSpriteByte3 & PPU_SPRITE_PRIO) == 0) + { + //sprite so far has highest prio so set color + if(cSpriteByte3 & PPU_SPRITE_PAL) + tCol = (~(PPU_Reg[9]>>(sprCol*2)))&3; + else + tCol = (~(PPU_Reg[8]>>(sprCol*2)))&3; + //keep looking if there is a lower X + cPrioSpriteX = OAMcXpos; + continue; + } //sprite has low priority and BG is not 0, keep BG for now + else if((color&3) != 0) + continue; + //background is 0 so set color + if(cSpriteByte3 & PPU_SPRITE_PAL) + tCol = (~(PPU_Reg[9]>>(sprCol*2)))&3; + else + tCol = (~(PPU_Reg[8]>>(sprCol*2)))&3; + //keep looking if there is a lower X + cPrioSpriteX = OAMcXpos; + continue; + } + //Sprite is 0, keep looking for sprites + } + } + return tCol; +}