Skip to content

Commit 4b490c1

Browse files
committed
ks_nes_draw: more fixes, bug documentaiton, and score improvements
1 parent ca57bda commit 4b490c1

2 files changed

Lines changed: 52 additions & 34 deletions

File tree

include/Famicom/ks_nes_common.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ typedef struct ksNesOAMEntry {
162162

163163
typedef struct ksNesDrawCtx {
164164
/* 0x0000 */ union {
165-
u8 sprite_scanline_limit[KS_NES_SCANLINE_COUNT]; // tracks the number of sprites that have been drawn on each scanline
165+
u8 sprite_scanline_limit[KS_NES_SCANLINE_SPRITE_OVERDRAW_COUNT]; // tracks the number of sprites that have been drawn on each scanline
166166
u8 scanline_y_coords[2 * 256]; // tracks the Y coordinate of the top & bottom of each scanline
167167
};
168168

src/static/Famicom/ks_nes_draw.cpp

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ void ksNesDrawClearEFBFirst(ksNesCommonWorkObj* wp) {
7474
GXClearVtxDesc();
7575
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
7676
GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
77-
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_RGBA4, 0);
77+
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
7878
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
7979
GXSetCurrentMtx(0);
8080
GXLoadPosMtxImm(wp->draw_ctx.draw_mtx, 0);
@@ -366,9 +366,9 @@ void ksNesDrawBG(ksNesCommonWorkObj* wp, ksNesStateObj* sp) {
366366
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
367367
GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
368368
GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
369-
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_RGBA4, 0);
369+
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
370370
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
371-
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_CLR_RGBA, GX_RGBX8, 10);
371+
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_S, GX_U16, 10);
372372
GXInitTexObj(&obj0, wp->draw_ctx.bg_palette_attr_texture, 40, 256, GX_TF_I4, GX_CLAMP, GX_REPEAT, 0);
373373
GXInitTexObjLOD(&obj0, GX_NEAR, GX_NEAR, 0.0, 0.0, 0.0, 0, 0, GX_ANISO_1);
374374
GXLoadTexObj(&obj0, GX_TEXMAP0);
@@ -478,7 +478,7 @@ void ksNesDrawBG(ksNesCommonWorkObj* wp, ksNesStateObj* sp) {
478478
GXClearVtxDesc();
479479
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
480480
GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
481-
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_RGBA4, 0);
481+
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
482482
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
483483
GXSetTevDirect(GX_TEVSTAGE0);
484484
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
@@ -577,11 +577,21 @@ void ksNesDrawOBJ(ksNesCommonWorkObj* wp, ksNesStateObj* state, u32 sprite_prior
577577
wp->draw_ctx.sprite_scanline_limit[i] = i < KS_NES_SCANLINE_COUNT ? ((state->frame_flags & KS_NES_FLAG_NINES_OVER_MODE) ? 255 : KS_NES_SPRITES_PER_SCANLINE) : 0;
578578

579579
// Disable sprites if the ppu's config on this scanline doesn't have sprites enabled.
580+
// @BUG - ppu_scanline_regs only has 240 entries, but this loop iterates 272 times.
581+
// When i >= 240, this reads 32 bytes past the array into OAMTable memory,
582+
// treating random OAM data as PPU mask flags to decide if sprites should be disabled.
583+
#ifndef BUGFIXES
580584
if ((wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SHOW_SPRITES) == 0) {
581585
wp->draw_ctx.sprite_scanline_limit[i] = 0;
582586
}
587+
#else
588+
if (i < KS_NES_SCANLINE_COUNT && (wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SHOW_SPRITES) == 0) {
589+
wp->draw_ctx.sprite_scanline_limit[i] = 0;
590+
}
591+
#endif
583592
}
584593

594+
585595
if (state->prg_size == 0x40000 && memcmp(state->prgromp + 0x3ffe9, "MARIO 3", 7) == 0) {
586596
for (i = 0; i < ARRAY_COUNT(wp->draw_ctx.ppu_scanline_regs); i++) {
587597
// This is a hack for SMB3 where the emulator was drawing sprites (OBJ) on transition screens because
@@ -599,42 +609,47 @@ void ksNesDrawOBJ(ksNesCommonWorkObj* wp, ksNesStateObj* state, u32 sprite_prior
599609
int a;
600610
int _j;
601611
ksNesSpriteQuadData* quad_p = wp->draw_ctx.sprite_quad_data;
612+
int idx2;
602613

614+
idx2 = 0;
603615
for (i = 0; i < 0x100; i += 4) {
604616
_j = 8; // 8x8 sprite
605-
if (wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.OAMTable[i].y_pos].ppu_ctrl & KS_NES_PPU_CTRL_SPRITE_SIZE) {
617+
if (wp->draw_ctx.ppu_scanline_regs[((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + i))->y_pos].ppu_ctrl & KS_NES_PPU_CTRL_SPRITE_SIZE) {
606618
_j = 16; // 8x16 sprite
607619
}
608620
a = 0;
609-
if (wp->draw_ctx.OAMTable[i].attributes & KS_NES_OAM_ATTR_FLIP_VERTICAL) {
621+
if (((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + i))->attributes & KS_NES_OAM_ATTR_FLIP_VERTICAL) {
610622
b = _j << 2;
611623
_c = -4;
612624
} else {
613625
b = 0;
614626
_c = 4;
615627
}
616628
for (j = 0; j < _j; j++) {
617-
if (wp->draw_ctx.sprite_scanline_limit[wp->draw_ctx.OAMTable[i].y_pos + j] != 0) {
618-
wp->draw_ctx.sprite_scanline_limit[wp->draw_ctx.OAMTable[i].y_pos + j]--;
629+
u32 ypos = ((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + i))->y_pos;
630+
ypos += j;
631+
if (wp->draw_ctx.sprite_scanline_limit[((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + i))->y_pos + j] != 0) {
632+
wp->draw_ctx.sprite_scanline_limit[((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + i))->y_pos + j]--;
619633
if ((a & 2) == 0) {
620-
quad_p->y_and_v_pairs[a] = j + wp->draw_ctx.OAMTable[i].y_pos;
634+
quad_p->y_and_v_pairs[a] = ((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + i))->y_pos + j;
621635
quad_p->y_and_v_pairs[a + 1] = b;
622636
a += 2;
623637
}
624638
} else if ((a & 2) != 0) {
625-
quad_p->y_and_v_pairs[a] = j + wp->draw_ctx.OAMTable[i].y_pos;
639+
quad_p->y_and_v_pairs[a] = ((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + i))->y_pos + j;
626640
quad_p->y_and_v_pairs[a + 1] = b;
627641
a += 2;
628642
}
629643
b += _c;
630644
}
631645
if ((a & 2) != 0) {
632-
quad_p->y_and_v_pairs[a] = j + wp->draw_ctx.OAMTable[i].y_pos;
646+
quad_p->y_and_v_pairs[a] = ((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + i))->y_pos + j;
633647
quad_p->y_and_v_pairs[a + 1] = b;
634648
a += 2;
635649
}
636650
wp->draw_ctx.sprite_vertex_count[i >> 2] = a;
637651
quad_p++;
652+
idx2++;
638653
}
639654
}
640655
GXSetNumChans(1);
@@ -647,10 +662,10 @@ void ksNesDrawOBJ(ksNesCommonWorkObj* wp, ksNesStateObj* state, u32 sprite_prior
647662
GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT);
648663
GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
649664
GXSetVtxDesc(GX_VA_TEX1, GX_DIRECT);
650-
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_RGBA4, 0);
665+
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0);
651666
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
652-
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_CLR_RGBA, GX_RGBX8, 10);
653-
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX1, GX_CLR_RGBA, GX_RGBX8, 10);
667+
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_S, GX_U16, 10);
668+
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX1, GX_TEX_S, GX_U16, 10);
654669
GXInitTexObj(&GStack_7c, wp->chr_to_u8_bufp, 0x400, (u16)(size >> 10), GX_TF_I8, GX_MIRROR, GX_CLAMP, 0);
655670
GXInitTexObjLOD(&GStack_7c, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1);
656671
GXLoadTexObj(&GStack_7c, GX_TEXMAP0);
@@ -707,37 +722,40 @@ void ksNesDrawOBJ(ksNesCommonWorkObj* wp, ksNesStateObj* state, u32 sprite_prior
707722
GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_ALWAYS, 0);
708723

709724
u32 n_verts = 0;
725+
u32 idx2 = 0;
710726
for (i = 0; i < ARRAY_COUNT(wp->draw_ctx.OAMTable); i++) {
711727
// draw sprites
712-
if (sprite_priority_pass == 0 || (wp->draw_ctx.OAMTable[i].attributes & KS_NES_OAM_ATTR_PRIORITY) != 0) {
713-
n_verts += wp->draw_ctx.sprite_vertex_count[i];
728+
if (sprite_priority_pass == 0 || (((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx2))->attributes & KS_NES_OAM_ATTR_PRIORITY) != 0) {
729+
n_verts += wp->draw_ctx.sprite_vertex_count[idx2 >> 2];
714730
}
731+
732+
idx2 += 4;
715733
}
716734

717-
// u32 idx = 0x100-4;
718-
u32 idx2 = ARRAY_COUNT(wp->draw_ctx.OAMTable) - 1;
735+
u32 idx = 0x100-4;
736+
idx2 = ARRAY_COUNT(wp->draw_ctx.OAMTable) - 1;
719737
GXBegin(GX_QUADS, GX_VTXFMT0, n_verts);
720738
while (TRUE) {
721-
ksNesSpriteQuadData* quad_p = &wp->draw_ctx.sprite_quad_data[idx2];
722-
u8* quad_pairs = quad_p->y_and_v_pairs;
739+
// ksNesSpriteQuadData* quad_p = &wp->draw_ctx.sprite_quad_data[idx2];
723740
// u32 scanline_idx = wp->draw_ctx.OAMTable[idx];
724-
u32 tile_index = wp->draw_ctx.OAMTable[idx2].tile_index;
741+
u8* quad_pairs = ((ksNesSpriteQuadData*)((u8*)wp->draw_ctx.sprite_quad_data + (idx << 3)))->y_and_v_pairs;
742+
u32 tile_index = ((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->tile_index;
725743
u32 flags2;
726744

727-
if (wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.OAMTable[idx2].y_pos].ppu_ctrl & KS_NES_PPU_CTRL_SPRITE_SIZE) {
745+
if (wp->draw_ctx.ppu_scanline_regs[((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->y_pos].ppu_ctrl & KS_NES_PPU_CTRL_SPRITE_SIZE) {
728746
// 8x16 sprite
729-
flags2 = wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.OAMTable[idx2].y_pos].chr_bank_sprite[(tile_index >> 6) | ((tile_index & KS_NES_OAM_TILE_BANK) << 2)];
747+
flags2 = wp->draw_ctx.ppu_scanline_regs[((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->y_pos].chr_bank_sprite[(tile_index >> 6) | ((tile_index & KS_NES_OAM_TILE_BANK) << 2)];
730748
tile_index &= KS_NES_OAM_TILE_IDX;
731749
} else {
732750
// 8x8 sprite
733-
flags2 = wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.OAMTable[idx2].y_pos].chr_bank_sprite[(tile_index >> 6) | ((wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.OAMTable[idx2].y_pos].ppu_ctrl >> 1) & 4)];
751+
flags2 = wp->draw_ctx.ppu_scanline_regs[((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->y_pos].chr_bank_sprite[(tile_index >> 6) | ((wp->draw_ctx.ppu_scanline_regs[((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->y_pos].ppu_ctrl >> 1) & 4)];
734752
}
735753

736754
// u32 flags3 = wp->draw_ctx.OAMTable[idx + 2];
737-
u32 oam_attrs = wp->draw_ctx.OAMTable[idx2].attributes;
738-
u32 x0 = wp->draw_ctx.OAMTable[idx2].x_pos + 128;
739-
u32 x1 = wp->draw_ctx.OAMTable[idx2].x_pos + 136;
740-
u32 color = ((wp->draw_ctx.OAMTable[idx2].x_pos & 3) * 16 + 4) * 0x01000000;
755+
u32 oam_attrs = ((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->attributes;
756+
u32 x0 = ((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->x_pos + 128;
757+
u32 x1 = ((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->x_pos + 136;
758+
u32 color = ((((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->x_pos & 3) * 16 + 4) * 0x01000000;
741759

742760
if (sprite_priority_pass != 0) {
743761
// We're in the BG pass so skip if the sprite is supposed to be drawn in front of the BG
@@ -764,15 +782,15 @@ void ksNesDrawOBJ(ksNesCommonWorkObj* wp, ksNesStateObj* state, u32 sprite_prior
764782
t0 = (flags2 & 0xFE) * 2; // select chr bank offset
765783
temp = ((tile_index & 0x3F) * 0x20) | ((flags2 & 0x01) * 0x800); // flags2 bit0 determines the pattern table
766784

767-
if (wp->draw_ctx.OAMTable[idx2].attributes & KS_NES_OAM_ATTR_FLIP_HORIZONTAL) {
785+
if (((ksNesOAMEntry*)((u8*)wp->draw_ctx.OAMTable + idx))->attributes & KS_NES_OAM_ATTR_FLIP_HORIZONTAL) {
768786
s1 = temp + 32;
769787
s1_2 = temp;
770788
} else {
771789
s1 = temp;
772790
s1_2 = temp + 32;
773791
}
774792

775-
for (j = 0; j < wp->draw_ctx.sprite_vertex_count[idx2]; j += 4) {
793+
for (j = 0; j < wp->draw_ctx.sprite_vertex_count[idx >> 2]; j += 4) {
776794
u32 y0 = -129 - quad_pairs[0];
777795
t1 = quad_pairs[1];
778796
u32 y1 = -129 - quad_pairs[2];
@@ -802,12 +820,12 @@ void ksNesDrawOBJ(ksNesCommonWorkObj* wp, ksNesStateObj* state, u32 sprite_prior
802820

803821

804822
loop_condition:
805-
if (idx2 == 0) {
823+
if (idx == 0) {
806824
break;
807825
}
808826

809-
// idx -= 4;
810-
idx2--;
827+
idx -= 4;
828+
// idx2--;
811829
}
812830

813831
GXEnd();

0 commit comments

Comments
 (0)