[ppu] Make registers loopy

Change the PPU register engines to use the proper behaviour
    as described by loopy and documented in the nesdev wiki
    (https://www.nesdev.org/wiki/PPU_scrolling) <- here
    Thus, ppu.address and ppu.scroll become obsolete and replace
    with ppu.tReg and ppu.vReg that contain both, but in different
    moments in time, we sometimes sync t -> v at certain triggers.

    Basically we work with t and read from v, and sync some parts
    between them when neccesary, loopy does a great job at documenting
    this.

    !!! PPU.ADDRESS and PPU.SCROLL ARE NOW OBSOLETE, WILL BE RM'D
main
Joaquin 3 years ago
parent bb61cc15b0
commit a775024844
Signed by: puly
GPG Key ID: 9E9299CD96C65EC6
  1. 62
      ppu.c
  2. 38
      ppu.h

62
ppu.c

@ -48,6 +48,7 @@ void ppuRegWrite(word address, byte data){
case 0: //ppuctrl
ppu.control.full = data;
ppu.tReg.bits.nameTableID = ppu.control.nameTableID;
break;
@ -73,35 +74,55 @@ void ppuRegWrite(word address, byte data){
case 5: //ppuScroll
if(ppu.scroll.expectingY){
ppu.scroll.y = data;
if(ppu.expectingLsb){
//second write
ppu.tReg.bits.fineY = data & 0b00000111; //set fineY
ppu.tReg.bits.coarseY = data >> 3; //set coarse Y
}else{
ppu.scroll.x = data;
//first write
ppu.xReg = data & 0b00000111; //set fineX
ppu.tReg.bits.coarseX = data >> 3; //set coarse X
}
ppu.scroll.expectingY = !ppu.scroll.expectingY;
ppu.expectingLsb = !ppu.expectingLsb;
break;
case 6: //ppuAddr
if(ppu.address.expectLsb){
ppu.address.lsb = data;
if(ppu.expectingLsb){
//second write
ppu.tReg.data = (ppu.tReg.data & 0xFF00) | (data & 0x00FF);
ppu.vReg.data = ppu.tReg.data; //IMPORTANT V SYNC
}else{
ppu.address.msb = data;
//first write
word buf;
buf = data & 0b00111111; //eliminate highest 2 bits
buf = buf << 8;
buf = buf | (ppu.tReg.data & 0xFF);
ppu.tReg.data = buf;
ppu.tReg.bits.fineY = ppu.tReg.bits.fineY & 0b011; //clear highest bit of fine Y
}
ppu.address.expectLsb = !ppu.address.expectLsb;
ppu.expectingLsb = !ppu.expectingLsb;
break;
case 7: //ppuData
ppuWrite(ppu.address.complete, data);
ppuWrite(ppu.vReg.data, data);
if(ppu.control.vramIncrement) ppu.address.complete += 32;
else ppu.address.complete++;
if(ppu.control.vramIncrement) ppu.vReg.data += 32;
else ppu.vReg.data++;
break;
default:
fprintf(stderr, "ERR: Invalid write to PPU register of address %llX\n!", address + 0x2000);
abort();
break;
}
}
@ -129,7 +150,7 @@ byte ppuRegRead(word address){ //send the registers to the bus so the components
return ppu.status.full;
ppu.status.vblank = 0;
ppu.address.expectLsb = false;
ppu.expectingLsb = false;
break;
@ -158,15 +179,20 @@ byte ppuRegRead(word address){ //send the registers to the bus so the components
returnData = ppu.dataByteBuffer; //reads are lagged back by one cycle, the data you read is the data of the last query
ppu.dataByteBuffer = ppuRead(ppu.address.complete);
ppu.dataByteBuffer = ppuRead(ppu.vReg.data);
if(ppu.address.complete >= 0x3F00 && 0x3FFF <= ppu.address.complete) //except when reading palette info
returnData = ppuRead(ppu.address.complete); //then the query is immediate
if(ppu.vReg.data >= 0x3F00 && 0x3FFF <= ppu.vReg.data) //except when reading palette info
returnData = ppuRead(ppu.vReg.data); //then the query is immediate
return returnData;
break;
default:
fprintf(stderr, "ERR: Invalid read to PPU register of address %llX\n!", address + 0x2000);
abort();
break;
}
}
@ -181,12 +207,14 @@ void dumpPpuBus(){
void initPpu(){
ppu.dataByteBuffer = 0;
ppu.address.expectLsb = 0;
ppu.scroll.expectingY = 0;
ppu.expectingLsb = 0;
ppu.control.full = 0;
ppu.dataByteBuffer = 0;
ppu.mask.full = 0;
ppu.status.full = 0;
ppu.vReg.bits.fixedOne = 1;
ppu.tReg.bits.fixedOne = 1;
}
void ppuClock(){

38
ppu.h

@ -26,7 +26,7 @@ typedef struct{
*/
union {
struct {
byte nametable : 2;
byte nameTableID : 2;
byte vramIncrement : 1;
byte spritePatternTable : 1;
byte backgroundPatternTable : 1;
@ -39,7 +39,6 @@ typedef struct{
struct {
byte x;
byte y;
byte expectingY : 1;
}scroll;
@ -94,10 +93,43 @@ typedef struct{
word complete;
};
byte expectLsb : 1;
}address;
byte dataByteBuffer;
struct{
union{
struct{
byte fixedOne : 1;
byte fineY : 3;
byte nameTableID : 2;
byte coarseY : 5;
byte coarseX : 5;
}bits;
word data;
};
}vReg; //Search for: "IMPORTANT V SYNC" in ppu.c to see moments where the 2 sync
struct{
union{
struct{
byte fixedOne : 1;
byte fineY : 3;
byte nameTableID : 2;
byte coarseY : 5;
byte coarseX : 5;
}bits;
word data;
};
}tReg; //Search for: "IMPORTANT V SYNC" in ppu.c to see moments where the 2 sync
byte xReg;
bool expectingLsb;
}PPU;
void initPpu();

Loading…
Cancel
Save