mirror of
https://github.com/libretro/fixGB.git
synced 2024-11-23 09:09:40 +00:00
-added game boy color support
This commit is contained in:
parent
096f760361
commit
35d91ce00c
2
apu.c
2
apu.c
@ -135,7 +135,9 @@ void apuInit()
|
||||
p1dacenable = false; p2dacenable = false;
|
||||
wavdacenable = false; noisedacenable = false;
|
||||
noiseMode1 = false;
|
||||
//GB Bootrom
|
||||
soundEnabled = true;
|
||||
APU_IO_Reg[0x25] = 0x77;
|
||||
}
|
||||
|
||||
extern uint32_t cpu_oam_dma;
|
||||
|
36
cpu.c
36
cpu.c
@ -25,7 +25,12 @@ extern uint16_t gbsLoadAddr;
|
||||
extern uint16_t gbsInitAddr;
|
||||
extern uint16_t gbsPlayAddr;
|
||||
extern uint16_t gbsSP;
|
||||
extern uint8_t cpuTimer;
|
||||
extern bool allowCgbRegs;
|
||||
|
||||
//used externally
|
||||
bool cpuDoStopSwitch = false;
|
||||
bool cpuCgbSpeed = false;
|
||||
void cpuSetupActionArr();
|
||||
|
||||
static uint16_t sp, pc, cpuTmp16;
|
||||
@ -37,13 +42,14 @@ void cpuInit()
|
||||
{
|
||||
sub_in_val=0,cpuTmp=0,cpuTmp16=0;
|
||||
//From GB Bootrom
|
||||
a=0x01,b=0,c=0x13,d=0,e=0xD8,f=0xB0,h=1,l=0x4D;
|
||||
a=0x01|(allowCgbRegs<<4),b=0,c=0x13,d=0,e=0xD8,f=0xB0,h=1,l=0x4D;
|
||||
sp = 0xFFFE; //Boot Stack Pointer
|
||||
pc = 0x0100; //hardcoded ROM entrypoint
|
||||
irqEnable = false;
|
||||
cpuHaltLoop = false;
|
||||
cpuStopLoop = false;
|
||||
cpuHaltBug = false;
|
||||
cpuCgbSpeed = false;
|
||||
cpuSetupActionArr();
|
||||
}
|
||||
|
||||
@ -583,7 +589,14 @@ void cpuDAA(uint8_t *reg)
|
||||
static void cpuSTOP(uint8_t *none)
|
||||
{
|
||||
(void)none;
|
||||
cpuStopLoop = true;
|
||||
printf("CPU called STOP instruction\n");
|
||||
if(cpuDoStopSwitch)
|
||||
{
|
||||
cpuSetSpeed(!cpuCgbSpeed);
|
||||
cpuDoStopSwitch = false;
|
||||
}
|
||||
else
|
||||
cpuStopLoop = true;
|
||||
//takes up 2 instructions?
|
||||
pc++;
|
||||
}
|
||||
@ -1087,6 +1100,7 @@ void cpuGetInstruction()
|
||||
/* Main CPU Interpreter */
|
||||
int testCounter = 0;
|
||||
int waitCycles = 0;
|
||||
bool cpuDmaHalt = false;
|
||||
|
||||
bool cpuCycle()
|
||||
{
|
||||
@ -1096,6 +1110,8 @@ bool cpuCycle()
|
||||
waitCycles--;
|
||||
return true;
|
||||
}
|
||||
if(cpuDmaHalt)
|
||||
return true;
|
||||
uint8_t cpu_action, sub_instr;
|
||||
cpu_action = cpu_action_arr[cpu_arr_pos];
|
||||
cpu_arr_pos++;
|
||||
@ -1583,6 +1599,22 @@ uint16_t cpuCurPC()
|
||||
return pc;
|
||||
}
|
||||
|
||||
void cpuSetSpeed(bool cgb)
|
||||
{
|
||||
if(cgb)
|
||||
{
|
||||
printf("CPU: CGB Speed\n");
|
||||
cpuCgbSpeed = true;
|
||||
cpuTimer = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("CPU: DMG Speed\n");
|
||||
cpuCgbSpeed = false;
|
||||
cpuTimer = 4;
|
||||
}
|
||||
}
|
||||
|
||||
void cpuPlayGBS()
|
||||
{
|
||||
//push back detect pc
|
||||
|
1
cpu.h
1
cpu.h
@ -11,6 +11,7 @@
|
||||
void cpuInit();
|
||||
bool cpuCycle();
|
||||
uint16_t cpuCurPC();
|
||||
void cpuSetSpeed(bool cgb);
|
||||
void cpuLoadGBS(uint8_t song);
|
||||
void cpuPlayGBS();
|
||||
|
||||
|
76
main.c
76
main.c
@ -26,7 +26,7 @@
|
||||
#define DEBUG_KEY 0
|
||||
#define DEBUG_LOAD_INFO 1
|
||||
|
||||
static const char *VERSION_STRING = "fixGB Alpha v0.2";
|
||||
static const char *VERSION_STRING = "fixGB Alpha v0.3";
|
||||
|
||||
static void gbEmuDisplayFrame(void);
|
||||
static void gbEmuMainLoop(void);
|
||||
@ -50,6 +50,8 @@ uint16_t gbsInitAddr = 0;
|
||||
uint16_t gbsPlayAddr = 0;
|
||||
uint16_t gbsSP = 0;
|
||||
uint8_t gbsTracksTotal = 0, gbsTMA = 0, gbsTAC = 0;
|
||||
uint8_t cpuTimer = 4;
|
||||
bool allowCgbRegs = false;
|
||||
|
||||
static bool inPause = false;
|
||||
static bool inResize = false;
|
||||
@ -78,7 +80,6 @@ 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];
|
||||
|
||||
@ -120,13 +121,13 @@ int main(int argc, char** argv)
|
||||
}
|
||||
if(gbsTAC&0x80)
|
||||
{
|
||||
printf("CPU: CGB Speed\n");
|
||||
cpuTimer = 2;
|
||||
cpuSetSpeed(true);
|
||||
allowCgbRegs = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("CPU: DMG Speed\n");
|
||||
cpuTimer = 4;
|
||||
cpuSetSpeed(false);
|
||||
allowCgbRegs = false;
|
||||
}
|
||||
if(tmpROM[0x10] != 0)
|
||||
printf("Game: %.32s\n",(char*)(tmpROM+0x10));
|
||||
@ -170,8 +171,11 @@ int main(int argc, char** argv)
|
||||
printf("Exit...\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
//DMG Mode
|
||||
cpuTimer = 4;
|
||||
//CPU DMG Mode
|
||||
cpuSetSpeed(false);
|
||||
//Set CGB Regs allowed
|
||||
allowCgbRegs = !!(emuGBROM[0x143]&0x80);
|
||||
printf("CGB Regs are %sallowed\n", allowCgbRegs?"":"dis");
|
||||
apuInitBufs();
|
||||
cpuInit();
|
||||
ppuInit();
|
||||
@ -266,13 +270,14 @@ bool emuSkipFrame = false;
|
||||
//static uint32_t mCycles = 0;
|
||||
//static bool emuApuDoCycle = false;
|
||||
|
||||
static uint16_t mainClock = 1;
|
||||
static uint16_t apuClock = 1;
|
||||
static uint16_t cpuClock = 1;
|
||||
static uint16_t memClock = 1;
|
||||
//static uint16_t vrc7Clock = 1;
|
||||
|
||||
static void gbEmuMainLoop(void)
|
||||
{
|
||||
//do one scanline loop
|
||||
do
|
||||
{
|
||||
if((!emuSkipVsync && emuRenderFrame) || nesPause)
|
||||
@ -283,19 +288,8 @@ 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)
|
||||
//run APU first to make sure its synced
|
||||
if(apuClock >= 4)
|
||||
{
|
||||
if(!apuCycle())
|
||||
{
|
||||
@ -305,24 +299,42 @@ static void gbEmuMainLoop(void)
|
||||
audioSleep();
|
||||
return;
|
||||
}
|
||||
if(memClock == 4)
|
||||
{
|
||||
memClockTimers();
|
||||
memClock = 1;
|
||||
}
|
||||
else
|
||||
memClock++;
|
||||
//channel timer updates
|
||||
apuLenCycle();
|
||||
/*//mapper related irqs
|
||||
if(mapperCycle != NULL)
|
||||
mapperCycle();*/
|
||||
//mCycles++;
|
||||
mainClock = 1;
|
||||
apuClock = 1;
|
||||
}
|
||||
else
|
||||
mainClock++;
|
||||
apuClock++;
|
||||
apuClockTimers();
|
||||
//run possible DMA next
|
||||
memDmaClockTimers();
|
||||
//run CPU (and mem clocks) next
|
||||
if(cpuClock >= cpuTimer)
|
||||
{
|
||||
//main CPU clock
|
||||
if(!cpuCycle())
|
||||
{
|
||||
//memDumpMainMem();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
//mem clock tied to CPU clock, so
|
||||
//double speed in CGB mode!
|
||||
if(memClock >= 4)
|
||||
{
|
||||
memClockTimers();
|
||||
memClock = 1;
|
||||
}
|
||||
else
|
||||
memClock++;
|
||||
cpuClock = 1;
|
||||
}
|
||||
else
|
||||
cpuClock++;
|
||||
//run PPU last
|
||||
if(!ppuCycle())
|
||||
exit(EXIT_SUCCESS);
|
||||
if(ppuDrawDone())
|
||||
@ -332,6 +344,7 @@ static void gbEmuMainLoop(void)
|
||||
//printf("%i\n",mCycles);
|
||||
//mCycles = 0;
|
||||
emuRenderFrame = true;
|
||||
//update console stats if requested
|
||||
#if (WINDOWS_BUILD && DEBUG_HZ)
|
||||
emuTimesCalled++;
|
||||
DWORD end = GetTickCount();
|
||||
@ -354,6 +367,7 @@ static void gbEmuMainLoop(void)
|
||||
}
|
||||
while(mainLoopPos--);
|
||||
mainLoopPos = mainLoopRuns;
|
||||
//update console stats if requested
|
||||
#if (WINDOWS_BUILD && DEBUG_MAIN_CALLS)
|
||||
emuMainTimesCalled++;
|
||||
DWORD end = GetTickCount();
|
||||
|
171
mem.c
171
mem.c
@ -8,6 +8,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include "mem.h"
|
||||
#include "cpu.h"
|
||||
#include "ppu.h"
|
||||
#include "apu.h"
|
||||
@ -15,7 +16,7 @@
|
||||
#include "mbc.h"
|
||||
|
||||
static uint8_t Ext_Mem[0x20000];
|
||||
static uint8_t Main_Mem[0x2000];
|
||||
static uint8_t Main_Mem[0x8000];
|
||||
static uint8_t High_Mem[0x80];
|
||||
static uint8_t gbs_prevValReads[8];
|
||||
static uint8_t memLastVal;
|
||||
@ -29,11 +30,21 @@ static uint8_t timerRegVal;
|
||||
static uint8_t timerResetVal;
|
||||
static uint8_t timerRegClock;
|
||||
static uint8_t timerRegTimer;
|
||||
static uint8_t cgbMainBank;
|
||||
static bool cgbDmaActive;
|
||||
static uint16_t cgbDmaSrc;
|
||||
static uint16_t cgbDmaDst;
|
||||
static uint8_t cgbDmaLen;
|
||||
static uint8_t memDmaClock;
|
||||
static bool cgbDmaHBlankMode;
|
||||
static bool timerRegEnable = false;
|
||||
|
||||
extern uint8_t *emuGBROM;
|
||||
static bool emuSaveEnabled = false;
|
||||
|
||||
//from main.c
|
||||
extern bool allowCgbRegs;
|
||||
extern uint8_t *emuGBROM;
|
||||
|
||||
//from mbc.c
|
||||
extern uint16_t cBank;
|
||||
extern uint16_t extBank;
|
||||
@ -42,6 +53,13 @@ extern uint16_t extMask;
|
||||
extern uint16_t extTotalMask;
|
||||
extern size_t extTotalSize;
|
||||
|
||||
//from cpu.c
|
||||
extern bool cpuCgbSpeed;
|
||||
extern bool cpuDoStopSwitch;
|
||||
|
||||
//from ppu.c
|
||||
extern uint8_t ppuCgbBank;
|
||||
|
||||
extern bool extMemUsed;
|
||||
extern bool bankUsed;
|
||||
extern bool extSelect;
|
||||
@ -202,6 +220,8 @@ bool memInit(bool romcheck, bool gbs)
|
||||
memSetExtVal();
|
||||
memLoadSave();
|
||||
break;
|
||||
case 0x0F:
|
||||
//TODO: RTC Support
|
||||
case 0x11:
|
||||
printf("ROM Only (MBC3)\n");
|
||||
mbcInit(MBC_TYPE_3);
|
||||
@ -214,6 +234,8 @@ bool memInit(bool romcheck, bool gbs)
|
||||
memSetBankVal();
|
||||
memSetExtVal();
|
||||
break;
|
||||
case 0x10:
|
||||
//TODO: RTC Support
|
||||
case 0x13:
|
||||
printf("ROM and RAM (with save) (MBC3)\n");
|
||||
mbcInit(MBC_TYPE_3);
|
||||
@ -249,7 +271,7 @@ bool memInit(bool romcheck, bool gbs)
|
||||
}
|
||||
}
|
||||
}
|
||||
memset(Main_Mem,0,0x2000);
|
||||
memset(Main_Mem,0,0x8000);
|
||||
memset(High_Mem,0,0x80);
|
||||
memLastVal = 0;
|
||||
irqEnableReg = 0;
|
||||
@ -261,6 +283,13 @@ bool memInit(bool romcheck, bool gbs)
|
||||
timerResetVal = 0;
|
||||
timerRegClock = 1;
|
||||
timerRegTimer = 64; //262144 / 64 = 4096
|
||||
cgbMainBank = 1;
|
||||
cgbDmaActive = false;
|
||||
cgbDmaSrc = 0;
|
||||
cgbDmaDst = 0;
|
||||
cgbDmaLen = 0;
|
||||
memDmaClock = 1;
|
||||
cgbDmaHBlankMode = false;
|
||||
timerRegEnable = false;
|
||||
return true;
|
||||
}
|
||||
@ -285,7 +314,17 @@ uint8_t memGet8(uint16_t addr)
|
||||
else if(addr >= 0xA000 && addr < 0xC000 && extMemUsed)
|
||||
val = Ext_Mem[((extBank<<13)+(addr&0x1FFF))&extTotalMask];
|
||||
else if(addr >= 0xC000 && addr < 0xFE00)
|
||||
val = Main_Mem[addr&0x1FFF];
|
||||
{
|
||||
if(!allowCgbRegs)
|
||||
val = Main_Mem[addr&0x1FFF];
|
||||
else
|
||||
{
|
||||
if(addr < 0xD000)
|
||||
val = Main_Mem[addr&0xFFF];
|
||||
else
|
||||
val = Main_Mem[(cgbMainBank<<12)|(addr&0xFFF)];
|
||||
}
|
||||
}
|
||||
else if(addr >= 0xFE00 && addr < 0xFEA0)
|
||||
val = ppuGet8(addr);
|
||||
else if(addr == 0xFF00)
|
||||
@ -305,8 +344,37 @@ uint8_t memGet8(uint16_t addr)
|
||||
}
|
||||
else if(addr >= 0xFF10 && addr < 0xFF40)
|
||||
val = apuGet8(addr&0xFF);
|
||||
else if(addr >= 0xFF40 && addr < 0xFF70)
|
||||
else if(addr >= 0xFF40 && addr < 0xFF4C)
|
||||
val = ppuGet8(addr);
|
||||
else if(addr >= 0xFF4D && addr < 0xFF80)
|
||||
{
|
||||
if(allowCgbRegs)
|
||||
{
|
||||
if(addr == 0xFF4D)
|
||||
val = (cpuDoStopSwitch | (cpuCgbSpeed<<7));
|
||||
else if(addr == 0xFF4F)
|
||||
val = ppuCgbBank;
|
||||
else if(addr == 0xFF51)
|
||||
val = cgbDmaSrc>>8;
|
||||
else if(addr == 0xFF52)
|
||||
val = (cgbDmaSrc&0xFF);
|
||||
else if(addr == 0xFF53)
|
||||
val = cgbDmaDst>>8;
|
||||
else if(addr == 0xFF54)
|
||||
val = (cgbDmaDst&0xFF);
|
||||
else if(addr == 0xFF55)
|
||||
{
|
||||
val = cgbDmaLen-1;
|
||||
//bit 7 = 1 means NOT active
|
||||
if(!cgbDmaActive)
|
||||
val |= 0x80;
|
||||
}
|
||||
else if(addr >= 0xFF68 && addr < 0xFF6C)
|
||||
val = ppuGet8(addr);
|
||||
else if(addr == 0xFF70)
|
||||
val = cgbMainBank;
|
||||
}
|
||||
}
|
||||
else if(addr >= 0xFF80 && addr < 0xFFFF)
|
||||
val = High_Mem[addr&0x7F];
|
||||
else if(addr == 0xFFFF)
|
||||
@ -325,7 +393,17 @@ void memSet8(uint16_t addr, uint8_t val)
|
||||
else if(addr >= 0xA000 && addr < 0xC000 && extMemUsed)
|
||||
Ext_Mem[((extBank<<13)+(addr&0x1FFF))&extTotalMask] = val;
|
||||
else if(addr >= 0xC000 && addr < 0xFE00)
|
||||
Main_Mem[addr&0x1FFF] = val;
|
||||
{
|
||||
if(!allowCgbRegs)
|
||||
Main_Mem[addr&0x1FFF] = val;
|
||||
else
|
||||
{
|
||||
if(addr < 0xD000)
|
||||
Main_Mem[addr&0xFFF] = val;
|
||||
else
|
||||
Main_Mem[(cgbMainBank<<12)|(addr&0xFFF)] = val;
|
||||
}
|
||||
}
|
||||
else if(addr >= 0xFE00 && addr < 0xFEA0)
|
||||
ppuSet8(addr, val);
|
||||
else if(addr == 0xFF00)
|
||||
@ -358,8 +436,49 @@ void memSet8(uint16_t addr, uint8_t val)
|
||||
}
|
||||
else if(addr >= 0xFF10 && addr < 0xFF40)
|
||||
apuSet8(addr&0xFF, val);
|
||||
else if(addr >= 0xFF40 && addr < 0xFF70)
|
||||
else if(addr >= 0xFF40 && addr < 0xFF4C)
|
||||
ppuSet8(addr, val);
|
||||
else if(addr >= 0xFF4D && addr < 0xFF80)
|
||||
{
|
||||
if(allowCgbRegs)
|
||||
{
|
||||
if(addr == 0xFF4D)
|
||||
cpuDoStopSwitch = !!(val&1);
|
||||
else if(addr == 0xFF4F)
|
||||
ppuCgbBank = (val&1);
|
||||
else if(addr == 0xFF51)
|
||||
cgbDmaSrc = (cgbDmaSrc&0x00FF)|(val<<8);
|
||||
else if(addr == 0xFF52)
|
||||
cgbDmaSrc = (cgbDmaSrc&0xFF00)|(val&~0xF);
|
||||
else if(addr == 0xFF53)
|
||||
cgbDmaDst = (cgbDmaDst&0x00FF)|(val<<8)|0x8000;
|
||||
else if(addr == 0xFF54)
|
||||
cgbDmaDst = (cgbDmaDst&0xFF00)|(val&~0xF);
|
||||
else if(addr == 0xFF55)
|
||||
{
|
||||
//disabling ongoing HBlank DMA when disabling HBlank mode
|
||||
if(cgbDmaActive && cgbDmaHBlankMode && !(val&0x80))
|
||||
cgbDmaActive = false;
|
||||
else //enable DMA in all other cases
|
||||
{
|
||||
cgbDmaActive = true;
|
||||
cgbDmaLen = (val&0x7F)+1;
|
||||
cgbDmaHBlankMode = !!(val&0x80);
|
||||
//trigger immediately
|
||||
memDmaClock = 16;
|
||||
memDmaClockTimers();
|
||||
}
|
||||
}
|
||||
else if(addr >= 0xFF68 && addr < 0xFF6C)
|
||||
ppuSet8(addr,val);
|
||||
else if(addr == 0xFF70)
|
||||
{
|
||||
cgbMainBank = (val&7);
|
||||
if(cgbMainBank == 0)
|
||||
cgbMainBank = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(addr >= 0xFF80 && addr < 0xFFFF)
|
||||
High_Mem[addr&0x7F] = val;
|
||||
else if(addr == 0xFFFF)
|
||||
@ -398,7 +517,7 @@ void memDumpMainMem()
|
||||
FILE *f = fopen("MainMem.bin","wb");
|
||||
if(f)
|
||||
{
|
||||
fwrite(Main_Mem,1,0x2000,f);
|
||||
fwrite(Main_Mem,1,allowCgbRegs?0x8000:0x2000,f);
|
||||
fclose(f);
|
||||
}
|
||||
f = fopen("HighMem.bin","wb");
|
||||
@ -451,7 +570,7 @@ extern bool gbEmuGBSPlayback;
|
||||
extern bool gbsTimerMode;
|
||||
extern uint8_t inValReads[8];
|
||||
|
||||
//clocked at 262144 Hz
|
||||
//clocked at 262144 Hz (or 2x that in CGB Mode)
|
||||
void memClockTimers()
|
||||
{
|
||||
if(gbEmuGBSPlayback)
|
||||
@ -511,3 +630,37 @@ void memClockTimers()
|
||||
else
|
||||
timerRegClock++;
|
||||
}
|
||||
|
||||
extern bool cpuDmaHalt;
|
||||
|
||||
//clocked at 131072 Hz
|
||||
void memDmaClockTimers()
|
||||
{
|
||||
if(memDmaClock >= 16)
|
||||
{
|
||||
cpuDmaHalt = false;
|
||||
if(!cgbDmaActive)
|
||||
return;
|
||||
//printf("%04x %04x %02x\n", cgbDmaSrc, cgbDmaDst, cgbDmaLen);
|
||||
if(cgbDmaLen && ((cgbDmaSrc < 0x8000) || (cgbDmaSrc >= 0xA000 && cgbDmaSrc < 0xE000)) && (cgbDmaDst >= 0x8000 && cgbDmaDst < 0xA000))
|
||||
{
|
||||
if(!cgbDmaHBlankMode || (cgbDmaHBlankMode && ppuInHBlank()))
|
||||
{
|
||||
uint8_t i;
|
||||
for(i = 0; i < 0x10; i++)
|
||||
memSet8(cgbDmaDst+i, memGet8(cgbDmaSrc+i));
|
||||
cgbDmaLen--;
|
||||
if(cgbDmaLen == 0)
|
||||
cgbDmaActive = false;
|
||||
cgbDmaSrc += 0x10;
|
||||
cgbDmaDst += 0x10;
|
||||
cpuDmaHalt = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
cgbDmaActive = false;
|
||||
memDmaClock = 1;
|
||||
}
|
||||
else
|
||||
memDmaClock++;
|
||||
}
|
||||
|
1
mem.h
1
mem.h
@ -15,6 +15,7 @@ void memSet8(uint16_t addr, uint8_t val);
|
||||
void memSet16(uint16_t addr, uint16_t val);
|
||||
void memDumpMainMem();
|
||||
void memClockTimers();
|
||||
void memDmaClockTimers();
|
||||
void memSaveGame();
|
||||
|
||||
uint8_t memGetCurIrqList();
|
||||
|
418
ppu.c
418
ppu.c
@ -21,6 +21,8 @@
|
||||
#define PPU_WINDOW_ENABLE (1<<5)
|
||||
#define PPU_WINDOW_TILEMAP_UP (1<<6)
|
||||
#define PPU_ENABLE (1<<7)
|
||||
//FF40 in CGB Mode is different!
|
||||
#define PPU_BG_WINDOW_PRIO (1<<0)
|
||||
|
||||
//FF41
|
||||
#define PPU_LINEMATCH (1<<2)
|
||||
@ -29,15 +31,24 @@
|
||||
#define PPU_OAM_IRQ (1<<5)
|
||||
#define PPU_LINEMATCH_IRQ (1<<6)
|
||||
|
||||
//sprite byte 3
|
||||
#define PPU_SPRITE_PAL (1<<4)
|
||||
#define PPU_SPRITE_FLIP_X (1<<5)
|
||||
#define PPU_SPRITE_FLIP_Y (1<<6)
|
||||
#define PPU_SPRITE_PRIO (1<<7)
|
||||
//sprite byte 3 and CGB BG
|
||||
#define PPU_TILE_CGB_BANK (1<<3)
|
||||
#define PPU_TILE_DMG_PAL (1<<4)
|
||||
#define PPU_TILE_FLIP_X (1<<5)
|
||||
#define PPU_TILE_FLIP_Y (1<<6)
|
||||
#define PPU_TILE_PRIO (1<<7)
|
||||
|
||||
static uint8_t ppuDoSprites(uint8_t color, uint8_t tCol);
|
||||
static void ppuDrawDotDMG(size_t drawPos);
|
||||
static void ppuDrawDotCGB(size_t drawPos);
|
||||
|
||||
typedef void (*drawFunc)(size_t);
|
||||
static drawFunc ppuDrawDot = NULL;
|
||||
|
||||
//from main.c
|
||||
extern uint8_t *textureImage;
|
||||
extern bool allowCgbRegs;
|
||||
//used externally
|
||||
uint8_t ppuCgbBank = 0;
|
||||
|
||||
static uint32_t ppuClock;
|
||||
static uint32_t ppuTestClock;
|
||||
@ -45,13 +56,19 @@ static uint8_t ppuMode;
|
||||
static uint8_t ppuDots;
|
||||
static uint8_t ppuOAMpos;
|
||||
static uint8_t ppuOAM2pos;
|
||||
static uint8_t ppuCgbBgPalPos;
|
||||
static uint8_t ppuCgbObjPalPos;
|
||||
static uint8_t PPU_Reg[12];
|
||||
static uint8_t PPU_CGB_BGPAL[0x40];
|
||||
static uint8_t PPU_CGB_OBJPAL[0x40];
|
||||
static uint8_t PPU_OAM[0xA0];
|
||||
static uint8_t PPU_OAM2[0x28];
|
||||
static uint8_t PPU_VRAM[0x2000];
|
||||
static uint8_t PPU_VRAM[0x4000];
|
||||
static bool ppuFrameDone;
|
||||
static bool ppuVBlank;
|
||||
static bool ppuVBlankTriggered;
|
||||
static bool ppuHBlank;
|
||||
static bool ppuHBlankTriggered;
|
||||
|
||||
void ppuInit()
|
||||
{
|
||||
@ -61,13 +78,21 @@ void ppuInit()
|
||||
ppuDots = 0;
|
||||
ppuOAMpos = 0;
|
||||
ppuOAM2pos = 0;
|
||||
ppuCgbBgPalPos = 0;
|
||||
ppuCgbObjPalPos = 0;
|
||||
ppuCgbBank = 0;
|
||||
ppuFrameDone = false;
|
||||
ppuVBlank = false;
|
||||
ppuVBlankTriggered = false;
|
||||
ppuHBlank = false;
|
||||
ppuHBlankTriggered = false;
|
||||
memset(PPU_Reg,0,12);
|
||||
memset(PPU_CGB_BGPAL,0xFF,0x40);
|
||||
memset(PPU_CGB_OBJPAL,0xFF,0x40);
|
||||
memset(PPU_OAM,0,0xA0);
|
||||
memset(PPU_OAM2,0,0x28);
|
||||
memset(PPU_VRAM,0,0x2000);
|
||||
memset(PPU_VRAM,0,0x4000);
|
||||
ppuDrawDot = allowCgbRegs?ppuDrawDotCGB:ppuDrawDotDMG;
|
||||
//From GB Bootrom
|
||||
PPU_Reg[0] = 0x91;
|
||||
PPU_Reg[7] = 0xFC;
|
||||
@ -75,8 +100,11 @@ void ppuInit()
|
||||
PPU_Reg[9] = 0xFF;
|
||||
}
|
||||
|
||||
extern bool gbEmuGBSPlayback;
|
||||
bool ppuCycle()
|
||||
{
|
||||
if(gbEmuGBSPlayback)
|
||||
goto ppuIncreasePos;
|
||||
if(!(PPU_Reg[0] & PPU_ENABLE))
|
||||
return true;
|
||||
if(PPU_Reg[4] < 144)
|
||||
@ -86,6 +114,7 @@ bool ppuCycle()
|
||||
ppuOAMpos = 0; //Reset check pos
|
||||
ppuOAM2pos = 0; //Reset array pos
|
||||
ppuMode = 2; //OAM
|
||||
ppuHBlank = false;
|
||||
if(PPU_Reg[1]&PPU_OAM_IRQ)
|
||||
{
|
||||
//printf("OAM STAT IRQ\n");
|
||||
@ -96,10 +125,12 @@ bool ppuCycle()
|
||||
{
|
||||
ppuDots = 0; //Reset Draw Pos
|
||||
ppuMode = 3; //Main Mode
|
||||
ppuHBlank = false;
|
||||
}
|
||||
if(ppuClock == 252)
|
||||
{
|
||||
ppuMode = 0; //HBlank
|
||||
ppuHBlank = true;
|
||||
if(PPU_Reg[1]&PPU_HBLANK_IRQ)
|
||||
{
|
||||
//printf("HBlank STAT IRQ\n");
|
||||
@ -126,76 +157,13 @@ bool ppuCycle()
|
||||
if(ppuClock >= 80 && ppuClock < 240)
|
||||
{
|
||||
//makes it possible to draw 160x144 in here :)
|
||||
uint8_t ChrRegA = 0, ChrRegB = 0, color = 0, tCol = 0;
|
||||
if(PPU_Reg[0]&PPU_BG_ENABLE)
|
||||
{
|
||||
uint8_t bgXPos = ppuDots+PPU_Reg[3];
|
||||
uint8_t bgYPos = PPU_Reg[4]+PPU_Reg[2];
|
||||
uint16_t vramTilePos = ((PPU_Reg[0]&PPU_BG_TILEMAP_UP)?0x1C00:0x1800)+(((bgXPos/8)+(bgYPos/8*32))&0x3FFF);
|
||||
if(PPU_Reg[0]&PPU_BG_TILEDAT_LOW)
|
||||
{
|
||||
uint8_t tVal = PPU_VRAM[vramTilePos&0x1FFF];
|
||||
uint16_t tPos = tVal*16;
|
||||
tPos+=(bgYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[(tPos)&0x1FFF];
|
||||
ChrRegB = PPU_VRAM[(tPos+1)&0x1FFF];
|
||||
}
|
||||
else
|
||||
{
|
||||
int8_t tVal = (int8_t)PPU_VRAM[vramTilePos&0x1FFF];
|
||||
int16_t tPos = tVal*16;
|
||||
tPos+=(bgYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[(0x1000+tPos)&0x1FFF];
|
||||
ChrRegB = PPU_VRAM[(0x1000+tPos+1)&0x1FFF];
|
||||
}
|
||||
if(ChrRegA & (0x80>>(bgXPos&7)))
|
||||
color |= 1;
|
||||
if(ChrRegB & (0x80>>(bgXPos&7)))
|
||||
color |= 2;
|
||||
//again, not sure
|
||||
tCol = (~(PPU_Reg[7]>>(color*2)))&3;
|
||||
}
|
||||
if(PPU_Reg[0]&PPU_WINDOW_ENABLE && (PPU_Reg[0xB]) <= ppuDots+7 && PPU_Reg[0xA] <= PPU_Reg[4])
|
||||
{
|
||||
uint8_t windowXPos = ppuDots+7-PPU_Reg[0xB];
|
||||
uint8_t windowYPos = PPU_Reg[4]-PPU_Reg[0xA];
|
||||
uint16_t vramTilePos = ((PPU_Reg[0]&PPU_WINDOW_TILEMAP_UP)?0x1C00:0x1800)+(((windowXPos/8)+(windowYPos/8*32))&0x3FF);
|
||||
if(PPU_Reg[0]&PPU_BG_TILEDAT_LOW)
|
||||
{
|
||||
uint8_t tVal = PPU_VRAM[vramTilePos&0x1FFF];
|
||||
uint16_t tPos = tVal*16;
|
||||
tPos+=(windowYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[(tPos)&0x1FFF];
|
||||
ChrRegB = PPU_VRAM[(tPos+1)&0x1FFF];
|
||||
}
|
||||
else
|
||||
{
|
||||
int8_t tVal = (int8_t)PPU_VRAM[vramTilePos&0x1FFF];
|
||||
int16_t tPos = tVal*16;
|
||||
tPos+=(windowYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[(0x1000+tPos)&0x1FFF];
|
||||
ChrRegB = PPU_VRAM[(0x1000+tPos+1)&0x1FFF];
|
||||
}
|
||||
color = 0;
|
||||
if(ChrRegA & (0x80>>(windowXPos&7)))
|
||||
color |= 1;
|
||||
if(ChrRegB & (0x80>>(windowXPos&7)))
|
||||
color |= 2;
|
||||
//again, not sure
|
||||
tCol = (~(PPU_Reg[7]>>(color*2)))&3;
|
||||
}
|
||||
if(PPU_Reg[0]&PPU_SPRITE_ENABLE)
|
||||
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);
|
||||
textureImage[drawPos] = draw;
|
||||
textureImage[drawPos+1] = draw;
|
||||
textureImage[drawPos+2] = draw;
|
||||
}
|
||||
size_t drawPos = (ppuDots*4)+(PPU_Reg[4]*160*4);
|
||||
ppuDrawDot(drawPos);
|
||||
ppuDots++;
|
||||
}
|
||||
}
|
||||
ppuIncreasePos:
|
||||
/* increase pos */
|
||||
ppuTestClock++;
|
||||
ppuClock++;
|
||||
if(ppuClock == 456)
|
||||
@ -213,6 +181,7 @@ bool ppuCycle()
|
||||
if(PPU_Reg[4] == 144)
|
||||
{
|
||||
ppuMode = 1; //VBlank
|
||||
ppuHBlank = false;
|
||||
ppuVBlank = true;
|
||||
memEnableVBlankIrq();
|
||||
if(PPU_Reg[1]&PPU_VBLANK_IRQ)
|
||||
@ -254,7 +223,12 @@ uint8_t ppuGet8(uint16_t addr)
|
||||
if(addr >= 0x8000 && addr < 0xA000)
|
||||
{
|
||||
if(!(PPU_Reg[0] & PPU_ENABLE) || (ppuMode != 3))
|
||||
val = PPU_VRAM[addr&0x1FFF];
|
||||
{
|
||||
if(!allowCgbRegs)
|
||||
val = PPU_VRAM[addr&0x1FFF];
|
||||
else
|
||||
val = PPU_VRAM[(ppuCgbBank<<13)|(addr&0x1FFF)];
|
||||
}
|
||||
else
|
||||
val = 0xFF;
|
||||
}
|
||||
@ -279,6 +253,17 @@ uint8_t ppuGet8(uint16_t addr)
|
||||
//if(addr != 0xFF44)
|
||||
// printf("at instr %04x:ppuGet8(%04x, %02x)\n",cpuCurPC(),addr,val);
|
||||
}
|
||||
else if(addr >= 0xFF68 && addr < 0xFF6C)
|
||||
{
|
||||
if(addr == 0xFF68)
|
||||
val = ppuCgbBgPalPos;
|
||||
else if(addr == 0xFF69)
|
||||
val = PPU_CGB_BGPAL[ppuCgbBgPalPos&0x3F];
|
||||
else if(addr == 0xFF6A)
|
||||
val = ppuCgbObjPalPos;
|
||||
else if(addr == 0xFF6B)
|
||||
val = PPU_CGB_OBJPAL[ppuCgbObjPalPos&0x3F];
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -287,7 +272,12 @@ void ppuSet8(uint16_t addr, uint8_t val)
|
||||
if(addr >= 0x8000 && addr < 0xA000)
|
||||
{
|
||||
if(!(PPU_Reg[0] & PPU_ENABLE) || (ppuMode != 3))
|
||||
PPU_VRAM[addr&0x1FFF] = val;
|
||||
{
|
||||
if(!allowCgbRegs)
|
||||
PPU_VRAM[addr&0x1FFF] = val;
|
||||
else
|
||||
PPU_VRAM[(ppuCgbBank<<13)|(addr&0x1FFF)] = val;
|
||||
}
|
||||
}
|
||||
else if(addr >= 0xFE00 && addr < 0xFEA0)
|
||||
{
|
||||
@ -320,6 +310,27 @@ void ppuSet8(uint16_t addr, uint8_t val)
|
||||
// printf("ppuSet8(%04x, %02x)\n",addr,val);
|
||||
}
|
||||
}
|
||||
else if(addr >= 0xFF68 && addr < 0xFF6C)
|
||||
{
|
||||
if(addr == 0xFF68)
|
||||
ppuCgbBgPalPos = val;
|
||||
else if(addr == 0xFF69)
|
||||
{
|
||||
//printf("BG Write %02x to %02x\n", val, ppuCgbBgPalPos&0x3F);
|
||||
PPU_CGB_BGPAL[ppuCgbBgPalPos&0x3F] = val;
|
||||
if(ppuCgbBgPalPos&0x80) //auto-increment
|
||||
ppuCgbBgPalPos = ((ppuCgbBgPalPos+1)&0x3F)|0x80;
|
||||
}
|
||||
else if(addr == 0xFF6A)
|
||||
ppuCgbObjPalPos = val;
|
||||
else if(addr == 0xFF6B)
|
||||
{
|
||||
//printf("OBJ Write %02x to %02x\n", val, ppuCgbObjPalPos&0x3F);
|
||||
PPU_CGB_OBJPAL[ppuCgbObjPalPos&0x3F] = val;
|
||||
if(ppuCgbObjPalPos&0x80) //auto-increment
|
||||
ppuCgbObjPalPos = ((ppuCgbObjPalPos+1)&0x3F)|0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ppuDumpMem()
|
||||
@ -327,7 +338,7 @@ void ppuDumpMem()
|
||||
FILE *f = fopen("PPU_VRAM.bin","wb");
|
||||
if(f)
|
||||
{
|
||||
fwrite(PPU_VRAM,1,0x2000,f);
|
||||
fwrite(PPU_VRAM,1,allowCgbRegs?0x4000:0x2000,f);
|
||||
fclose(f);
|
||||
}
|
||||
f = fopen("PPU_OAM.bin","wb");
|
||||
@ -360,7 +371,23 @@ bool ppuInVBlank()
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint8_t ppuDoSprites(uint8_t color, uint8_t tCol)
|
||||
bool ppuInHBlank()
|
||||
{
|
||||
if(ppuHBlank)
|
||||
{
|
||||
if(ppuHBlankTriggered == false)
|
||||
{
|
||||
ppuHBlankTriggered = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
ppuHBlankTriggered = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint8_t ppuDoSpritesDMG(uint8_t color, uint8_t tCol)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t cSpriteAnd = (PPU_Reg[0] & PPU_SPRITE_8_16) ? 15 : 7;
|
||||
@ -387,7 +414,7 @@ static uint8_t ppuDoSprites(uint8_t color, uint8_t tCol)
|
||||
cSpriteAdd = 16;
|
||||
cSpriteY &= 7;
|
||||
}
|
||||
if(cSpriteByte3 & PPU_SPRITE_FLIP_Y)
|
||||
if(cSpriteByte3 & PPU_TILE_FLIP_Y)
|
||||
{
|
||||
cSpriteY ^= 7;
|
||||
if(PPU_Reg[0] & PPU_SPRITE_8_16)
|
||||
@ -399,7 +426,7 @@ static uint8_t ppuDoSprites(uint8_t color, uint8_t tCol)
|
||||
ChrRegB = PPU_VRAM[(tPos+cSpriteAdd+1)&0x1FFF];
|
||||
|
||||
uint8_t cSpriteX = (ppuDots - OAMcXpos)&7;
|
||||
if(cSpriteByte3 & PPU_SPRITE_FLIP_X)
|
||||
if(cSpriteByte3 & PPU_TILE_FLIP_X)
|
||||
cSpriteX ^= 7;
|
||||
uint8_t sprCol = 0;
|
||||
if(ChrRegA & (0x80>>cSpriteX))
|
||||
@ -414,10 +441,10 @@ static uint8_t ppuDoSprites(uint8_t color, uint8_t tCol)
|
||||
if(cPrioSpriteX < OAMcXpos)
|
||||
continue;
|
||||
//sprite has highest priority, return sprite
|
||||
if((cSpriteByte3 & PPU_SPRITE_PRIO) == 0)
|
||||
if((cSpriteByte3 & PPU_TILE_PRIO) == 0)
|
||||
{
|
||||
//sprite so far has highest prio so set color
|
||||
if(cSpriteByte3 & PPU_SPRITE_PAL)
|
||||
if(cSpriteByte3 & PPU_TILE_DMG_PAL)
|
||||
tCol = (~(PPU_Reg[9]>>(sprCol*2)))&3;
|
||||
else
|
||||
tCol = (~(PPU_Reg[8]>>(sprCol*2)))&3;
|
||||
@ -428,7 +455,7 @@ static uint8_t ppuDoSprites(uint8_t color, uint8_t tCol)
|
||||
else if((color&3) != 0)
|
||||
continue;
|
||||
//background is 0 so set color
|
||||
if(cSpriteByte3 & PPU_SPRITE_PAL)
|
||||
if(cSpriteByte3 & PPU_TILE_DMG_PAL)
|
||||
tCol = (~(PPU_Reg[9]>>(sprCol*2)))&3;
|
||||
else
|
||||
tCol = (~(PPU_Reg[8]>>(sprCol*2)))&3;
|
||||
@ -441,3 +468,226 @@ static uint8_t ppuDoSprites(uint8_t color, uint8_t tCol)
|
||||
}
|
||||
return tCol;
|
||||
}
|
||||
|
||||
static void ppuDrawDotDMG(size_t drawPos)
|
||||
{
|
||||
uint8_t ChrRegA = 0, ChrRegB = 0, color = 0, tCol = 0;
|
||||
if(PPU_Reg[0]&PPU_BG_ENABLE)
|
||||
{
|
||||
uint8_t bgXPos = ppuDots+PPU_Reg[3];
|
||||
uint8_t bgYPos = PPU_Reg[4]+PPU_Reg[2];
|
||||
uint16_t vramTilePos = ((PPU_Reg[0]&PPU_BG_TILEMAP_UP)?0x1C00:0x1800)+(((bgXPos/8)+(bgYPos/8*32))&0x3FF);
|
||||
if(PPU_Reg[0]&PPU_BG_TILEDAT_LOW)
|
||||
{
|
||||
uint8_t tVal = PPU_VRAM[vramTilePos&0x1FFF];
|
||||
uint16_t tPos = tVal*16;
|
||||
tPos+=(bgYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[(tPos)&0x1FFF];
|
||||
ChrRegB = PPU_VRAM[(tPos+1)&0x1FFF];
|
||||
}
|
||||
else
|
||||
{
|
||||
int8_t tVal = (int8_t)PPU_VRAM[vramTilePos&0x1FFF];
|
||||
int16_t tPos = tVal*16;
|
||||
tPos+=(bgYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[(0x1000+tPos)&0x1FFF];
|
||||
ChrRegB = PPU_VRAM[(0x1000+tPos+1)&0x1FFF];
|
||||
}
|
||||
if(ChrRegA & (0x80>>(bgXPos&7)))
|
||||
color |= 1;
|
||||
if(ChrRegB & (0x80>>(bgXPos&7)))
|
||||
color |= 2;
|
||||
//again, not sure
|
||||
tCol = (~(PPU_Reg[7]>>(color*2)))&3;
|
||||
}
|
||||
if(PPU_Reg[0]&PPU_WINDOW_ENABLE && (PPU_Reg[0xB]) <= ppuDots+7 && PPU_Reg[0xA] <= PPU_Reg[4])
|
||||
{
|
||||
uint8_t windowXPos = ppuDots+7-PPU_Reg[0xB];
|
||||
uint8_t windowYPos = PPU_Reg[4]-PPU_Reg[0xA];
|
||||
uint16_t vramTilePos = ((PPU_Reg[0]&PPU_WINDOW_TILEMAP_UP)?0x1C00:0x1800)+(((windowXPos/8)+(windowYPos/8*32))&0x3FF);
|
||||
if(PPU_Reg[0]&PPU_BG_TILEDAT_LOW)
|
||||
{
|
||||
uint8_t tVal = PPU_VRAM[vramTilePos&0x1FFF];
|
||||
uint16_t tPos = tVal*16;
|
||||
tPos+=(windowYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[(tPos)&0x1FFF];
|
||||
ChrRegB = PPU_VRAM[(tPos+1)&0x1FFF];
|
||||
}
|
||||
else
|
||||
{
|
||||
int8_t tVal = (int8_t)PPU_VRAM[vramTilePos&0x1FFF];
|
||||
int16_t tPos = tVal*16;
|
||||
tPos+=(windowYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[(0x1000+tPos)&0x1FFF];
|
||||
ChrRegB = PPU_VRAM[(0x1000+tPos+1)&0x1FFF];
|
||||
}
|
||||
color = 0;
|
||||
if(ChrRegA & (0x80>>(windowXPos&7)))
|
||||
color |= 1;
|
||||
if(ChrRegB & (0x80>>(windowXPos&7)))
|
||||
color |= 2;
|
||||
//again, not sure
|
||||
tCol = (~(PPU_Reg[7]>>(color*2)))&3;
|
||||
}
|
||||
if(PPU_Reg[0]&PPU_SPRITE_ENABLE)
|
||||
tCol = ppuDoSpritesDMG(color, tCol);
|
||||
uint8_t draw = (tCol == 0) ? 0 : (tCol == 1) ? 0x55 : (tCol == 2) ? 0xAA : 0xFF;
|
||||
textureImage[drawPos] = draw;
|
||||
textureImage[drawPos+1] = draw;
|
||||
textureImage[drawPos+2] = draw;
|
||||
}
|
||||
|
||||
static uint16_t ppuDoSpritesCGB(uint8_t color, uint16_t cgbRGB)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t cSpriteAnd = (PPU_Reg[0] & PPU_SPRITE_8_16) ? 15 : 7;
|
||||
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];
|
||||
uint16_t tCgbBank = (cSpriteByte3&PPU_TILE_CGB_BANK)?0x2000:0x0;
|
||||
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_TILE_FLIP_Y)
|
||||
{
|
||||
cSpriteY ^= 7;
|
||||
if(PPU_Reg[0] & PPU_SPRITE_8_16)
|
||||
cSpriteAdd ^= 16; //8 by 16 select
|
||||
}
|
||||
tPos+=(cSpriteY)*2;
|
||||
|
||||
ChrRegA = PPU_VRAM[tCgbBank|((tPos+cSpriteAdd)&0x1FFF)];
|
||||
ChrRegB = PPU_VRAM[tCgbBank|((tPos+cSpriteAdd+1)&0x1FFF)];
|
||||
|
||||
uint8_t cSpriteX = (ppuDots - OAMcXpos)&7;
|
||||
if(cSpriteByte3 & PPU_TILE_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)
|
||||
{
|
||||
//BG Master Disable, return sprite
|
||||
if((PPU_Reg[0] & PPU_BG_WINDOW_PRIO) == 0)
|
||||
{
|
||||
uint8_t pByte = ((cSpriteByte3&7)<<3)|(sprCol<<1);
|
||||
cgbRGB = (PPU_CGB_OBJPAL[pByte])|(PPU_CGB_OBJPAL[pByte+1]<<8);
|
||||
break;
|
||||
}
|
||||
//sprite has highest priority, return sprite
|
||||
if((cSpriteByte3 & PPU_TILE_PRIO) == 0)
|
||||
{
|
||||
uint8_t pByte = ((cSpriteByte3&7)<<3)|(sprCol<<1);
|
||||
cgbRGB = (PPU_CGB_OBJPAL[pByte])|(PPU_CGB_OBJPAL[pByte+1]<<8);
|
||||
break;
|
||||
} //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
|
||||
uint8_t pByte = ((cSpriteByte3&7)<<3)|(sprCol<<1);
|
||||
cgbRGB = (PPU_CGB_OBJPAL[pByte])|(PPU_CGB_OBJPAL[pByte+1]<<8);
|
||||
break;
|
||||
}
|
||||
//Sprite is 0, keep looking for sprites
|
||||
}
|
||||
}
|
||||
return cgbRGB;
|
||||
}
|
||||
|
||||
static void ppuDrawDotCGB(size_t drawPos)
|
||||
{
|
||||
uint8_t ChrRegA = 0, ChrRegB = 0, color = 0;
|
||||
uint8_t bgXPos = ppuDots+PPU_Reg[3];
|
||||
uint8_t bgYPos = PPU_Reg[4]+PPU_Reg[2];
|
||||
uint16_t vramTilePos = ((PPU_Reg[0]&PPU_BG_TILEMAP_UP)?0x1C00:0x1800)+(((bgXPos/8)+(bgYPos/8*32))&0x3FF);
|
||||
uint8_t tCgbVal = PPU_VRAM[0x2000|(vramTilePos&0x1FFF)];
|
||||
uint16_t tCgbBank = (tCgbVal&PPU_TILE_CGB_BANK)?0x2000:0x0;
|
||||
if(tCgbVal & PPU_TILE_FLIP_Y)
|
||||
bgYPos ^= 7;
|
||||
if(PPU_Reg[0]&PPU_BG_TILEDAT_LOW)
|
||||
{
|
||||
uint8_t tVal = PPU_VRAM[vramTilePos&0x1FFF];
|
||||
uint16_t tPos = tVal*16;
|
||||
tPos+=(bgYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[tCgbBank|((tPos)&0x1FFF)];
|
||||
ChrRegB = PPU_VRAM[tCgbBank|((tPos+1)&0x1FFF)];
|
||||
}
|
||||
else
|
||||
{
|
||||
int8_t tVal = (int8_t)PPU_VRAM[vramTilePos&0x1FFF];
|
||||
int16_t tPos = tVal*16;
|
||||
tPos+=(bgYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[tCgbBank|((0x1000+tPos)&0x1FFF)];
|
||||
ChrRegB = PPU_VRAM[tCgbBank|((0x1000+tPos+1)&0x1FFF)];
|
||||
}
|
||||
if(tCgbVal & PPU_TILE_FLIP_X)
|
||||
bgXPos ^= 7;
|
||||
if(ChrRegA & (0x80>>(bgXPos&7)))
|
||||
color |= 1;
|
||||
if(ChrRegB & (0x80>>(bgXPos&7)))
|
||||
color |= 2;
|
||||
bool bgHighestPrio = (color && (tCgbVal & PPU_TILE_PRIO) && (PPU_Reg[0] & PPU_BG_WINDOW_PRIO));
|
||||
uint8_t pByte = ((tCgbVal&7)<<3)|(color<<1);
|
||||
uint16_t cgbRGB = (PPU_CGB_BGPAL[pByte])|(PPU_CGB_BGPAL[pByte+1]<<8);
|
||||
if(PPU_Reg[0]&PPU_WINDOW_ENABLE && (PPU_Reg[0xB]) <= ppuDots+7 && PPU_Reg[0xA] <= PPU_Reg[4])
|
||||
{
|
||||
uint8_t windowXPos = ppuDots+7-PPU_Reg[0xB];
|
||||
uint8_t windowYPos = PPU_Reg[4]-PPU_Reg[0xA];
|
||||
vramTilePos = ((PPU_Reg[0]&PPU_WINDOW_TILEMAP_UP)?0x1C00:0x1800)+(((windowXPos/8)+(windowYPos/8*32))&0x3FF);
|
||||
tCgbVal = PPU_VRAM[0x2000|(vramTilePos&0x1FFF)];
|
||||
tCgbBank = (tCgbVal&PPU_TILE_CGB_BANK)?0x2000:0x0;
|
||||
bgHighestPrio = (color && (tCgbVal & PPU_TILE_PRIO) && (PPU_Reg[0] & PPU_BG_WINDOW_PRIO));
|
||||
if(tCgbVal & PPU_TILE_FLIP_Y)
|
||||
windowYPos ^= 7;
|
||||
if(PPU_Reg[0]&PPU_BG_TILEDAT_LOW)
|
||||
{
|
||||
uint8_t tVal = PPU_VRAM[vramTilePos&0x1FFF];
|
||||
uint16_t tPos = tVal*16;
|
||||
tPos+=(windowYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[tCgbBank|((tPos)&0x1FFF)];
|
||||
ChrRegB = PPU_VRAM[tCgbBank|((tPos+1)&0x1FFF)];
|
||||
}
|
||||
else
|
||||
{
|
||||
int8_t tVal = (int8_t)PPU_VRAM[vramTilePos&0x1FFF];
|
||||
int16_t tPos = tVal*16;
|
||||
tPos+=(windowYPos&7)*2;
|
||||
ChrRegA = PPU_VRAM[tCgbBank|((0x1000+tPos)&0x1FFF)];
|
||||
ChrRegB = PPU_VRAM[tCgbBank|((0x1000+tPos+1)&0x1FFF)];
|
||||
}
|
||||
if(tCgbVal & PPU_TILE_FLIP_X)
|
||||
windowXPos ^= 7;
|
||||
color = 0;
|
||||
if(ChrRegA & (0x80>>(windowXPos&7)))
|
||||
color |= 1;
|
||||
if(ChrRegB & (0x80>>(windowXPos&7)))
|
||||
color |= 2;
|
||||
pByte = ((tCgbVal&7)<<3)|(color<<1);
|
||||
cgbRGB = (PPU_CGB_BGPAL[pByte])|(PPU_CGB_BGPAL[pByte+1]<<8);
|
||||
}
|
||||
if(!bgHighestPrio && PPU_Reg[0]&PPU_SPRITE_ENABLE)
|
||||
cgbRGB = ppuDoSpritesCGB(color, cgbRGB);
|
||||
textureImage[drawPos] = ((cgbRGB>>10)&0x1F)<<3; //Blue
|
||||
textureImage[drawPos+1] = ((cgbRGB>>5)&0x1F)<<3; //Green
|
||||
textureImage[drawPos+2] = (cgbRGB&0x1F)<<3; //Red
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user