#include "app/zelda3/dungeon/object_renderer.h" namespace yaze { namespace app { namespace zelda3 { namespace dungeon { void DungeonObjectRenderer::LoadObject(uint16_t objectId, std::array& sheet_ids) { vram_.sheets = sheet_ids; rom_data_ = rom()->vector(); // Prepare the CPU and memory environment memory_.Initialize(rom_data_); // Fetch the subtype pointers for the given object ID auto subtypeInfo = FetchSubtypeInfo(objectId); // Configure the object based on the fetched information ConfigureObject(subtypeInfo); // Run the CPU emulation for the object's draw routines RenderObject(subtypeInfo); } SubtypeInfo DungeonObjectRenderer::FetchSubtypeInfo(uint16_t object_id) { SubtypeInfo info; // Determine the subtype based on objectId uint8_t subtype = 1; // Based on the subtype, fetch the correct pointers switch (subtype) { case 1: // Subtype 1 info.subtype_ptr = core::subtype1_tiles + (object_id & 0xFF) * 2; info.routine_ptr = core::subtype1_tiles + 0x200 + (object_id & 0xFF) * 2; std::cout << "Subtype 1 " << std::hex << info.subtype_ptr << std::endl; std::cout << "Subtype 1 " << std::hex << info.routine_ptr << std::endl; break; case 2: // Subtype 2 info.subtype_ptr = core::subtype2_tiles + (object_id & 0x7F) * 2; info.routine_ptr = core::subtype2_tiles + 0x80 + (object_id & 0x7F) * 2; break; case 3: // Subtype 3 info.subtype_ptr = core::subtype3_tiles + (object_id & 0xFF) * 2; info.routine_ptr = core::subtype3_tiles + 0x100 + (object_id & 0xFF) * 2; break; default: // Handle unknown subtype throw std::runtime_error("Unknown subtype for object ID: " + std::to_string(object_id)); } return info; } void DungeonObjectRenderer::ConfigureObject(const SubtypeInfo& info) { cpu.A = 0x03D8; cpu.X = 0x03D8; cpu.DB = 0x7E; // VRAM target destinations cpu.WriteLong(0xBF, 0x7E2000); cpu.WriteLong(0xCB, 0x7E2080); cpu.WriteLong(0xC2, 0x7E2002); cpu.WriteLong(0xCE, 0x7E2082); cpu.SetAccumulatorSize(false); cpu.SetIndexSize(false); } /** * Example: * the STA $BF, $CD, $C2, $CE are the location of the object in the room * $B2 is used for size loop * so if object size is setted on 07 that draw code will be repeated 7 times * and since Y is increasing by 4 it makes the object draw from left to right RoomDraw_Rightwards2x2_1to15or32: #_018B89: JSR RoomDraw_GetSize_1to15or32 .next #_018B8C: JSR RoomDraw_Rightwards2x2 #_018B8F: DEC.b $B2 #_018B91: BNE .next #_018B93: RTS RoomDraw_Rightwards2x2: #_019895: LDA.w RoomDrawObjectData+0,X #_019898: STA.b [$BF],Y #_01989A: LDA.w RoomDrawObjectData+2,X #_01989D: STA.b [$CB],Y #_01989F: LDA.w RoomDrawObjectData+4,X #_0198A2: STA.b [$C2],Y #_0198A4: LDA.w RoomDrawObjectData+6,X #_0198A7: STA.b [$CE],Y #_0198A9: INY #4 #_0198AD: RTS */ void DungeonObjectRenderer::RenderObject(const SubtypeInfo& info) { cpu.PB = 0x01; cpu.PC = cpu.ReadWord(0x01 << 16 | info.routine_ptr); // Push an initial value to the stack we can read later to confirm we are // done cpu.PushLong(0x01 << 16 | info.routine_ptr); int i = 0; while (true) { uint8_t opcode = cpu.ReadByte(cpu.PB << 16 | cpu.PC); cpu.ExecuteInstruction(opcode); cpu.HandleInterrupts(); if ((i != 0 && (cpu.ReadWord((0x00 << 16 | cpu.SP() + 2)) == info.routine_ptr) || 0x8b93 == cpu.PC)) { std::cout << std::hex << cpu.ReadWord((0x00 << 16 | cpu.SP() + 3)) << std::endl; break; } i++; } UpdateObjectBitmap(); } // In the underworld, this holds a copy of the entire BG tilemap for // Layer 1 (BG2) in TILEMAPA // Layer 2 (BG1) in TILEMAPB void DungeonObjectRenderer::UpdateObjectBitmap() { tilemap_.reserve(0x2000); for (int i = 0; i < 0x2000; ++i) { tilemap_.push_back(0); } int tilemap_offset = 0; // Iterate over tilemap in memory to read tile IDs for (int tile_index = 0; tile_index < 512; tile_index++) { // Read the tile ID from memory int tile_id = memory_.ReadWord(0x7E2000 + tile_index); std::cout << "Tile ID: " << std::hex << tile_id << std::endl; int sheet_number = tile_id / 32; std::cout << "Sheet number: " << std::hex << sheet_number << std::endl; int row = tile_id / 8; int column = tile_id % 8; int x = column * 8; int y = row * 8; auto sheet = rom()->mutable_graphics_sheet(vram_.sheets[sheet_number]); // Copy the tile from VRAM using the read tile_id sheet->Get8x8Tile(tile_id, x, y, tilemap_, tilemap_offset); } bitmap_.Create(256, 256, 8, tilemap_); } } // namespace dungeon } // namespace zelda3 } // namespace app } // namespace yaze