#include "global.h" #include "bg.h" #include "dma3.h" #include "gpu_regs.h" #define DISPCNT_ALL_BG_AND_MODE_BITS (DISPCNT_BG_ALL_ON | 0x7) struct BgControl { struct BgConfig { u16 visible:1; u16 unknown_1:1; u16 screenSize:2; u16 priority:2; u16 mosaic:1; u16 wraparound:1; u16 charBaseIndex:2; u16 mapBaseIndex:5; u16 paletteMode:1; u8 unknown_2; u8 unknown_3; } configs[4]; u16 bgVisibilityAndMode; }; struct BgConfig2 { u32 baseTile:10; u32 basePalette:4; u32 unk_3:18; void* tilemap; u32 bg_x; u32 bg_y; }; static IWRAM_DATA struct BgControl sGpuBgConfigs; static IWRAM_DATA struct BgConfig2 sGpuBgConfigs2[4]; static IWRAM_DATA u32 sDmaBusyBitfield[4]; u32 gUnneededFireRedVariable; static const struct BgConfig sZeroedBgControlStruct = { 0 }; void ResetBgs(void) { ResetBgControlStructs(); sGpuBgConfigs.bgVisibilityAndMode = 0; SetTextModeAndHideBgs(); } static void SetBgModeInternal(u8 bgMode) { sGpuBgConfigs.bgVisibilityAndMode &= 0xFFF8; sGpuBgConfigs.bgVisibilityAndMode |= bgMode; } u8 GetBgMode(void) { return sGpuBgConfigs.bgVisibilityAndMode & 0x7; } void ResetBgControlStructs(void) { struct BgConfig* bgConfigs = &sGpuBgConfigs.configs[0]; struct BgConfig zeroedConfig = sZeroedBgControlStruct; int i; for (i = 0; i < 4; i++) { bgConfigs[i] = zeroedConfig; } } void Unused_ResetBgControlStruct(u8 bg) { if (IsInvalidBg(bg) == FALSE) { sGpuBgConfigs.configs[bg] = sZeroedBgControlStruct; } } void SetBgControlAttributes(u8 bg, u8 charBaseIndex, u8 mapBaseIndex, u8 screenSize, u8 paletteMode, u8 priority, u8 mosaic, u8 wraparound) { if (IsInvalidBg(bg) == FALSE) { if (charBaseIndex != 0xFF) { sGpuBgConfigs.configs[bg].charBaseIndex = charBaseIndex & 0x3; } if (mapBaseIndex != 0xFF) { sGpuBgConfigs.configs[bg].mapBaseIndex = mapBaseIndex & 0x1F; } if (screenSize != 0xFF) { sGpuBgConfigs.configs[bg].screenSize = screenSize & 0x3; } if (paletteMode != 0xFF) { sGpuBgConfigs.configs[bg].paletteMode = paletteMode; } if (priority != 0xFF) { sGpuBgConfigs.configs[bg].priority = priority & 0x3; } if (mosaic != 0xFF) { sGpuBgConfigs.configs[bg].mosaic = mosaic & 0x1; } if (wraparound != 0xFF) { sGpuBgConfigs.configs[bg].wraparound = wraparound; } sGpuBgConfigs.configs[bg].unknown_2 = 0; sGpuBgConfigs.configs[bg].unknown_3 = 0; sGpuBgConfigs.configs[bg].visible = 1; } } u16 GetBgControlAttribute(u8 bg, u8 attributeId) { if (IsInvalidBg(bg) == FALSE && sGpuBgConfigs.configs[bg].visible != FALSE) { switch (attributeId) { case BG_CTRL_ATTR_VISIBLE: return sGpuBgConfigs.configs[bg].visible; case BG_CTRL_ATTR_CHARBASEINDEX: return sGpuBgConfigs.configs[bg].charBaseIndex; case BG_CTRL_ATTR_MAPBASEINDEX: return sGpuBgConfigs.configs[bg].mapBaseIndex; case BG_CTRL_ATTR_SCREENSIZE: return sGpuBgConfigs.configs[bg].screenSize; case BG_CTRL_ATTR_PALETTEMODE: return sGpuBgConfigs.configs[bg].paletteMode; case BG_CTRL_ATTR_PRIORITY: return sGpuBgConfigs.configs[bg].priority; case BG_CTRL_ATTR_MOSAIC: return sGpuBgConfigs.configs[bg].mosaic; case BG_CTRL_ATTR_WRAPAROUND: return sGpuBgConfigs.configs[bg].wraparound; } } return 0xFF; } u8 LoadBgVram(u8 bg, const void *src, u16 size, u16 destOffset, u8 mode) { u16 offset; s8 cursor; if (IsInvalidBg(bg) == FALSE && sGpuBgConfigs.configs[bg].visible != FALSE) { switch (mode) { case 0x1: offset = sGpuBgConfigs.configs[bg].charBaseIndex * BG_CHAR_SIZE; break; case 0x2: offset = sGpuBgConfigs.configs[bg].mapBaseIndex * BG_SCREEN_SIZE; break; default: cursor = -1; goto end; } offset = destOffset + offset; cursor = RequestDma3Copy(src, (void*)(offset + BG_VRAM), size, 0); if (cursor == -1) { return -1; } } else { return -1; } end: return cursor; } static void ShowBgInternal(u8 bg) { u16 value; if (IsInvalidBg(bg) == FALSE && sGpuBgConfigs.configs[bg].visible != FALSE) { value = sGpuBgConfigs.configs[bg].priority | (sGpuBgConfigs.configs[bg].charBaseIndex << 2) | (sGpuBgConfigs.configs[bg].mosaic << 6) | (sGpuBgConfigs.configs[bg].paletteMode << 7) | (sGpuBgConfigs.configs[bg].mapBaseIndex << 8) | (sGpuBgConfigs.configs[bg].wraparound << 13) | (sGpuBgConfigs.configs[bg].screenSize << 14); SetGpuReg((bg << 1) + 0x8, value); sGpuBgConfigs.bgVisibilityAndMode |= 1 << (bg + 8); sGpuBgConfigs.bgVisibilityAndMode &= DISPCNT_ALL_BG_AND_MODE_BITS; } } static void HideBgInternal(u8 bg) { if (IsInvalidBg(bg) == FALSE) { sGpuBgConfigs.bgVisibilityAndMode &= ~(1 << (bg + 8)); sGpuBgConfigs.bgVisibilityAndMode &= DISPCNT_ALL_BG_AND_MODE_BITS; } } static void SyncBgVisibilityAndMode(void) { SetGpuReg(0, (GetGpuReg(0) & ~DISPCNT_ALL_BG_AND_MODE_BITS) | sGpuBgConfigs.bgVisibilityAndMode); } void SetTextModeAndHideBgs(void) { SetGpuReg(0, GetGpuReg(0) & ~DISPCNT_ALL_BG_AND_MODE_BITS); } static void SetBgAffineInternal(u8 bg, u32 srcCenterX, u32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle) { struct BgAffineSrcData src; struct BgAffineDstData dest; switch (sGpuBgConfigs.bgVisibilityAndMode & 0x7) { case 1: if (bg != 2) return; break; case 2: if (bg < 2 || bg > 3) return; break; case 0: default: return; } src.texX = srcCenterX; src.texY = srcCenterY; src.scrX = dispCenterX; src.scrY = dispCenterY; src.sx = scaleX; src.sy = scaleY; src.alpha = rotationAngle; BgAffineSet(&src, &dest, 1); SetGpuReg(REG_OFFSET_BG2PA, dest.pa); SetGpuReg(REG_OFFSET_BG2PB, dest.pb); SetGpuReg(REG_OFFSET_BG2PC, dest.pc); SetGpuReg(REG_OFFSET_BG2PD, dest.pd); SetGpuReg(REG_OFFSET_BG2PA, dest.pa); SetGpuReg(REG_OFFSET_BG2X_L, (s16)(dest.dx)); SetGpuReg(REG_OFFSET_BG2X_H, (s16)(dest.dx >> 16)); SetGpuReg(REG_OFFSET_BG2Y_L, (s16)(dest.dy)); SetGpuReg(REG_OFFSET_BG2Y_H, (s16)(dest.dy >> 16)); } bool8 IsInvalidBg(u8 bg) { if (bg > 3) return TRUE; return FALSE; } int DummiedOutFireRedLeafGreenTileAllocFunc(int a1, int a2, int a3, int a4) { return 0; } void ResetBgsAndClearDma3BusyFlags(u32 leftoverFireRedLeafGreenVariable) { int i; ResetBgs(); for (i = 0; i < 4; i++) { sDmaBusyBitfield[i] = 0; } gUnneededFireRedVariable = leftoverFireRedLeafGreenVariable; } void InitBgsFromTemplates(u8 bgMode, const struct BgTemplate *templates, u8 numTemplates) { int i; u8 bg; SetBgModeInternal(bgMode); ResetBgControlStructs(); for (i = 0; i < numTemplates; i++) { bg = templates[i].bg; if (bg < 4) { SetBgControlAttributes(bg, templates[i].charBaseIndex, templates[i].mapBaseIndex, templates[i].screenSize, templates[i].paletteMode, templates[i].priority, 0, 0); sGpuBgConfigs2[bg].baseTile = templates[i].baseTile; sGpuBgConfigs2[bg].basePalette = 0; sGpuBgConfigs2[bg].unk_3 = 0; sGpuBgConfigs2[bg].tilemap = NULL; sGpuBgConfigs2[bg].bg_x = 0; sGpuBgConfigs2[bg].bg_y = 0; } } } void InitBgFromTemplate(const struct BgTemplate *template) { u8 bg = template->bg; if (bg < 4) { SetBgControlAttributes(bg, template->charBaseIndex, template->mapBaseIndex, template->screenSize, template->paletteMode, template->priority, 0, 0); sGpuBgConfigs2[bg].baseTile = template->baseTile; sGpuBgConfigs2[bg].basePalette = 0; sGpuBgConfigs2[bg].unk_3 = 0; sGpuBgConfigs2[bg].tilemap = NULL; sGpuBgConfigs2[bg].bg_x = 0; sGpuBgConfigs2[bg].bg_y = 0; } } void SetBgMode(u8 bgMode) { SetBgModeInternal(bgMode); } u16 LoadBgTiles(u8 bg, const void* src, u16 size, u16 destOffset) { u16 tileOffset; u8 cursor; if (GetBgControlAttribute(bg, BG_CTRL_ATTR_PALETTEMODE) == 0) { tileOffset = (sGpuBgConfigs2[bg].baseTile + destOffset) * 0x20; } else { tileOffset = (sGpuBgConfigs2[bg].baseTile + destOffset) * 0x40; } cursor = LoadBgVram(bg, src, size, tileOffset, DISPCNT_MODE_1); if (cursor == 0xFF) { return -1; } sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20)); if (gUnneededFireRedVariable == 1) { DummiedOutFireRedLeafGreenTileAllocFunc(bg, tileOffset / 0x20, size / 0x20, 1); } return cursor; } u16 LoadBgTilemap(u8 bg, const void *src, u16 size, u16 destOffset) { u8 cursor; cursor = LoadBgVram(bg, src, size, destOffset * 2, DISPCNT_MODE_2); if (cursor == 0xFF) { return -1; } sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20)); return cursor; } u16 Unused_LoadBgPalette(u8 bg, const void *src, u16 size, u16 destOffset) { u16 paletteOffset; s8 cursor; if (IsInvalidBg32(bg) == FALSE) { paletteOffset = (sGpuBgConfigs2[bg].basePalette * 0x20) + (destOffset * 2); cursor = RequestDma3Copy(src, (void*)(paletteOffset + BG_PLTT), size, 0); if (cursor == -1) { return -1; } } else { return -1; } sDmaBusyBitfield[cursor / 0x20] |= (1 << (cursor % 0x20)); return (u8)cursor; } #ifdef NONMATCHING // Matches everything but r5 and r6 are flipped, rrr bool8 IsDma3ManagerBusyWithBgCopy(void) { u8 mod; u8 div; s8 reqSpace; int i; for (i = 0; i < 0x80; i++) { div = i / 0x20; mod = i % 0x20; if ((sDmaBusyBitfield[div] & (1 << mod)) != FALSE) { reqSpace = CheckForSpaceForDma3Request(i); if (reqSpace == -1) { return TRUE; } sDmaBusyBitfield[div] &= ~(1 << mod); } } return FALSE; } #else __attribute__((naked)) bool8 IsDma3ManagerBusyWithBgCopy(void) { asm("push {r4-r7,lr}\n\ mov r5, #0\n\ mov r7, #0x1\n\ neg r7, r7\n\ _08001ADC:\n\ add r0, r5, #0\n\ cmp r5, #0\n\ bge _08001AE4\n\ add r0, #0x1F\n\ _08001AE4:\n\ asr r0, #5\n\ lsl r2, r0, #24\n\ lsl r0, #5\n\ sub r0, r5, r0\n\ lsl r0, #24\n\ lsr r0, #24\n\ ldr r1, =sDmaBusyBitfield\n\ lsr r2, #22\n\ add r4, r2, r1\n\ mov r6, #0x1\n\ lsl r6, r0\n\ ldr r0, [r4]\n\ and r0, r6\n\ cmp r0, #0\n\ beq _08001B22\n\ lsl r0, r5, #16\n\ asr r0, #16\n\ bl CheckForSpaceForDma3Request\n\ lsl r0, #24\n\ asr r0, #24\n\ cmp r0, r7\n\ bne _08001B1C\n\ mov r0, #0x1\n\ b _08001B2A\n\ .pool\n\ _08001B1C:\n\ ldr r0, [r4]\n\ bic r0, r6\n\ str r0, [r4]\n\ _08001B22:\n\ add r5, #0x1\n\ cmp r5, #0x7F\n\ ble _08001ADC\n\ mov r0, #0\n\ _08001B2A:\n\ pop {r4-r7}\n\ pop {r1}\n\ bx r1\n"); } #endif // NONMATCHING void ShowBg(u8 bg) { ShowBgInternal(bg); SyncBgVisibilityAndMode(); } void HideBg(u8 bg) { HideBgInternal(bg); SyncBgVisibilityAndMode(); } void SetBgAttribute(u8 bg, u8 attributeId, u8 value) { switch (attributeId) { case 1: SetBgControlAttributes(bg, value, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); break; case 2: SetBgControlAttributes(bg, 0xFF, value, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); break; case 3: SetBgControlAttributes(bg, 0xFF, 0xFF, value, 0xFF, 0xFF, 0xFF, 0xFF); break; case 4: SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, value, 0xFF, 0xFF, 0xFF); break; case 7: SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, value, 0xFF, 0xFF); break; case 5: SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, value, 0xFF); break; case 6: SetBgControlAttributes(bg, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, value); break; } } u16 GetBgAttribute(u8 bg, u8 attributeId) { switch (attributeId) { case 1: return GetBgControlAttribute(bg, BG_CTRL_ATTR_CHARBASEINDEX); case 2: return GetBgControlAttribute(bg, BG_CTRL_ATTR_MAPBASEINDEX); case 3: return GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); case 4: return GetBgControlAttribute(bg, BG_CTRL_ATTR_PALETTEMODE); case 7: return GetBgControlAttribute(bg, BG_CTRL_ATTR_PRIORITY); case 5: return GetBgControlAttribute(bg, BG_CTRL_ATTR_MOSAIC); case 6: return GetBgControlAttribute(bg, BG_CTRL_ATTR_WRAPAROUND); case 8: switch (GetBgType(bg)) { case 0: return GetBgMetricTextMode(bg, 0) * 0x800; case 1: return GetBgMetricAffineMode(bg, 0) * 0x100; default: return 0; } case 9: return GetBgType(bg); case 10: return sGpuBgConfigs2[bg].baseTile; default: return -1; } } u32 ChangeBgX(u8 bg, u32 value, u8 op) { u8 mode; u16 temp1; u16 temp2; if (IsInvalidBg32(bg) != FALSE || GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE) == 0) { return -1; } switch (op) { case 0: default: sGpuBgConfigs2[bg].bg_x = value; break; case 1: sGpuBgConfigs2[bg].bg_x += value; break; case 2: sGpuBgConfigs2[bg].bg_x -= value; break; } mode = GetBgMode(); switch (bg) { case 0: temp1 = sGpuBgConfigs2[0].bg_x >> 0x8; SetGpuReg(REG_OFFSET_BG0HOFS, temp1); break; case 1: temp1 = sGpuBgConfigs2[1].bg_x >> 0x8; SetGpuReg(REG_OFFSET_BG1HOFS, temp1); break; case 2: if (mode == 0) { temp1 = sGpuBgConfigs2[2].bg_x >> 0x8; SetGpuReg(REG_OFFSET_BG2HOFS, temp1); } else { temp1 = sGpuBgConfigs2[2].bg_x >> 0x10; temp2 = sGpuBgConfigs2[2].bg_x & 0xFFFF; SetGpuReg(REG_OFFSET_BG2X_H, temp1); SetGpuReg(REG_OFFSET_BG2X_L, temp2); } break; case 3: if (mode == 0) { temp1 = sGpuBgConfigs2[3].bg_x >> 0x8; SetGpuReg(REG_OFFSET_BG3HOFS, temp1); } else if (mode == 2) { temp1 = sGpuBgConfigs2[3].bg_x >> 0x10; temp2 = sGpuBgConfigs2[3].bg_x & 0xFFFF; SetGpuReg(REG_OFFSET_BG3X_H, temp1); SetGpuReg(REG_OFFSET_BG3X_L, temp2); } break; } return sGpuBgConfigs2[bg].bg_x; } u32 GetBgX(u8 bg) { if (IsInvalidBg32(bg) != FALSE) return -1; if (GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE) == 0) return -1; return sGpuBgConfigs2[bg].bg_x; } u32 ChangeBgY(u8 bg, u32 value, u8 op) { u8 mode; u16 temp1; u16 temp2; if (IsInvalidBg32(bg) != FALSE || GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE) == 0) { return -1; } switch (op) { case 0: default: sGpuBgConfigs2[bg].bg_y = value; break; case 1: sGpuBgConfigs2[bg].bg_y += value; break; case 2: sGpuBgConfigs2[bg].bg_y -= value; break; } mode = GetBgMode(); switch (bg) { case 0: temp1 = sGpuBgConfigs2[0].bg_y >> 0x8; SetGpuReg(REG_OFFSET_BG0VOFS, temp1); break; case 1: temp1 = sGpuBgConfigs2[1].bg_y >> 0x8; SetGpuReg(REG_OFFSET_BG1VOFS, temp1); break; case 2: if (mode == 0) { temp1 = sGpuBgConfigs2[2].bg_y >> 0x8; SetGpuReg(REG_OFFSET_BG2VOFS, temp1); } else { temp1 = sGpuBgConfigs2[2].bg_y >> 0x10; temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF; SetGpuReg(REG_OFFSET_BG2Y_H, temp1); SetGpuReg(REG_OFFSET_BG2Y_L, temp2); } break; case 3: if (mode == 0) { temp1 = sGpuBgConfigs2[3].bg_y >> 0x8; SetGpuReg(REG_OFFSET_BG3VOFS, temp1); } else if (mode == 2) { temp1 = sGpuBgConfigs2[3].bg_y >> 0x10; temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF; SetGpuReg(REG_OFFSET_BG3Y_H, temp1); SetGpuReg(REG_OFFSET_BG3Y_L, temp2); } break; } return sGpuBgConfigs2[bg].bg_y; } u32 ChangeBgY_ScreenOff(u8 bg, u32 value, u8 op) { u8 mode; u16 temp1; u16 temp2; if (IsInvalidBg32(bg) != FALSE || GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE) == 0) { return -1; } switch (op) { case 0: default: sGpuBgConfigs2[bg].bg_y = value; break; case 1: sGpuBgConfigs2[bg].bg_y += value; break; case 2: sGpuBgConfigs2[bg].bg_y -= value; break; } mode = GetBgMode(); switch (bg) { case 0: temp1 = sGpuBgConfigs2[0].bg_y >> 0x8; SetGpuReg_ForcedBlank(REG_OFFSET_BG0VOFS, temp1); break; case 1: temp1 = sGpuBgConfigs2[1].bg_y >> 0x8; SetGpuReg_ForcedBlank(REG_OFFSET_BG1VOFS, temp1); break; case 2: if (mode == 0) { temp1 = sGpuBgConfigs2[2].bg_y >> 0x8; SetGpuReg_ForcedBlank(REG_OFFSET_BG2VOFS, temp1); } else { temp1 = sGpuBgConfigs2[2].bg_y >> 0x10; temp2 = sGpuBgConfigs2[2].bg_y & 0xFFFF; SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_H, temp1); SetGpuReg_ForcedBlank(REG_OFFSET_BG2Y_L, temp2); } break; case 3: if (mode == 0) { temp1 = sGpuBgConfigs2[3].bg_y >> 0x8; SetGpuReg_ForcedBlank(REG_OFFSET_BG3VOFS, temp1); } else if (mode == 2) { temp1 = sGpuBgConfigs2[3].bg_y >> 0x10; temp2 = sGpuBgConfigs2[3].bg_y & 0xFFFF; SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_H, temp1); SetGpuReg_ForcedBlank(REG_OFFSET_BG3Y_L, temp2); } break; } return sGpuBgConfigs2[bg].bg_y; } u32 GetBgY(u8 bg) { if (IsInvalidBg32(bg) != FALSE) return -1; if (GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE) == 0) return -1; return sGpuBgConfigs2[bg].bg_y; } void SetBgAffine(u8 bg, u32 srcCenterX, u32 srcCenterY, s16 dispCenterX, s16 dispCenterY, s16 scaleX, s16 scaleY, u16 rotationAngle) { SetBgAffineInternal(bg, srcCenterX, srcCenterY, dispCenterX, dispCenterY, scaleX, scaleY, rotationAngle); } u8 Unused_AdjustBgMosaic(u8 a1, u8 a2) { u16 result; s16 test1; s16 test2; result = GetGpuReg(REG_OFFSET_MOSAIC); test1 = result & 0xF; test2 = (result >> 4) & 0xF; result &= 0xFF00; switch (a2) { case 0: default: test1 = a1 & 0xF; test2 = a1 >> 0x4; break; case 1: test1 = a1 & 0xF; break; case 2: if ((test1 + a1) > 0xF) { test1 = 0xF; } else { test1 += a1; } break; case 3: if ((test1 - a1) < 0) { test1 = 0x0; } else { test1 -= a1; } break; case 4: test2 = a1 & 0xF; break; case 5: if ((test2 + a1) > 0xF) { test2 = 0xF; } else { test2 += a1; } break; case 6: if ((test2 - a1) < 0) { test2 = 0x0; } else { test2 -= a1; } break; } result |= ((test2 << 0x4) & 0xF0); result |= (test1 & 0xF); SetGpuReg(REG_OFFSET_MOSAIC, result); return result; } void SetBgTilemapBuffer(u8 bg, void *tilemap) { if (IsInvalidBg32(bg) == FALSE && GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE) != 0x0) { sGpuBgConfigs2[bg].tilemap = tilemap; } } void UnsetBgTilemapBuffer(u8 bg) { if (IsInvalidBg32(bg) == FALSE && GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE) != 0x0) { sGpuBgConfigs2[bg].tilemap = NULL; } } void* GetBgTilemapBuffer(u8 bg) { if (IsInvalidBg32(bg) != FALSE) return NULL; if (GetBgControlAttribute(bg, BG_CTRL_ATTR_VISIBLE) == 0) return NULL; return sGpuBgConfigs2[bg].tilemap; } void CopyToBgTilemapBuffer(u8 bg, const void *src, u16 mode, u16 destOffset) { if (IsInvalidBg32(bg) == FALSE && IsTileMapOutsideWram(bg) == FALSE) { if (mode != 0) { CpuCopy16(src, (void *)(sGpuBgConfigs2[bg].tilemap + (destOffset * 2)), mode); } else { LZ77UnCompWram(src, (void *)(sGpuBgConfigs2[bg].tilemap + (destOffset * 2))); } } } void CopyBgTilemapBufferToVram(u8 bg) { u16 sizeToLoad; if (IsInvalidBg32(bg) == FALSE && IsTileMapOutsideWram(bg) == FALSE) { switch (GetBgType(bg)) { case 0: sizeToLoad = GetBgMetricTextMode(bg, 0) * 0x800; break; case 1: sizeToLoad = GetBgMetricAffineMode(bg, 0) * 0x100; break; default: sizeToLoad = 0; break; } LoadBgVram(bg, sGpuBgConfigs2[bg].tilemap, sizeToLoad, 0, 2); } } void CopyToBgTilemapBufferRect(u8 bg, void* src, u8 destX, u8 destY, u8 width, u8 height) { void* srcCopy; u16 destX16; u16 destY16; u16 mode; if (IsInvalidBg32(bg) == FALSE && IsTileMapOutsideWram(bg) == FALSE) { switch (GetBgType(bg)) { case 0: srcCopy = src; for (destY16 = destY; destY16 < (destY + height); destY16++) { for (destX16 = destX; destX16 < (destX + width); destX16++) { ((u16*)sGpuBgConfigs2[bg].tilemap)[((destY16 * 0x20) + destX16)] = *((u16*)srcCopy)++; } } break; case 1: srcCopy = src; mode = GetBgMetricAffineMode(bg, 0x1); for (destY16 = destY; destY16 < (destY + height); destY16++) { for (destX16 = destX; destX16 < (destX + width); destX16++) { ((u8*)sGpuBgConfigs2[bg].tilemap)[((destY16 * mode) + destX16)] = *((u8*)srcCopy)++; } } break; } } } void CopyToBgTilemapBufferRect_ChangePalette(u8 bg, void *src, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette) { CopyRectToBgTilemapBufferRect(bg, src, 0, 0, rectWidth, rectHeight, destX, destY, rectWidth, rectHeight, palette, 0, 0); } // Skipping for now, it probably uses structs passed by value /* void CopyRectToBgTilemapBufferRect(u8 bg, void* src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette1, u16 tileOffset, u16 palette2) { u16 attribute; u16 mode; u16 mode2; void* srcCopy; u16 destX16; u16 destY16; if (IsInvalidBg32(bg) == FALSE && IsTileMapOutsideWram(bg) == FALSE) { attribute = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); mode = GetBgMetricTextMode(bg, 0x1) * 0x20; mode2 = GetBgMetricTextMode(bg, 0x2) * 0x20; switch (GetBgType(bg)) { case 0: srcCopy = src; for (destY16 = destY; destY16 < (destY + rectHeight); destY16++) { for (destX16 = destX; destX16 < (destX + rectWidth); destX16++) { CopyTileMapEntry(&((u16*)srcCopy)[(srcY * rectWidth) + srcX], &((u16*)sGpuBgConfigs2[bg].tilemap)[GetTileMapIndexFromCoords(destX16, destY16, attribute, mode, mode2)], palette1, tileOffset, palette2); } } break; case 1: srcCopy = src; mode = GetBgMetricAffineMode(bg, 0x1); for (destY16 = destY; destY16 < (destY + rectHeight); destY16++) { for (destX16 = destX; destX16 < (destX + rectWidth); destX16++) { CopyTileMapEntry(&((u16*)srcCopy)[(srcY * rectWidth) + srcX], &((u16*)sGpuBgConfigs2[bg].tilemap)[GetTileMapIndexFromCoords(destX16, destY16, attribute, mode, mode2)], palette1, tileOffset, palette2); } } break; } } }*/ __attribute__((naked)) void CopyRectToBgTilemapBufferRect(u8 bg, void* src, u8 srcX, u8 srcY, u8 srcWidth, u8 srcHeight, u8 destX, u8 destY, u8 rectWidth, u8 rectHeight, u8 palette1, u16 tileOffset, u16 palette2) { asm("push {r4-r7,lr}\n\ mov r7, r10\n\ mov r6, r9\n\ mov r5, r8\n\ push {r5-r7}\n\ sub sp, #0x40\n\ str r1, [sp, #0x8]\n\ ldr r1, [sp, #0x60]\n\ ldr r4, [sp, #0x68]\n\ ldr r5, [sp, #0x6C]\n\ ldr r6, [sp, #0x70]\n\ ldr r7, [sp, #0x74]\n\ mov r8, r7\n\ ldr r7, [sp, #0x78]\n\ mov r9, r7\n\ ldr r7, [sp, #0x7C]\n\ mov r10, r7\n\ ldr r7, [sp, #0x80]\n\ mov r12, r7\n\ lsl r0, #24\n\ lsr r0, #24\n\ str r0, [sp, #0x4]\n\ lsl r2, #24\n\ lsr r2, #24\n\ str r2, [sp, #0xC]\n\ lsl r3, #24\n\ lsr r3, #24\n\ str r3, [sp, #0x10]\n\ lsl r1, #24\n\ lsr r7, r1, #24\n\ lsl r4, #24\n\ lsr r4, #24\n\ str r4, [sp, #0x14]\n\ lsl r5, #24\n\ lsr r5, #24\n\ lsl r6, #24\n\ lsr r6, #24\n\ str r6, [sp, #0x18]\n\ mov r0, r8\n\ lsl r0, #24\n\ lsr r4, r0, #24\n\ mov r1, r9\n\ lsl r1, #24\n\ lsr r1, #24\n\ str r1, [sp, #0x1C]\n\ mov r2, r10\n\ lsl r2, #16\n\ lsr r2, #16\n\ str r2, [sp, #0x20]\n\ mov r0, r12\n\ lsl r0, #16\n\ lsr r0, #16\n\ str r0, [sp, #0x24]\n\ ldr r0, [sp, #0x4]\n\ bl IsInvalidBg32\n\ cmp r0, #0\n\ beq _08002592\n\ b _080026EE\n\ _08002592:\n\ ldr r0, [sp, #0x4]\n\ bl IsTileMapOutsideWram\n\ cmp r0, #0\n\ beq _0800259E\n\ b _080026EE\n\ _0800259E:\n\ ldr r0, [sp, #0x4]\n\ mov r1, #0x4\n\ bl GetBgControlAttribute\n\ lsl r0, #16\n\ lsr r0, #16\n\ str r0, [sp, #0x30]\n\ ldr r0, [sp, #0x4]\n\ mov r1, #0x1\n\ bl GetBgMetricTextMode\n\ lsl r0, #21\n\ lsr r0, #16\n\ str r0, [sp, #0x28]\n\ ldr r0, [sp, #0x4]\n\ mov r1, #0x2\n\ bl GetBgMetricTextMode\n\ lsl r0, #21\n\ lsr r0, #16\n\ str r0, [sp, #0x2C]\n\ ldr r0, [sp, #0x4]\n\ bl GetBgType\n\ cmp r0, #0\n\ beq _080025D8\n\ cmp r0, #0x1\n\ beq _08002674\n\ b _080026EE\n\ _080025D8:\n\ ldr r1, [sp, #0x10]\n\ add r0, r1, #0\n\ mul r0, r7\n\ ldr r2, [sp, #0xC]\n\ add r0, r2\n\ lsl r0, #1\n\ ldr r1, [sp, #0x8]\n\ add r6, r1, r0\n\ add r0, r5, r4\n\ cmp r5, r0\n\ blt _080025F0\n\ b _080026EE\n\ _080025F0:\n\ ldr r2, [sp, #0x18]\n\ sub r2, r7, r2\n\ str r2, [sp, #0x34]\n\ str r0, [sp, #0x38]\n\ _080025F8:\n\ ldr r4, [sp, #0x14]\n\ ldr r7, [sp, #0x18]\n\ add r0, r4, r7\n\ add r1, r5, #0x1\n\ str r1, [sp, #0x3C]\n\ cmp r4, r0\n\ bge _0800265A\n\ ldr r2, [sp, #0x4]\n\ lsl r0, r2, #4\n\ ldr r1, =sGpuBgConfigs2+4\n\ add r0, r1\n\ mov r10, r0\n\ ldr r7, [sp, #0x20]\n\ lsl r7, #16\n\ mov r9, r7\n\ ldr r1, [sp, #0x24]\n\ lsl r0, r1, #16\n\ asr r0, #16\n\ mov r8, r0\n\ _0800261E:\n\ ldr r2, [sp, #0x2C]\n\ str r2, [sp]\n\ add r0, r4, #0\n\ add r1, r5, #0\n\ ldr r2, [sp, #0x30]\n\ ldr r3, [sp, #0x28]\n\ bl GetTileMapIndexFromCoords\n\ lsl r0, #16\n\ lsr r0, #15\n\ mov r7, r10\n\ ldr r1, [r7]\n\ add r1, r0\n\ mov r0, r8\n\ str r0, [sp]\n\ add r0, r6, #0\n\ ldr r2, [sp, #0x1C]\n\ mov r7, r9\n\ asr r3, r7, #16\n\ bl CopyTileMapEntry\n\ add r6, #0x2\n\ add r0, r4, #0x1\n\ lsl r0, #16\n\ lsr r4, r0, #16\n\ ldr r1, [sp, #0x14]\n\ ldr r2, [sp, #0x18]\n\ add r0, r1, r2\n\ cmp r4, r0\n\ blt _0800261E\n\ _0800265A:\n\ ldr r5, [sp, #0x34]\n\ lsl r0, r5, #1\n\ add r6, r0\n\ ldr r7, [sp, #0x3C]\n\ lsl r0, r7, #16\n\ lsr r5, r0, #16\n\ ldr r0, [sp, #0x38]\n\ cmp r5, r0\n\ blt _080025F8\n\ b _080026EE\n\ .pool\n\ _08002674:\n\ ldr r1, [sp, #0x10]\n\ add r0, r1, #0\n\ mul r0, r7\n\ ldr r2, [sp, #0xC]\n\ add r0, r2\n\ ldr r1, [sp, #0x8]\n\ add r6, r1, r0\n\ ldr r0, [sp, #0x4]\n\ mov r1, #0x1\n\ bl GetBgMetricAffineMode\n\ lsl r0, #16\n\ lsr r0, #16\n\ mov r9, r0\n\ add r0, r5, r4\n\ cmp r5, r0\n\ bge _080026EE\n\ ldr r2, [sp, #0x18]\n\ sub r2, r7, r2\n\ str r2, [sp, #0x34]\n\ str r0, [sp, #0x38]\n\ ldr r7, =sGpuBgConfigs2+4\n\ mov r10, r7\n\ ldr r0, [sp, #0x4]\n\ lsl r0, #4\n\ mov r8, r0\n\ _080026A8:\n\ ldr r4, [sp, #0x14]\n\ ldr r1, [sp, #0x18]\n\ add r0, r4, r1\n\ add r2, r5, #0x1\n\ str r2, [sp, #0x3C]\n\ cmp r4, r0\n\ bge _080026DE\n\ mov r3, r8\n\ add r3, r10\n\ mov r7, r9\n\ mul r7, r5\n\ mov r12, r7\n\ add r2, r0, #0\n\ _080026C2:\n\ ldr r1, [r3]\n\ mov r5, r12\n\ add r0, r5, r4\n\ add r1, r0\n\ ldrb r0, [r6]\n\ ldr r7, [sp, #0x20]\n\ add r0, r7\n\ strb r0, [r1]\n\ add r6, #0x1\n\ add r0, r4, #0x1\n\ lsl r0, #16\n\ lsr r4, r0, #16\n\ cmp r4, r2\n\ blt _080026C2\n\ _080026DE:\n\ ldr r0, [sp, #0x34]\n\ add r6, r0\n\ ldr r1, [sp, #0x3C]\n\ lsl r0, r1, #16\n\ lsr r5, r0, #16\n\ ldr r2, [sp, #0x38]\n\ cmp r5, r2\n\ blt _080026A8\n\ _080026EE:\n\ add sp, #0x40\n\ pop {r3-r5}\n\ mov r8, r3\n\ mov r9, r4\n\ mov r10, r5\n\ pop {r4-r7}\n\ pop {r0}\n\ bx r0\n\ .pool\n"); } void FillBgTilemapBufferRect_Palette0(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height) { u16 x16; u16 y16; u16 mode; if (IsInvalidBg32(bg) == FALSE && IsTileMapOutsideWram(bg) == FALSE) { switch (GetBgType(bg)) { case 0: for (y16 = y; y16 < (y + height); y16++) { for (x16 = x; x16 < (x + width); x16++) { ((u16*)sGpuBgConfigs2[bg].tilemap)[((y16 * 0x20) + x16)] = tileNum; } } break; case 1: mode = GetBgMetricAffineMode(bg, 0x1); for (y16 = y; y16 < (y + height); y16++) { for (x16 = x; x16 < (x + width); x16++) { ((u8*)sGpuBgConfigs2[bg].tilemap)[((y16 * mode) + x16)] = tileNum; } } break; } } } void FillBgTilemapBufferRect(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height, u8 palette) { WriteSequenceToBgTilemapBuffer(bg, tileNum, x, y, width, height, palette, 0); } void WriteSequenceToBgTilemapBuffer(u8 bg, u16 firstTileNum, u8 x, u8 y, u8 width, u8 height, u8 paletteSlot, s16 tileNumDelta) { u16 mode; u16 mode2; u16 attribute; u16 mode3; u16 x16; u16 y16; if (IsInvalidBg32(bg) == FALSE && IsTileMapOutsideWram(bg) == FALSE) { attribute = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); mode = GetBgMetricTextMode(bg, 0x1) * 0x20; mode2 = GetBgMetricTextMode(bg, 0x2) * 0x20; switch (GetBgType(bg)) { case 0: for (y16 = y; y16 < (y + height); y16++) { for (x16 = x; x16 < (x + width); x16++) { CopyTileMapEntry(&firstTileNum, &((u16*)sGpuBgConfigs2[bg].tilemap)[(u16)GetTileMapIndexFromCoords(x16, y16, attribute, mode, mode2)], paletteSlot, 0, 0); firstTileNum = (firstTileNum & 0xFC00) + ((firstTileNum + tileNumDelta) & 0x3FF); } } break; case 1: mode3 = GetBgMetricAffineMode(bg, 0x1); for (y16 = y; y16 < (y + height); y16++) { for (x16 = x; x16 < (x + width); x16++) { ((u8*)sGpuBgConfigs2[bg].tilemap)[(y16 * mode3) + x16] = firstTileNum; firstTileNum = (firstTileNum & 0xFC00) + ((firstTileNum + tileNumDelta) & 0x3FF); } } break; } } } u16 GetBgMetricTextMode(u8 bg, u8 whichMetric) { u8 attribute; attribute = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); switch (whichMetric) { case 0: switch (attribute) { case 0: return 1; case 1: case 2: return 2; case 3: return 4; } break; case 1: switch (attribute) { case 0: return 1; case 1: return 2; case 2: return 1; case 3: return 2; } break; case 2: switch (attribute) { case 0: case 1: return 1; case 2: case 3: return 2; } break; } return 0; } u32 GetBgMetricAffineMode(u8 bg, u8 whichMetric) { u8 attribute; attribute = GetBgControlAttribute(bg, BG_CTRL_ATTR_SCREENSIZE); switch (whichMetric) { case 0: switch (attribute) { case 0: return 0x1; case 1: return 0x4; case 2: return 0x10; case 3: return 0x40; } break; case 1: case 2: return 0x10 << attribute; } return 0; } u32 GetTileMapIndexFromCoords(s32 x, s32 y, s32 screenSize, u32 screenWidth, u32 screenHeight) { x = x & (screenWidth - 1); y = y & (screenHeight - 1); switch (screenSize) { case 0: case 2: break; case 3: if (y >= 0x20) y += 0x20; case 1: if (x >= 0x20) { x -= 0x20; y += 0x20; } } return (y * 0x20) + x; } #ifdef NONMATCHING // This one has some weird switch statement cases that refuse to cooperate void CopyTileMapEntry(u16 *src, u16 *dest, s32 palette1, u32 tileOffset, u32 palette2) { u16 test; switch (palette1) { default: if (palette1 > 0x10 || palette1 < 0) test = *src + tileOffset + (palette2 << 12); else test = ((*src + tileOffset) & 0xFFF) + ((palette1 + palette2) << 12); break; case 0x10: test = ((*dest & 0xFC00) + (palette2 << 12)) | ((*src + tileOffset) & 0x3FF); break; } *dest = test; } #else __attribute__((naked)) void CopyTileMapEntry(u16 *src, u16 *dest, s32 palette1, u32 tileOffset, u32 palette2) { asm("push {r4-r6,lr}\n\ add r4, r0, #0\n\ add r6, r1, #0\n\ ldr r5, [sp, #0x10]\n\ cmp r2, #0x10\n\ beq _08002B14\n\ cmp r2, #0x10\n\ bgt _08002B34\n\ cmp r2, #0\n\ blt _08002B34\n\ ldrh r0, [r4]\n\ add r0, r3\n\ ldr r3, =0x00000fff\n\ add r1, r3, #0\n\ and r0, r1\n\ add r1, r2, r5\n\ lsl r1, #12\n\ b _08002B3A\n\ .pool\n\ _08002B14:\n\ ldrh r1, [r6]\n\ mov r0, #0xFC\n\ lsl r0, #8\n\ and r1, r0\n\ lsl r2, r5, #12\n\ add r2, r1, r2\n\ ldrh r0, [r4]\n\ add r0, r3\n\ ldr r3, =0x000003ff\n\ add r1, r3, #0\n\ and r0, r1\n\ orr r0, r2\n\ b _08002B3C\n\ .pool\n\ _08002B34:\n\ ldrh r0, [r4]\n\ add r0, r3\n\ lsl r1, r5, #12\n\ _08002B3A:\n\ add r0, r1\n\ _08002B3C:\n\ lsl r0, #16\n\ lsr r1, r0, #16\n\ strh r1, [r6]\n\ pop {r4-r6}\n\ pop {r0}\n\ bx r0\n"); } #endif // NONMATCHING u32 GetBgType(u8 bg) { u8 mode; mode = GetBgMode(); switch (bg) { case 0: case 1: switch (mode) { case 0: case 1: return 0; } break; case 2: switch (mode) { case 0: return 0; case 1: case 2: return 1; } break; case 3: switch (mode) { case 0: return 0; case 2: return 1; } break; } return 0xFFFF; } bool32 IsInvalidBg32(u8 bg) { if (bg > 3) return TRUE; return FALSE; } bool32 IsTileMapOutsideWram(u8 bg) { if (sGpuBgConfigs2[bg].tilemap > (void*)IWRAM_END) return TRUE; if (sGpuBgConfigs2[bg].tilemap == 0x0) return TRUE; return FALSE; }