more apu work and added gbs music support

This commit is contained in:
FIX94 2017-05-15 02:37:48 +02:00
parent be6fefddd4
commit 096f760361
No known key found for this signature in database
GPG Key ID: CE39016A19D8EADA
10 changed files with 456 additions and 256 deletions

View File

@ -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

65
apu.c
View File

@ -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)

72
cpu.c
View File

@ -9,6 +9,8 @@
#include <stdbool.h>
#include <inttypes.h>
#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);
}

2
cpu.h
View File

@ -11,5 +11,7 @@
void cpuInit();
bool cpuCycle();
uint16_t cpuCurPC();
void cpuLoadGBS(uint8_t song);
void cpuPlayGBS();
#endif

160
main.c
View File

@ -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;
}

9
mbc.c
View File

@ -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;
}

1
mbc.h
View File

@ -17,6 +17,7 @@ enum {
MBC_TYPE_5,
MBC_TYPE_6,
MBC_TYPE_7,
MBC_TYPE_GBS,
};
typedef void (*set8FuncT)(uint16_t, uint8_t);

213
mem.c
View File

@ -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;
}

3
mem.h
View File

@ -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);

186
ppu.c
View File

@ -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;
}