#include "gx/GXEnum.h" #include "gx/GXTexture.h" #include "gx/GXVert.h" #define FIX_SQRT_LINKAGE #include "Famicom/ks_nes_draw.h" #include "dolphin/gx.h" #include "dolphin/PPCArch.h" #include "_mem.h" #include "dolphin/os.h" u8 ksNesPaletteNormal[] = { 0xc2, 0x10, 0x80, 0x17, 0x98, 0x17, 0xc0, 0x14, 0xdc, 0x0d, 0xd8, 0x03, 0xd8, 0x00, 0xc8, 0x80, 0xbc, 0xa0, 0x80, 0xe0, 0x81, 0x21, 0x80, 0xe4, 0x80, 0xac, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xe7, 0x39, 0x81, 0x7f, 0xa0, 0xff, 0xd8, 0xd9, 0xfc, 0xd5, 0xfc, 0xcb, 0xfc, 0xc3, 0xe9, 0x20, 0xe1, 0x80, 0x9d, 0xe0, 0x8e, 0x02, 0x82, 0x4c, 0x82, 0x18, 0x88, 0x42, 0x80, 0x00, 0x80, 0x00, 0xff, 0xff, 0x82, 0x5f, 0xb6, 0x1f, 0xe9, 0xbf, 0xfd, 0xd9, 0xfd, 0xb3, 0xfd, 0xeb, 0xfe, 0x4b, 0xfe, 0x86, 0xd2, 0xe0, 0xab, 0x6d, 0xa7, 0x55, 0x83, 0x7f, 0xb1, 0x8c, 0x80, 0x00, 0x80, 0x00, 0xff, 0xff, 0xc2, 0xff, 0xde, 0xff, 0xea, 0xff, 0xfe, 0xfd, 0xfe, 0xf9, 0xff, 0x16, 0xff, 0x35, 0xff, 0x74, 0xe7, 0x93, 0xd7, 0xb6, 0xd7, 0xdd, 0xdb, 0xbf, 0xef, 0x7b, 0x80, 0x00, 0x80, 0x00, }; void ksNesDrawInit(ksNesCommonWorkObj* wp) { Mtx44 mtx; Vec v1 = { 0.f, 0.f, 800.f }; Vec v3 = { 0.f, 0.f, -100.f }; Vec v2 = { 0.f, 1.f, 0.f }; GXInvalidateTexAll(); GXInvalidateVtxCache(); GXSetClipMode(GX_CLIP_DISABLE); GXSetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); GXSetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); GXSetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); GXSetZTexture(GX_ZT_DISABLE, GX_TF_Z8, 0); GXSetZCompLoc(GX_FALSE); GXSetColorUpdate(GX_TRUE); GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevSwapMode(GX_TEVSTAGE2, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevSwapMode(GX_TEVSTAGE3, GX_TEV_SWAP0, GX_TEV_SWAP0); C_MTXOrtho(mtx, 0, -480.f, 0.f, 640.f, 0.f, 2000.f); GXSetProjection(mtx, GX_ORTHOGRAPHIC); C_MTXLookAt(wp->draw_ctx.draw_mtx, &v1, &v2, &v3); } void ksNesDrawEnd() { GXSetClipMode(GX_CLIP_ENABLE); GXSetZCompLoc(GX_TRUE); GXSetNumIndStages(0); GXSetTevDirect(GX_TEVSTAGE0); GXSetTevDirect(GX_TEVSTAGE1); GXSetTevDirect(GX_TEVSTAGE2); GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevSwapMode(GX_TEVSTAGE2, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevSwapMode(GX_TEVSTAGE3, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_FALSE, 0, 0); GXSetTexCoordScaleManually(GX_TEXCOORD1, GX_FALSE, 0, 0); } void ksNesDrawClearEFBFirst(ksNesCommonWorkObj* wp) { DCFlushRange(wp->draw_ctx.post_process_lut, sizeof(wp->draw_ctx.post_process_lut)); GXSetNumChans(1); GXSetNumTexGens(0); GXSetNumTevStages(1); GXSetNumIndStages(0); GXSetTevDirect(GX_TEVSTAGE0); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR); GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_REG, GX_SRC_VTX, 0, GX_DF_NONE, GX_AF_NONE); GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); GXSetBlendMode(GX_BM_LOGIC, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_COPY); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_RGBA4, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetCurrentMtx(0); GXLoadPosMtxImm(wp->draw_ctx.draw_mtx, 0); GXBegin(GX_QUADS, GX_VTXFMT0, 4); { GXPosition2s16(128, -128); GXColor1u32(0x00ff0000); GXPosition2s16(384, -128); GXColor1u32(0x00000000); GXPosition2s16(384, -384); GXColor1u32(0x0000ff00); GXPosition2s16(128, -384); GXColor1u32(0x00000000); } GXEnd(); } void ksNesDrawMakeBGIndTex(ksNesCommonWorkObj* wp, u32 mmc3) { u32 trigger_col = mmc3 ? 9 : 0x7fff; u32 CHR_flag_xor = wp->chr_to_i8_buf_size <= CHR_TO_I8_BUF_SIZE ? (wp->chr_to_i8_buf_size >> 13) : 0x80; u32 row; u32 col; for (row = 8; row < 236; row++) { u32 scanline_ctrl0 = wp->draw_ctx.ppu_scanline_regs[row].vram_addr_y; 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; u32 tile_byte; u32 palette_bits; u32 dst_idx; u8* nametable_p; mask = mask = (scanline_ctrl0 & 0x04) ? CHR_flag_xor : 0; // bruh for (col = 0; col < 34; col++) { if (col == trigger_col) { patternPtrBase -= 8; } nametable_p = wp->draw_ctx.ppu_scanline_regs[row].nametable_ptrs[((scanline_ctrl1 >> 8) & 1)]; if (((s32)nametable_p) >= 0) { nibble_acc = (((u32)nametable_p) & 3) | (nibble_acc << 4); tile_byte = (((u32)nametable_p) >> 8) & 0xFF; } else { nibble_acc = (((nametable_p[0x3C0 + ((scanline_ctrl0 & 0xE0) >> 2) + ((scanline_ctrl1 & 0xE0) >> 5)] >> ((((scanline_ctrl1 & 0x10) >> 3) | (scanline_ctrl0 & 0x10) >> 2))) & 3) & 0x0F) | ((nibble_acc << 4)); 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)]; // 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); wp->draw_ctx.bg_tile_index_texture[(((col & 0x3C) * 8) + ((col & 3) * 2) + ((row >> 2) * 288) + ((row & 3) * 8)) + 1] = (palette_bits >> 1) ^ mask; if ((col & 1) != 0) { wp->draw_ctx.bg_palette_attr_texture[((col >> 1) & 3) + (col & 0x38) * 4 + ((row & 7) * 4 + (row >> 3) * 160)] = nibble_acc; } scanline_ctrl1 += 8; } } DCFlushRangeNoSync(wp->draw_ctx.bg_tile_index_texture, sizeof(wp->draw_ctx.bg_tile_index_texture)); DCFlushRangeNoSync(wp->draw_ctx.bg_palette_attr_texture, sizeof(wp->draw_ctx.bg_palette_attr_texture)); } void ksNesDrawMakeBGIndTexMMC5(ksNesCommonWorkObj* wp, ksNesStateObj* sp) { u32 banks_mask = sp->chr_banks & 0x1FC; u32 row; u32 col; for (row = 8; row < 236; row++) { u32 tile_byte; u32 scanline_ctrl0 = wp->draw_ctx.ppu_scanline_regs[row].vram_addr_y; u32 scanline_ctrl1 = wp->draw_ctx.ppu_scanline_regs[row].vram_addr_coarse_x; u32 nibble_acc; // @bug - uninitialized u8* patternPtrBase = (u8*)&wp->draw_ctx.ppu_scanline_regs[row].chr_bank_bg; u32 palette_bits; u32 tmp; u8* nametable_p; for (col = 0; col < 34; col++) { nametable_p = wp->draw_ctx.ppu_scanline_regs[row].nametable_ptrs[((scanline_ctrl1 >> 8) & 1)]; if (((s32)nametable_p) >= 0) { nibble_acc = (((u32)nametable_p) & 3) | (nibble_acc << 4); tile_byte = (((u32)nametable_p) >> 8) & 0xFF; } else { nibble_acc = ((nametable_p[0x3C0 + ((scanline_ctrl0 & 0xE0) >> 2) + ((scanline_ctrl1 & 0xE0) >> 5)] >> ((((scanline_ctrl1 & 0x10) >> 3) | (scanline_ctrl0 & 0x10) >> 2))) & 3) | (nibble_acc << 4); tile_byte = nametable_p[((scanline_ctrl0 & 0xF8) << 2) + ((scanline_ctrl1 & 0xF8) >> 3)]; } // issue in this area too probably if (wp->draw_ctx.ppu_scanline_regs[row].mmc5_ext_mode & 0x20) { // MMC5 extension ram nibble_acc = (nibble_acc & 0xF0) | (sp->mmc5_extension_ram[((scanline_ctrl1 >> 3) & 0x1F) + ((scanline_ctrl0 & 0xF8) << 2)] >> 6) & 0x0F; palette_bits = ((sp->mmc5_extension_ram[((scanline_ctrl1 >> 3) & 0x1F) + ((scanline_ctrl0 & 0xF8) << 2)] << 2) & banks_mask) | ((u8)tile_byte >> 6); } else { tmp = ((wp->draw_ctx.ppu_scanline_regs[row].ppu_ctrl & 0x10) >> 2) | ((tile_byte >> 6) & 0x3); palette_bits = ((wp->draw_ctx.ppu_scanline_regs[row].chr_bank_ext_upper_bg << (tmp + 1)) & 0x100) | (patternPtrBase[tmp]); } // issue is here wp->draw_ctx.bg_tile_index_texture[(((col & 3) * 2) + ((col & 0x3C) * 8) + ((row >> 2) * 288) + ((row & 3) * 8)) + 0] = (((palette_bits & 1) << 6) | (tile_byte & 0x3F)) - (col & 1); wp->draw_ctx.bg_tile_index_texture[(((col & 3) * 2) + ((col & 0x3C) * 8) + ((row >> 2) * 288) + ((row & 3) * 8)) + 1] = (palette_bits >> 1); if ((col & 1) != 0) { wp->draw_ctx.bg_palette_attr_texture[((col >> 1) & 3) + (col & 0x38) * 4 + ((row & 7) * 4 + (row >> 3) * 160)] = nibble_acc; } scanline_ctrl1 += 8; } } DCFlushRangeNoSync(wp->draw_ctx.bg_tile_index_texture, sizeof(wp->draw_ctx.bg_tile_index_texture)); DCFlushRangeNoSync(wp->draw_ctx.bg_palette_attr_texture, sizeof(wp->draw_ctx.bg_palette_attr_texture)); } #define ksNes_MMC2_LATCH_LO 0xFD // select chr bank 0/2 #define ksNes_MMC2_LATCH_HI 0xFE // select chr bank 1/3 void ksNesDrawMakeBGIndTexMMC2(ksNesCommonWorkObj* wp, u32 default_bank) { u32 CHR_flag_xor = wp->chr_to_i8_buf_size <= CHR_TO_I8_BUF_SIZE ? (wp->chr_to_i8_buf_size >> 13) : 0x80; u32 mmc2_bank = 0x02; // 0x02 = unset u32 row; u32 col; u32 i; int j; i = 239; do { u32 scanline_ctrl1 = wp->draw_ctx.ppu_scanline_regs[i].vram_addr_coarse_x; u32 idx = (wp->draw_ctx.ppu_scanline_regs[i].vram_addr_y & 0xF8) * 4; for (j = 0; j < 34; j++) { u32 val = (wp->draw_ctx.ppu_scanline_regs[i].nametable_ptrs[(scanline_ctrl1 >> 8) & 1] + ((scanline_ctrl1 >> 3) & 0x1F))[idx]; if (val == ksNes_MMC2_LATCH_HI) { mmc2_bank = 0x00; } else if (val == ksNes_MMC2_LATCH_LO) { mmc2_bank = 0x01; } scanline_ctrl1 += 8; } } while (i-- != 0 && mmc2_bank == 0x02); // If the bank is still unset, use the default bank if (mmc2_bank == 0x02) { mmc2_bank = default_bank; } for (i = 0; i < 8; i++) { u32 scanline_ctrl1 = wp->draw_ctx.ppu_scanline_regs[i].vram_addr_coarse_x; u32 idx = (wp->draw_ctx.ppu_scanline_regs[i].vram_addr_y & 0xF8) * 4; for (j = 0; j < 34; j++) { u32 val = (wp->draw_ctx.ppu_scanline_regs[i].nametable_ptrs[(scanline_ctrl1 >> 8) & 1] + ((scanline_ctrl1 >> 3) & 0x1F))[idx]; if (val == ksNes_MMC2_LATCH_HI) { mmc2_bank = 0x00; } else if (val == ksNes_MMC2_LATCH_LO) { mmc2_bank = 0x01; } scanline_ctrl1 += 8; } } for (row = 8; row < 236; row++) { u32 scanline_ctrl0 = wp->draw_ctx.ppu_scanline_regs[row].vram_addr_y; u32 scanline_ctrl1 = wp->draw_ctx.ppu_scanline_regs[row].vram_addr_coarse_x; u32 mask; u32 nibble_acc; // @bug - uninitialized u8* patternPtrBase = wp->draw_ctx.ppu_scanline_regs[row].chr_bank_sprite; u32 tile_byte; u32 palette_bits; u32 dst_idx; u8* nametable_p; mask = mask = (scanline_ctrl0 & 0x04) ? CHR_flag_xor : 0; // bruh for (col = 0; col < 34; col++) { nametable_p = wp->draw_ctx.ppu_scanline_regs[row].nametable_ptrs[((scanline_ctrl1 >> 8) & 1)]; if (((s32)nametable_p) >= 0) { nibble_acc = (((u32)nametable_p) & 3) | (nibble_acc << 4); tile_byte = (((u32)nametable_p) >> 8) & 0xFF; } else { nibble_acc = (((nametable_p[0x3C0 + ((scanline_ctrl0 & 0xE0) >> 2) + ((scanline_ctrl1 & 0xE0) >> 5)] >> ((((scanline_ctrl1 & 0x10) >> 3) | (scanline_ctrl0 & 0x10) >> 2))) & 3) & 0x0F) | ((nibble_acc << 4)); tile_byte = nametable_p[((scanline_ctrl0 & 0xF8) << 2) + ((scanline_ctrl1 & 0xF8) >> 3)]; if (tile_byte == ksNes_MMC2_LATCH_HI) { palette_bits = patternPtrBase[(mmc2_bank) | ((wp->draw_ctx.ppu_scanline_regs[row].ppu_ctrl & 0x10) >> 2)] | 0x03; mmc2_bank = 0x00; goto set; } else if (tile_byte == ksNes_MMC2_LATCH_LO) { mmc2_bank = 0x01; } } palette_bits = ((u8)tile_byte >> 6) | patternPtrBase[((mmc2_bank) | ((wp->draw_ctx.ppu_scanline_regs[row].ppu_ctrl & 0x10) >> 2))]; set: // 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); wp->draw_ctx.bg_tile_index_texture[(((col & 0x3C) * 8) + ((col & 3) * 2) + ((row >> 2) * 288) + ((row & 3) * 8)) + 1] = (palette_bits >> 1) ^ mask; if ((col & 1) != 0) { wp->draw_ctx.bg_palette_attr_texture[((col >> 1) & 3) + (col & 0x38) * 4 + ((row & 7) * 4 + (row >> 3) * 160)] = nibble_acc; } scanline_ctrl1 += 8; } } DCFlushRangeNoSync(wp->draw_ctx.bg_tile_index_texture, sizeof(wp->draw_ctx.bg_tile_index_texture)); DCFlushRangeNoSync(wp->draw_ctx.bg_palette_attr_texture, sizeof(wp->draw_ctx.bg_palette_attr_texture)); } void ksNesDrawOBJSetupMMC2(ksNesCommonWorkObj* wp) { u32 i; u32 j; u32 k; memset(wp->draw_ctx.mmc2_scanline_latch_tiles, 0, sizeof(wp->draw_ctx.mmc2_scanline_latch_tiles)); for (i = 0; i < ARRAY_COUNT(wp->draw_ctx.OAMTable); i++) { // ppu_scanline_regs is the scanline state, OAMTable is list of scanline triggers? if (wp->draw_ctx.OAMTable[i].y_pos >= 236 || (wp->draw_ctx.OAMTable[i].tile_index != ksNes_MMC2_LATCH_HI && wp->draw_ctx.OAMTable[i].tile_index != ksNes_MMC2_LATCH_LO)) { continue; } j = wp->draw_ctx.OAMTable[i].y_pos; k = (((wp->draw_ctx.ppu_scanline_regs[j].ppu_ctrl >> 2) & 0x08) + 8) + j; do { wp->draw_ctx.mmc2_scanline_latch_tiles[j] = wp->draw_ctx.OAMTable[i].tile_index; j++; } while(j < k); } // _0200 is the scanline marker entries for (i = 0, k = 0; i < ARRAY_COUNT(wp->draw_ctx.mmc2_scanline_latch_tiles); i++) { if (wp->draw_ctx.mmc2_scanline_latch_tiles[i] == ksNes_MMC2_LATCH_HI) { k = 0; } else if (wp->draw_ctx.mmc2_scanline_latch_tiles[i] == ksNes_MMC2_LATCH_LO) { k = 1; } if (k) { wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[0] = wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[1] - 1; wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[2] = wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[1] + 1; wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[3] = wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[1] + 2; } else { wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[1] = wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[0] + 1; wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[2] = wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[0] + 2; wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[3] = wp->draw_ctx.ppu_scanline_regs[i].chr_bank_sprite[0] + 3; } } } void ksNesDrawBG(ksNesCommonWorkObj* wp, ksNesStateObj* sp) { static const GXColor color_r_0xf0 = { 240, 0, 0, 0 }; static f32 indtexmtx_screen[2][3] = { {0.5f, 0.0f, 0.0f}, // S offset = (Intensity from bg_tile_index_texture) × 0.5 {0.0f, 0.0625f, 0.0f}, // T offset = (Alpha from bg_tile_index_texture) × 0.0625 (1/16) }; GXTexObj obj0; GXTexObj obj1; GXTexObj obj2; u32 curline; u32 cnt; u32 color; u32 x0; u32 x1; s16 y; u32 i; u32 j; GXSetNumChans(1); GXSetNumTexGens(2); GXSetNumTevStages(3); GXSetNumIndStages(1); GXSetBlendMode(GX_BM_LOGIC, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_COPY); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_RGBA4, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_CLR_RGBA, GX_RGBX8, 10); GXInitTexObj(&obj0, wp->draw_ctx.bg_palette_attr_texture, 40, 256, GX_TF_I4, GX_CLAMP, GX_REPEAT, 0); GXInitTexObjLOD(&obj0, GX_NEAR, GX_NEAR, 0.0, 0.0, 0.0, 0, 0, GX_ANISO_1); GXLoadTexObj(&obj0, GX_TEXMAP0); GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_TRUE, 128, 1025); GXSetTexCoordBias(GX_TEXCOORD0, GX_FALSE, GX_FALSE); GXSetTevDirect(GX_TEVSTAGE0); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); GXSetTevColor(GX_TEVREG0, color_r_0xf0); GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_C0, GX_CC_TEXC); GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_SUB, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV); GXInitTexObj(&obj1, wp->draw_ctx.bg_tile_index_texture, 36, 256, GX_TF_IA8, GX_CLAMP, GX_CLAMP, 0); GXInitTexObjLOD(&obj1, GX_NEAR, GX_NEAR, 0.0, 0.0, 0.0, 0, 0, GX_ANISO_1); GXLoadTexObj(&obj1, GX_TEXMAP1); GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); GXSetTexCoordScaleManually(GX_TEXCOORD1, GX_TRUE, 1024, 1025); GXSetTexCoordBias(GX_TEXCOORD1, GX_FALSE, GX_FALSE); GXSetIndTexOrder(GX_INDTEXSTAGE0, GX_TEXCOORD1, GX_TEXMAP1); GXSetIndTexCoordScale(GX_INDTEXSTAGE0, GX_ITS_8, GX_ITS_1); GXSetIndTexMtx(GX_ITM_0, indtexmtx_screen, 36); GXSetTevIndirect(GX_TEVSTAGE1, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_0, GX_ITW_16, GX_ITW_0, GX_FALSE, GX_FALSE, GX_ITBA_OFF); GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP2, GX_COLOR_NULL); GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_CPREV); GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA); GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV); GXSetTevDirect(GX_TEVSTAGE2); GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); GXSetChanCtrl(GX_COLOR0A0, 0, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE); GXSetTevColorIn(GX_TEVSTAGE2, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_CPREV); GXSetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE2, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV); GXSetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV); GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_ALWAYS, 0); GXSetLineWidth(6, GX_TO_ZERO); for (i = 0; i < 8; i++) { if (sp->ppu_render_latches[i] != 0) { if (sp->mapper == KS_NES_MAPPER_MMC5) { GXInitTexObj(&obj2, wp->chr_to_u8_bufp + (wp->chr_to_i8_buf_size >> 3) * i, 1024, wp->chr_to_i8_buf_size >> 13, GX_TF_I8, GX_MIRROR, GX_CLAMP, 0); GXInitTexObjLOD(&obj2, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); GXLoadTexObj(&obj2, GX_TEXMAP2); } else if ((i & 1) == 0) { if (wp->chr_to_i8_buf_size > CHR_TO_I8_BUF_SIZE) { GXInitTexObj(&obj2, wp->chr_to_u8_bufp + 0x20000 * (i & 6), 1024, 256, GX_TF_I8, GX_MIRROR, GX_CLAMP, 0); } else { GXInitTexObj(&obj2, wp->chr_to_u8_bufp + (wp->chr_to_i8_buf_size >> 3) * (i & 6), 1024, wp->chr_to_i8_buf_size >> 12, GX_TF_I8, GX_MIRROR, GX_CLAMP, 0); } GXInitTexObjLOD(&obj2, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); GXLoadTexObj(&obj2, GX_TEXMAP2); } cnt = 0; curline = sp->ppu_render_latches[i + 8]; j = curline; do { if ((wp->draw_ctx.ppu_scanline_regs[j].ppumask_flags & KS_NES_PPU_MASK_SHOW_BG) != 0 && j >= 8 && j < 236) { cnt += 2; } j = wp->draw_ctx.ppu_scanline_regs[j].fine_x_and_next; } while (j != 0); if (cnt != 0) { GXBegin(GX_LINES, GX_VTXFMT0, cnt); do { if ((wp->draw_ctx.ppu_scanline_regs[curline].ppumask_flags & KS_NES_PPU_MASK_SHOW_BG) != 0 && curline >= 8 && curline < 236) { if ((wp->draw_ctx.ppu_scanline_regs[curline].ppumask_flags & KS_NES_PPU_MASK_SHOW_BG_LEFT) == 0) { x0 = 0x80 + 8; x1 = (wp->draw_ctx.ppu_scanline_regs[curline].vram_addr_coarse_x & 0x07) + 8; } else { x0 = 0x80 - (wp->draw_ctx.ppu_scanline_regs[curline].vram_addr_coarse_x & 0x07); x1 = 0; } color = (wp->draw_ctx.ppu_scanline_regs[curline].ppu_ctrl & 0xC0) << 24; GXPosition2s16(x0, -0x81 - curline); GXColor1u32(color); GXTexCoord2u16(x1, curline); GXPosition2s16(x0 + 0x100 + 8, -0x81 - curline); GXColor1u32(color); GXTexCoord2u16(x1 + 0x100 + 8, curline); } curline = wp->draw_ctx.ppu_scanline_regs[curline].fine_x_and_next; } while (curline != 0); GXEnd(); } } } cnt = 0; for (i = 8; i < 236; i++) { if ((wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SHOW_BG) == 0) { cnt += 2; } } if (cnt != 0) { GXSetNumTexGens(0); GXSetNumTevStages(1); GXSetNumIndStages(0); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_RGBA4, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetTevDirect(GX_TEVSTAGE0); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV); GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); GXBegin(GX_LINES, GX_VTXFMT0, cnt); for (i = 8; i < 236; i++) { if ((wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SHOW_BG) == 0) { s16 y = -0x81 - i; u32 color = (wp->draw_ctx.ppu_scanline_regs[i].ppu_ctrl & 0xC0) << 24; GXPosition2s16(0x80, y); GXColor1u32(color); GXPosition2s16(0x180, y); GXColor1u32(color); } } GXEnd(); } } u32 ksNesDrawMakeOBJBlankVtxList(ksNesCommonWorkObj* wp) { u32 ret = 0; u32 comparison_mask = KS_NES_PPU_MASK_SPRITES_COMBINED; int i; for (i = 8; i < ARRAY_COUNT(wp->draw_ctx.ppu_scanline_regs); i++) { int bMask = wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SPRITES_COMBINED; if (bMask != comparison_mask && ((bMask != 0) || (comparison_mask != KS_NES_PPU_MASK_SHOW_SPRITES_LEFT)) && (bMask != KS_NES_PPU_MASK_SHOW_SPRITES_LEFT || comparison_mask != 0)) { if ((ret & 1) != 0) { wp->draw_ctx.scanline_y_coords[ret] = i - wp->_004C[ret + 19]; ret++; } if ((comparison_mask == KS_NES_PPU_MASK_SPRITES_COMBINED) || (wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SHOW_SPRITES) == 0) { wp->draw_ctx.scanline_y_coords[ret++] = i; } comparison_mask = wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SPRITES_COMBINED; } } if (ret & 1) { wp->draw_ctx.scanline_y_coords[ret] = i - wp->_004C[ret + 19]; ret++; } return ret; } u32 ksNesDrawMakeOBJAppearVtxList(ksNesCommonWorkObj* wp) { u32 ret = 0; u32 comparison_mask = 0; int i; for (i = 8; i < ARRAY_COUNT(wp->draw_ctx.ppu_scanline_regs); i++) { int bMask = wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SPRITES_COMBINED; if (bMask != comparison_mask && ((bMask != 0) || (comparison_mask != KS_NES_PPU_MASK_SHOW_SPRITES_LEFT)) && (bMask != KS_NES_PPU_MASK_SHOW_SPRITES_LEFT || comparison_mask != 0)) { if ((ret & 1) != 0) { wp->draw_ctx.scanline_y_coords[ret] = i - wp->_004C[ret + 19]; ret++; } if ((wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SHOW_SPRITES)) { wp->draw_ctx.scanline_y_coords[ret++] = i; } comparison_mask = wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SPRITES_COMBINED; } } if (ret & 1) { wp->draw_ctx.scanline_y_coords[ret] = i - wp->_004C[ret + 19]; ret++; } return ret; } void ksNesDrawOBJ(ksNesCommonWorkObj* wp, ksNesStateObj* state, u32 sprite_priority_pass) { u32 size = wp->chr_to_i8_buf_size <= CHR_TO_I8_BUF_SIZE ? wp->chr_to_i8_buf_size : CHR_TO_I8_BUF_SIZE; // int i; GXTexObj GStack_7c; GXTexObj GStack_9c; GXTexObj GStack_bc; u32 i; u32 j; // check the pass is for foreground sprites if (sprite_priority_pass == 0) { for (i = 0; i < KS_NES_SCANLINE_SPRITE_OVERDRAW_COUNT; i++) { 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; // Disable sprites if the ppu's config on this scanline doesn't have sprites enabled. if ((wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_SHOW_SPRITES) == 0) { wp->draw_ctx.sprite_scanline_limit[i] = 0; } } if (state->prg_size == 0x40000 && memcmp(state->prgromp + 0x3ffe9, "MARIO 3", 7) == 0) { for (i = 0; i < ARRAY_COUNT(wp->draw_ctx.ppu_scanline_regs); i++) { // This is a hack for SMB3 where the emulator was drawing sprites (OBJ) on transition screens because // it isn't accurate enough. So, when this specific bank setup is detected on the MMC3 chip's alternate // banks, sprites are entirely disabled by setting the per-scanline limit to 0. if (wp->draw_ctx.ppu_scanline_regs[i].chr_bank_bg_mmc3[1] == 0x7e7e7e7e) { wp->draw_ctx.sprite_scanline_limit[i] = 0; } } } memset(wp->draw_ctx.sprite_quad_data, 0, sizeof(wp->draw_ctx.sprite_quad_data)); int b; int _c; int a; int _j; ksNesSpriteQuadData* quad_p = wp->draw_ctx.sprite_quad_data; for (i = 0; i < 0x100; i += 4) { _j = 8; // 8x8 sprite if (wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.OAMTable[i].y_pos].ppu_ctrl & KS_NES_PPU_CTRL_SPRITE_SIZE) { _j = 16; // 8x16 sprite } a = 0; if (wp->draw_ctx.OAMTable[i].attributes & KS_NES_OAM_ATTR_FLIP_VERTICAL) { b = _j << 2; _c = -4; } else { b = 0; _c = 4; } for (j = 0; j < _j; j++) { if (wp->draw_ctx.sprite_scanline_limit[wp->draw_ctx.OAMTable[i].y_pos + j] != 0) { wp->draw_ctx.sprite_scanline_limit[wp->draw_ctx.OAMTable[i].y_pos + j]--; if ((a & 2) == 0) { quad_p->y_and_v_pairs[a] = j + wp->draw_ctx.OAMTable[i].y_pos; quad_p->y_and_v_pairs[a + 1] = b; a += 2; } } else if ((a & 2) != 0) { quad_p->y_and_v_pairs[a] = j + wp->draw_ctx.OAMTable[i].y_pos; quad_p->y_and_v_pairs[a + 1] = b; a += 2; } b += _c; } if ((a & 2) != 0) { quad_p->y_and_v_pairs[a] = j + wp->draw_ctx.OAMTable[i].y_pos; quad_p->y_and_v_pairs[a + 1] = b; a += 2; } wp->draw_ctx.sprite_vertex_count[i >> 2] = a; quad_p++; } } GXSetNumChans(1); GXSetNumTexGens(2); GXSetNumTevStages(3); GXSetNumIndStages(2); GXSetBlendMode(GX_BM_LOGIC, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_COPY); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX1, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGB, GX_RGBA4, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_CLR_RGBA, GX_RGBX8, 10); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX1, GX_CLR_RGBA, GX_RGBX8, 10); GXInitTexObj(&GStack_7c, wp->chr_to_u8_bufp, 0x400, (u16)(size >> 10), GX_TF_I8, GX_MIRROR, GX_CLAMP, 0); GXInitTexObjLOD(&GStack_7c, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); GXLoadTexObj(&GStack_7c, GX_TEXMAP0); #ifndef BUGFIXES GXInitTexObj(&GStack_9c, wp->draw_ctx.sprite_chr_bank_lut, 8, 4, GX_TF_IA8, GX_CLAMP, GX_CLAMP, 0); #else GXInitTexObj(&GStack_9c, wp->draw_ctx.sprite_chr_bank_lut, 5, 4, GX_TF_IA8, GX_CLAMP, GX_CLAMP, 0); #endif GXInitTexObjLOD(&GStack_9c, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); GXLoadTexObj(&GStack_9c, GX_TEXMAP1); GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 0x3c); GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_TRUE, 0x100, 0x101); GXSetTexCoordBias(GX_TEXCOORD0, 0, 0); GXSetIndTexOrder(GX_INDTEXSTAGE0, GX_TEXCOORD0, GX_TEXMAP1); GXSetIndTexCoordScale(GX_INDTEXSTAGE0, GX_ITS_8, GX_ITS_1); GXSetTevIndirect(GX_TEVSTAGE0, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_OFF, GX_ITW_OFF, GX_FALSE, GX_FALSE, GX_ITBA_OFF); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEX_DISABLE, GX_COLOR0A0); GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_VTX, GX_SRC_VTX, 0, GX_DF_NONE, GX_AF_NONE); GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVREG2); GXInitTexObj(&GStack_bc, wp->draw_ctx.sprite_indirect_lut, 4, 16, GX_TF_IA8, GX_CLAMP, GX_CLAMP, 0); GXInitTexObjLOD(&GStack_bc, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); GXLoadTexObj(&GStack_bc, GX_TEXMAP2); GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX1, 0x3c); GXSetTexCoordScaleManually(GX_TEXCOORD1, GX_TRUE, 0x100, 0x101); GXSetTexCoordBias(GX_TEXCOORD1, 0, 0); GXSetIndTexOrder(GX_INDTEXSTAGE1, GX_TEXCOORD1, GX_TEXMAP2); GXSetIndTexCoordScale(GX_INDTEXSTAGE1, GX_ITS_8, GX_ITS_1); { // u32 i; for (i = 0; size > (0x8000 << i); i++) {} static f32 indtexmtx_obj[2][3] = { { 0.5f, 0.f, 0.f }, { 0.f, 0.0625f, 0.f } }; indtexmtx_obj[0][0] = 0.5f / (i >= 4 ? (1 << (i - 3)) : 1); indtexmtx_obj[1][1] = 0.5f / (i <= 2 ? (1 << (3 - i)) : 1); GXSetIndTexMtx(GX_ITM_0, indtexmtx_obj, 36 + (i < 4 ? 0 : i - 3)); } GXSetTevIndirect(GX_TEVSTAGE1, GX_INDTEXSTAGE1, GX_ITF_8, GX_ITB_NONE, GX_ITM_0, GX_ITW_OFF, GX_ITW_0, GX_TRUE, GX_FALSE, GX_ITBA_OFF); GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP0, GX_COLOR_NULL); GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_C2, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC); GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA); GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevDirect(GX_TEVSTAGE2); GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL); static const GXColor color_thres = { 255, 1, 0, 0 }; GXSetTevColor(GX_TEVREG0, color_thres); GXSetTevColorIn(GX_TEVSTAGE2, GX_CC_C0, GX_CC_C2, GX_CC_CPREV, GX_CC_ZERO); GXSetTevColorOp(GX_TEVSTAGE2, GX_TEV_COMP_GR16_GT, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE2, GX_CA_APREV, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); GXSetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_ALWAYS, 0); u32 n_verts = 0; for (i = 0; i < ARRAY_COUNT(wp->draw_ctx.OAMTable); i++) { // draw sprites if (sprite_priority_pass == 0 || (wp->draw_ctx.OAMTable[i].attributes & KS_NES_OAM_ATTR_PRIORITY) != 0) { n_verts += wp->draw_ctx.sprite_vertex_count[i]; } } // u32 idx = 0x100-4; u32 idx2 = ARRAY_COUNT(wp->draw_ctx.OAMTable) - 1; GXBegin(GX_QUADS, GX_VTXFMT0, n_verts); while (TRUE) { ksNesSpriteQuadData* quad_p = &wp->draw_ctx.sprite_quad_data[idx2]; u8* quad_pairs = quad_p->y_and_v_pairs; // u32 scanline_idx = wp->draw_ctx.OAMTable[idx]; u32 tile_index = wp->draw_ctx.OAMTable[idx2].tile_index; u32 flags2; if (wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.OAMTable[idx2].y_pos].ppu_ctrl & KS_NES_PPU_CTRL_SPRITE_SIZE) { // 8x16 sprite 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)]; tile_index &= KS_NES_OAM_TILE_IDX; } else { // 8x8 sprite 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)]; } // u32 flags3 = wp->draw_ctx.OAMTable[idx + 2]; u32 oam_attrs = wp->draw_ctx.OAMTable[idx2].attributes; u32 x0 = wp->draw_ctx.OAMTable[idx2].x_pos + 128; u32 x1 = wp->draw_ctx.OAMTable[idx2].x_pos + 136; u32 color = ((wp->draw_ctx.OAMTable[idx2].x_pos & 3) * 16 + 4) * 0x01000000; if (sprite_priority_pass != 0) { // We're in the BG pass so skip if the sprite is supposed to be drawn in front of the BG if ((oam_attrs & KS_NES_OAM_ATTR_PRIORITY) == 0) { goto loop_condition; } } else { // We're in the FG pass if ((oam_attrs & KS_NES_OAM_ATTR_PRIORITY) != 0) { color |= 0xFF010000; } } u32 s0; u32 t0; u32 s1; u32 s1_2; u32 t1; u32 t1_2; u32 temp; // u32 j; s0 = 0; t0 = (flags2 & 0xFE) * 2; // select chr bank offset temp = ((tile_index & 0x3F) * 0x20) | ((flags2 & 0x01) * 0x800); // flags2 bit0 determines the pattern table if (wp->draw_ctx.OAMTable[idx2].attributes & KS_NES_OAM_ATTR_FLIP_HORIZONTAL) { s1 = temp + 32; s1_2 = temp; } else { s1 = temp; s1_2 = temp + 32; } for (j = 0; j < wp->draw_ctx.sprite_vertex_count[idx2]; j += 4) { u32 y0 = -129 - quad_pairs[0]; t1 = quad_pairs[1]; u32 y1 = -129 - quad_pairs[2]; t1_2 = quad_pairs[3]; quad_pairs += 4; GXPosition2s16(x0, y0); GXColor1u32(color); GXTexCoord2u16(s0, t0); GXTexCoord2u16(s1, t1); GXPosition2s16(x1, y0); GXColor1u32(color); GXTexCoord2u16(s0, t0); GXTexCoord2u16(s1_2, t1); GXPosition2s16(x1, y1); GXColor1u32(color); GXTexCoord2u16(s0, t0); GXTexCoord2u16(s1_2, t1_2); GXPosition2s16(x0, y1); GXColor1u32(color); GXTexCoord2u16(s0, t0); GXTexCoord2u16(s1, t1_2); } loop_condition: if (idx2 == 0) { break; } // idx -= 4; idx2--; } GXEnd(); if (sprite_priority_pass != 0) { u32 n = ksNesDrawMakeOBJBlankVtxList(wp); if (n != 0) { GXSetNumChans(1); GXSetNumTexGens(0); GXSetNumTevStages(1); GXSetNumIndStages(0); GXSetBlendMode(GX_BM_LOGIC, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_COPY); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetTevDirect(GX_TEVSTAGE0); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE); GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR); GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); GXBegin(GX_QUADS, GX_VTXFMT0, n * 2); for (u32 i = 0; i < n; i += 2) { s16 x1 = (wp->draw_ctx.ppu_scanline_regs[wp->draw_ctx.scanline_y_coords[i]].ppumask_flags & KS_NES_PPU_MASK_SPRITES_COMBINED) == KS_NES_PPU_MASK_SHOW_SPRITES ? 136 : 384; GXPosition2s16(128, -128 - wp->draw_ctx.scanline_y_coords[i]); GXColor1u32(0x00000000); GXPosition2s16(x1, -128 - wp->draw_ctx.scanline_y_coords[i]); GXColor1u32(0x00000000); GXPosition2s16(x1, -128 - wp->draw_ctx.scanline_y_coords[i] - wp->draw_ctx.scanline_y_coords[i + 1]); GXColor1u32(0x00000000); GXPosition2s16(128, -128 - wp->draw_ctx.scanline_y_coords[i] - wp->draw_ctx.scanline_y_coords[i + 1]); GXColor1u32(0x00000000); } GXEnd(); } } } void ksNesDrawOBJMMC5(ksNesCommonWorkObj* wp, ksNesStateObj* sp, u32 sprite_priority_pass) { static const GXColor color_thres = { 255, 1, 0, 0 }; // clang-format off static f32 indtexmtx_obj[2][3] = { { 0.5f, 0.0f, 0.0f }, { 0.0f, 0.0625f, 0.0f }, }; // clang-format on GXTexObj obj3; GXTexObj obj; GXTexObj obj2; size_t var_r25 = wp->chr_to_i8_buf_size; u32 sft; u32 var_r3; u32 i, j; u32 var_r15; u32 var_r24; u32 x; u32 x1, x2, y1, y2; u32 u1, u2, v; u32 clr; u32 oam_attrs; u8 *work; GXSetNumChans(1); GXSetNumTexGens(2); GXSetNumTevStages(3); GXSetNumIndStages(2); GXSetBlendMode(GX_BM_LOGIC, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_COPY); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX1, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_U16, 10); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX1, GX_TEX_ST, GX_U16, 10); #ifndef BUGFIXES GXInitTexObj(&obj, wp->draw_ctx.sprite_chr_bank_lut, 8, 4, GX_TF_IA8, GX_CLAMP, GX_CLAMP, 0); #else GXInitTexObj(&GStack_9c, wp->draw_ctx.sprite_chr_bank_lut, 5, 4, GX_TF_IA8, GX_CLAMP, GX_CLAMP, 0); #endif GXInitTexObjLOD(&obj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); GXLoadTexObj(&obj, GX_TEXMAP1); GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 60); GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_TRUE, 0x100, 0x101); GXSetTexCoordBias(GX_TEXCOORD0, 0, 0); GXSetIndTexOrder(GX_INDTEXSTAGE0, GX_TEXCOORD0, GX_TEXMAP1); GXSetIndTexCoordScale(GX_INDTEXSTAGE0, GX_ITS_8, GX_ITS_1); GXSetTevIndirect(GX_TEVSTAGE0, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_OFF, GX_ITW_OFF, GX_ITW_OFF, GX_FALSE, GX_FALSE, GX_ITBA_OFF); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEX_DISABLE, GX_COLOR0A0); GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_VTX, GX_SRC_VTX, 0, GX_DF_NONE, GX_AF_NONE); GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVREG2); GXInitTexObj(&obj2, wp->draw_ctx.sprite_indirect_lut, 4, 16, GX_TF_IA8, GX_CLAMP, GX_CLAMP, 0); GXInitTexObjLOD(&obj2, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); GXLoadTexObj(&obj2, GX_TEXMAP2); GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX1, 60); GXSetTexCoordScaleManually(GX_TEXCOORD1, GX_TRUE, 0x100, 0x101); GXSetTexCoordBias(GX_TEXCOORD1, 0, 0); GXSetIndTexOrder(GX_INDTEXSTAGE1, GX_TEXCOORD1, GX_TEXMAP2); GXSetIndTexCoordScale(GX_INDTEXSTAGE1, GX_ITS_8, GX_ITS_1); sft = 0; while (var_r25 > (0x8000 << sft)) { sft++; } indtexmtx_obj[0][0] = 0.5f / (sft >= 4 ? (1 << (sft - 3)) : 1); indtexmtx_obj[1][1] = 0.5f / (sft <= 2 ? (1 << (3 - sft)) : 1); GXSetIndTexMtx(GX_ITM_0, indtexmtx_obj, 36 + (sft < 4 ? 0 : sft - 3)); GXSetTevIndirect(GX_TEVSTAGE1, GX_INDTEXSTAGE1, GX_ITF_8, GX_ITB_NONE, GX_ITM_0, GX_ITW_OFF, GX_ITW_0, GX_TRUE, GX_FALSE, GX_ITBA_OFF); GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP0, GX_COLOR_NULL); GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_C2, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC); GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA); GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevDirect(GX_TEVSTAGE2); GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL); GXSetTevColor(GX_TEVREG0, color_thres); GXSetTevColorIn(GX_TEVSTAGE2, GX_CC_C0, GX_CC_C2, GX_CC_CPREV, GX_CC_ZERO); GXSetTevColorOp(GX_TEVSTAGE2, GX_TEV_COMP_GR16_GT, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE2, GX_CA_APREV, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); GXSetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_ALWAYS, 0); 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) { 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; } } for (i = 0; i < 8; i += 4) { GXInitTexObj(&obj3, &wp->chr_to_u8_bufp[(var_r25 >> 3) * i], 1024, var_r25 >> 11, GX_TF_I8, GX_MIRROR, GX_CLAMP, 0); GXInitTexObjLOD(&obj3, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); GXLoadTexObj(&obj3, GX_TEXMAP0); // why is this loop value loaded here??? 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 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; if (size_mode != 0) { // 8x16 sprite palette_bits = (tile_idx >> 6) | ((tile_idx & KS_NES_OAM_TILE_BANK) << 2); palette_bits = ((wp->draw_ctx.ppu_scanline_regs[scanline].chr_bank_ext_upper_sprite << (palette_bits + 1)) & 0x100) | ((wp->draw_ctx.ppu_scanline_regs[scanline].chr_bank_sprite[palette_bits])); tile_idx &= KS_NES_OAM_TILE_IDX; } else { // 8x8 sprite palette_bits = (tile_idx >> 6) | (wp->draw_ctx.ppu_scanline_regs[scanline].ppu_ctrl >> 1) & 4; palette_bits = ((wp->draw_ctx.ppu_scanline_regs[scanline].chr_bank_ext_upper_sprite << (palette_bits + 1)) & 0x100) | ((wp->draw_ctx.ppu_scanline_regs[scanline].chr_bank_sprite[palette_bits])); } 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 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) { if ((oam_attrs & KS_NES_OAM_ATTR_PRIORITY) == 0) { goto loop_point; } } else if ((oam_attrs & KS_NES_OAM_ATTR_PRIORITY) != 0) { clr |= 0xFF010000; } v = (palette_bits << 1) & 0x3FC; tmp = ((tile_idx << 5) & 0x7E0) | ((palette_bits << 0xB) & 0x800 & ~0x7E0); if (oam_attrs & KS_NES_OAM_ATTR_FLIP_HORIZONTAL) { u2 = tmp; u1 = tmp + 0x20; } else { u1 = tmp; u2 = tmp + 0x20; } if ((oam_attrs & KS_NES_OAM_ATTR_FLIP_VERTICAL) != 0) { tmp = (-0x81 - scanline) + i; y1 = tmp - 8; y2 = tmp - 4; if (var_r24 != 0) { u1 += 0x20; u2 += 0x20; } } else { tmp = (-0x81 - scanline) - i; y1 = tmp; y2 = tmp - 4; } do { GXPosition2s16(x1, y1); GXColor1u32(clr); GXTexCoord2u16(0, v); GXTexCoord2u16(u1, 0); GXPosition2s16(x2, y1); GXColor1u32(clr); GXTexCoord2u16(0, v); GXTexCoord2u16(u2, 0); GXPosition2s16(x2, y2); GXColor1u32(clr); GXTexCoord2u16(0, v); GXTexCoord2u16(u2, 0x10); GXPosition2s16(x1, y2); GXColor1u32(clr); GXTexCoord2u16(0, v); GXTexCoord2u16(u1, 0x10); y1 -= 8; y2 -= 8; if ((oam_attrs & KS_NES_OAM_ATTR_FLIP_VERTICAL) != 0) { u1 -= 0x20; u2 -= 0x20; } else { u1 += 0x20; u2 += 0x20; } } while (var_r24-- != 0); loop_point: if (j == 0) break; j -= sizeof(wp->draw_ctx.OAMTable[0]); } GXEnd(); } if (sprite_priority_pass == 0) return; u32 quads = ksNesDrawMakeOBJBlankVtxList(wp); if (quads == 0) return; GXSetNumChans(1); GXSetNumTexGens(0); GXSetNumTevStages(1); GXSetNumIndStages(0); GXSetBlendMode(GX_BM_LOGIC, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_COPY); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetTevDirect(GX_TEVSTAGE0); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_VTX, GX_SRC_VTX, 0, GX_DF_NONE, GX_AF_NONE); GXSetTevOp(GX_TEVSTAGE0, GX_PASSCLR); GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 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; } GXPosition2s16(0x80, -128 - x); GXColor1u32(0); GXPosition2s16(x, -128 - work[0x60]); GXColor1u32(0); GXPosition2s16(x, -128 - work[0x60] - work[0x61]); GXColor1u32(0); GXPosition2s16(0x80, -128 - work[0x60] - work[0x61]); GXColor1u32(0); } GXEnd(); } 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); GXSetCopyClear(black, 0xffffff); GXCopyTex(buf, GX_FALSE); GXPixModeSync(); GXInvalidateTexAll(); } void ksNesDrawOBJI8ToEFB(ksNesCommonWorkObj* wp, u8* buf) { GXTexObj obj; u32 i; s32 u; u8* work; u32 cnt; GXSetNumChans(0); GXSetNumTexGens(1); GXSetNumTevStages(1); GXSetNumIndStages(0); GXSetBlendMode(GX_BM_LOGIC, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_COPY); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_U16, 8); GXInitTexObj(&obj, (void*)buf, 256, 228, GX_TF_I8, GX_CLAMP, GX_CLAMP, 0); GXInitTexObjLOD(&obj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); GXLoadTexObj(&obj, GX_TEXMAP0); GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 60); GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_TRUE, 0x100, 0x100); GXSetTexCoordBias(GX_TEXCOORD0, 0, 0); GXSetTevDirect(GX_TEVSTAGE0); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE); GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_ALWAYS, 0); cnt = ksNesDrawMakeOBJAppearVtxList(wp); if (cnt == 0) return; 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); GXPosition2s16(0x180, -128 - work[0x60]); GXTexCoord2u16(0x100, work[0x60] - 8); GXPosition2s16(0x180, -128 - work[0x60] - work[0x61]); GXTexCoord2u16(0x100, work[0x60] + work[0x61] - 8); GXPosition2s16(u + 0x80, -128 - work[0x60] - work[0x61]); GXTexCoord2u16(u, work[0x60] + work[0x61] - 8); } GXEnd(); } void ksNesDrawEmuResult(ksNesCommonWorkObj* wp) { static f32 indtexmtx[2][3] = { { 0.5f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }; static const GXColor black = { 0, 0, 0, 0 }; static const GXColor color0 = { 0x3A, 0x3A, 0x3A, 0x00 }; static const GXColor color1 = { 0x71, 0x71, 0x71, 0x00 }; static const GXColor color2 = { 0x15, 0x15, 0x15, 0x00 }; u32 cnt; u32 i; u32 color_effects_state; u8 y; u32 val; u32 clr; u8 *work; GXTexObj obj; GXTexObj obj2; cnt = 0; i = 8; color_effects_state = 0xFF; for (; i < ARRAY_COUNT(wp->draw_ctx.ppu_scanline_regs); i++) { val = wp->draw_ctx.ppu_scanline_regs[i].ppumask_flags & KS_NES_PPU_MASK_COLOR_EFFECTS; if (val != color_effects_state) { color_effects_state = val; if ((cnt & 1) != 0) { wp->draw_ctx.scanline_y_coords[cnt++] = i - 8; } wp->draw_ctx.scanline_y_coords[cnt++] = i - 8; } } if ((cnt & 1) != 0) { wp->draw_ctx.scanline_y_coords[cnt++] = i - 8; } wp->draw_ctx.scanline_y_coords[cnt] = 0xFF; GXInitTexObj(&obj2, wp->result_bufp, KS_NES_WIDTH, KS_NES_HEIGHT, GX_TF_I8, GX_CLAMP, GX_CLAMP, 0); GXInitTexObjLOD(&obj2, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); GXLoadTexObj(&obj2, GX_TEXMAP1); // Sampling LUT for applying color effects like color emphasis (I think) GXInitTexObj(&obj, wp->draw_ctx.post_process_lut, 256, 4, GX_TF_RGB5A3, GX_CLAMP, GX_CLAMP, 0); GXInitTexObjLOD(&obj, GX_NEAR, GX_NEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); GXLoadTexObj(&obj, GX_TEXMAP0); GXSetTexCoordGen2(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 60, GX_FALSE, 125); GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_TRUE, 256, 256); GXSetTexCoordBias(GX_TEXCOORD0, 0, 0); GXSetIndTexOrder(GX_INDTEXSTAGE0, GX_TEXCOORD0, GX_TEXMAP1); GXSetIndTexCoordScale(GX_INDTEXSTAGE0, GX_ITS_1, GX_ITS_1); GXSetIndTexMtx(GX_ITM_0, indtexmtx, 1); GXSetTevIndirect(GX_TEVSTAGE0, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_0, GX_ITW_0, GX_ITW_0, GX_FALSE, GX_FALSE, GX_ITBA_OFF); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 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; if ((val & KS_NES_PPU_MASK_COLOR_EFFECTS) == 0) { cnt += 4; // has color and no color emphasis } } if (cnt != 0) { GXSetNumChans(0); GXSetNumTexGens(1); GXSetNumTevStages(1); GXSetNumIndStages(1); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_U16, 8); GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE); 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; 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 - work[0x61]); GXTexCoord2u16(256, work[0x61]); GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x61]); GXTexCoord2u16(0, work[0x61]); GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x60]); GXTexCoord2u16(0, work[0x60]); } } while (i != 0); } 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; if ((val & KS_NES_PPU_MASK_COLOR_EFFECTS) != 0) { cnt += 4; } } if (cnt != 0) { GXSetNumChans(1); GXSetNumTexGens(1); GXSetNumTevStages(4); GXSetNumIndStages(1); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_CLR0, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_U16, 8); GXSetTevIndirect(GX_TEVSTAGE0, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_0, GX_ITW_0, GX_ITW_0, GX_FALSE, GX_FALSE, GX_ITBA_OFF); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); GXSetTevIndirect(GX_TEVSTAGE1, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_0, GX_ITW_0, GX_ITW_0, GX_FALSE, GX_FALSE, GX_ITBA_OFF); GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); GXSetTevIndirect(GX_TEVSTAGE2, GX_INDTEXSTAGE0, GX_ITF_8, GX_ITB_NONE, GX_ITM_0, GX_ITW_0, GX_ITW_0, GX_FALSE, GX_FALSE, GX_ITBA_OFF); GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); GXSetTevDirect(GX_TEVSTAGE3); GXSetTevOrder(GX_TEVSTAGE3, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0); GXSetChanCtrl(GX_COLOR0A0, GX_FALSE, GX_SRC_VTX, GX_SRC_VTX, 0, GX_DF_NONE, GX_AF_NONE); GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA); GXSetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_ALPHA); GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA); GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP1); GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP2); GXSetTevSwapMode(GX_TEVSTAGE2, GX_TEV_SWAP0, GX_TEV_SWAP3); GXSetTevSwapMode(GX_TEVSTAGE3, GX_TEV_SWAP0, GX_TEV_SWAP0); GXSetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_C0, GX_CC_ZERO); GXSetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_TEXC, GX_CC_C1, GX_CC_CPREV); GXSetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_TEXC, GX_CC_C2, GX_CC_CPREV); GXSetTevColorIn(GX_TEVSTAGE3, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_CPREV); GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevColorOp(GX_TEVSTAGE3, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); GXSetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); GXSetTevAlphaIn(GX_TEVSTAGE2, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); GXSetTevAlphaIn(GX_TEVSTAGE3, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevAlphaOp(GX_TEVSTAGE3, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); GXSetTevColor(GX_TEVREG0, color0); GXSetTevColor(GX_TEVREG1, color1); GXSetTevColor(GX_TEVREG2, color2); 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) { clr += 0x10000000; } if ((wp->draw_ctx.ppu_scanline_regs[work[0x60]].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) { clr += 0x00001000; } GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x60]); GXColor1u32(clr); GXTexCoord2u16(256, work[0x60]); GXPosition2s16(KS_NES_WIDTH + KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x61]); GXColor1u32(clr); GXTexCoord2u16(256, work[0x61]); GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x61]); GXColor1u32(clr); GXTexCoord2u16(0, work[0x61]); GXPosition2s16(KS_NES_CENTER_X, -KS_NES_CENTER_Y - work[0x60]); GXColor1u32(clr); GXTexCoord2u16(0, work[0x60]); } } while (i != 0); } GXSetTexCopySrc(KS_NES_CENTER_X, KS_NES_CENTER_Y, KS_NES_WIDTH, KS_NES_HEIGHT); GXSetTexCopyDst(KS_NES_WIDTH, KS_NES_HEIGHT, GX_TF_RGB565, GX_FALSE); GXSetCopyClear(black, 0xFFFFFF); GXCopyTex(wp->result_bufp, GX_FALSE); GXPixModeSync(); } void ksNesDraw(ksNesCommonWorkObj* wp, ksNesStateObj* state) { ksNesDrawInit(wp); ksNesDrawClearEFBFirst(wp); if (state->mapper == KS_NES_MAPPER_MMC2 || state->mapper == KS_NES_MAPPER_MMC4) { ksNesDrawMakeBGIndTexMMC2(wp, state->mapper == KS_NES_MAPPER_MMC2 ? TRUE : FALSE); ksNesDrawOBJSetupMMC2(wp); } else if (state->mapper == KS_NES_MAPPER_MMC5) { ksNesDrawMakeBGIndTexMMC5(wp, state); } else { ksNesDrawMakeBGIndTex(wp, state->mapper == KS_NES_MAPPER_MMC3 ? TRUE : FALSE); } PPCSync(); if (state->mapper == KS_NES_MAPPER_MMC5) { ksNesDrawOBJMMC5(wp, state, 0); } else { ksNesDrawOBJ(wp, state, 0); } ksNesDrawFlushEFBToRed8(wp->result_bufp); if (state->mapper == KS_NES_MAPPER_MMC5) { ksNesDrawOBJMMC5(wp, state, 1); } else { ksNesDrawOBJ(wp, state, 1); } ksNesDrawBG(wp, state); ksNesDrawOBJI8ToEFB(wp, wp->result_bufp); ksNesDrawFlushEFBToRed8(wp->result_bufp); ksNesDrawEmuResult(wp); ksNesDrawEnd(); }