mirror of
https://github.com/libretro/fixGB.git
synced 2024-11-23 00:59:43 +00:00
-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:
parent
fca433f1a0
commit
89d2f6656a
27
apu.c
27
apu.c
@ -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
82
cpu.c
@ -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
2
main.c
@ -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
92
mem.c
@ -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
136
ppu.c
@ -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
1
ppu.h
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user