-fixed playback for some gbs music files

-implemented APU zombie mode for volume control
-corrected several CPU instruction timing details
-properly implemented OAM DMA with all its timings etc
-corrected some memory bugs and improved timer accuracy
-added basic serial I/O control support as some games rely on its interrupt
-improved PPU accuracy and implemented STAT DMC bug required for some games
This commit is contained in:
FIX94 2017-09-09 01:48:34 +02:00
parent fca433f1a0
commit 89d2f6656a
No known key found for this signature in database
GPG Key ID: CE39016A19D8EADA
6 changed files with 236 additions and 104 deletions

27
apu.c
View File

@ -638,8 +638,15 @@ void apuSetReg8(uint16_t addr, uint8_t val)
break;
case 0x12:
p1Env.vol = (val>>4)&0xF;
p1Env.curVol = p1Env.vol;
p1Env.modeadd = (val&8)!=0;
if(p1Env.modeadd && p1Env.period == 0 && (val&7) == 0)
{
//"Zombie" Mode
p1Env.curVol++;
p1Env.curVol &= 0xF;
}
else //Normal behaviour
p1Env.curVol = p1Env.vol;
p1dacenable = (p1Env.modeadd || p1Env.vol);
if(!p1dacenable)
p1enable = false;
@ -705,8 +712,15 @@ void apuSetReg8(uint16_t addr, uint8_t val)
break;
case 0x17:
p2Env.vol = (val>>4)&0xF;
p2Env.curVol = p2Env.vol;
p2Env.modeadd = (val&8)!=0;
if(p2Env.modeadd && p2Env.period == 0 && (val&7) == 0)
{
//"Zombie" Mode
p2Env.curVol++;
p2Env.curVol &= 0xF;
}
else //Normal behaviour
p2Env.curVol = p2Env.vol;
p2dacenable = (p2Env.modeadd || p2Env.vol);
if(!p2dacenable)
p2enable = false;
@ -822,8 +836,15 @@ void apuSetReg8(uint16_t addr, uint8_t val)
break;
case 0x21:
noiseEnv.vol = (val>>4)&0xF;
noiseEnv.curVol = noiseEnv.vol;
noiseEnv.modeadd = (val&8)!=0;
if(noiseEnv.modeadd && noiseEnv.period == 0 && (val&7) == 0)
{
//"Zombie" Mode
noiseEnv.curVol++;
noiseEnv.curVol &= 0xF;
}
else //Normal behaviour
noiseEnv.curVol = noiseEnv.vol;
noisedacenable = (noiseEnv.modeadd || noiseEnv.vol);
if(!noisedacenable)
noiseenable = false;

82
cpu.c
View File

@ -32,7 +32,11 @@ extern bool gbCgbBootrom;
//used externally
bool cpuDoStopSwitch = false;
bool cpuCgbSpeed = false;
uint8_t cpuAddSpeed = 1;
void cpuSetupActionArr();
bool cpu_oam_dma = false;
bool cpu_oam_dma_running = false;
uint16_t cpu_oam_dma_addr = 0;
static uint16_t sp, pc, cpuTmp16;
static uint8_t a,b,c,d,e,f,h,l,cpuTmp;
@ -68,6 +72,9 @@ void cpuInit()
cpuHaltBug = false;
cpuCgbSpeed = false;
cpuPrevInAny = false;
cpu_oam_dma = false;
cpu_oam_dma_running = false;
cpu_oam_dma_addr = 0;
cpuSetupActionArr();
//gbs stuff
gbsInitRet = false; //for first init
@ -634,7 +641,6 @@ enum {
CPU_GET_INSTRUCTION = 0,
CPU_GET_SUBINSTRUCTION,
CPU_DELAY_CYCLE,
CPU_DELAY_RETI_CYCLE,
CPU_ACTION_GET_INSTRUCTION,
CPU_A_ACTION_GET_INSTRUCTION,
CPU_B_ACTION_GET_INSTRUCTION,
@ -726,6 +732,7 @@ enum {
CPU_TMP16_WRITE8_SPH,
CPU_DI_GET_INSTRUCTION,
CPU_EI_GET_INSTRUCTION,
CPU_GET_INSTRUCTION_EI,
CPU_SCF_GET_INSTRUCTION,
CPU_CCF_GET_INSTRUCTION,
CPU_PC_FROM_HL_GET_INSTRUCTION,
@ -766,17 +773,17 @@ static uint8_t cpu_absjmpz_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T1
static uint8_t cpu_absjmpc_arr[4] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_JPC_CHK, CPU_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_abscall_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_abscallnz_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNZ_CHK, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_abscallnc_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNC_CHK, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_abscallz_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CZ_CHK, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_abscallc_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CC_CHK, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_abscallnz_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNZ_CHK, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_abscallnc_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CNC_CHK, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_abscallz_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CZ_CHK, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_abscallc_arr[6] = { CPU_TMP_READ8_PC_INC, CPU_T16L_FROM_TMP_T16H_READ8_PC_INC_CC_CHK, CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_T16, CPU_GET_INSTRUCTION };
static uint8_t cpu_ret_arr[4] = { CPU_DELAY_CYCLE, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_GET_INSTRUCTION };
static uint8_t cpu_reti_arr[4] = { CPU_DELAY_RETI_CYCLE, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_EI_GET_INSTRUCTION };
static uint8_t cpu_retnz_arr[5] = { CPU_RET_NZ_CHK, CPU_DELAY_CYCLE, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_GET_INSTRUCTION };
static uint8_t cpu_retnc_arr[5] = { CPU_RET_NC_CHK, CPU_DELAY_CYCLE, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_GET_INSTRUCTION };
static uint8_t cpu_retz_arr[5] = { CPU_RET_Z_CHK, CPU_DELAY_CYCLE, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_GET_INSTRUCTION };
static uint8_t cpu_retc_arr[5] = { CPU_RET_C_CHK, CPU_DELAY_CYCLE, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_GET_INSTRUCTION };
static uint8_t cpu_ret_arr[4] = { CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static uint8_t cpu_reti_arr[4] = { CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_EI_GET_INSTRUCTION };
static uint8_t cpu_retnz_arr[5] = { CPU_RET_NZ_CHK, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static uint8_t cpu_retnc_arr[5] = { CPU_RET_NC_CHK, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static uint8_t cpu_retz_arr[5] = { CPU_RET_Z_CHK, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static uint8_t cpu_retc_arr[5] = { CPU_RET_C_CHK, CPU_TMP_READ8_SP_INC, CPU_PCL_FROM_TMP_PCH_READ8_SP_INC, CPU_DELAY_CYCLE, CPU_GET_INSTRUCTION };
static uint8_t cpu_rst00_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_00, CPU_GET_INSTRUCTION };
static uint8_t cpu_rst08_arr[4] = { CPU_DELAY_CYCLE, CPU_SP_WRITE8_PCH_DEC, CPU_SP_WRITE8_PCL_DEC_PC_FROM_08, CPU_GET_INSTRUCTION };
@ -845,7 +852,7 @@ static uint8_t cpu_jrnc_arr[3] = { CPU_TMP_READ8_PC_INC_JRNC_CHK, CPU_TMP_ADD_PC
static uint8_t cpu_jrc_arr[3] = { CPU_TMP_READ8_PC_INC_JRC_CHK, CPU_TMP_ADD_PC, CPU_GET_INSTRUCTION };
static uint8_t cpu_di_arr[1] = { CPU_DI_GET_INSTRUCTION };
static uint8_t cpu_ei_arr[1] = { CPU_EI_GET_INSTRUCTION };
static uint8_t cpu_ei_arr[1] = { CPU_GET_INSTRUCTION_EI };
static uint8_t cpu_scf_arr[1] = { CPU_SCF_GET_INSTRUCTION };
static uint8_t cpu_ccf_arr[1] = { CPU_CCF_GET_INSTRUCTION };
@ -1111,12 +1118,13 @@ void cpuGetInstruction()
}
if(cpuHaltLoop)
{
cpu_action_arr = cpu_nop_arr;
cpu_arr_pos = 0;
//happens when IME=0
if(!irqEnable && memGetCurIrqList())
cpuHaltLoop = false;
cpu_action_arr = cpu_nop_arr;
cpu_arr_pos = 0;
return;
else //keep waiting
return;
}
if(cpuStopLoop)
{
@ -1146,6 +1154,35 @@ void cpuGetInstruction()
cpuHaltBug = false;
}
static bool cpu_oam_dma_started = false;
static uint8_t cpu_oam_dma_pos = 0;
static void cpuHandleOAMDMA()
{
if(cpu_oam_dma)
{
if(!cpu_oam_dma_started)
cpu_oam_dma_started = true;
else
{
cpu_oam_dma = false;
cpu_oam_dma_started = false;
cpu_oam_dma_running = true;
cpu_oam_dma_pos = 0;
}
}
if(cpu_oam_dma_running)
{
if(cpu_oam_dma_pos == 0xA0)
cpu_oam_dma_running = false;
else
{
//printf("OAM Copying %02x\n", cpu_oam_dma_pos);
ppuSetOAMDMAVal(cpu_oam_dma_pos,memGet8(cpu_oam_dma_addr+cpu_oam_dma_pos));
cpu_oam_dma_pos++;
}
}
}
/* Main CPU Interpreter */
bool cpuDmaHalt = false;
@ -1153,6 +1190,7 @@ void cpuCycle()
{
if(cpuDmaHalt)
return;
cpuHandleOAMDMA();
uint8_t cpu_action, sub_instr;
cpu_action = cpu_action_arr[cpu_arr_pos];
cpu_arr_pos++;
@ -1245,9 +1283,6 @@ void cpuCycle()
break;
case CPU_DELAY_CYCLE:
break;
case CPU_DELAY_RETI_CYCLE:
//printf("RETI from %04x\n", pc);
break;
case CPU_ACTION_GET_INSTRUCTION:
cpu_action_func(&cpuTmp);
cpuGetInstruction();
@ -1587,6 +1622,12 @@ void cpuCycle()
cpuGetInstruction();
break;
case CPU_EI_GET_INSTRUCTION:
irqEnable = true;
//printf("Enabled IRQs and jmp to %04x ",pc);
cpuGetInstruction();
//printf("%04x\n",pc);
break;
case CPU_GET_INSTRUCTION_EI:
//printf("Enabled IRQs and jmp to %04x ",pc);
cpuGetInstruction();
//printf("%04x\n",pc);
@ -1635,12 +1676,14 @@ void cpuSetSpeed(bool cgb)
{
//printf("CPU: CGB Speed\n");
cpuCgbSpeed = true;
cpuAddSpeed = 2;
cpuTimer = 1;
}
else
{
//printf("CPU: DMG Speed\n");
cpuCgbSpeed = false;
cpuAddSpeed = 1;
cpuTimer = 3;
}
}
@ -1656,7 +1699,8 @@ void cpuPlayGBS()
sp--;
memSet8(sp, 0x65);
//IMPORTANT: some GBS files dont work without this
a = 0, b = 0, c = 0, d = 0, e = 0, h = 0, l = 0;
//Keep HL though, some GBS files use them as global address
a = 0, b = 0, c = 0, d = 0, e = 0;//, h = 0, l = 0;
//jump to play
pc = gbsPlayAddr;
cpu_action_arr = cpu_nop_arr;

2
main.c
View File

@ -29,7 +29,7 @@
#define DEBUG_KEY 0
#define DEBUG_LOAD_INFO 1
static const char *VERSION_STRING = "fixGB Alpha v0.7.2";
static const char *VERSION_STRING = "fixGB Alpha v0.8";
static char window_title[256];
static char window_title_pause[256];

92
mem.c
View File

@ -29,13 +29,14 @@ static uint8_t irqEnableReg;
static uint8_t irqFlagsReg;
//writable, undocumented regs
static uint8_t genericReg[4];
static uint8_t divRegVal;
static uint8_t divRegClock;
static uint16_t divRegVal;
static uint8_t timerReg;
static uint8_t timerRegVal;
static uint8_t timerResetVal;
static uint8_t timerRegClock;
static uint8_t timerRegTimer;
static uint16_t timerRegBit;
static uint8_t sioTimerRegClock;
static uint8_t sioTimerRegTimer;
static uint8_t sioBitsTransfered;
static uint8_t cgbMainBank;
static bool cgbDmaActive;
static uint16_t cgbDmaSrc;
@ -45,6 +46,7 @@ static uint8_t memDmaClock;
static bool cgbDmaHBlankMode;
static bool cgbBootromEnabled = false;
static bool timerRegEnable = false;
static bool sioTimerRegEnable = false;
static bool emuSaveEnabled = false;
//from main.c
@ -415,12 +417,13 @@ bool memInit(bool romcheck, bool gbs)
irqEnableReg = 0;
irqFlagsReg = 0;
divRegVal = 0;
divRegClock = 1;
timerReg = 0;
timerRegVal = 0;
timerResetVal = 0;
timerRegClock = 1;
timerRegTimer = 64; //262144 / 64 = 4096
timerRegBit = (1<<9); //Freq 0
sioTimerRegClock = 1;
sioTimerRegTimer = 32;
sioBitsTransfered = 0;
cgbMainBank = 1;
cgbDmaActive = false;
cgbDmaSrc = 0;
@ -429,6 +432,7 @@ bool memInit(bool romcheck, bool gbs)
memDmaClock = 1;
cgbDmaHBlankMode = false;
timerRegEnable = false;
sioTimerRegEnable = false;
memInitGetSetPointers();
return true;
}
@ -589,11 +593,13 @@ void memClearCurIrqList(uint8_t num)
void memEnableVBlankIrq()
{
//printf("VBlank IRQ\n");
irqFlagsReg |= 1;
}
void memEnableStatIrq()
{
//printf("STAT IRQ\n");
irqFlagsReg |= 2;
}
@ -642,9 +648,9 @@ static uint8_t memGetGeneralReg8(uint16_t addr)
case 0x01:
return serialReg;
case 0x02:
return serialCtrlReg|0x7E;
return serialCtrlReg | (gbCgbMode ? 0x7C : 0x7E);
case 0x04:
return divRegVal;
return (divRegVal>>8);
case 0x05:
return timerRegVal;
case 0x06:
@ -729,8 +735,14 @@ static void memSetGeneralReg8(uint16_t addr, uint8_t val)
{
case 0x01:
serialReg = val;
break;
case 0x02:
serialCtrlReg = val;
serialCtrlReg = val&(gbCgbMode ? 0x83 : 0x81);
sioTimerRegTimer = (serialCtrlReg&2) ? 1 : 32;
sioTimerRegEnable = (serialCtrlReg&0x81) == 0x81;
sioBitsTransfered = 0;
sioTimerRegClock = 1;
break;
case 0x04:
divRegVal = 0; //writing any val resets to 0
break;
@ -746,13 +758,13 @@ static void memSetGeneralReg8(uint16_t addr, uint8_t val)
timerReg = val; //for readback
timerRegEnable = ((val&4)!=0);
if((val&3)==0) //0 for 4096 Hz
timerRegTimer = 64; //262144 / 64 = 4096
timerRegBit = (1<<9);
else if((val&3)==1) //1 for 262144 Hz
timerRegTimer = 1; //262144 / 1 = 262144
timerRegBit = (1<<3);
else if((val&3)==2) //2 for 65536 Hz
timerRegTimer = 4; //262144 / 4 = 65536
timerRegBit = (1<<5);
else if((val&3)==3) //3 for 16384 Hz
timerRegTimer = 16; //262144 / 16 = 16384
timerRegBit = (1<<7);
break;
case 0x0F:
irqFlagsReg = val&0x1F;
@ -794,6 +806,7 @@ static void memSetGeneralReg8(uint16_t addr, uint8_t val)
break;
case 0x56:
irReq = val;
break;
case 0x70:
if(gbCgbMode)
{
@ -895,7 +908,7 @@ void memSaveGame()
extern bool gbEmuGBSPlayback;
extern bool gbsTimerMode;
extern uint8_t inValReads[8];
static bool timerPrevTicked = false;
//clocked at 262144 Hz (or 2x that in CGB Mode)
void memClockTimers()
{
@ -928,20 +941,39 @@ void memClockTimers()
gbs_prevValReads[BUTTON_LEFT] = 0;
}
//clocked at 16384 Hz (262144 / 16 = 16384)
if(divRegClock == 16)
if(sioTimerRegEnable)
{
divRegVal++;
divRegClock = 1;
//clocked at specified rate
if(sioTimerRegClock == sioTimerRegTimer)
{
sioTimerRegClock = 1;
serialReg <<= 1;
serialReg |= 1; //no serial cable=bit set
sioBitsTransfered++;
if(sioBitsTransfered == 8)
{
//printf("SIO interrupt\n");
sioBitsTransfered = 0;
irqFlagsReg |= 8;
sioTimerRegEnable = false;
serialCtrlReg &= 3;
}
}
else
sioTimerRegClock++;
}
else
divRegClock++;
if(!timerRegEnable)
return;
}
extern bool cpuDmaHalt;
extern uint8_t cpuAddSpeed;
//clocked at 131072 Hz
void memDmaClockTimers()
{
divRegVal += cpuAddSpeed;
//clocked at specified rate
if(timerRegClock == timerRegTimer)
if((timerRegBit&divRegVal) && timerRegEnable)
timerPrevTicked = true;
else if(timerPrevTicked)
{
timerRegVal++;
if(timerRegVal == 0) //set on overflow
@ -953,17 +985,9 @@ void memClockTimers()
else if(gbsTimerMode)
cpuPlayGBS();
}
timerRegClock = 1;
timerPrevTicked = false;
}
else
timerRegClock++;
}
extern bool cpuDmaHalt;
//clocked at 131072 Hz
void memDmaClockTimers()
{
if(memDmaClock >= 16)
{
cpuDmaHalt = false;

136
ppu.c
View File

@ -186,6 +186,43 @@ void ppuInitDrawPointer()
ppuDrawDot = ppuDrawDotDMG;
}
static bool ppuHadIRQs = false;
void ppuCheckIRQs()
{
bool ppuHasIRQs = false;
if(ppuLineMatch && (PPU_Reg[1]&PPU_LINEMATCH_IRQ))
{
//printf("Line STAT IRQ line %i clock %i\n", ppuLines, ppuClock);
ppuHasIRQs = true;
}
else if(ppuMode == 0 && (PPU_Reg[1]&PPU_HBLANK_IRQ))
{
//printf("HBlank STAT IRQ line %i clock %i\n", ppuLines, ppuClock);
ppuHasIRQs = true;
}
else if((ppuMode == 1) && PPU_Reg[1]&PPU_VBLANK_IRQ)
{
//printf("VBlank STAT IRQ line %i clock %i\n", ppuLines, ppuClock);
ppuHasIRQs = true;
}
else if((ppuMode == 1 || ppuMode == 2) && (PPU_Reg[1]&PPU_OAM_IRQ))
{
//printf("OAM STAT IRQ line %i clock %i\n", ppuLines, ppuClock);
ppuHasIRQs = true;
}
if(ppuHasIRQs)
{
if(ppuHadIRQs == false)
{
//printf("Setting CPU IRQ\n");
memEnableStatIrq();
}
ppuHadIRQs = true;
}
else
ppuHadIRQs = false;
}
extern bool gbEmuGBSPlayback;
void ppuCycle()
{
@ -198,15 +235,8 @@ void ppuCycle()
//check for line IRQ on first clock
if(ppuClock == 0)
{
//DONT check line 0 here, already done further below
if((ppuLines > 0) && ppuLineMatch)
{
if(PPU_Reg[1]&PPU_LINEMATCH_IRQ)
{
//printf("Line STAT IRQ at %i\n",ppuLines);
memEnableStatIrq();
}
}
if(ppuLines > 0) //DONT check line 0 here, already done further below
ppuCheckIRQs();
}
if(ppuLines < 144)
{
@ -219,11 +249,7 @@ void ppuCycle()
ppuOAM2pos = 0; //Reset array pos
ppuMode = 2; //OAM
ppuHBlank = false;
if(PPU_Reg[1]&PPU_OAM_IRQ)
{
//printf("OAM STAT IRQ\n");
memEnableStatIrq();
}
ppuCheckIRQs();
}
if(((ppuClock&1) == 0) && ppuOAM2pos < 10)
{
@ -259,11 +285,7 @@ void ppuCycle()
{
ppuMode = 0; //HBlank
ppuHBlank = true;
if(PPU_Reg[1]&PPU_HBLANK_IRQ)
{
//printf("HBlank STAT IRQ\n");
memEnableStatIrq();
}
ppuCheckIRQs();
}
} //VERY important, reg 4 to the outside gets set back to 0 early!
else if(ppuLines == 153 && ppuClock == 4)
@ -271,14 +293,7 @@ void ppuCycle()
PPU_Reg[4] = 0;
//check for linematch and IRQ early too
ppuLineMatch = ((PPU_Reg[4] == PPU_Reg[5]) ? PPU_LINEMATCH : 0);
if(ppuLineMatch)
{
if(PPU_Reg[1]&PPU_LINEMATCH_IRQ)
{
//printf("Line STAT IRQ at %i\n",PPU_Reg[4]);
memEnableStatIrq();
}
}
ppuCheckIRQs();
}
ppuIncreasePos:
/* increase pos */
@ -294,16 +309,7 @@ ppuIncreasePos:
ppuHBlank = false;
ppuVBlank = true;
memEnableVBlankIrq();
if(PPU_Reg[1]&PPU_VBLANK_IRQ)
{
//printf("VBlank STAT IRQ\n");
memEnableStatIrq();
}
if(PPU_Reg[1]&PPU_OAM_IRQ)
{
//printf("OAM STAT IRQ\n");
memEnableStatIrq();
}
ppuCheckIRQs();
//printf("VBlank Start\n");
}
else if(ppuLines == 154)
@ -342,9 +348,10 @@ uint8_t ppuGetVRAMNoBank8(uint16_t addr)
return 0xFF;
}
extern bool cpu_oam_dma_running;
uint8_t ppuGetOAM8(uint16_t addr)
{
if(gbAllowInvVRAM || !(PPU_Reg[0] & PPU_ENABLE) || (ppuMode == 0) || (ppuMode == 1))
if(gbAllowInvVRAM || ((!(PPU_Reg[0] & PPU_ENABLE) || (ppuMode == 0) || (ppuMode == 1)) && !cpu_oam_dma_running))
return PPU_OAM[addr&0xFF];
return 0xFF;
}
@ -394,22 +401,34 @@ void ppuSetVRAMNoBank8(uint16_t addr, uint8_t val)
void ppuSetOAM8(uint16_t addr, uint8_t val)
{
if(gbAllowInvVRAM || !(PPU_Reg[0] & PPU_ENABLE) || (ppuMode == 0) || (ppuMode == 1))
if(gbAllowInvVRAM || ((!(PPU_Reg[0] & PPU_ENABLE) || (ppuMode == 0) || (ppuMode == 1)) && !cpu_oam_dma_running))
PPU_OAM[addr&0xFF] = val;
}
extern bool cpu_oam_dma;
extern uint16_t cpu_oam_dma_addr;
void ppuSetReg8(uint16_t addr, uint8_t val)
{
bool prevEnable;
uint8_t reg = addr&0x3F;
//printf("line %i clock %i reg %02x val %02x\n", ppuLines, ppuClock, reg, val);
switch(reg)
{
/*case 0x0: case 0x1:*/ case 0x2: case 0x3: /*case 0x4:*/ case 0x5:
/*case 0x0: case 0x1:*/ case 0x2: case 0x3: /*case 0x4: case 0x5:*/
/*case 0x6:*/ case 0x7: case 0x8: case 0x9: case 0xA: case 0xB:
PPU_Reg[reg] = val;
break;
case 0x0: //Control reg
prevEnable = (PPU_Reg[0]&PPU_ENABLE);
PPU_Reg[0] = val;
if(!(val&PPU_ENABLE))
if(prevEnable && !(val&PPU_ENABLE))
{
PPU_Reg[4] = 0;
ppuMode = 0;
ppuHadIRQs = false;
}
else if((val&PPU_ENABLE) && !prevEnable)
{
ppuLines = 0;
PPU_Reg[4] = 0;
@ -420,19 +439,37 @@ void ppuSetReg8(uint16_t addr, uint8_t val)
ppuOAM2pos = 0; //Reset array pos
ppuMode = 2; //OAM
ppuHBlank = false;
//re-check IRQs right on enable
ppuLineMatch = ((PPU_Reg[4] == PPU_Reg[5]) ? PPU_LINEMATCH : 0);
ppuCheckIRQs();
}
break;
case 0x1: //STAT RO Regs
if(!gbCgbMode)
{
//printf("DMG Write STAT at line %i clock %i, %d %d %d\n", ppuLines, ppuClock, (ppuMode == 0 && !(PPU_Reg[1]&PPU_HBLANK_IRQ)),
// (ppuMode == 1 && !(PPU_Reg[1]&PPU_VBLANK_IRQ)), !(ppuLineMatch && (PPU_Reg[1]&PPU_LINEMATCH_IRQ)));
//DMG STAT IRQ on HBlank/VBlank when no other IRQs previously enabled
if((ppuMode == 0 || ppuMode == 1) && !ppuHadIRQs)
{
//printf("DMG Write STAT IRQ line %i clock %i\n", ppuLines, ppuClock);
memEnableStatIrq();
}
}
PPU_Reg[1] = (val&(~7));
if(PPU_Reg[0]&PPU_ENABLE)
ppuCheckIRQs();
break;
case 0x5: //LYC
PPU_Reg[5] = val;
ppuLineMatch = ((PPU_Reg[4] == PPU_Reg[5]) ? PPU_LINEMATCH : 0);
if(PPU_Reg[0]&PPU_ENABLE)
ppuCheckIRQs();
break;
case 0x6: //OAM DMA
PPU_Reg[6] = val;
if(val < 0xFE)
{
uint8_t i;
for(i = 0; i < 0xA0; i++)
PPU_OAM[i] = memGet8((val<<8) | i);
}
cpu_oam_dma = true;
cpu_oam_dma_addr = (val<<8);
break;
case 0x28: //FF68
ppuCgbBgPalPos = val;
@ -1072,3 +1109,8 @@ void ppuDrawGBSTrackNum(uint8_t cTrack, uint8_t trackTotal)
ppuDrawRest(curX, trackTotal%10);
curX+=10;
}
void ppuSetOAMDMAVal(uint8_t pos, uint8_t val)
{
PPU_OAM[pos] = val;
}

1
ppu.h
View File

@ -20,6 +20,7 @@ void ppuSetVRAMBank8(uint16_t addr, uint8_t val);
void ppuSetVRAMNoBank8(uint16_t addr, uint8_t val);
void ppuSetOAM8(uint16_t addr, uint8_t val);
void ppuSetReg8(uint16_t addr, uint8_t val);
void ppuSetOAMDMAVal(uint8_t pos, uint8_t val);
bool ppuInVBlank();
bool ppuInHBlank();
void ppuDumpMem();