Improve ks_nes_draw funcs scores some, a bunch of documentation and improvements in ks_nes_core.

This commit is contained in:
Cuyler36
2025-10-29 17:02:39 -04:00
parent 4b490c1aaa
commit 10efef5ffe
4 changed files with 351 additions and 292 deletions
+49 -12
View File
@@ -13,6 +13,8 @@ extern "C" {
#define KS_NES_WIDTH 256
#define KS_NES_HEIGHT 228
#define KS_NES_PPU_CYCLES_PER_SCANLINE 341
#define KS_NES_CPU_CYCLES_PER_SCANLINE 114
#define KS_NES_SCANLINE_COUNT 240 // 228 visible + 8 pre-render + 8 post-render
#define KS_NES_SCANLINE_SPRITE_OVERDRAW_COUNT (KS_NES_SCANLINE_COUNT + 32) // 272
@@ -123,6 +125,32 @@ extern "C" {
#define KS_NES_MAPPER_KONAMI_VRC6A 24
#define KS_NES_MAPPER_KONAMI_VRC6B 26
// Memory mapped register addresses
// APU Registers & Flags
#define KS_NES_REG_APU_STATUS 0x4015
#define KS_NES_REG_APU_STATUS_FLG_PULSE1_ENABLE (1 << 0)
#define KS_NES_REG_APU_STATUS_FLG_PULSE2_ENABLE (1 << 1)
#define KS_NES_REG_APU_STATUS_FLG_TRIANGLE_ENABLE (1 << 2)
#define KS_NES_REG_APU_STATUS_FLG_NOISE_ENABLE (1 << 3)
#define KS_NES_REG_APU_STATUS_FLG_DMC_ENABLE (1 << 4)
// MMC5 Audio Registers & Flags
#define KS_NES_REG_MMC5_AUDIO_PULSE1_TIMER 0x5000
#define KS_NES_REG_MMC5_AUDIO_PULSE1_LENGTH_CTR 0x5001
#define KS_NES_REG_MMC5_AUDIO_PULSE1_ENVELOPE 0x5002
#define KS_NES_REG_MMC5_AUDIO_PULSE1_SWEEP 0x5003
#define KS_NES_REG_MMC5_AUDIO_PULSE2_TIMER 0x5004
#define KS_NES_REG_MMC5_AUDIO_PULSE2_LENGTH_CTR 0x5005
#define KS_NES_REG_MMC5_AUDIO_PULSE2_ENVELOPE 0x5006
#define KS_NES_REG_MMC5_AUDIO_PULSE2_SWEEP 0x5007
#define KS_NES_REG_MMC5_AUDIO_PCM_MODE_IRQ 0x5010 // bit0 = mode select, 0 = write, 1 = read & bit7 = PCM IRQ enable
#define KS_NES_REG_MMC5_AUDIO_RAW_PCM 0x5011
#define KS_NES_REG_MMC5_AUDIO_STATUS 0x5015 // only bottom two bits are used (bit0/1) and toggle pulse1/2
#define KS_NES_REG_MMC5_AUDIO_STATUS_PULSE1 (1 << 0)
#define KS_NES_REG_MMC5_AUDIO_STATUS_PULSE2 (1 << 1)
#define KS_NES_REG_MMC5_AUDIO_STATUS_PULSE_MASK (KS_NES_REG_MMC5_AUDIO_STATUS_PULSE1 | KS_NES_REG_MMC5_AUDIO_STATUS_PULSE2)
// Emulator flags
#define KS_NES_FLAG_NINES_OVER_MODE (1 << 13) // 0x2000, enables "nines over" mode which allows drawing more than 8 sprites per scanline
@@ -160,10 +188,17 @@ typedef struct ksNesOAMEntry {
u8 x_pos;
} ksNesOAMEntry;
// I suspect this struct is fake but it makes readability easier and matches.
typedef struct ksNesScanlineYCoords {
u8 top;
u8 bottom;
} ksNesScanlineYCoords;
typedef struct ksNesDrawCtx {
/* 0x0000 */ union {
u8 sprite_scanline_limit[KS_NES_SCANLINE_SPRITE_OVERDRAW_COUNT]; // tracks the number of sprites that have been drawn on each scanline
u8 scanline_y_coords[2 * 256]; // tracks the Y coordinate of the top & bottom of each scanline
u8 scanline_raw_buf[512]; // raw buffer (most likely definition?)
};
/* 0x0200 */ u8 mmc2_scanline_latch_tiles[KS_NES_SCANLINE_COUNT + 16]; // tracks which tiles should be accessible on each scanline based on MMC2 latch settings
@@ -179,23 +214,25 @@ typedef struct ksNesDrawCtx {
/* 0x8EE8 */ Mtx34 draw_mtx;
} ksNesDrawCtx;
#define KS_NES_TYPE_FROM_DRAW_CTX_SCANLINE_BUF_OFS(type, draw_ctx, ofs) ((type*)((((u8*)(draw_ctx).scanline_raw_buf) + (ofs))))
#define KS_NES_TYPE_FROM_DRAW_CTX_SCANLINE_BUF(type, draw_ctx, idx) ((type*)(((u8*)(draw_ctx).scanline_raw_buf) + ((idx) * sizeof(type))))
typedef struct ksNesCommonWorkObj {
/* 0x0000 */ u8* nesromp;
/* 0x0004 */ u8* noise_bufp;
/* 0x0008 */ size_t chr_to_i8_buf_size;
/* 0x000C */ u8* chr_to_u8_bufp;
/* 0x0010 */ u8* result_bufp;
/* 0x0014 */ int _0014;
/* 0x0018 */ int _0018;
/* 0x0014 */ u32 cpu_cycle_count;
/* 0x0018 */ u32 total_cpu_cycles;
/* 0x001C */ u8 frames;
/* 0x001D */ u8 _001D;
/* 0x001D */ u8 fds_disk_count;
/* 0x001E */ u8 _001E;
/* 0x001F */ u8 _001F;
/* 0x0020 */ u32 pads[4];
/* 0x0030 */ u32 _0030;
/* 0x0034 */ u32 _0034;
/* 0x0038 */ u32 _0038;
/* 0x003C */ u8 _003C[0x0048 - 0x003C];
/* 0x0020 */ u32 pads[4+3];
/* 0x003C */ u8 _003C;
/* 0x0040 */ u32 _0040;
/* 0x0044 */ u32 _0044;
/* 0x0048 */ size_t prg_size;
/* 0x004C */ u8 _004C[0x0060 - 0x004C];
/* 0x0060 */ ksNesDrawCtx draw_ctx;
@@ -205,16 +242,16 @@ typedef struct ksNesStateObj {
/* 0x0000 */ u8 wram[KS_NES_WRAM_SIZE];
/* 0x0800 */ u8 ppu_nametable_ram[KS_NES_PPU_NAMETABLE_RAM_SIZE];
/* 0x1000 */ u8 cartridge_nametable_ram[28];
/* 0x101C */ u8 primary_oam[0x100];
/* 0x111C */ u8 _pad[4]; // this might not exist and instead the next member might be ATTRIBUTE_ALIGN(16/32)
/* 0x101C */ u8 _pad[4]; // this might not exist and instead the next member might be ATTRIBUTE_ALIGN(16/32)
/* 0x1020 */ ksNesOAMEntry primary_oam[KS_NES_OAM_TABLE_SIZE]; // u8 primary_oam[0x100];
/* 0x1120 */ u8 mmc5_extension_ram[0x400];
// these are all function pointers.
// leaving these as void pointers until we figure out the function signature.
/* 0x1520 */ void* store_ppu_func[8]; // ksNesStorePPUFuncTblDefault
/* 0x1540 */ void* store_io_func[0x28]; // ksNesStoreIOFuncTblDefault
/* 0x1540 */ void* store_io_func[40]; // ksNesStoreIOFuncTblDefault
/* 0x15E0 */ void* store_func[8]; // ksNesStoreFuncTblDefault
/* 0x1600 */ void* load_func[8]; // ksNesLoadFuncTblDefault
/* 0x1620 */ void* load_io_func[0x18]; // ksNesLoadIOFuncTblDefault
/* 0x1620 */ void* load_io_func[24]; // ksNesLoadIOFuncTblDefault
/* 0x1680 */ u8* cpu_0000_1fff; // work RAM and its mirrors
/* 0x1684 */ u8* cpu_2000_3fff; // PPU registers and their mirrors
/* 0x1688 */ u8* cpu_4000_5fff; // APU registers, I/O registers, and usually unmapped cartridge addresses
+6 -6
View File
@@ -2119,11 +2119,11 @@ static int famicom_rom_load() {
u32 flags = 0;
wp->prg_size = KS_NES_PRGROM_SIZE;
wp->noise_bufp = famicomCommon.noise_bufp;
wp->_001D = 4;
wp->fds_disk_count = 4;
wp->chr_to_u8_bufp = famicomCommon.chr_to_i8_bufp;
wp->chr_to_i8_buf_size = CHR_TO_I8_BUF_SIZE;
wp->result_bufp = famicomCommon.result_bufp;
wp->_0018 = 0;
wp->total_cpu_cycles = 0;
if (famicomCommon.noise_bufp == nullptr) {
flags |= 0x40; // no sound?
@@ -2412,9 +2412,9 @@ extern void famicom_1frame() {
}
famicom_key_convert();
famicomCommon.wp->_0030 = 0;
famicomCommon.wp->_0034 = 0;
famicomCommon.wp->_0038 = 0;
famicomCommon.wp->pads[4] = 0;
famicomCommon.wp->pads[5] = 0;
famicomCommon.wp->pads[6] = 0;
/* Special input modes activated when L & R are held */
/*
@@ -2502,7 +2502,7 @@ extern void famicom_1frame() {
speed_show--;
}
famicomCommon.wp->_0014 = 0;
famicomCommon.wp->cpu_cycle_count = 0;
famicomCommon.wp->frames = frames;
do {
File diff suppressed because it is too large Load Diff
+73 -61
View File
@@ -106,11 +106,14 @@ void ksNesDrawMakeBGIndTex(ksNesCommonWorkObj* wp, u32 mmc3) {
u32 scanline_ctrl1 = wp->draw_ctx.ppu_scanline_regs[row].vram_addr_coarse_x;
u32 mask;
u32 nibble_acc; // @bug - uninitialized
u8* patternPtrBase = (u8*)wp->draw_ctx.ppu_scanline_regs[row].chr_bank_bg;
u8* patternPtrBase;
u32 tile_byte;
u32 palette_bits;
u32 dst_idx;
u8* nametable_p;
u32 flags;
patternPtrBase = (u8*)wp->draw_ctx.ppu_scanline_regs[row].chr_bank_bg;
mask = mask = (scanline_ctrl0 & 0x04) ? CHR_flag_xor : 0; // bruh
@@ -128,7 +131,7 @@ void ksNesDrawMakeBGIndTex(ksNesCommonWorkObj* wp, u32 mmc3) {
tile_byte = nametable_p[((scanline_ctrl0 & 0xF8) << 2) + ((scanline_ctrl1 & 0xF8) >> 3)];
}
palette_bits = patternPtrBase[((u8)tile_byte >> 6) | ((wp->draw_ctx.ppu_scanline_regs[row].ppu_ctrl & 0x10) >> 2)];
palette_bits = patternPtrBase[((u8)tile_byte >> 6) | ((wp->draw_ctx.ppu_scanline_regs[row].ppu_ctrl & KS_NES_PPU_CTRL_BG_PATTERN) >> 2)];
// issue is here
wp->draw_ctx.bg_tile_index_texture[(((col & 0x3C) * 8) + ((col & 3) * 2) + ((row >> 2) * 288) + ((row & 3) * 8)) + 0] = (((palette_bits & 1) << 6) | (tile_byte & 0x3F)) - (col & 1);
@@ -892,6 +895,7 @@ void ksNesDrawOBJMMC5(ksNesCommonWorkObj* wp, ksNesStateObj* sp, u32 sprite_prio
u32 clr;
u32 oam_attrs;
u8 *work;
ksNesScanlineYCoords* coords_p;
GXSetNumChans(1);
GXSetNumTexGens(2);
@@ -968,7 +972,7 @@ void ksNesDrawOBJMMC5(ksNesCommonWorkObj* wp, ksNesStateObj* sp, u32 sprite_prio
var_r24 = 0;
for (i = 0; i < ARRAY_COUNT(wp->draw_ctx.OAMTable); i++) {
if (sprite_priority_pass == 0 || (wp->draw_ctx.OAMTable[i].attributes & KS_NES_PPU_CTRL_SPRITE_SIZE) != 0) {
if (sprite_priority_pass == 0 || (wp->draw_ctx.OAMTable[i].attributes & KS_NES_OAM_ATTR_PRIORITY) != 0) {
var_r24 += (wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.OAMTable[i].y_pos].ppu_ctrl & KS_NES_PPU_CTRL_SPRITE_SIZE) ? 8 : 4;
}
}
@@ -982,8 +986,9 @@ void ksNesDrawOBJMMC5(ksNesCommonWorkObj* wp, ksNesStateObj* sp, u32 sprite_prio
j = (ARRAY_COUNT(wp->draw_ctx.OAMTable) - 1) * sizeof(wp->draw_ctx.OAMTable[0]);
GXBegin(GX_QUADS, GX_VTXFMT0, var_r24);
while (TRUE) {
u8 scanline = ((u8*)wp->draw_ctx.OAMTable)[j + 0];
u32 tile_idx = ((u8*)wp->draw_ctx.OAMTable)[j + 1]; // loading tile index & bank
ksNesOAMEntry* oam_p = KS_NES_TYPE_FROM_DRAW_CTX_SCANLINE_BUF_OFS(ksNesOAMEntry, wp->draw_ctx, j);
u8 scanline = oam_p->y_pos;
u32 tile_idx = oam_p->tile_index; // loading tile index & bank
u32 size_mode = (wp->draw_ctx.ppu_scanline_regs[scanline].ppu_ctrl >> 5) & 1; // sprite size mode, 0 = 8x8, 1 = 8x16
u32 palette_bits;
u32 tmp;
@@ -1000,9 +1005,9 @@ void ksNesDrawOBJMMC5(ksNesCommonWorkObj* wp, ksNesStateObj* sp, u32 sprite_prio
}
oam_attrs = ((u8*)wp->draw_ctx.OAMTable)[j + 2]; // OAM attributes
x1 = ((u8*)wp->draw_ctx.OAMTable)[j + 3] + 128; // OAM X position
x2 = ((u8*)wp->draw_ctx.OAMTable)[j + 3] + 128 + 8; // OAM X position + 8
oam_attrs = oam_p->attributes; // OAM attributes
x1 = oam_p->x_pos + 128; // OAM X position
x2 = oam_p->x_pos + 128 + 8; // OAM X position + 8
clr = ((((oam_attrs & KS_NES_OAM_ATTR_PALETTE_MASK) * 16) + 4) | (wp->draw_ctx.ppu_scanline_regs[scanline].ppu_ctrl & (KS_NES_PPU_CTRL_NMI_ENABLE | KS_NES_PPU_CTRL_MASTER_SLAVE))) << 24;
if (sprite_priority_pass != 0) {
@@ -1069,7 +1074,7 @@ void ksNesDrawOBJMMC5(ksNesCommonWorkObj* wp, ksNesStateObj* sp, u32 sprite_prio
loop_point:
if (j == 0) break;
j -= sizeof(wp->draw_ctx.OAMTable[0]);
j -= sizeof(ksNesOAMEntry);
}
GXEnd();
@@ -1102,18 +1107,20 @@ loop_point:
GXBegin(GX_QUADS, GX_VTXFMT0, quads << 1);
for (i = 0; i < quads; i += 2) {
work = (u8*)wp + i; // wp->draw_ctx.scanline_y_coords[i, i + 1] (top & bottom)
x = 0x180;
if ((wp->draw_ctx.ppu_scanline_regs[(u8)((u8*)wp + i)[0x60]].ppumask_flags & KS_NES_PPU_MASK_SPRITES_COMBINED) == KS_NES_PPU_MASK_SHOW_SPRITES) {
x = 0x88;
ksNesScanlineYCoords* coords_p = KS_NES_TYPE_FROM_DRAW_CTX_SCANLINE_BUF_OFS(ksNesScanlineYCoords, wp->draw_ctx, i);
u8 scanline_top = (u8)KS_NES_TYPE_FROM_DRAW_CTX_SCANLINE_BUF_OFS(ksNesScanlineYCoords, wp->draw_ctx, i)->top;
// work = (u8*)wp + i; // wp->draw_ctx.scanline_y_coords[i, i + 1] (top & bottom)
x = 384;
if ((wp->draw_ctx.ppu_scanline_regs[scanline_top].ppumask_flags & KS_NES_PPU_MASK_SPRITES_COMBINED) == KS_NES_PPU_MASK_SHOW_SPRITES) {
x = 136;
}
GXPosition2s16(0x80, -128 - x);
GXPosition2s16(128, -128 - x);
GXColor1u32(0);
GXPosition2s16(x, -128 - work[0x60]);
GXPosition2s16(x, -128 - coords_p->top);
GXColor1u32(0);
GXPosition2s16(x, -128 - work[0x60] - work[0x61]);
GXPosition2s16(x, -128 - coords_p->top - coords_p->bottom);
GXColor1u32(0);
GXPosition2s16(0x80, -128 - work[0x60] - work[0x61]);
GXPosition2s16(128, -128 - coords_p->top - coords_p->bottom);
GXColor1u32(0);
}
GXEnd();
@@ -1121,8 +1128,8 @@ loop_point:
void ksNesDrawFlushEFBToRed8(u8* buf) {
static const GXColor black = { 0, 0, 0, 0 };
GXSetTexCopySrc(0x80, 0x88, 0x100, 0xe4);
GXSetTexCopyDst(0x100, 0xe4, GX_CTF_R8, GX_FALSE);
GXSetTexCopySrc(128, 136, 256, 228);
GXSetTexCopyDst(256, 228, GX_CTF_R8, GX_FALSE);
GXSetCopyClear(black, 0xffffff);
GXCopyTex(buf, GX_FALSE);
GXPixModeSync();
@@ -1132,8 +1139,7 @@ void ksNesDrawFlushEFBToRed8(u8* buf) {
void ksNesDrawOBJI8ToEFB(ksNesCommonWorkObj* wp, u8* buf) {
GXTexObj obj;
u32 i;
s32 u;
u8* work;
u32 x;
u32 cnt;
GXSetNumChans(0);
@@ -1167,21 +1173,21 @@ void ksNesDrawOBJI8ToEFB(ksNesCommonWorkObj* wp, u8* buf) {
GXBegin(GX_QUADS, GX_VTXFMT0, cnt * 2);
for (i = 0; i < cnt; i += 2) {
work = (u8*)wp + i; // if it works...
u = (wp->draw_ctx.ppu_scanline_regs[(u8)((u8*)wp)[i + 0x60]].ppumask_flags & KS_NES_PPU_MASK_SPRITES_COMBINED) == KS_NES_PPU_MASK_SHOW_SPRITES ? 8 : 0;
GXPosition2s16(u + 0x80, -128 - work[0x60]);
GXTexCoord2u16(u, work[0x60] - 8);
for (i = 0; i < cnt; i += sizeof(ksNesScanlineYCoords)) {
ksNesScanlineYCoords* scanline_y_coords = KS_NES_TYPE_FROM_DRAW_CTX_SCANLINE_BUF_OFS(ksNesScanlineYCoords, wp->draw_ctx, i);
x = (wp->draw_ctx.ppu_scanline_regs[(u8)KS_NES_TYPE_FROM_DRAW_CTX_SCANLINE_BUF_OFS(ksNesScanlineYCoords, wp->draw_ctx, i)->top].ppumask_flags & KS_NES_PPU_MASK_SPRITES_COMBINED) == KS_NES_PPU_MASK_SHOW_SPRITES ? 8 : 0;
GXPosition2s16(0x180, -128 - work[0x60]);
GXTexCoord2u16(0x100, work[0x60] - 8);
GXPosition2s16(x + 0x80, -128 - scanline_y_coords->top);
GXTexCoord2u16(x, scanline_y_coords->top - 8);
GXPosition2s16(0x180, -128 - work[0x60] - work[0x61]);
GXTexCoord2u16(0x100, work[0x60] + work[0x61] - 8);
GXPosition2s16(0x180, -128 - scanline_y_coords->top);
GXTexCoord2u16(0x100, scanline_y_coords->top - 8);
GXPosition2s16(u + 0x80, -128 - work[0x60] - work[0x61]);
GXTexCoord2u16(u, work[0x60] + work[0x61] - 8);
GXPosition2s16(0x180, -128 - scanline_y_coords->top - scanline_y_coords->bottom);
GXTexCoord2u16(0x100, scanline_y_coords->top + scanline_y_coords->bottom - 8);
GXPosition2s16(x + 0x80, -128 - scanline_y_coords->top - scanline_y_coords->bottom);
GXTexCoord2u16(x, scanline_y_coords->top + scanline_y_coords->bottom - 8);
}
GXEnd();
@@ -1203,7 +1209,7 @@ void ksNesDrawEmuResult(ksNesCommonWorkObj* wp) {
u8 y;
u32 val;
u32 clr;
u8 *work;
ksNesScanlineYCoords* scanline_y_coords;
GXTexObj obj;
GXTexObj obj2;
@@ -1246,14 +1252,16 @@ void ksNesDrawEmuResult(ksNesCommonWorkObj* wp) {
GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
// Count non-grayscale scanlines
cnt = 0;
for (i = 0; wp->draw_ctx.scanline_y_coords[i] != 0xFF; i += 2) {
for (i = 0; wp->draw_ctx.scanline_y_coords[i] != 0xFF; i += sizeof(ksNesScanlineYCoords)) {
val = wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.scanline_y_coords[i]].ppumask_flags;
if ((val & KS_NES_PPU_MASK_COLOR_EFFECTS) == 0) {
cnt += 4; // has color and no color emphasis
}
}
// Draw non-grayscale scanlines
if (cnt != 0) {
GXSetNumChans(0);
GXSetNumTexGens(1);
@@ -1270,25 +1278,26 @@ void ksNesDrawEmuResult(ksNesCommonWorkObj* wp) {
GXBegin(GX_QUADS, GX_VTXFMT0, cnt);
do {
i -= 2;
work = ((u8*)wp + i);
val = wp->draw_ctx.ppu_scanline_regs[work[0x60]].ppumask_flags & KS_NES_PPU_MASK_COLOR_EFFECTS;
i -= sizeof(ksNesScanlineYCoords);
scanline_y_coords = KS_NES_TYPE_FROM_DRAW_CTX_SCANLINE_BUF_OFS(ksNesScanlineYCoords, wp->draw_ctx, i);
val = wp->draw_ctx.ppu_scanline_regs[scanline_y_coords->top].ppumask_flags & KS_NES_PPU_MASK_COLOR_EFFECTS;
if (val == 0) {
GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x60]);
GXTexCoord2u16(256, work[0x60]);
GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - scanline_y_coords->top);
GXTexCoord2u16(256, scanline_y_coords->top);
GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x61]);
GXTexCoord2u16(256, work[0x61]);
GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - scanline_y_coords->bottom);
GXTexCoord2u16(256, scanline_y_coords->bottom);
GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x61]);
GXTexCoord2u16(0, work[0x61]);
GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - scanline_y_coords->bottom);
GXTexCoord2u16(0, scanline_y_coords->bottom);
GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x60]);
GXTexCoord2u16(0, work[0x60]);
GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - scanline_y_coords->top);
GXTexCoord2u16(0, scanline_y_coords->top);
}
} while (i != 0);
}
// Count grayscale scanlines
cnt = 0;
for (i = 0; wp->draw_ctx.scanline_y_coords[i] != 0xFF; i += 2) {
val = wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.scanline_y_coords[i]].ppumask_flags;
@@ -1297,6 +1306,7 @@ void ksNesDrawEmuResult(ksNesCommonWorkObj* wp) {
}
}
// Draw grayscale scanlines
if (cnt != 0) {
GXSetNumChans(1);
GXSetNumTexGens(1);
@@ -1358,35 +1368,37 @@ void ksNesDrawEmuResult(ksNesCommonWorkObj* wp) {
GXBegin(GX_QUADS, GX_VTXFMT0, cnt);
do {
i -= 2;
work = (u8*)wp + i; // wp->draw_ctx.scanline_y_coords[i, i + 1] (top & bottom)
if ((wp->draw_ctx.ppu_scanline_regs[work[0x60]].ppumask_flags & KS_NES_PPU_MASK_COLOR_EFFECTS) != 0) {
clr = 0x2F2F2F00;
if ((wp->draw_ctx.ppu_scanline_regs[work[0x60]].ppumask_flags & KS_NES_PPU_MASK_EMPHASIZE_RED) != 0) {
i -= sizeof(ksNesScanlineYCoords);
scanline_y_coords = KS_NES_TYPE_FROM_DRAW_CTX_SCANLINE_BUF_OFS(ksNesScanlineYCoords, wp->draw_ctx, i);
if ((wp->draw_ctx.ppu_scanline_regs[scanline_y_coords->top].ppumask_flags & KS_NES_PPU_MASK_COLOR_EFFECTS) != 0) {
clr = 0x2F2F2F00; // If any of the grayscale color bits are set, force the color to RGB8 (47, 47, 47) (chroma removed, only luma left)
// For each emphasis bit set, add one to the luma value to get 48 (0x30).
if ((wp->draw_ctx.ppu_scanline_regs[scanline_y_coords->top].ppumask_flags & KS_NES_PPU_MASK_EMPHASIZE_RED) != 0) {
clr += 0x10000000;
}
if ((wp->draw_ctx.ppu_scanline_regs[work[0x60]].ppumask_flags & KS_NES_PPU_MASK_EMPHASIZE_GREEN) != 0) {
if ((wp->draw_ctx.ppu_scanline_regs[scanline_y_coords->top].ppumask_flags & KS_NES_PPU_MASK_EMPHASIZE_GREEN) != 0) {
clr += 0x00100000;
}
if ((wp->draw_ctx.ppu_scanline_regs[work[0x60]].ppumask_flags & KS_NES_PPU_MASK_EMPHASIZE_BLUE) != 0) {
if ((wp->draw_ctx.ppu_scanline_regs[scanline_y_coords->top].ppumask_flags & KS_NES_PPU_MASK_EMPHASIZE_BLUE) != 0) {
clr += 0x00001000;
}
GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x60]);
GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - scanline_y_coords->top);
GXColor1u32(clr);
GXTexCoord2u16(256, work[0x60]);
GXTexCoord2u16(256, scanline_y_coords->top);
GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x61]);
GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - scanline_y_coords->bottom);
GXColor1u32(clr);
GXTexCoord2u16(256, work[0x61]);
GXTexCoord2u16(256, scanline_y_coords->bottom);
GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x61]);
GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - scanline_y_coords->bottom);
GXColor1u32(clr);
GXTexCoord2u16(0, work[0x61]);
GXTexCoord2u16(0, scanline_y_coords->bottom);
GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x60]);
GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - scanline_y_coords->top);
GXColor1u32(clr);
GXTexCoord2u16(0, work[0x60]);
GXTexCoord2u16(0, scanline_y_coords->top);
}
} while (i != 0);
}