-added game boy color support

This commit is contained in:
FIX94 2017-05-25 05:01:55 +02:00
parent 096f760361
commit 35d91ce00c
No known key found for this signature in database
GPG Key ID: CE39016A19D8EADA
8 changed files with 580 additions and 126 deletions

2
apu.c
View File

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

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

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

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

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

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

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

1
ppu.h
View File

@ -14,6 +14,7 @@ bool ppuDrawDone();
uint8_t ppuGet8(uint16_t addr);
void ppuSet8(uint16_t addr, uint8_t val);
bool ppuInVBlank();
bool ppuInHBlank();
void ppuDumpMem();
#endif