Dungeon object updates

This commit is contained in:
scawful
2023-11-22 12:23:02 -05:00
parent e93ff212af
commit 041e365416
25 changed files with 311 additions and 150 deletions

View File

@@ -14,8 +14,8 @@ set(
app/editor/overworld_editor.cc
app/editor/screen_editor.cc
app/editor/sprite_editor.cc
app/editor/resources/music_editor.cc
app/editor/resources/palette_editor.cc
app/editor/modules/music_editor.cc
app/editor/modules/palette_editor.cc
app/editor/modules/assembly_editor.cc
app/editor/modules/tile16_editor.cc
app/editor/modules/gfx_group_editor.cc

View File

@@ -23,6 +23,12 @@ uint32_t PcToSnes(uint32_t addr) {
return addr;
}
uint32_t MapBankToWordAddress(uint8_t bank, uint16_t addr) {
uint32_t result = 0;
result = (bank << 16) | addr;
return result;
}
int AddressFromBytes(uint8_t addr1, uint8_t addr2, uint8_t addr3) {
return (addr1 << 16) | (addr2 << 8) | addr3;
}

View File

@@ -19,7 +19,7 @@ class ExperimentFlags {
bool kUseBitmapManager = true;
// Log instructions to the GUI debugger.
bool kLogInstructions = true;
bool kLogInstructions = false;
// Flag to enable ImGui input config flags. Currently is
// handled manually by controller class but should be
@@ -35,7 +35,7 @@ class ExperimentFlags {
bool kSaveWithChangeQueue = false;
// Attempt to run the dungeon room draw routine when opening a room.
bool kDrawDungeonRoomGraphics = false;
bool kDrawDungeonRoomGraphics = true;
};
ExperimentFlags() = default;
@@ -58,11 +58,43 @@ class ExperimentFlags {
static std::shared_ptr<Flags> flags_;
};
// NotifyFlag is a special type class which stores two copies of a type
// and uses that to check if the value was updated last or not
// It should have an accessor which says if it was modified or not
// and when that is read it should reset the value and state
template <typename T>
class NotifyFlag {
public:
NotifyFlag() : value_(), modified_(false) {}
void set(const T &value) {
value_ = value;
modified_ = true;
}
const T &get() {
modified_ = false;
return value_;
}
void operator=(const T &value) { set(value); }
operator T() const { return get(); }
bool isModified() const { return modified_; }
private:
T value_;
bool modified_;
};
uint32_t SnesToPc(uint32_t addr);
uint32_t PcToSnes(uint32_t addr);
uint32_t MapBankToWordAddress(uint8_t bank, uint16_t addr);
int AddressFromBytes(uint8_t addr1, uint8_t addr2, uint8_t addr3);
int HexToDec(char *input, int length);
bool StringReplace(std::string &str, const std::string &from,
const std::string &to);

View File

@@ -58,92 +58,8 @@ absl::Status DungeonEditor::Update() {
return absl::OkStatus();
}
void DungeonEditor::DrawRoomSelector() {
if (rom()->isLoaded()) {
gui::InputHexWord("Room ID", &current_room_id_);
// gui::InputHexByte("Palette ID", &rooms_[current_room_id_].palette);
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)9);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
int i = 0;
for (const auto each_room_name : zelda3::dungeon::kRoomNames) {
ImGui::Selectable(each_room_name.data(), current_room_id_ == i,
ImGuiSelectableFlags_AllowDoubleClick);
if (ImGui::IsItemClicked()) {
active_rooms_.push_back(i);
}
i += 1;
}
}
ImGui::EndChild();
}
}
void DungeonEditor::DrawDungeonTabView() {
static int next_tab_id = 0;
if (ImGui::BeginTabBar("MyTabBar", kDungeonTabBarFlags)) {
// TODO: Manage the room that is being added to the tab bar.
if (ImGui::TabItemButton("##tabitem", kDungeonTabFlags)) {
active_rooms_.push_back(next_tab_id++); // Add new tab
}
// Submit our regular tabs
for (int n = 0; n < active_rooms_.Size;) {
bool open = true;
if (ImGui::BeginTabItem(
zelda3::dungeon::kRoomNames[active_rooms_[n]].data(), &open,
ImGuiTabItemFlags_None)) {
DrawDungeonCanvas(active_rooms_[n]);
ImGui::EndTabItem();
}
if (!open)
active_rooms_.erase(active_rooms_.Data + n);
else
n++;
}
ImGui::EndTabBar();
}
ImGui::Separator();
}
void DungeonEditor::DrawDungeonCanvas(int room_id) {
ImGui::BeginGroup();
gui::InputHexByte("Layout", &rooms_[room_id].layout);
ImGui::SameLine();
gui::InputHexByte("Blockset", &rooms_[room_id].blockset);
ImGui::SameLine();
gui::InputHexByte("Spriteset", &rooms_[room_id].spriteset);
ImGui::SameLine();
gui::InputHexByte("Palette", &rooms_[room_id].palette);
gui::InputHexByte("Floor1", &rooms_[room_id].floor1);
ImGui::SameLine();
gui::InputHexByte("Floor2", &rooms_[room_id].floor2);
ImGui::SameLine();
gui::InputHexWord("Message ID", &rooms_[room_id].message_id_);
ImGui::SameLine();
ImGui::EndGroup();
canvas_.DrawBackground();
canvas_.DrawContextMenu();
canvas_.DrawGrid();
canvas_.DrawOverlay();
}
void DungeonEditor::DrawToolset() {
if (ImGui::BeginTable("DWToolset", 12, ImGuiTableFlags_SizingFixedFit,
if (ImGui::BeginTable("DWToolset", 13, ImGuiTableFlags_SizingFixedFit,
ImVec2(0, 0))) {
TableSetupColumn("#undoTool");
TableSetupColumn("#redoTool");
@@ -231,10 +147,99 @@ void DungeonEditor::DrawToolset() {
ImGui::SetTooltip("Blocks");
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_MD_PALETTE)) {
// Open the palette module
}
ImGui::EndTable();
}
}
void DungeonEditor::DrawRoomSelector() {
if (rom()->isLoaded()) {
gui::InputHexWord("Room ID", &current_room_id_);
// gui::InputHexByte("Palette ID", &rooms_[current_room_id_].palette);
if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)9);
ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
int i = 0;
for (const auto each_room_name : zelda3::dungeon::kRoomNames) {
ImGui::Selectable(each_room_name.data(), current_room_id_ == i,
ImGuiSelectableFlags_AllowDoubleClick);
if (ImGui::IsItemClicked()) {
active_rooms_.push_back(i);
}
i += 1;
}
}
ImGui::EndChild();
}
}
void DungeonEditor::DrawDungeonTabView() {
static int next_tab_id = 0;
if (ImGui::BeginTabBar("MyTabBar", kDungeonTabBarFlags)) {
// TODO: Manage the room that is being added to the tab bar.
if (ImGui::TabItemButton("##tabitem", kDungeonTabFlags)) {
active_rooms_.push_back(next_tab_id++); // Add new tab
}
// Submit our regular tabs
for (int n = 0; n < active_rooms_.Size;) {
bool open = true;
if (ImGui::BeginTabItem(
zelda3::dungeon::kRoomNames[active_rooms_[n]].data(), &open,
ImGuiTabItemFlags_None)) {
DrawDungeonCanvas(active_rooms_[n]);
ImGui::EndTabItem();
}
if (!open)
active_rooms_.erase(active_rooms_.Data + n);
else
n++;
}
ImGui::EndTabBar();
}
ImGui::Separator();
}
void DungeonEditor::DrawDungeonCanvas(int room_id) {
ImGui::BeginGroup();
gui::InputHexByte("Layout", &rooms_[room_id].layout);
ImGui::SameLine();
gui::InputHexByte("Blockset", &rooms_[room_id].blockset);
ImGui::SameLine();
gui::InputHexByte("Spriteset", &rooms_[room_id].spriteset);
ImGui::SameLine();
gui::InputHexByte("Palette", &rooms_[room_id].palette);
gui::InputHexByte("Floor1", &rooms_[room_id].floor1);
ImGui::SameLine();
gui::InputHexByte("Floor2", &rooms_[room_id].floor2);
ImGui::SameLine();
gui::InputHexWord("Message ID", &rooms_[room_id].message_id_);
ImGui::SameLine();
ImGui::EndGroup();
canvas_.DrawBackground();
canvas_.DrawContextMenu();
canvas_.DrawGrid();
canvas_.DrawOverlay();
}
void DungeonEditor::DrawRoomGraphics() {
const auto height = 0x40;
room_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
@@ -301,6 +306,9 @@ void DungeonEditor::DrawObjectRenderer() {
for (const auto object_name : zelda3::dungeon::Type1RoomObjectNames) {
if (ImGui::Selectable(object_name.data(), selected_object == i)) {
selected_object = i;
object_renderer_.LoadObject(i);
rom()->RenderBitmap(object_renderer_.bitmap());
object_loaded_ = true;
}
i += 1;
}
@@ -312,11 +320,14 @@ void DungeonEditor::DrawObjectRenderer() {
ImGui::BeginChild("DungeonObjectCanvas", ImVec2(276, 0x10 * 0x40 + 1),
true);
dungeon_object_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
dungeon_object_canvas_.DrawContextMenu();
dungeon_object_canvas_.DrawTileSelector(32);
dungeon_object_canvas_.DrawGrid(32.0f);
dungeon_object_canvas_.DrawOverlay();
object_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1));
object_canvas_.DrawContextMenu();
object_canvas_.DrawTileSelector(32);
// if (object_loaded_) {
// object_canvas_.DrawBitmap(*object_renderer_.bitmap(), 0, 0);
// }
object_canvas_.DrawGrid(32.0f);
object_canvas_.DrawOverlay();
ImGui::EndChild();

View File

@@ -53,6 +53,7 @@ class DungeonEditor : public Editor,
uint16_t current_room_id_ = 0;
bool is_loaded_ = false;
bool show_object_render_ = false;
bool object_loaded_ = false;
gfx::Bitmap room_gfx_bmp_;
@@ -60,6 +61,11 @@ class DungeonEditor : public Editor,
std::vector<zelda3::dungeon::Room> rooms_;
zelda3::dungeon::DungeonObjectRenderer object_renderer_;
gui::Canvas canvas_;
gui::Canvas room_gfx_canvas_;
gui::Canvas object_canvas_;
std::vector<gfx::BitmapManager> room_graphics_;
enum BackgroundType {
kNoBackground,
@@ -73,13 +79,6 @@ class DungeonEditor : public Editor,
int background_type_ = kNoBackground;
int placement_type_ = kNoType;
gui::Canvas canvas_;
gui::Canvas room_gfx_canvas_;
gui::Canvas dungeon_object_canvas_;
std::vector<gfx::BitmapManager> room_graphics_;
ImGuiTableFlags toolset_table_flags_ =
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable |
ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable;

View File

@@ -8,7 +8,7 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/pipeline.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/compression.h"
#include "app/gfx/scad_format.h"

View File

@@ -9,7 +9,7 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/pipeline.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"

View File

@@ -14,8 +14,8 @@
#include "app/editor/graphics_editor.h"
#include "app/editor/modules/assembly_editor.h"
#include "app/editor/overworld_editor.h"
#include "app/editor/resources/music_editor.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/music_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/editor/screen_editor.h"
#include "app/editor/sprite_editor.h"
#include "app/emu/emulator.h"

View File

@@ -14,9 +14,9 @@
#include "app/editor/dungeon_editor.h"
#include "app/editor/graphics_editor.h"
#include "app/editor/modules/assembly_editor.h"
#include "app/editor/resources/music_editor.h"
#include "app/editor/modules/music_editor.h"
#include "app/editor/overworld_editor.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/editor/screen_editor.h"
#include "app/editor/sprite_editor.h"
#include "app/emu/emulator.h"

View File

@@ -8,7 +8,7 @@
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/core/pipeline.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"

View File

@@ -9,7 +9,7 @@
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/core/pipeline.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"

View File

@@ -8,7 +8,7 @@
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/core/pipeline.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"

View File

@@ -9,7 +9,7 @@
#include "absl/status/statusor.h"
#include "app/core/editor.h"
#include "app/core/pipeline.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"

View File

@@ -10,7 +10,7 @@
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "app/core/pipeline.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"

View File

@@ -15,7 +15,7 @@
#include "app/core/pipeline.h"
#include "app/editor/modules/gfx_group_editor.h"
#include "app/editor/modules/tile16_editor.h"
#include "app/editor/resources/palette_editor.h"
#include "app/editor/modules/palette_editor.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"

View File

@@ -623,18 +623,18 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void XCE();
// Memory access routines
uint8_t ReadByte(uint16_t address) const override {
uint8_t ReadByte(uint32_t address) const override {
auto value = memory.ReadByte(address);
return value;
}
uint16_t ReadWord(uint16_t address) const override {
uint16_t ReadWord(uint32_t address) const override {
return memory.ReadWord(address);
}
uint32_t ReadWordLong(uint16_t address) const override {
uint32_t ReadWordLong(uint32_t address) const override {
return memory.ReadWordLong(address);
}
std::vector<uint8_t> ReadByteVector(uint16_t address,
std::vector<uint8_t> ReadByteVector(uint32_t address,
uint16_t size) const override {
return memory.ReadByteVector(address, size);
}

View File

@@ -48,11 +48,21 @@ void DrawMemoryWindow(Memory* memory) {
// Display memory areas
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ROM");
ImGui::Text("ROM Bank 0-63");
ImGui::TableNextColumn();
ImGui::Text("0x000000");
ImGui::Text("0x8000");
ImGui::TableNextColumn();
ImGui::Text("%d MB", memoryImpl->rom_.size());
ImGui::Text("128 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ROM Bank 64-111");
ImGui::TableNextColumn();
ImGui::Text("0x0000");
ImGui::TableNextColumn();
ImGui::Text("64 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
@@ -60,9 +70,39 @@ void DrawMemoryWindow(Memory* memory) {
ImGui::TableNextColumn();
ImGui::Text("RAM");
ImGui::TableNextColumn();
ImGui::Text("0x700000");
ImGui::TableNextColumn();
ImGui::Text("64 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("System RAM (WRAM)");
ImGui::TableNextColumn();
ImGui::Text("0x7E0000");
ImGui::TableNextColumn();
ImGui::Text("%d KB", memoryImpl->ram_.size());
ImGui::Text("128 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ROM Bank 128-191");
ImGui::TableNextColumn();
ImGui::Text("0x8000");
ImGui::TableNextColumn();
ImGui::Text("128 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ROM Bank 192-255");
ImGui::TableNextColumn();
ImGui::Text("0x0000");
ImGui::TableNextColumn();
ImGui::Text("64 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
}
@@ -109,7 +149,6 @@ void Emulator::Run() {
ImGui::Button(ICON_MD_DOUBLE_ARROW);
ImGui::SameLine();
ImGui::Button(ICON_MD_SUBDIRECTORY_ARROW_RIGHT);
ImGui::SameLine();
if (running_) {
HandleEvents();
@@ -331,8 +370,9 @@ void Emulator::RenderCPUInstructionLog(
ImGui::Checkbox("Show All Opcodes", &showAllOpcodes);
// Instruction list
ImGui::BeginChild("InstructionList", ImVec2(0, 0),
ImGuiChildFlags_AlwaysAutoResize);
ImGui::BeginChild(
"InstructionList", ImVec2(0, 0),
ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY);
for (const auto& entry : instructionLog) {
if (ShouldDisplay(entry, filterBuf, showAllOpcodes)) {
if (ImGui::Selectable(absl::StrFormat("%04X: %02X %s %s", entry.address,

View File

@@ -109,10 +109,10 @@ constexpr uint32_t kOAMSize = 0x220;
class Memory {
public:
virtual ~Memory() = default;
virtual uint8_t ReadByte(uint16_t address) const = 0;
virtual uint16_t ReadWord(uint16_t address) const = 0;
virtual uint32_t ReadWordLong(uint16_t address) const = 0;
virtual std::vector<uint8_t> ReadByteVector(uint16_t address,
virtual uint8_t ReadByte(uint32_t address) const = 0;
virtual uint16_t ReadWord(uint32_t address) const = 0;
virtual uint32_t ReadWordLong(uint32_t address) const = 0;
virtual std::vector<uint8_t> ReadByteVector(uint32_t address,
uint16_t length) const = 0;
virtual void WriteByte(uint32_t address, uint8_t value) = 0;
@@ -149,7 +149,7 @@ class MemoryImpl : public Memory, public Loggable {
return;
}
memory_.reserve(0x1000000); // 16 MB
memory_.resize(0x1000000); // 16 MB
const size_t ROM_CHUNK_SIZE = 0x8000; // 32 KB
const size_t SRAM_SIZE = 0x10000; // 64 KB
@@ -229,25 +229,25 @@ class MemoryImpl : public Memory, public Loggable {
memory_.begin() + kOAMStart + kOAMSize, oam_.begin());
}
uint8_t ReadByte(uint16_t address) const override {
uint8_t ReadByte(uint32_t address) const override {
uint32_t mapped_address = GetMappedAddress(address);
NotifyObservers(mapped_address, /*data=*/0);
return memory_.at(mapped_address);
}
uint16_t ReadWord(uint16_t address) const override {
uint16_t ReadWord(uint32_t address) const override {
uint32_t mapped_address = GetMappedAddress(address);
NotifyObservers(mapped_address, /*data=*/0);
return static_cast<uint16_t>(memory_.at(mapped_address)) |
(static_cast<uint16_t>(memory_.at(mapped_address + 1)) << 8);
}
uint32_t ReadWordLong(uint16_t address) const override {
uint32_t ReadWordLong(uint32_t address) const override {
uint32_t mapped_address = GetMappedAddress(address);
NotifyObservers(mapped_address, /*data=*/0);
return static_cast<uint32_t>(memory_.at(mapped_address)) |
(static_cast<uint32_t>(memory_.at(mapped_address + 1)) << 8) |
(static_cast<uint32_t>(memory_.at(mapped_address + 2)) << 16);
}
std::vector<uint8_t> ReadByteVector(uint16_t address,
std::vector<uint8_t> ReadByteVector(uint32_t address,
uint16_t length) const override {
uint32_t mapped_address = GetMappedAddress(address);
NotifyObservers(mapped_address, /*data=*/0);

View File

@@ -23,11 +23,11 @@ class MockClock : public Clock {
class MockMemory : public Memory {
public:
MOCK_CONST_METHOD1(ReadByte, uint8_t(uint16_t address));
MOCK_CONST_METHOD1(ReadWord, uint16_t(uint16_t address));
MOCK_CONST_METHOD1(ReadWordLong, uint32_t(uint16_t address));
MOCK_CONST_METHOD1(ReadByte, uint8_t(uint32_t address));
MOCK_CONST_METHOD1(ReadWord, uint16_t(uint32_t address));
MOCK_CONST_METHOD1(ReadWordLong, uint32_t(uint32_t address));
MOCK_METHOD(std::vector<uint8_t>, ReadByteVector,
(uint16_t address, uint16_t length), (const, override));
(uint32_t address, uint16_t length), (const, override));
MOCK_METHOD2(WriteByte, void(uint32_t address, uint8_t value));
MOCK_METHOD2(WriteWord, void(uint32_t address, uint16_t value));
@@ -76,20 +76,20 @@ class MockMemory : public Memory {
void Init() {
ON_CALL(*this, ReadByte(::testing::_))
.WillByDefault(
[this](uint16_t address) { return memory_.at(address); });
[this](uint32_t address) { return memory_.at(address); });
ON_CALL(*this, ReadWord(::testing::_))
.WillByDefault([this](uint16_t address) {
.WillByDefault([this](uint32_t address) {
return static_cast<uint16_t>(memory_.at(address)) |
(static_cast<uint16_t>(memory_.at(address + 1)) << 8);
});
ON_CALL(*this, ReadWordLong(::testing::_))
.WillByDefault([this](uint16_t address) {
.WillByDefault([this](uint32_t address) {
return static_cast<uint32_t>(memory_.at(address)) |
(static_cast<uint32_t>(memory_.at(address + 1)) << 8) |
(static_cast<uint32_t>(memory_.at(address + 2)) << 16);
});
ON_CALL(*this, ReadByteVector(::testing::_, ::testing::_))
.WillByDefault([this](uint16_t address, uint16_t length) {
.WillByDefault([this](uint32_t address, uint16_t length) {
std::vector<uint8_t> data;
for (int i = 0; i < length; i++) {
data.push_back(memory_.at(address + i));

View File

@@ -8,6 +8,7 @@
#include <vector>
#include "app/emu/cpu.h"
#include "app/emu/memory/memory.h"
#include "app/emu/video/ppu.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
@@ -31,8 +32,9 @@ class DungeonObjectRenderer : public SharedROM {
}
void LoadObject(uint16_t objectId) {
rom_data_ = rom()->vector();
// Prepare the CPU and memory environment
memory_.Initialize(rom()->vector());
memory_.Initialize(rom_data_);
// Fetch the subtype pointers for the given object ID
auto subtypeInfo = FetchSubtypeInfo(objectId);
@@ -44,11 +46,12 @@ class DungeonObjectRenderer : public SharedROM {
RenderObject(subtypeInfo);
}
gfx::Bitmap* bitmap() { return &bitmap_; }
private:
struct SubtypeInfo {
uint16_t subtypePtr;
uint16_t routinePtr;
// Additional fields as needed
uint32_t subtypePtr;
uint32_t routinePtr;
};
SubtypeInfo FetchSubtypeInfo(uint16_t objectId) {
@@ -56,13 +59,19 @@ class DungeonObjectRenderer : public SharedROM {
// Determine the subtype based on objectId
// Assuming subtype is determined by some bits in objectId; modify as needed
uint8_t subtype = (objectId >> 8) & 0xFF; // Example: top 8 bits
uint8_t subtype = 1; // Example: top 8 bits
// Based on the subtype, fetch the correct pointers
switch (subtype) {
case 1: // Subtype 1
info.subtypePtr = core::subtype1_tiles + (objectId & 0xFF) * 2;
info.routinePtr = core::subtype1_tiles + 0x200 + (objectId & 0xFF) * 2;
std::cout << "Subtype 1 " << std::hex << info.subtypePtr << std::endl;
info.routinePtr =
memory_.ReadWord(core::MapBankToWordAddress(0x01, info.routinePtr));
std::cout << "Subtype 1 " << std::hex << info.routinePtr << std::endl;
std::cout << "Subtype 1 " << std::hex << core::SnesToPc(info.routinePtr)
<< std::endl;
break;
case 2: // Subtype 2
info.subtypePtr = core::subtype2_tiles + (objectId & 0x7F) * 2;
@@ -86,13 +95,57 @@ class DungeonObjectRenderer : public SharedROM {
}
void ConfigureObject(const SubtypeInfo& info) {
// TODO: Use the information in info to set up the object's initial state
cpu.A = 0x00;
cpu.X = 0x00;
// Might need to set the height and width manually?
}
/**
* 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
#_0198AA: INY
#_0198AB: INY
#_0198AC: INY
#_0198AD: RTS
*
*/
void RenderObject(const SubtypeInfo& info) {
cpu.PC = info.routinePtr;
cpu.PB = 0x01;
int i = 0;
while (true) {
uint8_t opcode = cpu.FetchByte();
cpu.ExecuteInstruction(opcode);
@@ -103,12 +156,32 @@ class DungeonObjectRenderer : public SharedROM {
break;
}
if (i > 50) {
break;
}
i++;
UpdateObjectBitmap();
}
}
void UpdateObjectBitmap() {
// TODO: Implement logic to transfer object draw data to the Bitmap
// Object draw data
uint8_t room_object_draw_data0 = memory_.ReadByte(0x7E00BF);
uint8_t room_object_draw_data1 = memory_.ReadByte(0x7E00CB);
uint8_t room_object_draw_data2 = memory_.ReadByte(0x7E00C2);
uint8_t room_object_draw_data3 = memory_.ReadByte(0x7E00CE);
// Used with Y to index the room object draw data
uint8_t size_loop = memory_.ReadByte(0x7E00B2);
// Update the bitmap with this data by copying the tiles from vram.
std::cout << "Object draw data: " << std::hex << (int)room_object_draw_data0
<< " " << (int)room_object_draw_data1 << " "
<< (int)room_object_draw_data2 << " "
<< (int)room_object_draw_data3 << std::endl;
std::cout << "Size loop: " << std::hex << (int)size_loop << std::endl;
}
std::vector<uint8_t> rom_data_;
@@ -116,7 +189,7 @@ class DungeonObjectRenderer : public SharedROM {
emu::ClockImpl clock_;
emu::CPU cpu{memory_, clock_};
emu::PPU ppu{memory_, clock_};
gfx::Bitmap bitmap;
gfx::Bitmap bitmap_;
PseudoVram vram_;
};