From dc74483f2c7031377e7ff1905f75eb1aefdc24f8 Mon Sep 17 00:00:00 2001 From: scawful Date: Thu, 8 Feb 2024 09:19:22 -0500 Subject: [PATCH] Add object_renderer.cc --- src/CMakeLists.txt | 1 + src/app/zelda3/dungeon/object_renderer.cc | 161 ++++++++++++++++++++ src/app/zelda3/dungeon/object_renderer.h | 176 ++-------------------- 3 files changed, 177 insertions(+), 161 deletions(-) create mode 100644 src/app/zelda3/dungeon/object_renderer.cc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 99ff9d17..dbf55f5f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,6 +41,7 @@ set( app/zelda3/music/tracker.cc app/zelda3/dungeon/room.cc app/zelda3/dungeon/room_object.cc + app/zelda3/dungeon/object_renderer.cc ) set( diff --git a/src/app/zelda3/dungeon/object_renderer.cc b/src/app/zelda3/dungeon/object_renderer.cc new file mode 100644 index 00000000..35c5c6a3 --- /dev/null +++ b/src/app/zelda3/dungeon/object_renderer.cc @@ -0,0 +1,161 @@ +#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 diff --git a/src/app/zelda3/dungeon/object_renderer.h b/src/app/zelda3/dungeon/object_renderer.h index 006da19c..fc07b4b2 100644 --- a/src/app/zelda3/dungeon/object_renderer.h +++ b/src/app/zelda3/dungeon/object_renderer.h @@ -18,176 +18,30 @@ namespace app { namespace zelda3 { namespace dungeon { +struct PseudoVram { + std::array sheets; + std::vector palettes; +}; + +struct SubtypeInfo { + uint32_t subtype_ptr; + uint32_t routine_ptr; +}; + class DungeonObjectRenderer : public SharedROM { public: - struct PseudoVram { - std::array sheets; - std::vector palettes; - }; - DungeonObjectRenderer() = default; - void 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); - } - + void LoadObject(uint16_t objectId, std::array& sheet_ids); gfx::Bitmap* bitmap() { return &bitmap_; } auto memory() { return memory_; } auto mutable_memory() { return &memory_; } private: - struct SubtypeInfo { - uint32_t subtype_ptr; - uint32_t routine_ptr; - }; - - SubtypeInfo 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 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 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 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_); - } + SubtypeInfo FetchSubtypeInfo(uint16_t object_id); + void ConfigureObject(const SubtypeInfo& info); + void RenderObject(const SubtypeInfo& info); + void UpdateObjectBitmap(); std::vector tilemap_; uint16_t pc_with_rts_;