From 2a52e88b8d4f3c8c93f7e6779e0adee7ef804d1b Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Thu, 25 Jan 2024 00:50:39 -0300 Subject: [PATCH] Adding LinkWirelessMultiboot_demo --- examples/LinkWirelessMultiboot_demo/Makefile | 289 ++++++++++++++++++ .../LinkWirelessMultiboot_demo/src/main.cpp | 45 +++ .../src/scenes/MultibootScene.cpp | 209 +++++++++++++ .../src/scenes/MultibootScene.h | 35 +++ .../src/utils/InputHandler.h | 39 +++ .../src/utils/SceneUtils.h | 52 ++++ examples/compile.sh | 5 + 7 files changed, 674 insertions(+) create mode 100644 examples/LinkWirelessMultiboot_demo/Makefile create mode 100644 examples/LinkWirelessMultiboot_demo/src/main.cpp create mode 100644 examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp create mode 100644 examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h create mode 100644 examples/LinkWirelessMultiboot_demo/src/utils/InputHandler.h create mode 100644 examples/LinkWirelessMultiboot_demo/src/utils/SceneUtils.h diff --git a/examples/LinkWirelessMultiboot_demo/Makefile b/examples/LinkWirelessMultiboot_demo/Makefile new file mode 100644 index 0000000..9ad0b2f --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/Makefile @@ -0,0 +1,289 @@ +# === SETUP =========================================================== + +# --- No implicit rules --- +.SUFFIXES: + +# --- Paths --- +export TONCLIB := $(DEVKITPRO)/libtonc + +# === TONC RULES ====================================================== +# +# Yes, this is almost, but not quite, completely like to +# DKP's base_rules and gba_rules +# + +export PATH := $(DEVKITARM)/bin:$(PATH) + + +# --- Executable names --- + +PREFIX ?= arm-none-eabi- + +export CC := $(PREFIX)gcc +export CXX := $(PREFIX)g++ +export AS := $(PREFIX)as +export AR := $(PREFIX)ar +export NM := $(PREFIX)nm +export OBJCOPY := $(PREFIX)objcopy + +# LD defined in Makefile + + +# === LINK / TRANSLATE ================================================ + +%.gba : %.elf + @$(OBJCOPY) -O binary $< $@ + @echo built ... $(notdir $@) + @gbafix $@ -t$(TITLE) + +#---------------------------------------------------------------------- + +%.mb.elf : + @echo Linking multiboot + $(LD) -specs=gba_mb.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + $(NM) -Sn $@ > $(basename $(notdir $@)).map + +#---------------------------------------------------------------------- + +%.elf : + @echo Linking cartridge + $(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + $(NM) -Sn $@ > $(basename $(notdir $@)).map + +#---------------------------------------------------------------------- + +%.a : + @echo $(notdir $@) + @rm -f $@ + $(AR) -crs $@ $^ + + +# === OBJECTIFY ======================================================= + +%.iwram.o : %.iwram.cpp + @echo $(notdir $<) + $(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(IARCH) -c $< -o $@ + +#---------------------------------------------------------------------- +%.iwram.o : %.iwram.c + @echo $(notdir $<) + $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) $(IARCH) -c $< -o $@ + +#---------------------------------------------------------------------- + +%.o : %.cpp + @echo $(notdir $<) + $(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(RARCH) -c $< -o $@ + +#---------------------------------------------------------------------- + +%.o : %.c + @echo $(notdir $<) + $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) $(RARCH) -c $< -o $@ + +#---------------------------------------------------------------------- + +%.o : %.s + @echo $(notdir $<) + $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ + +#---------------------------------------------------------------------- + +%.o : %.S + @echo $(notdir $<) + $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@ + + +#---------------------------------------------------------------------- +# canned command sequence for binary data +#---------------------------------------------------------------------- + +define bin2o + bin2s $< | $(AS) -o $(@) + echo "extern const u8" `(echo $( `(echo $(> `(echo $(> `(echo $( $(BUILD)/$(TARGET).map + +all : $(BUILD) + +clean: + @echo clean ... + @rm -rf $(BUILD) $(TARGET).elf $(TARGET).gba $(TARGET).sav + +else # If we're here, we should be in the BUILD dir + +DEPENDS := $(OFILES:.o=.d) + +# --- Main targets ---- + +$(OUTPUT).gba : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +-include $(DEPENDS) + +endif # End BUILD switch + +# --- More targets ---------------------------------------------------- + +.PHONY: clean rebuild start + +rebuild: clean $(BUILD) + +start: + start "$(TARGET).gba" + +restart: rebuild start + +# EOF diff --git a/examples/LinkWirelessMultiboot_demo/src/main.cpp b/examples/LinkWirelessMultiboot_demo/src/main.cpp new file mode 100644 index 0000000..d5418d0 --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/main.cpp @@ -0,0 +1,45 @@ +#include +#include +#include "../../../lib/LinkRawWireless.hpp" +#include "../../_lib/interrupt.h" +#include "scenes/MultibootScene.h" +#include "utils/SceneUtils.h" + +void setUpInterrupts(); +void printTutorial(); +static std::shared_ptr engine{new GBAEngine()}; +static std::unique_ptr multibootScene{ + new MultibootScene(engine)}; + +LinkRawWireless* linkRawWireless = new LinkRawWireless(); + +int main() { + setUpInterrupts(); + + engine->setScene(multibootScene.get()); + + while (true) { + engine->update(); + + VBlankIntrWait(); + } + + return 0; +} + +inline void ISR_reset() { + RegisterRamReset(RESET_REG | RESET_VRAM); + SoftReset(); +} + +inline void setUpInterrupts() { + interrupt_init(); + + interrupt_set_handler(INTR_VBLANK, [] {}); + interrupt_enable(INTR_VBLANK); + + // A+B+START+SELECT + REG_KEYCNT = 0b1100000000001111; + interrupt_set_handler(INTR_KEYPAD, ISR_reset); + interrupt_enable(INTR_KEYPAD); +} diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp new file mode 100644 index 0000000..cccda8c --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.cpp @@ -0,0 +1,209 @@ +#include "MultibootScene.h" + +#include +#include +#include +#include + +#include "../../../../lib/LinkRawWireless.hpp" +#include "utils/InputHandler.h" +#include "utils/SceneUtils.h" + +MultibootScene::MultibootScene(std::shared_ptr engine) + : Scene(engine) {} + +static std::unique_ptr aHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr bHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr upHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr downHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr lHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr rHandler = + std::unique_ptr(new InputHandler()); +static std::unique_ptr selectHandler = + std::unique_ptr(new InputHandler()); + +static std::vector logLines; +static u32 currentLogLine = 0; +static bool useVerboseLog = true; + +#define MAX_LINES 20 +#define DRAW_LINE 0 + +void printScrollableText(u32 currentLine, + std::vector lines, + bool withCursor = false) { + for (u32 i = 0; i < MAX_LINES; i++) { + u32 lastLineIndex = MAX_LINES - 1; + u32 index = max(currentLine, lastLineIndex) - lastLineIndex + i; + if (index < lines.size()) { + std::string cursor = currentLine == index ? "> " : " "; + TextStream::instance().setText((withCursor ? cursor : "") + lines[index], + DRAW_LINE + i, -3); + } else { + TextStream::instance().setText(" ", + DRAW_LINE + i, -3); + } + } +} + +void print() { + printScrollableText(currentLogLine, logLines); +} + +void scrollBack() { + if (currentLogLine <= 0) + return; + currentLogLine--; + print(); +} + +void scrollForward() { + if (currentLogLine < MAX_LINES - 1) + currentLogLine = min(MAX_LINES - 1, logLines.size() - 1); + if (currentLogLine == logLines.size() - 1) + return; + currentLogLine++; + print(); +} + +void scrollPageUp() { + currentLogLine = max(currentLogLine - MAX_LINES, 0); + print(); +} + +void scrollPageDown() { + currentLogLine = min(currentLogLine + MAX_LINES, logLines.size() - 1); + print(); +} + +void scrollToTop() { + currentLogLine = 0; + print(); +} + +void scrollToBottom() { + currentLogLine = logLines.size() - 1; + print(); +} + +void clear() { + logLines.clear(); + currentLogLine = 0; + print(); +} + +void log(std::string string) { + logLines.push_back(string); + scrollPageDown(); +} + +std::vector MultibootScene::backgrounds() { + return {}; +} + +std::vector MultibootScene::sprites() { + std::vector sprites; + return sprites; +} + +void MultibootScene::load() { + SCENE_init(); + BACKGROUND_enable(true, false, false, false); + + linkRawWireless->logger = [](std::string string) { + if (useVerboseLog) + log(string); + }; + + log("---"); + log("LinkWirelessMultiboot demo"); + log(""); + log("A: send ROM"); + log("B: toggle log level"); + log("UP/DOWN: scroll up/down"); + log("L/R: scroll page up/down"); + log("UP+L/DOWN+R: scroll to top/bottom"); + log("SELECT: clear"); + log("---"); + log(""); + toggleLogLevel(); +} + +void MultibootScene::tick(u16 keys) { + if (engine->isTransitioning()) + return; + + processKeys(keys); + processButtons(); + + __qran_seed += keys; + __qran_seed += REG_RCNT; + __qran_seed += REG_SIOCNT; +} + +void MultibootScene::processKeys(u16 keys) { + aHandler->setIsPressed(keys & KEY_A); + bHandler->setIsPressed(keys & KEY_B); + upHandler->setIsPressed(keys & KEY_UP); + downHandler->setIsPressed(keys & KEY_DOWN); + lHandler->setIsPressed(keys & KEY_L); + rHandler->setIsPressed(keys & KEY_R); + selectHandler->setIsPressed(keys & KEY_SELECT); +} + +void MultibootScene::processButtons() { + if (bHandler->hasBeenPressedNow()) + toggleLogLevel(); + + if (aHandler->hasBeenPressedNow()) { + // TODO: IMPLEMENT + print(); + } + + if (lHandler->hasBeenPressedNow()) { + if (upHandler->getIsPressed()) + scrollToTop(); + else + scrollPageUp(); + } + + if (rHandler->hasBeenPressedNow()) { + if (downHandler->getIsPressed()) + scrollToBottom(); + else + scrollPageDown(); + } + + if (upHandler->getIsPressed()) + scrollBack(); + + if (downHandler->getIsPressed()) + scrollForward(); + + if (selectHandler->hasBeenPressedNow()) + clear(); +} + +void MultibootScene::toggleLogLevel() { + if (useVerboseLog) { + useVerboseLog = false; + log("! setting log level to NORMAL"); + } else { + useVerboseLog = true; + log("! setting log level to VERBOSE"); + } + log(""); +} + +void MultibootScene::logOperation(std::string name, + std::function operation) { + log("> " + name + "..."); + bool success = operation(); + log(success ? "< success :)" : "< failure :("); + log(""); +} diff --git a/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h new file mode 100644 index 0000000..0c0cca4 --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/scenes/MultibootScene.h @@ -0,0 +1,35 @@ +#ifndef MULTIBOOT_SCENE_H +#define MULTIBOOT_SCENE_H + +#include +#include +#include +#include + +#include +#include + +class MultibootScene : public Scene { + public: + MultibootScene(std::shared_ptr engine); + + std::vector backgrounds() override; + std::vector sprites() override; + + void load() override; + void tick(u16 keys) override; + + private: + struct CommandMenuOption { + std::string name; + u8 command; + }; + int lastSelectedCommandIndex = 0; + + void processKeys(u16 keys); + void processButtons(); + void toggleLogLevel(); + void logOperation(std::string name, std::function operation); +}; + +#endif // MULTIBOOT_SCENE_H diff --git a/examples/LinkWirelessMultiboot_demo/src/utils/InputHandler.h b/examples/LinkWirelessMultiboot_demo/src/utils/InputHandler.h new file mode 100644 index 0000000..163e02e --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/utils/InputHandler.h @@ -0,0 +1,39 @@ +#ifndef INPUT_HANDLER_H +#define INPUT_HANDLER_H + +#include + +class InputHandler { + public: + InputHandler() { + this->isPressed = false; + this->isWaiting = true; + } + + inline bool getIsPressed() { return isPressed; } + + inline bool hasBeenPressedNow() { return isNewPressEvent; } + inline bool hasBeenReleasedNow() { return isNewReleaseEvent; } + + inline bool getHandledFlag() { return handledFlag; } + inline void setHandledFlag(bool value) { handledFlag = value; } + + inline void setIsPressed(bool isPressed) { + bool isNewPressEvent = !this->isWaiting && !this->isPressed && isPressed; + bool isNewReleaseEvent = !this->isWaiting && this->isPressed && !isPressed; + this->isPressed = isPressed; + this->isWaiting = this->isWaiting && isPressed; + + this->isNewPressEvent = isNewPressEvent; + this->isNewReleaseEvent = isNewReleaseEvent; + } + + protected: + bool isPressed = false; + bool isNewPressEvent = false; + bool isNewReleaseEvent = false; + bool handledFlag = false; + bool isWaiting = false; +}; + +#endif // INPUT_HANDLER_H \ No newline at end of file diff --git a/examples/LinkWirelessMultiboot_demo/src/utils/SceneUtils.h b/examples/LinkWirelessMultiboot_demo/src/utils/SceneUtils.h new file mode 100644 index 0000000..32258be --- /dev/null +++ b/examples/LinkWirelessMultiboot_demo/src/utils/SceneUtils.h @@ -0,0 +1,52 @@ +#ifndef SCENE_UTILS_H +#define SCENE_UTILS_H + +#include +#include +#include +#include + +const u32 TEXT_MIDDLE_COL = 12; + +inline std::string asStr(u16 data) { + return std::to_string(data); +} + +inline void BACKGROUND_enable(bool bg0, bool bg1, bool bg2, bool bg3) { + REG_DISPCNT = bg0 ? REG_DISPCNT | DCNT_BG0 : REG_DISPCNT & ~DCNT_BG0; + REG_DISPCNT = bg1 ? REG_DISPCNT | DCNT_BG1 : REG_DISPCNT & ~DCNT_BG1; + REG_DISPCNT = bg2 ? REG_DISPCNT | DCNT_BG2 : REG_DISPCNT & ~DCNT_BG2; + REG_DISPCNT = bg3 ? REG_DISPCNT | DCNT_BG3 : REG_DISPCNT & ~DCNT_BG3; +} + +inline void SPRITE_disable() { + REG_DISPCNT = REG_DISPCNT & ~DCNT_OBJ; +} + +inline void SCENE_init() { + TextStream::instance().clear(); + TextStream::instance().scroll(0, 0); + TextStream::instance().setMosaic(false); + + BACKGROUND_enable(false, false, false, false); + SPRITE_disable(); +} + +inline void SCENE_write(std::string text, u32 row) { + TextStream::instance().setText(text, row, + TEXT_MIDDLE_COL - text.length() / 2); +} + +inline void SCENE_wait(u32 verticalLines) { + u32 count = 0; + u32 vCount = REG_VCOUNT; + + while (count < verticalLines) { + if (REG_VCOUNT != vCount) { + count++; + vCount = REG_VCOUNT; + } + }; +} + +#endif // SCENE_UTILS_H diff --git a/examples/compile.sh b/examples/compile.sh index 1877194..5e541b4 100644 --- a/examples/compile.sh +++ b/examples/compile.sh @@ -86,3 +86,8 @@ mv backup.gba LinkWireless_demo.gba sed -i -e "s/#define LINK_WIRELESS_PUT_ISR_IN_IWRAM/\/\/ #define LINK_WIRELESS_PUT_ISR_IN_IWRAM/g" ../../lib/LinkWireless.hpp sed -i -e "s/#define PROFILING_ENABLED/\/\/ #define PROFILING_ENABLED/g" ../../lib/LinkWireless.hpp cd .. + +cd LinkWirelessMultiboot_demo/ +make rebuild +cp LinkWirelessMultiboot_demo.gba ../ +cd ..