Skip to content

Commit

Permalink
PPU: emulate BG enable delay; update internal BG X/Y only if the BG i…
Browse files Browse the repository at this point in the history
…s enabled (fixes #94, fixes #177).
  • Loading branch information
fleroviux committed Sep 22, 2021
1 parent fa5792c commit 754271c
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 24 deletions.
2 changes: 1 addition & 1 deletion docs/ACCURACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Multiply Long | 72 | 52 | 52 | 52 |
BIOS math | 615 | 615 | 615 | 615 | 615 |
DMA tests | 1256 | 1256 | 1232 | 1032 | 1212 |
Misc Edge Case| 10 | 7 - 8 | 7 - 8 | 7 | 1 |
Layer Toggle | 1 | fail | pass | pass | fail |
Layer Toggle | 1 | pass | pass | pass | fail |
OAM Update | 1 | pass | fail | fail | fail |

In addition NanoBoyAdvance passes the following tests:
Expand Down
16 changes: 8 additions & 8 deletions src/nba/src/hw/ppu/compose.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void PPU::RenderScanline() {
// BG Mode 0 - 240x160 pixels, Text mode
case 0: {
for (int i = 0; i < 4; i++) {
if (mmio.dispcnt.enable[i]) {
if (enable_bg[i]) {
RenderLayerText(i);
}
}
Expand All @@ -49,11 +49,11 @@ void PPU::RenderScanline() {
// BG Mode 1 - 240x160 pixels, Text and RS mode mixed
case 1: {
for (int i = 0; i < 2; i++) {
if (mmio.dispcnt.enable[i]) {
if (enable_bg[i]) {
RenderLayerText(i);
}
}
if (mmio.dispcnt.enable[ENABLE_BG2]) {
if (enable_bg[ENABLE_BG2]) {
RenderLayerAffine(0);
}
ComposeScanline(0, 2);
Expand All @@ -62,7 +62,7 @@ void PPU::RenderScanline() {
// BG Mode 2 - 240x160 pixels, RS mode
case 2: {
for (int i = 0; i < 2; i++) {
if (mmio.dispcnt.enable[2 + i]) {
if (enable_bg[2 + i]) {
RenderLayerAffine(i);
}
}
Expand All @@ -71,23 +71,23 @@ void PPU::RenderScanline() {
}
// BG Mode 3 - 240x160 pixels, 32768 colors
case 3: {
if (mmio.dispcnt.enable[2]) {
if (enable_bg[2]) {
RenderLayerBitmap1();
}
ComposeScanline(2, 2);
break;
}
// BG Mode 4 - 240x160 pixels, 256 colors (out of 32768 colors)
case 4: {
if (mmio.dispcnt.enable[2]) {
if (enable_bg[2]) {
RenderLayerBitmap2();
}
ComposeScanline(2, 2);
break;
}
// BG Mode 5 - 160x128 pixels, 32768 colors
case 5: {
if (mmio.dispcnt.enable[2]) {
if (enable_bg[2]) {
RenderLayerBitmap3();
}
ComposeScanline(2, 2);
Expand Down Expand Up @@ -122,7 +122,7 @@ void PPU::ComposeScanlineTmpl(int bg_min, int bg_max) {
// Sort enabled backgrounds by their respective priority in ascending order.
for (int prio = 3; prio >= 0; prio--) {
for (int bg = bg_max; bg >= bg_min; bg--) {
if (dispcnt.enable[bg] && bgcnt[bg].priority == prio) {
if (enable_bg[bg] && bgcnt[bg].priority == prio) {
bg_list[bg_count++] = bg;
}
}
Expand Down
43 changes: 33 additions & 10 deletions src/nba/src/hw/ppu/ppu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ PPU::PPU(
, irq(irq)
, dma(dma)
, config(config) {
Reset();
mmio.dispcnt.ppu = this;
mmio.dispstat.ppu = this;
Reset();
}

void PPU::Reset() {
Expand All @@ -34,6 +35,9 @@ void PPU::Reset() {
mmio.vcount = 0;

for (int i = 0; i < 4; i++) {
enable_bg[i] = false;
enable_bg_delay[i] = 0;

mmio.bgcnt[i].Reset();
mmio.bghofs[i] = 0;
mmio.bgvofs[i] = 0;
Expand Down Expand Up @@ -65,6 +69,16 @@ void PPU::Reset() {
scheduler.Add(1006, this, &PPU::OnScanlineComplete);
}

void PPU::EnablePendingBGs() {
for (int i = 0; i < 4; i++) {
if (enable_bg_delay[i] > 0) {
if (--enable_bg_delay[i] == 0) {
enable_bg[i] = true;
}
}
}
}

void PPU::CheckVerticalCounterIRQ() {
auto& dispstat = mmio.dispstat;
auto vcount_flag_new = dispstat.vcount_setting == mmio.vcount;
Expand Down Expand Up @@ -108,20 +122,26 @@ void PPU::OnScanlineComplete(int cycles_late) {
}

/* Mode 0 doesn't have any affine backgrounds,
* in that case the internal registers seemingly aren't updated.
* TODO: needs more research, e.g. what happens if all affine backgrounds are disabled?
* in that case the internal X/Y registers will never be updated.
*/
if (mmio.dispcnt.mode != 0) {
// Advance internal affine registers and apply vertical mosaic if applicable.
for (int i = 0; i < 2; i++) {
if (mmio.bgcnt[2 + i].mosaic_enable) {
if (mosaic.bg._counter_y == 0) {
bgx[i]._current += mosaic.bg.size_y * bgpb[i];
bgy[i]._current += mosaic.bg.size_y * bgpd[i];
/* Do not update internal X/Y unless the latched BG enable bit is set.
* This behavior was confirmed on real hardware.
*/
auto bg_id = 2 + i;

if (enable_bg[bg_id]) {
if (mmio.bgcnt[bg_id].mosaic_enable) {
if (mosaic.bg._counter_y == 0) {
bgx[i]._current += mosaic.bg.size_y * bgpb[i];
bgy[i]._current += mosaic.bg.size_y * bgpd[i];
}
} else {
bgx[i]._current += bgpb[i];
bgy[i]._current += bgpd[i];
}
} else {
bgx[i]._current += bgpb[i];
bgy[i]._current += bgpd[i];
}
}
}
Expand All @@ -138,6 +158,7 @@ void PPU::OnHblankComplete(int cycles_late) {
dispstat.hblank_flag = 0;
vcount++;
CheckVerticalCounterIRQ();
EnablePendingBGs();

if (dispcnt.enable[ENABLE_WIN0]) {
RenderWindow(0);
Expand Down Expand Up @@ -215,6 +236,8 @@ void PPU::OnVblankHblankComplete(int cycles_late) {
}
}

EnablePendingBGs();

if (mmio.dispcnt.enable[ENABLE_WIN0]) {
RenderWindow(0);
}
Expand Down
4 changes: 4 additions & 0 deletions src/nba/src/hw/ppu/ppu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ struct PPU {
int evy;
} mmio;

bool enable_bg[4];
int enable_bg_delay[4];

private:
friend struct DisplayStatus;

Expand Down Expand Up @@ -148,6 +151,7 @@ struct PPU {
ENABLE_OBJWIN = 7
};

void EnablePendingBGs();
void CheckVerticalCounterIRQ();
void OnScanlineComplete(int cycles_late);
void OnHblankComplete(int cycles_late);
Expand Down
17 changes: 12 additions & 5 deletions src/nba/src/hw/ppu/registers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ auto DisplayControl::Read(int address) -> u8 {
}

void DisplayControl::Write(int address, u8 value) {
auto mode_old = mode;
switch (address) {
case 0:
mode = value & 7;
Expand All @@ -50,7 +49,17 @@ void DisplayControl::Write(int address, u8 value) {
forced_blank = (value >> 7) & 1;
break;
case 1:
for (int i = 0; i < 8; i++) {
for (int i = 0; i < 4; i++) {
auto enable_new = (value >> i) & 1;
if (!enable[i] && enable_new) {
ppu->enable_bg_delay[i] = 3;
} else {
ppu->enable_bg[i] = enable_new;
ppu->enable_bg_delay[i] = 0;
}
enable[i] = enable_new;
}
for (int i = 4; i < 8; i++) {
enable[i] = (value >> i) & 1;
}
break;
Expand Down Expand Up @@ -90,9 +99,7 @@ void DisplayStatus::Write(int address, u8 value) {
break;
}

if (ppu != nullptr) {
ppu->CheckVerticalCounterIRQ();
}
ppu->CheckVerticalCounterIRQ();
}

void BackgroundControl::Reset() {
Expand Down
2 changes: 2 additions & 0 deletions src/nba/src/hw/ppu/registers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ struct DisplayControl {
void Reset();
auto Read(int address) -> u8;
void Write(int address, u8 value);

PPU* ppu = nullptr;
};

struct DisplayStatus {
Expand Down

0 comments on commit 754271c

Please sign in to comment.