diff --git a/.gitignore b/.gitignore index 1daee4e9..2bac7784 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ -build/ +# Directories +build +.vscode + +# Files *.elf *.gba *.sav -.vscode/ \ No newline at end of file diff --git a/README.md b/README.md index 452b1bbc..a2a5e20a 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,30 @@ # gba-link-connection -A GBA Link Cable library to add multiplayer support to homebrew games. +A GameBoy Advance Link Cable library to add multiplayer support to homebrew games. -![screenshot](https://user-images.githubusercontent.com/1631752/97110671-45c17800-16b9-11eb-8fd9-11e4c0248506.png) +The library uses message queues to send/receive data and transmits when it's possible. As it uses CPU interrupts, the connection is alive even if a console drops a frame or gets stucked in a long iteration loop. After such event, all nodes end up receiving all the pending messages, so a lockstep communication protocol can be used. + +![screenshot](https://user-images.githubusercontent.com/1631752/99154109-1d131980-268c-11eb-86b1-7a728f639e5e.png) ## Usage All the complexity is abstracted in a single header file that exposes an easy-to-use interface. -- Include [LinkConnection.h](src/lib/LinkConnection.h) in your game code. -- Check out an example implementation in [main.cpp](src/main.cpp). - * A build is available in *Releases*. - * It can be tested on real GBAs or with the *NO$GBA* emulator. +- Include [LinkConnection.h](lib/LinkConnection.h) in your game code, and read its comment with instructions. +- Check out the [examples](examples) folder + * Builds are available in *Releases*. + * They can be tested on real GBAs or with emulators (*NO$GBA*, *mGBA*, or *VBA-M*). + +## Constructor options + +`new LinkConnection(...)` accepts these **optional** parameters: + +Name | Type | Default | Description +--- | --- | --- | --- +`startNow` | **bool** | `true` | Automatically starts serial communication. Otherwise, you'll need to call `linkConnection->activate()`. +`baudRate` | **BaudRate** | `BaudRate::BAUD_RATE_3` | Sets a specific baud rate. +`timeout` | **u32** | `3` | Number of frames without an `II_SERIAL` IRQ to reset the connection. +`bufferSize` | **u32** | `60` | Number of messages that the queues will be able to store. ## Makefile actions diff --git a/Makefile b/examples/basic/Makefile similarity index 99% rename from Makefile rename to examples/basic/Makefile index 4672d803..f5af787b 100644 --- a/Makefile +++ b/examples/basic/Makefile @@ -136,7 +136,7 @@ LIBS := -ltonc BUILD := build SRCDIRS := src DATADIRS := data -INCDIRS := src +INCDIRS := src lib LIBDIRS := $(TONCLIB) # --- switches --- diff --git a/examples/basic/lib/LinkConnection.h b/examples/basic/lib/LinkConnection.h new file mode 120000 index 00000000..9686ff92 --- /dev/null +++ b/examples/basic/lib/LinkConnection.h @@ -0,0 +1 @@ +../../../lib/LinkConnection.h \ No newline at end of file diff --git a/examples/basic/src/main.cpp b/examples/basic/src/main.cpp new file mode 100644 index 00000000..c158aca7 --- /dev/null +++ b/examples/basic/src/main.cpp @@ -0,0 +1,64 @@ +#include + +#include + +// (0) Include the header +#include "../lib/LinkConnection.h" + +void log(std::string text); + +// (1) Create a LinkConnection instance +LinkConnection* linkConnection = new LinkConnection(); + +void init() { + REG_DISPCNT = DCNT_MODE0 | DCNT_BG0; + tte_init_se_default(0, BG_CBB(0) | BG_SBB(31)); + + irq_init(NULL); + + // (2) Add the interrupt service routines + irq_add(II_VBLANK, LINK_ISR_VBLANK); + irq_add(II_SERIAL, LINK_ISR_SERIAL); +} + +int main() { + init(); + + u16 data[LINK_MAX_PLAYERS]; + + while (1) { + // (3) Send/read messages messages + u16 keys = ~REG_KEYS & KEY_ANY; + u16 message = (keys << 1) | 1; + linkConnection->send(message); + auto linkState = linkConnection->linkState.get(); + + std::string output = ""; + if (linkState->isConnected()) { + output += "Players: " + std::to_string(linkState->playerCount) + "\n"; + + for (u32 i = 0; i < linkState->playerCount; i++) { + while (linkState->hasMessage(i)) + data[i] = linkState->readMessage(i) >> 1; + + output += "Player " + std::to_string(i) + ": " + + std::to_string(data[i]) + "\n"; + } + + output += "_sent: " + std::to_string(message) + "\n"; + output += "_self pID: " + std::to_string(linkState->currentPlayerId); + } else + output += std::string("Waiting..."); + log(output); + + VBlankIntrWait(); + } + + return 0; +} + +void log(std::string text) { + tte_erase_screen(); + tte_write("#{P:0,0}"); + tte_write(text.c_str()); +} \ No newline at end of file diff --git a/examples/full/Makefile b/examples/full/Makefile new file mode 100644 index 00000000..9123819a --- /dev/null +++ b/examples/full/Makefile @@ -0,0 +1,297 @@ +# === 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 check-env + +check-env: +ifndef BASE_DIR + $(warning Please export a BASE_DIR environment variable pointing to this directory.) + $(warning Example:) + $(warning export BASE_DIR="D:\work\gba\projects\gba-link-connection\examples\full") + $(error "Aborting") +endif + +rebuild: check-env clean $(BUILD) + +start: check-env + start "$(TARGET).gba" + +restart: check-env rebuild start + +# EOF diff --git a/examples/full/lib/LinkConnection.h b/examples/full/lib/LinkConnection.h new file mode 120000 index 00000000..9686ff92 --- /dev/null +++ b/examples/full/lib/LinkConnection.h @@ -0,0 +1 @@ +../../../lib/LinkConnection.h \ No newline at end of file diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/allocator.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/allocator.h new file mode 100644 index 00000000..bcb4d523 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/allocator.h @@ -0,0 +1,49 @@ +// +// Created by Wouter Groeneveld on 27/07/18. +// very loosely based on: +// https://github.com/Jambo51/GBA-Pokemon-Engine/blob/master/source/Entities/OAMObject.cpp +// + +#ifndef GBA_SPRITE_ENGINE_ALLOCATOR_H +#define GBA_SPRITE_ENGINE_ALLOCATOR_H + +#pragma GCC system_header + +#include +#include +#include + +#define MEM_OBJ_VRAM_BASE (MEM_VRAM + VRAM_BG_SIZE) + +u32 voidPtrToU32(void* ptr); + +class AllocatedData { + public: + void* pointer() const { return (void*)currentAddress; }; + u32 size, currentAddress, baseAddress; + + const int getTileLocation() const { + return (currentAddress - baseAddress) >> 5; + } + + AllocatedData(u32 address, u32 size, u32 base) + : currentAddress(address), size(size), baseAddress(base) {} +}; + +class Allocator { + private: + Allocator() = delete; + Allocator(Allocator& other) = delete; + Allocator(Allocator&& other) = delete; + + static u32 currentSpriteIndex; + + public: + static void free(); + static u32 getCurrentSpriteIndex() { return currentSpriteIndex; } + static int getAllocatedSprites() { return allocatedSprites.size(); } + static AllocatedData& allocateObjectTiles(u32 size); + static std::vector allocatedSprites; +}; + +#endif // GBA_SPRITE_ENGINE_ALLOCATOR_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/background/background.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/background/background.h new file mode 100644 index 00000000..ef745a85 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/background/background.h @@ -0,0 +1,74 @@ +// +// Created by Wouter Groeneveld on 28/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_BACKGROUND_H +#define GBA_SPRITE_ENGINE_BACKGROUND_H + +#pragma GCC system_header + +#include + +#define MAPLAYOUT_32X32 0 +#define MAPLAYOUT_32X64 1 +#define MAPLAYOUT_64X32 2 +#define MAPLAYOUT_64X64 3 + +class Background { + private: + void buildRegister(); + u32 getBgControlRegisterIndex(); + + protected: + const void* data; + const void* map; + int size, bgIndex; + int mapSize, mapLayout; + int screenBlockIndex, charBlockIndex, priority; + bool mosaicEnabled = false; + + public: + const int getScreenBlock() { return screenBlockIndex; } + const int getCharBlock() { return charBlockIndex; } + void useMapScreenBlock(int block) { screenBlockIndex = block; } + void useCharBlock(int block) { charBlockIndex = block; } + void usePriority(int value) { priority = value; } + void scroll(int x, int y); + void scrollSpeed(int dx, int dy); + void setMosaic(bool enabled) { mosaicEnabled = enabled; } + + Background(int bgIndex, + const void* data, + int size, + const void* map, + int mapSize, + int screenBlockIndex, + int charBlockIndex, + int mapLayout) + : Background(bgIndex, data, size, map, mapSize) { + this->screenBlockIndex = screenBlockIndex; + this->charBlockIndex = charBlockIndex; + this->mapLayout = mapLayout; + } + + Background(int bgIndex, + const void* data, + int size, + const void* map, + int mapSize) + : data(data), + bgIndex(bgIndex), + size(size), + map(map), + mapLayout(MAPLAYOUT_32X32), + screenBlockIndex(0), + charBlockIndex(bgIndex), + priority(bgIndex), + mapSize(mapSize) {} + virtual void persist(); + void updateMap(const void* map); + void clearMap(); + void clearData(); +}; + +#endif // GBA_SPRITE_ENGINE_BACKGROUND_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/background/text.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/background/text.h new file mode 100644 index 00000000..621388bc --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/background/text.h @@ -0,0 +1,530 @@ +// +// Created by Wouter Groeneveld on 28/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_TEXT_H +#define GBA_SPRITE_ENGINE_TEXT_H + +#pragma GCC system_header + +#define text_background_width 256 +#define text_background_height 24 + +#define text_bg_palette_default_color 0x7fff + +const unsigned char text_data[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#endif // GBA_SPRITE_ENGINE_TEXT_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/background/text_stream.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/background/text_stream.h new file mode 100644 index 00000000..e0007505 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/background/text_stream.h @@ -0,0 +1,58 @@ +// +// Created by Wouter Groeneveld on 28/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_TEXT_STREAM_H +#define GBA_SPRITE_ENGINE_TEXT_STREAM_H + +#pragma GCC system_header + +#include "background.h" +#include "text.h" + +#include +#include +#include + +#define CHAR_OFFSET_INDEX 32 +#define TILE_WIDTH 32 +#define PALETTE_COLOR_INDEX 15 +#define PALETTE_TEXT_BANK 15 + +#define failure_gba(__mess) \ + (consoleLog_func(__FILE__, __LINE__, __PRETTY_FUNCTION__, #__mess)) +void log_text(const char* text); +void consoleLog_func(const char* fileName, + const int lineNr, + const char* fnName, + const char* msg); + +class TextStream : public Background { + private: + int currRow, currCol; + std::unique_ptr palette; + + static TextStream* inst; + TextStream(); + TextStream(TextStream& other) = delete; + TextStream(TextStream&& other) = delete; + + public: + void clear(); + void setText(std::string text, int row, int col); + void setText(const char* text, int row, int col); + + void setFontColor(COLOR color); + void setFontStyle(const void* data, int size); + + static TextStream& instance(); + + void persist() override; + + TextStream& operator<<(const char* s); + TextStream& operator<<(const int s); + TextStream& operator<<(const u32 s); + TextStream& operator<<(const bool s); +}; + +#endif // GBA_SPRITE_ENGINE_TEXT_STREAM_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/effects/fade_out_scene.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/effects/fade_out_scene.h new file mode 100644 index 00000000..20706c11 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/effects/fade_out_scene.h @@ -0,0 +1,30 @@ +// +// Created by Wouter Groeneveld on 04/08/18. +// + +#ifndef GBA_SPRITE_ENGINE_FADE_OUT_SCENE_H +#define GBA_SPRITE_ENGINE_FADE_OUT_SCENE_H + +#pragma GCC system_header + +#include + +#include "scene_effect.h" + +enum FadeOutType { ToWhite, ToBlack }; + +class FadeOutScene : public SceneEffect { + private: + FadeOutType type; + int timesUpdated; + int speed; + std::unique_ptr palette; + + public: + FadeOutScene(int speed); + FadeOutScene(int speed, FadeOutType type); + void update() override; + bool isDone() override { return timesUpdated >= (32 / speed); } +}; + +#endif // GBA_SPRITE_ENGINE_FADE_OUT_SCENE_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/effects/scene_effect.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/effects/scene_effect.h new file mode 100644 index 00000000..3ccd72d9 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/effects/scene_effect.h @@ -0,0 +1,25 @@ +// +// Created by Wouter Groeneveld on 04/08/18. +// + +#ifndef GBA_SPRITE_ENGINE_SCENE_EFFECT_H +#define GBA_SPRITE_ENGINE_SCENE_EFFECT_H + +#pragma GCC system_header + +#include + +class SceneEffect { + protected: + // WHY no reference? Scene& operator= is implicitly deleted and no intentions + // to use that + Scene* sceneToAffect; + + public: + void setSceneToAffect(Scene* scene) { sceneToAffect = scene; }; + + virtual void update() = 0; + virtual bool isDone() = 0; +}; + +#endif // GBA_SPRITE_ENGINE_SCENE_EFFECT_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_asminc.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_asminc.h new file mode 100644 index 00000000..b1bc28a4 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_asminc.h @@ -0,0 +1,132 @@ +// +// tonc_asminc.h : header file with goodies for assembly. +// +//! \file tonc_asminc.h +//! \author J Vijn +//! \date 20081019 - 20120519 +// +/* === NOTES === + * Cleaned up the macros so that they work with comma-directives as well. + * For use in assembly only! +*/ + +#ifndef TONC_ASMINC_H +#define TONC_ASMINC_H + +#if !__ASSEMBLER__ +#error This header file is for use in assembly only! +#endif // /asm only + + +// -------------------------------------------------------------------- +// MACROS +// -------------------------------------------------------------------- + +#define DEF_SIZE(_name) .size _name, .-_name + +//! \name Section definitions for assembly. +//\{ + +#define CSEC_TEXT .text //!< Standard code section directive. +#define CSEC_EWRAM .section .ewram , "ax", %progbits //!< EWRAM code section directive. +#define CSEC_IWRAM .section .iwram, "ax", %progbits //!< IWRAM code section directive. + +#define DSEC_DATA .data //tonc:keys + and especially + gbatek:bios. + + \note While the speeds of the routines are fair, there + is a large overhead in calling the functions. +*/ + +/*! \defgroup grpBiosDef BIOS informalities + \ingroup grpBios +*/ +/*! \{ */ + + +// -------------------------------------------------------------------- +// CONSTANTS +// -------------------------------------------------------------------- + +//! \name SoftReset flags +//\{ +#define ROM_RESTART 0x00 //!< Restart from ROM entry point. +#define RAM_RESTART 0x01 //!< Restart from RAM entry point. +//\} + +//! \name RegisterRamReset flags +//\{ +#define RESET_EWRAM 0x0001 //!< Clear 256K on-board WRAM +#define RESET_IWRAM 0x0002 //!< Clear 32K in-chip WRAM +#define RESET_PALETTE 0x0004 //!< Clear Palette +#define RESET_VRAM 0x0008 //!< Clear VRAM +#define RESET_OAM 0x0010 //!< Clear OAM. does NOT disable OBJs! +#define RESET_REG_SIO 0x0020 //!< Switches to general purpose mode +#define RESET_REG_SOUND 0x0040 //!< Reset Sound registers +#define RESET_REG 0x0080 //!< All other registers + +//#define RESET_REG_VIDEO 0x0100 //!< video regs, 00h-60h (non standard!) +//#define RESET_REG_DMA 0x0200 //!< DMA regs, B0h-100h (non standard!) +//#define RESET_REG_TIMER 0x0400 //!< Timer regs (100h-110h) (non standard!) + +#define RESET_MEM_MASK 0x001F +#define RESET_REG_MASK 0x00E0 + +#define RESET_GFX 0x001C //!< Clear all gfx-related memory + +//\} + +//! \name Cpu(Fast)Set flags +//\{ +#define CS_CPY 0 //!< Copy mode +#define CS_FILL (1<<24) //!< Fill mode +#define CS_CPY16 0 //!< Copy in halfwords +#define CS_CPY32 (1<<26) //!< Copy words +#define CS_FILL32 (5<<24) //!< Fill words + +#define CFS_CPY CS_CPY //!< Copy words +#define CFS_FILL CS_FILL //!< Fill words +//\} + +//! \name ObjAffineSet P-element offsets +//\{ +#define BG_AFF_OFS 2 //!< BgAffineDest offsets +#define OBJ_AFF_OFS 8 //!< ObjAffineDest offsets +//\} + +//! \name Decompression routines +#define BUP_ALL_OFS (1<<31) + +#define LZ_TYPE 0x00000010 +#define LZ_SIZE_MASK 0xFFFFFF00 +#define LZ_SIZE_SHIFT 8 + +#define HUF_BPP_MASK 0x0000000F +#define HUF_TYPE 0x00000020 +#define HUF_SIZE_MASK 0xFFFFFF00 +#define HUF_SIZE_SHIFT 8 + +#define RL_TYPE 0x00000030 +#define RL_SIZE_MASK 0xFFFFFF00 +#define RL_SIZE_SHIFT 8 + +#define DIF_8 0x00000001 +#define DIF_16 0x00000002 +#define DIF_TYPE 0x00000080 +#define DIF_SIZE_MASK 0xFFFFFF00 +#define DIF_SIZE_SHIFT 8 +//\} + +//! \name Multiboot modes +//\{ +#define MBOOT_NORMAL 0x00 +#define MBOOT_MULTI 0x01 +#define MBOOT_FAST 0x02 +//\} + +// -------------------------------------------------------------------- +// MACROS +// -------------------------------------------------------------------- + + +//! BIOS calls from C +/*! You can use this macro in a C BIOS-call wrapper. The wrapper +* should declare the flags, then this call will do the rest. +* \param x Number of swi call (THUMB number) +* \note It checks the __thumb__ \#define to see whether we're +* in ARM or THUMB mode and fixes the swi number accordingly. +* Huzzah for the C proprocessor! +* \deprecated This macro will not work properly for functions that have IO. +*/ +#if defined ( __thumb__ ) +#define swi_call(x) __asm("swi\t"#x ::: "r0", "r1", "r2", "r3") +#else +#define swi_call(x) __asm("swi\t"#x"<<16" ::: "r0", "r1", "r2", "r3") +#endif + +// -------------------------------------------------------------------- +// CLASSES +// -------------------------------------------------------------------- + + +// --- affine function 0x0E and 0x0F --- + +/* +* Notational convention: postfix underscore is 2d vector +* +* p_ = (px, py) = texture coordinates +* q_ = (qx, qy) = screen coordinates +* P = | pa pb | = affine matrix +* | pc pd | +* d_ = (dx, dy) = background displacement +* +* Then: +* +* (1) p_ = P*q_ + d_ +* +* For transformation around a different point +* (texture point p0_ and screen point q0_), do +* +* (2) p_ - p0_ = P*(q_-q0_) +* +* Subtracting eq 2 from eq1 we immediately find: +* +* (3) _d = p0_ - P*q0_ +* +* For the special case of a texture->screen scale-then-rotate +* transformation with +* s_ = (sx, sy) = inverse scales (s>1 shrinks) +* a = alpha = Counter ClockWise (CCW) angle +* +* (4) P = | sx*cos(a) -sx*sin(a) | +* | sy*sin(a) sy*cos(a) | +* +* +* ObjAffineSet takes a and s_ as input and gives P +* BgAffineSet does that and fills in d_ as well +* +*/ + +// affine types in tonc_types.h + +//! BitUpPack ( for swi 10h) +typedef struct BUP +{ + u16 src_len; //!< source length (bytes) + u8 src_bpp; //!< source bitdepth (1,2,4,8) + u8 dst_bpp; //!< destination bitdepth (1,2,4,8,16,32) + u32 dst_ofs; //!< {0-30}: added offset {31}: zero-data offset flag +} BUP; + +//! Multiboot struct +typedef struct +{ + u32 reserved1[5]; + u8 handshake_data; + u8 padding; + u16 handshake_timeout; + u8 probe_count; + u8 client_data[3]; + u8 palette_data; + u8 response_bit; + u8 client_bit; + u8 reserved2; + u8 *boot_srcp; + u8 *boot_endp; + u8 *masterp; + u8 *reserved3[3]; + u32 system_work2[4]; + u8 sendflag; + u8 probe_target_bit; + u8 check_wait; + u8 server_type; +} MultiBootParam; + + +/*! \} */ + + + +// -------------------------------------------------------------------- +// BASIC BIOS ROUTINES +// -------------------------------------------------------------------- + + + +/*! \defgroup grpBiosMain BIOS functions +* \ingroup grpBios +*/ +/*! \{ */ + +//! \name Reset functions +//\{ +extern "C" void SoftReset(void); +extern "C" void RegisterRamReset(u32 flags); +//\} + +//! \name Halt functions +//\{ +extern "C" void Halt(void); +extern "C" void Stop(void); +extern "C" void IntrWait(u32 flagClear, u32 irq); +extern "C" void VBlankIntrWait(void); +//\} + + +//! \name Math functions +//\{ +extern "C" s32 Div(s32 num, s32 den); +extern "C" s32 DivArm(s32 den, s32 num); +extern "C" u32 Sqrt(u32 num); +extern "C" s16 ArcTan(s16 dydx); +extern "C" s16 ArcTan2(s16 x, s16 y); +//\} + +//! \name Memory copiers/fillers +//\{ +// Technically, these are misnomers. The convention is that +// xxxset is used for fills (comp memset, strset). Or perhaps +// the C library functions are misnomers, since set can be applied +// to both copies and fills. +extern "C" void CpuSet(const void *src, void *dst, u32 mode); +extern "C" void CpuFastSet(const void *src, void *dst, u32 mode); +//\} + +extern "C" u32 BiosCheckSum(void); + + +//! \name Rot/scale functions +//\{ +// These functions are misnomers, because ObjAffineSet is merely +// a special case of/precursor to BgAffineSet. Results from either +// can be used for both objs and bgs. Oh well. +extern "C" void ObjAffineSet(const ObjAffineSource *src, void *dst, s32 num, s32 offset); +extern "C" void BgAffineSet(const BgAffineSource *src, BgAffineDest *dst, s32 num); +//\} + +//! \name Decompression (see GBATek for format details) +//\{ +extern "C" void BitUnPack(const void *src, void *dst, const BUP *bup); +extern "C" void LZ77UnCompWram(const void *src, void *dst); +extern "C" void LZ77UnCompVram(const void *src, void *dst); +extern "C" void HuffUnComp(const void *src, void *dst); +extern "C" void RLUnCompWram(const void *src, void *dst); +extern "C" void RLUnCompVram(const void *src, void *dst); +extern "C" void Diff8bitUnFilterWram(const void *src, void *dst); +extern "C" void Diff8bitUnFilterVram(const void *src, void *dst); +extern "C" void Diff16bitUnFilter(const void *src, void *dst); +//\} + +//! \name Sound Functions +//\{ +// (I have even less of a clue what these do than for the others --- +extern "C" void SoundBias(u32 bias); +extern "C" void SoundDriverInit(void *src); +extern "C" void SoundDriverMode(u32 mode); +extern "C" void SoundDriverMain(void); +extern "C" void SoundDriverVSync(void); +extern "C" void SoundChannelClear(void); +extern "C" u32 MidiKey2Freq(void *wa, u8 mk, u8 fp); +extern "C" void SoundDriverVSyncOff(void); +extern "C" void SoundDriverVSyncOn(void); +//\} + +//! \name Multiboot handshake +//\{ +extern "C" int MultiBoot(MultiBootParam* mb, u32 mode); +//\} + + +/*! \} */ + + +/*! \defgroup grpBiosEx More BIOS functions +* \ingroup grpBios +*/ +/*! \{ */ + +//\{ + +// You can find these in swi_ex.s +extern "C" void VBlankIntrDelay(u32 count); +extern "C" int DivSafe(int num, int den); +extern "C" int Mod(int num, int den); +extern "C" u32 DivAbs(int num, int den); +extern "C" int DivArmMod(int den, int num); +extern "C" u32 DivArmAbs(int den, int num); +extern "C" void CpuFastFill(u32 wd, void *dst, u32 count); + +#define DivMod Mod +//\} + +/*! \} */ + + +#endif // TONC_BIOS \ No newline at end of file diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_core.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_core.h new file mode 100644 index 00000000..7e2d3789 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_core.h @@ -0,0 +1,573 @@ +// +// Core functionality +// +//! \file tonc_core.h +//! \author J Vijn +//! \date 20060508 - 20080128 +// +/* === NOTES === + * Contents: bits, random, dma, timer + * 20080129,jv: added tonccpy/set routines. +*/ + + +#ifndef TONC_CORE +#define TONC_CORE + +#include "tonc_memmap.h" +#include "tonc_memdef.h" + + +// -------------------------------------------------------------------- +// BITS and BITFIELDS +// -------------------------------------------------------------------- + + +/*! \defgroup grpCoreBit Bit(field) macros + \ingroup grpCore +*/ +/*! \{ */ + +//! \name Simple bit macros +//\{ + +//! Create value with bit \a n set +#define BIT(n) ( 1<<(n) ) + +//! Shift \a a by \a n +#define BIT_SHIFT(a, n) ( (a)<<(n) ) + +//! Create a bitmask \a len bits long +#define BIT_MASK(len) ( BIT(len)-1 ) + +//! Set the \a flag bits in \a word +#define BIT_SET(y, flag) ( y |= (flag) ) + +//! Clear the \a flag bits in \a word +#define BIT_CLEAR(y, flag) ( y &= ~(flag) ) + +//! Flip the \a flag bits in \a word +#define BIT_FLIP(y, flag) ( y ^= (flag) ) + +//! Test whether all the \a flag bits in \a word are set +#define BIT_EQ(y, flag) ( ((y)&(flag)) == (flag) ) + + + +//! Create a bitmask of length \a len starting at bit \a shift. +#define BF_MASK(shift, len) ( BIT_MASK(len)<<(shift) ) + + +//! Retrieve a bitfield mask of length \a starting at bit \a shift from \a y. +#define _BF_GET(y, shift, len) ( ((y)>>(shift))&BIT_MASK(len) ) + +//! Prepare a bitmask for insertion or combining. +#define _BF_PREP(x, shift, len) ( ((x)&BIT_MASK(len))<<(shift) ) + +//! Insert a new bitfield value \a x into \a y. +#define _BF_SET(y, x, shift, len) \ + ( y= ((y) &~ BF_MASK(shift, len)) | _BF_PREP(x, shift, len) ) + +//\} + + +/*! \name some EVIL bit-field operations, >:) +* These allow you to mimic bitfields with macros. Most of the +* bitfields in the registers have foo_SHIFT and +* foo_SHIFT macros indicating the mask and shift values +* of the bitfield named foo in a variable. +* These macros let you prepare, get and set the bitfields. +*/ +//\{ + +//! Prepare a named bit-field for for insterion or combination. +#define BFN_PREP(x, name) ( ((x)<>name##_SHIFT ) + +//! Set a named bitfield in \a y to \a x. Equivalent to y.name= x. +#define BFN_SET(y, x, name) (y = ((y)&~name##_MASK) | BFN_PREP(x,name) ) + +//! Compare a named bitfield to named literal \a x. +#define BFN_CMP(y, x, name) ( ((y)&name##_MASK) == (x) ) + + +//! Massage \a x for use in bitfield \a name with pre-shifted \a x +#define BFN_PREP2(x, name) ( (x) & name##_MASK ) + +//! Get the value of bitfield \a name from \a y, but don't down-shift +#define BFN_GET2(y, name) ( (y) & name##_MASK ) + +//! Set bitfield \a name from \a y to \a x with pre-shifted \a x +#define BFN_SET2(y,x,name) ( y = ((y)&~name##_MASK) | BFN_PREP2(x,name) ) + +//\} + +INLINE u32 bf_get(u32 y, uint shift, uint len); +INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len); +INLINE u32 bf_clamp(int x, uint len); + +INLINE int bit_tribool(u32 x, uint plus, uint minus); +INLINE u32 ROR(u32 x, uint ror); + + +/*! \} */ + + +// -------------------------------------------------------------------- +// DATA +// -------------------------------------------------------------------- + + +/*! \defgroup grpData Data routines + \ingroup grpCore +*/ +/*! \{ */ + +//! Get the number of elements in an array +#define countof(_array) ( sizeof(_array)/sizeof(_array[0]) ) + +//! Align \a x to the next multiple of \a width. +INLINE uint align(uint x, uint width); + +//! \name Copying and filling routines +//\{ + +//! Simplified copier for GRIT-exported data. +#define GRIT_CPY(dst, name) memcpy16(dst, name, name##Len/2) + + +// Base memcpy/set replacements. +void *tonccpy(void *dst, const void *src, uint size); + +void *__toncset(void *dst, u32 fill, uint size); +INLINE void *toncset(void *dst, u8 src, uint count); +INLINE void *toncset16(void *dst, u16 src, uint count); +INLINE void *toncset32(void *dst, u32 src, uint count); + + +// Fast memcpy/set +void memset16(void *dst, u16 hw, uint hwcount); +void memcpy16(void *dst, const void* src, uint hwcount); + +IWRAM_CODE void memset32(void *dst, u32 wd, uint wcount); +IWRAM_CODE void memcpy32(void *dst, const void* src, uint wcount); + + +//! Fastfill for halfwords, analogous to memset() +/*! Uses memset32() if \a hwcount>5 +* \param dst Destination address. +* \param hw Source halfword (not address). +* \param hwcount Number of halfwords to fill. +* \note \a dst must be halfword aligned. +* \note \a r0 returns as \a dst + \a hwcount*2. +*/ +void memset16(void *dst, u16 hw, uint hwcount); + +//! \brief Copy for halfwords. +/*! Uses memcpy32() if \a hwn>6 and + \a src and \a dst are aligned equally. + \param dst Destination address. + \param src Source address. + \param hwcount Number of halfwords to fill. + \note \a dst and \a src must be halfword aligned. + \note \a r0 and \a r1 return as + \a dst + \a hwcount*2 and \a src + \a hwcount*2. +*/ +void memcpy16(void *dst, const void* src, uint hwcount); + + +//! Fast-fill by words, analogous to memset() +/*! Like CpuFastSet(), only without the requirement of + 32byte chunks and no awkward store-value-in-memory-first issue. + \param dst Destination address. + \param wd Fill word (not address). + \param wdcount Number of words to fill. + \note \a dst must be word aligned. + \note \a r0 returns as \a dst + \a wdcount*4. +*/ +IWRAM_CODE void memset32(void *dst, u32 wd, uint wdcount); + + +//! \brief Fast-copy by words. +/*! Like CpuFastFill(), only without the requirement of 32byte chunks + \param dst Destination address. + \param src Source address. + \param wdcount Number of words. + \note \a src and \a dst must be word aligned. + \note \a r0 and \a r1 return as + \a dst + \a wdcount*4 and \a src + \a wdcount*4. +*/ +IWRAM_CODE void memcpy32(void *dst, const void* src, uint wdcount); + +//\} + + +/*! \name Repeated-value creators + These function take a hex-value and duplicate it to all fields, + like 0x88 -> 0x88888888. +*/ +//\{ +INLINE u16 dup8(u8 x); +INLINE u32 dup16(u16 x); +INLINE u32 quad8(u8 x); +INLINE u32 octup(u8 x); +//\} + +//! \name Packing routines. +//\{ +INLINE u16 bytes2hword(u8 b0, u8 b1); +INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3); +INLINE u32 hword2word(u16 h0, u16 h1); +//\} + +/*! \} */ + + +// -------------------------------------------------------------------- +// DMA +// -------------------------------------------------------------------- + + +/*! \addtogroup grpDma */ +/*! \{ */ + +//! General purpose DMA transfer macro +/*! \param _dst Destination address. + \param _src Source address. + \param count Number of transfers. + \param ch DMA channel. + \param mode DMA mode. +*/ +#define DMA_TRANSFER(_dst, _src, count, ch, mode) \ +do { \ + REG_DMA[ch].cnt= 0; \ + REG_DMA[ch].src= (const void*)(_src); \ + REG_DMA[ch].dst= (void*)(_dst); \ + REG_DMA[ch].cnt= (count) | (mode); \ +} while(0) + + +INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode); +INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode); + +INLINE void dma3_cpy(void *dst, const void *src, uint size); +INLINE void dma3_fill(void *dst, volatile u32 src, uint size); + +/*! \} */ + + +// -------------------------------------------------------------------- +// TIMER +// -------------------------------------------------------------------- + + +INLINE void profile_start(void); +INLINE uint profile_stop(void); + + +// -------------------------------------------------------------------- +// TONE GENERATOR +// -------------------------------------------------------------------- + + +typedef enum +{ + NOTE_C=0, NOTE_CIS, NOTE_D, NOTE_DIS, + NOTE_E, NOTE_F, NOTE_FIS, NOTE_G, + NOTE_GIS, NOTE_A, NOTE_BES, NOTE_B +} eSndNoteId; + +extern const uint __snd_rates[12]; + +//! Gives the period of a note for the tone-gen registers. +/*! GBA sound range: 8 octaves: [-2, 5]; 8*12= 96 notes (kinda). +* \param note ID (range: [0,11>). See eSndNoteId. +* \param oct octave (range [-2,4)>). +*/ +#define SND_RATE(note, oct) ( 2048-(__snd_rates[note]>>(4+(oct))) ) + + +// -------------------------------------------------------------------- +// MISC +// -------------------------------------------------------------------- + + +/*! \defgroup grpCoreMisc Miscellaneous routines +* \ingroup grpCore +*/ +/*! \{ */ + +#define STR(x) #x + +//! Create text string from a literal +#define XSTR(x) STR(x) + + +//! \name Inline assembly +//\{ + +//! Assembly comment +#define ASM_CMT(str) asm volatile("@# " str) + +//! No$gba breakpoint +#define ASM_BREAK() asm volatile("\tmov\t\tr11, r11") + +//! No-op; wait a bit. +#define ASM_NOP() asm volatile("\tnop") + +//\} + + +//! \name Sector checking +//\{ + +u32 octant(int x, int y); +u32 octant_rot(int x0, int y0); + +//\} + +//! \name Random numbers +//\{ + +#define QRAN_SHIFT 15 +#define QRAN_MASK ((1<>shift) & ( (1<>len; + if(y) + x= (~y)>>(32-len); + return x; +} + + +//! Gives a tribool (-1, 0, or +1) depending on the state of some bits. +/*! Looks at the \a plus and \a minus bits of \a flags, and subtracts + their status to give a +1, -1 or 0 result. Useful for direction flags. + \param flags Value with bit-flags. + \param plus Bit number for positive result. + \param minus Bit number for negative result. + \return +1 if \a plus bit is set but \a minus bit isn't
+ -1 if \a minus bit is set and \a plus bit isn't
+ 0 if neither or both are set. +*/ +INLINE int bit_tribool(u32 flags, uint plus, uint minus) +{ return ((flags>>plus)&1) - ((flags>>minus)&1); } + + +//! Rotate bits right. Yes, this does lead to a ror instruction. +INLINE u32 ROR(u32 x, uint ror) +{ return (x<<(32-ror)) | (x>>ror); } + + +// --- Data ----------------------------------------------------------- + +INLINE uint align(uint x, uint width) +{ return (x+width-1)/width*width; } + + +//! VRAM-safe memset, byte version. Size in bytes. +INLINE void *toncset(void *dst, u8 src, uint count) +{ return __toncset(dst, quad8(src), count); } + +//! VRAM-safe memset, halfword version. Size in hwords. +INLINE void *toncset16(void *dst, u16 src, uint count) +{ return __toncset(dst, src|src<<16, count*2); } + +//! VRAM-safe memset, word version. Size in words. +INLINE void *toncset32(void *dst, u32 src, uint count) +{ return __toncset(dst, src, count*4); } + + + +//! Duplicate a byte to form a halfword: 0x12 -> 0x1212. +INLINE u16 dup8(u8 x) { return x|(x<<8); } + +//! Duplicate a halfword to form a word: 0x1234 -> 0x12341234. +INLINE u32 dup16(u16 x) { return x|(x<<16); } + +//! Quadruple a byte to form a word: 0x12 -> 0x12121212. +INLINE u32 quad8(u8 x) { return x*0x01010101; } + +//! Octuple a nybble to form a word: 0x1 -> 0x11111111 +INLINE u32 octup(u8 x) { return x*0x11111111; } + + +//! Pack 2 bytes into a word. Little-endian order. +INLINE u16 bytes2hword(u8 b0, u8 b1) +{ return b0 | b1<<8; } + +//! Pack 4 bytes into a word. Little-endian order. +INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3) +{ return b0 | b1<<8 | b2<<16 | b3<<24; } + +INLINE u32 hword2word(u16 h0, u16 h1) +{ return h0 | h1<<16; } + + +// --- DMA ------------------------------------------------------------ + +/*! \addtogroup grpDma */ +/*! \{ */ + +//! Generic DMA copy routine. +/*! \param dst Destination address. +* \param src Source address. +* \param count Number of copies to perform. +* \param ch DMA channel. +* \param mode DMA transfer mode. +* \note \a count is the number of copies, not the size in bytes. +*/ +INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode) +{ + REG_DMA[ch].cnt= 0; + REG_DMA[ch].src= src; + REG_DMA[ch].dst= dst; + REG_DMA[ch].cnt= mode | count; +} + +//! Generic DMA fill routine. +/*! \param dst Destination address. +* \param src Source value. +* \param count Number of copies to perform. +* \param ch DMA channel. +* \param mode DMA transfer mode. +* \note \a count is the number of copies, not the size in bytes. +*/ +INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode) +{ + REG_DMA[ch].cnt= 0; + REG_DMA[ch].src= (const void*)&src; + REG_DMA[ch].dst= dst; + REG_DMA[ch].cnt= count | mode | DMA_SRC_FIXED; +} + +//! Specific DMA copier, using channel 3, word transfers. +/*! \param dst Destination address. +* \param src Source address. +* \param size Number of bytes to copy +* \note \a size is the number of bytes +*/ +INLINE void dma3_cpy(void *dst, const void *src, uint size) +{ dma_cpy(dst, src, size/4, 3, DMA_CPY32); } + +//! Specific DMA filler, using channel 3, word transfers. +/*! \param dst Destination address. +* \param src Source value. +* \param size Number of bytes to copy +* \note \a size is the number of bytes +*/ +INLINE void dma3_fill(void *dst, volatile u32 src, uint size) +{ dma_fill(dst, src, size/4, 3, DMA_FILL32); } + +/*! \} */ + + +// --- Random --------------------------------------------------------- + +//! Quick (and very dirty) pseudo-random number generator +/*! \return random in range [0,8000h> +*/ +INLINE int qran(void) +{ + __qran_seed= 1664525*__qran_seed+1013904223; + return (__qran_seed>>16) & QRAN_MAX; +} + + +//! Ranged random number +/*! \return random in range [\a min, \a max> +* \note (max-min) must be lower than 8000h +*/ +INLINE int qran_range(int min, int max) +{ return (qran()*(max-min)>>QRAN_SHIFT)+min; } + + +// --- Timer ---------------------------------------------------------- + +/*! \addtogroup grpTimer */ +/*! \{ */ + +//! Start a profiling run +/*! \note Routine uses timers 3 and 3; if you're already using these +* somewhere, chaos is going to ensue. +*/ +INLINE void profile_start(void) +{ + REG_TM2D= 0; REG_TM3D= 0; + REG_TM2CNT= 0; REG_TM3CNT= 0; + REG_TM3CNT= TM_ENABLE | TM_CASCADE; + REG_TM2CNT= TM_ENABLE; +} + +//! Stop a profiling run and return the time since its start. +/*! \return 32bit cycle count +*/ +INLINE uint profile_stop(void) +{ + REG_TM2CNT= 0; + return (REG_TM3D<<16)|REG_TM2D; +} + +/*! \} /addtogroup */ + + +#endif // TONC_CORE + diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_core_stub.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_core_stub.h new file mode 100644 index 00000000..cb35d9b0 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_core_stub.h @@ -0,0 +1,573 @@ +// +// Core functionality +// +//! \file tonc_core.h +//! \author J Vijn +//! \date 20060508 - 20080128 +// +/* === NOTES === + * Contents: bits, random, dma, timer + * 20080129,jv: added tonccpy/set routines. +*/ + + +#ifndef TONC_CORE_STUB +#define TONC_CORE_STUB + +#include "tonc_memmap.h" +#include "tonc_memdef.h" + + +// -------------------------------------------------------------------- +// BITS and BITFIELDS +// -------------------------------------------------------------------- + + +/*! \defgroup grpCoreBit Bit(field) macros + \ingroup grpCore +*/ +/*! \{ */ + +//! \name Simple bit macros +//\{ + +//! Create value with bit \a n set +#define BIT(n) ( 1<<(n) ) + +//! Shift \a a by \a n +#define BIT_SHIFT(a, n) ( (a)<<(n) ) + +//! Create a bitmask \a len bits long +#define BIT_MASK(len) ( BIT(len)-1 ) + +//! Set the \a flag bits in \a word +#define BIT_SET(y, flag) ( y |= (flag) ) + +//! Clear the \a flag bits in \a word +#define BIT_CLEAR(y, flag) ( y &= ~(flag) ) + +//! Flip the \a flag bits in \a word +#define BIT_FLIP(y, flag) ( y ^= (flag) ) + +//! Test whether all the \a flag bits in \a word are set +#define BIT_EQ(y, flag) ( ((y)&(flag)) == (flag) ) + + + +//! Create a bitmask of length \a len starting at bit \a shift. +#define BF_MASK(shift, len) ( BIT_MASK(len)<<(shift) ) + + +//! Retrieve a bitfield mask of length \a starting at bit \a shift from \a y. +#define _BF_GET(y, shift, len) ( ((y)>>(shift))&BIT_MASK(len) ) + +//! Prepare a bitmask for insertion or combining. +#define _BF_PREP(x, shift, len) ( ((x)&BIT_MASK(len))<<(shift) ) + +//! Insert a new bitfield value \a x into \a y. +#define _BF_SET(y, x, shift, len) \ + ( y= ((y) &~ BF_MASK(shift, len)) | _BF_PREP(x, shift, len) ) + +//\} + + +/*! \name some EVIL bit-field operations, >:) +* These allow you to mimic bitfields with macros. Most of the +* bitfields in the registers have foo_SHIFT and +* foo_SHIFT macros indicating the mask and shift values +* of the bitfield named foo in a variable. +* These macros let you prepare, get and set the bitfields. +*/ +//\{ + +//! Prepare a named bit-field for for insterion or combination. +#define BFN_PREP(x, name) ( ((x)<>name##_SHIFT ) + +//! Set a named bitfield in \a y to \a x. Equivalent to y.name= x. +#define BFN_SET(y, x, name) (y = ((y)&~name##_MASK) | BFN_PREP(x,name) ) + +//! Compare a named bitfield to named literal \a x. +#define BFN_CMP(y, x, name) ( ((y)&name##_MASK) == (x) ) + + +//! Massage \a x for use in bitfield \a name with pre-shifted \a x +#define BFN_PREP2(x, name) ( (x) & name##_MASK ) + +//! Get the value of bitfield \a name from \a y, but don't down-shift +#define BFN_GET2(y, name) ( (y) & name##_MASK ) + +//! Set bitfield \a name from \a y to \a x with pre-shifted \a x +#define BFN_SET2(y,x,name) ( y = ((y)&~name##_MASK) | BFN_PREP2(x,name) ) + +//\} + +INLINE u32 bf_get(u32 y, uint shift, uint len); +INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len); +INLINE u32 bf_clamp(int x, uint len); + +INLINE int bit_tribool(u32 x, uint plus, uint minus); +INLINE u32 ROR(u32 x, uint ror); + + +/*! \} */ + + +// -------------------------------------------------------------------- +// DATA +// -------------------------------------------------------------------- + + +/*! \defgroup grpData Data routines + \ingroup grpCore +*/ +/*! \{ */ + +//! Get the number of elements in an array +#define countof(_array) ( sizeof(_array)/sizeof(_array[0]) ) + +//! Align \a x to the next multiple of \a width. +INLINE uint align(uint x, uint width); + +//! \name Copying and filling routines +//\{ + +//! Simplified copier for GRIT-exported data. +#define GRIT_CPY(dst, name) memcpy16(dst, name, name##Len/2) + + +// Base memcpy/set replacements. +void *tonccpy(void *dst, const void *src, uint size); + +void *__toncset(void *dst, u32 fill, uint size); +INLINE void *toncset(void *dst, u8 src, uint count); +INLINE void *toncset16(void *dst, u16 src, uint count); +INLINE void *toncset32(void *dst, u32 src, uint count); + + +// Fast memcpy/set +void memset16(void *dst, u16 hw, uint hwcount); +void memcpy16(void *dst, const void* src, uint hwcount); + +void memset32(void *dst, u32 wd, uint wcount); +void memcpy32(void *dst, const void* src, uint wcount); + + +//! Fastfill for halfwords, analogous to memset() +/*! Uses memset32() if \a hwcount>5 +* \param dst Destination address. +* \param hw Source halfword (not address). +* \param hwcount Number of halfwords to fill. +* \note \a dst must be halfword aligned. +* \note \a r0 returns as \a dst + \a hwcount*2. +*/ +void memset16(void *dst, u16 hw, uint hwcount); + +//! \brief Copy for halfwords. +/*! Uses memcpy32() if \a hwn>6 and + \a src and \a dst are aligned equally. + \param dst Destination address. + \param src Source address. + \param hwcount Number of halfwords to fill. + \note \a dst and \a src must be halfword aligned. + \note \a r0 and \a r1 return as + \a dst + \a hwcount*2 and \a src + \a hwcount*2. +*/ +void memcpy16(void *dst, const void* src, uint hwcount); + + +//! Fast-fill by words, analogous to memset() +/*! Like CpuFastSet(), only without the requirement of + 32byte chunks and no awkward store-value-in-memory-first issue. + \param dst Destination address. + \param wd Fill word (not address). + \param wdcount Number of words to fill. + \note \a dst must be word aligned. + \note \a r0 returns as \a dst + \a wdcount*4. +*/ +void memset32(void *dst, u32 wd, uint wdcount); + + +//! \brief Fast-copy by words. +/*! Like CpuFastFill(), only without the requirement of 32byte chunks + \param dst Destination address. + \param src Source address. + \param wdcount Number of words. + \note \a src and \a dst must be word aligned. + \note \a r0 and \a r1 return as + \a dst + \a wdcount*4 and \a src + \a wdcount*4. +*/ +void memcpy32(void *dst, const void* src, uint wdcount); + +//\} + + +/*! \name Repeated-value creators + These function take a hex-value and duplicate it to all fields, + like 0x88 -> 0x88888888. +*/ +//\{ +INLINE u16 dup8(u8 x); +INLINE u32 dup16(u16 x); +INLINE u32 quad8(u8 x); +INLINE u32 octup(u8 x); +//\} + +//! \name Packing routines. +//\{ +INLINE u16 bytes2hword(u8 b0, u8 b1); +INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3); +INLINE u32 hword2word(u16 h0, u16 h1); +//\} + +/*! \} */ + + +// -------------------------------------------------------------------- +// DMA +// -------------------------------------------------------------------- + + +/*! \addtogroup grpDma */ +/*! \{ */ + +//! General purpose DMA transfer macro +/*! \param _dst Destination address. + \param _src Source address. + \param count Number of transfers. + \param ch DMA channel. + \param mode DMA mode. +*/ +#define DMA_TRANSFER(_dst, _src, count, ch, mode) \ +do { \ + REG_DMA[ch].cnt= 0; \ + REG_DMA[ch].src= (const void*)(_src); \ + REG_DMA[ch].dst= (void*)(_dst); \ + REG_DMA[ch].cnt= (count) | (mode); \ +} while(0) + + +INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode); +INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode); + +INLINE void dma3_cpy(void *dst, const void *src, uint size); +INLINE void dma3_fill(void *dst, volatile u32 src, uint size); + +/*! \} */ + + +// -------------------------------------------------------------------- +// TIMER +// -------------------------------------------------------------------- + + +INLINE void profile_start(void); +INLINE uint profile_stop(void); + + +// -------------------------------------------------------------------- +// TONE GENERATOR +// -------------------------------------------------------------------- + + +typedef enum +{ + NOTE_C=0, NOTE_CIS, NOTE_D, NOTE_DIS, + NOTE_E, NOTE_F, NOTE_FIS, NOTE_G, + NOTE_GIS, NOTE_A, NOTE_BES, NOTE_B +} eSndNoteId; + +extern const uint __snd_rates[12]; + +//! Gives the period of a note for the tone-gen registers. +/*! GBA sound range: 8 octaves: [-2, 5]; 8*12= 96 notes (kinda). +* \param note ID (range: [0,11>). See eSndNoteId. +* \param oct octave (range [-2,4)>). +*/ +#define SND_RATE(note, oct) ( 2048-(__snd_rates[note]>>(4+(oct))) ) + + +// -------------------------------------------------------------------- +// MISC +// -------------------------------------------------------------------- + + +/*! \defgroup grpCoreMisc Miscellaneous routines +* \ingroup grpCore +*/ +/*! \{ */ + +#define STR(x) #x + +//! Create text string from a literal +#define XSTR(x) STR(x) + + +//! \name Inline assembly +//\{ + +//! Assembly comment +#define ASM_CMT(str) asm volatile("@# " str) + +//! No$gba breakpoint +#define ASM_BREAK() asm volatile("\tmov\t\tr11, r11") + +//! No-op; wait a bit. +#define ASM_NOP() asm volatile("\tnop") + +//\} + + +//! \name Sector checking +//\{ + +u32 octant(int x, int y); +u32 octant_rot(int x0, int y0); + +//\} + +//! \name Random numbers +//\{ + +#define QRAN_SHIFT 15 +#define QRAN_MASK ((1<>shift) & ( (1<>len; + if(y) + x= (~y)>>(32-len); + return x; +} + + +//! Gives a tribool (-1, 0, or +1) depending on the state of some bits. +/*! Looks at the \a plus and \a minus bits of \a flags, and subtracts + their status to give a +1, -1 or 0 result. Useful for direction flags. + \param flags Value with bit-flags. + \param plus Bit number for positive result. + \param minus Bit number for negative result. + \return +1 if \a plus bit is set but \a minus bit isn't
+ -1 if \a minus bit is set and \a plus bit isn't
+ 0 if neither or both are set. +*/ +INLINE int bit_tribool(u32 flags, uint plus, uint minus) +{ return ((flags>>plus)&1) - ((flags>>minus)&1); } + + +//! Rotate bits right. Yes, this does lead to a ror instruction. +INLINE u32 ROR(u32 x, uint ror) +{ return (x<<(32-ror)) | (x>>ror); } + + +// --- Data ----------------------------------------------------------- + +INLINE uint align(uint x, uint width) +{ return (x+width-1)/width*width; } + + +//! VRAM-safe memset, byte version. Size in bytes. +INLINE void *toncset(void *dst, u8 src, uint count) +{ return __toncset(dst, quad8(src), count); } + +//! VRAM-safe memset, halfword version. Size in hwords. +INLINE void *toncset16(void *dst, u16 src, uint count) +{ return __toncset(dst, src|src<<16, count*2); } + +//! VRAM-safe memset, word version. Size in words. +INLINE void *toncset32(void *dst, u32 src, uint count) +{ return __toncset(dst, src, count*4); } + + + +//! Duplicate a byte to form a halfword: 0x12 -> 0x1212. +INLINE u16 dup8(u8 x) { return x|(x<<8); } + +//! Duplicate a halfword to form a word: 0x1234 -> 0x12341234. +INLINE u32 dup16(u16 x) { return x|(x<<16); } + +//! Quadruple a byte to form a word: 0x12 -> 0x12121212. +INLINE u32 quad8(u8 x) { return x*0x01010101; } + +//! Octuple a nybble to form a word: 0x1 -> 0x11111111 +INLINE u32 octup(u8 x) { return x*0x11111111; } + + +//! Pack 2 bytes into a word. Little-endian order. +INLINE u16 bytes2hword(u8 b0, u8 b1) +{ return b0 | b1<<8; } + +//! Pack 4 bytes into a word. Little-endian order. +INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3) +{ return b0 | b1<<8 | b2<<16 | b3<<24; } + +INLINE u32 hword2word(u16 h0, u16 h1) +{ return h0 | h1<<16; } + + +// --- DMA ------------------------------------------------------------ + +/*! \addtogroup grpDma */ +/*! \{ */ + +//! Generic DMA copy routine. +/*! \param dst Destination address. +* \param src Source address. +* \param count Number of copies to perform. +* \param ch DMA channel. +* \param mode DMA transfer mode. +* \note \a count is the number of copies, not the size in bytes. +*/ +INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode) +{ + REG_DMA[ch].cnt= 0; + REG_DMA[ch].src= src; + REG_DMA[ch].dst= dst; + REG_DMA[ch].cnt= mode | count; +} + +//! Generic DMA fill routine. +/*! \param dst Destination address. +* \param src Source value. +* \param count Number of copies to perform. +* \param ch DMA channel. +* \param mode DMA transfer mode. +* \note \a count is the number of copies, not the size in bytes. +*/ +INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode) +{ + REG_DMA[ch].cnt= 0; + REG_DMA[ch].src= (const void*)&src; + REG_DMA[ch].dst= dst; + REG_DMA[ch].cnt= count | mode | DMA_SRC_FIXED; +} + +//! Specific DMA copier, using channel 3, word transfers. +/*! \param dst Destination address. +* \param src Source address. +* \param size Number of bytes to copy +* \note \a size is the number of bytes +*/ +INLINE void dma3_cpy(void *dst, const void *src, uint size) +{ dma_cpy(dst, src, size/4, 3, DMA_CPY32); } + +//! Specific DMA filler, using channel 3, word transfers. +/*! \param dst Destination address. +* \param src Source value. +* \param size Number of bytes to copy +* \note \a size is the number of bytes +*/ +INLINE void dma3_fill(void *dst, volatile u32 src, uint size) +{ dma_fill(dst, src, size/4, 3, DMA_FILL32); } + +/*! \} */ + + +// --- Random --------------------------------------------------------- + +//! Quick (and very dirty) pseudo-random number generator +/*! \return random in range [0,8000h> +*/ +INLINE int qran(void) +{ + __qran_seed= 1664525*__qran_seed+1013904223; + return (__qran_seed>>16) & QRAN_MAX; +} + + +//! Ranged random number +/*! \return random in range [\a min, \a max> +* \note (max-min) must be lower than 8000h +*/ +INLINE int qran_range(int min, int max) +{ return (qran()*(max-min)>>QRAN_SHIFT)+min; } + + +// --- Timer ---------------------------------------------------------- + +/*! \addtogroup grpTimer */ +/*! \{ */ + +//! Start a profiling run +/*! \note Routine uses timers 3 and 3; if you're already using these +* somewhere, chaos is going to ensue. +*/ +INLINE void profile_start(void) +{ + REG_TM2D= 0; REG_TM3D= 0; + REG_TM2CNT= 0; REG_TM3CNT= 0; + REG_TM3CNT= TM_ENABLE | TM_CASCADE; + REG_TM2CNT= TM_ENABLE; +} + +//! Stop a profiling run and return the time since its start. +/*! \return 32bit cycle count +*/ +INLINE uint profile_stop(void) +{ + REG_TM2CNT= 0; + return (REG_TM3D<<16)|REG_TM2D; +} + +/*! \} /addtogroup */ + + +#endif // TONC_CORE + diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_math.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_math.h new file mode 100644 index 00000000..087ed7a0 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_math.h @@ -0,0 +1,691 @@ +// +// Mathematical functions +// +//! \file tonc_math.h +//! \author J Vijn +//! \date 20060508 - 20060908 +// +// === NOTES === + + +#ifndef TONC_MATH +#define TONC_MATH + +#include "tonc_types.h" + +// --- Doxygen modules --- + +/*! \defgroup grpMathBase Base math +* \brief Basic math macros and functions like MIN, MAX +* \ingroup grpMath +*/ + +/*! \defgroup grpMathFixed Fixed point math +* \ingroup grpMath +*/ + +/*! \defgroup grpMathLut Look-up tables +* \brief Tonc's internal look-up tables and related routines. +* \ingroup grpMath +*/ + +/*! \defgroup grpMathPoint Point functions +* \ingroup grpMath +*/ + +/*! \defgroup grpMathVector Vector functions +* \ingroup grpMath +*/ + +/*! \defgroup grpMathRect Rect functions +* \ingroup grpMath +*/ + + +// -------------------------------------------------------------------- +// GENERAL +// -------------------------------------------------------------------- + + +/*! \addtogroup grpMathBase */ +/*! \{ */ + +// Also available as inline functions + +//! \name core math macros +//\{ + +#ifndef ABS +//! Get the absolute value of \a x +#define ABS(x) ( (x)>=0 ? (x) : -(x) ) +#endif // ABS + +#ifndef SGN +//! Get the sign of \a x. +#define SGN(x) ( (x)>=0 ? 1 : -1 ) +#define SGN2 SGN +#endif // SGN + +#ifndef SGN3 +//! Tri-state sign: -1 for negative, 0 for 0, +1 for positive. +#define SGN3(x) ( (x)>0 ? 1 : ( (x)<0 ? -1 : 0) ) +#endif // SGN3 + +#ifndef MAX + +//! Get the maximum of \a a and \a b +#define MAX(a, b) ( ((a) > (b)) ? (a) : (b) ) + +//! Get the minimum of \a a and \a b +#define MIN(a, b) ( ((a) < (b)) ? (a) : (b) ) +#endif // MAX + +#ifndef SWAP +//! In-place swap. +#define SWAP2(a, b) do { a=(a)-(b); b=(a)+(b); a=(b)-(a); } while(0) + +#define SWAP SWAP2 + +//Alternative: +//#define SWAP2(a, b) ( (b) ^= ((a) ^= ((b) ^= (a))) ) + +//! Swaps \a a and \a b, using \a tmp as a temporary +#define SWAP3(a, b, tmp) do { (tmp)=(a); (a)=(b); (b)=(tmp); } while(0) +#endif // SWAP + + +INLINE int sgn(int x); +INLINE int sgn3(int x); +INLINE int max(int a, int b); +INLINE int min(int a, int b); + +//\} + + +//! \name Boundary response macros +//\{ + + +//! Range check +#define IN_RANGE(x, min, max) ( ((x)>=(min)) && ((x)<(max)) ) + +//! Truncates \a x to stay in range [\a min, \a max> +/*! \return Truncated value of \a x. +* \note \a max is exclusive! +*/ +#define CLAMP(x, min, max) \ + ( (x)>=(max) ? ((max)-1) : ( ((x)<(min)) ? (min) : (x) ) ) + +//! Reflects \a x at boundaries \a min and \a max +/*! If \a x is outside the range [\a min, \a max>, +* it'll be placed inside again with the same distance +* to the 'wall', but on the other side. Example for lower +* border: y = \a min - (\a x- \a min) = 2*\a min + \a x. +* \return Reflected value of \a x. +* \note \a max is exclusive! +*/ +#define REFLECT(x, min, max) \ + ( (x)>=(max) ? 2*((max)-1)-(x) : ( ((x)<(min)) ? 2*(min)-(x) : (x) ) ) + +//! Wraps \a x to stay in range [\a min, \a max> +#define WRAP(x, min, max) \ + ( (x)>=(max) ? (x)+(min)-(max) : ( ((x)<(min)) ? (x)+(max)-(min) : (x) ) ) + + +INLINE BOOL in_range(int x, int min, int max); +INLINE int clamp(int x, int min, int max); +INLINE int reflect(int x, int min, int max); +INLINE int wrap(int x, int min, int max); + +//\} + +/* \} */ + + +// -------------------------------------------------------------------- +// FIXED POINT +// -------------------------------------------------------------------- + + +/*! \addtogroup grpMathFixed */ +/*! \{ */ + +#define FIX_SHIFT 8 +#define FIX_SCALE ( 1<fp and m = (n+a-1)/a (i.e., rounding up) +* \li Maximum safe numerator \a x: x < n/(m*a-n) +* \li Minimum n for known \a x: n > x*(a-1) +*/ +#define FX_RECIMUL(x, a, fp) ( ((x)*((1<<(fp))+(a)-1)/(a))>>(fp) ) + +INLINE FIXED int2fx(int d); +INLINE FIXED float2fx(float f); +INLINE u32 fx2uint(FIXED fx); +INLINE u32 fx2ufrac(FIXED fx); +INLINE int fx2int(FIXED fx); +INLINE float fx2float(FIXED fx); +INLINE FIXED fxadd(FIXED fa, FIXED fb); +INLINE FIXED fxsub(FIXED fa, FIXED fb); +INLINE FIXED fxmul(FIXED fa, FIXED fb); +INLINE FIXED fxdiv(FIXED fa, FIXED fb); + +INLINE FIXED fxmul64(FIXED fa, FIXED fb); +INLINE FIXED fxdiv64(FIXED fa, FIXED fb); + +/*! \} */ + +// === LUT ============================================================ + + +/*! \addtogroup grpMathLut */ +/*! \{ */ + +#define SIN_LUT_SIZE 514 // 512 for main lut, 2 extra for lerp +#define DIV_LUT_SIZE 257 // 256 for main lut, 1 extra for lerp + +extern s32 div_lut[257]; // .16f +extern s16 sin_lut[514]; // .12f + +INLINE s32 lu_sin(uint theta); +INLINE s32 lu_cos(uint theta); +INLINE uint lu_div(uint x); + +INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift); +INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift); + +/*! \} */ + +// === POINT ========================================================== + +struct RECT; + +//! \addtogroup grpMathPoint +//! \{ + +//! 2D Point struct +typedef struct POINT { int x, y; } POINT, POINT32; + + +// --- Point functions --- +INLINE POINT *pt_set(POINT *pd, int x, int y); +INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb); +INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb); +INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c); + +INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb); +INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb); +INLINE POINT *pt_scale_eq(POINT *pd, int c); + +INLINE int pt_cross(const POINT *pa, const POINT *pb); +INLINE int pt_dot(const POINT *pa, const POINT *pb); + +int pt_in_rect(const POINT *pt, const struct RECT *rc); + +//! \} + + +// === RECT =========================================================== + +/*! \addtogroup grpMathRect */ +/*! \{ */ + +//! Rectangle struct +typedef struct RECT +{ + int left, top; + int right, bottom; +} RECT, RECT32; + +INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b); +INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h); +INLINE int rc_width(const RECT *rc); +INLINE int rc_height(const RECT *rc); +INLINE RECT *rc_set_pos(RECT *rc, int x, int y); +INLINE RECT *rc_set_size(RECT *rc, int w, int h); +INLINE RECT *rc_move(RECT *rc, int dx, int dy); +INLINE RECT *rc_inflate(RECT *rc, int dw, int dh); +INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr); + +RECT *rc_normalize(RECT *rc); + +/*! \} */ + + +// === VECTOR ========================================================= + +/*! \addtogroup grpMathVector */ +/*! \{ */ + +//! Vector struct +typedef struct VECTOR { FIXED x, y, z; } VECTOR; + + +INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z); +INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb); +INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb); +INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb); +INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c); +INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb); + +INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb); +INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb); +INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb); +INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c); + +VECTOR *vec_cross(VECTOR *vd, const VECTOR *va, const VECTOR *vb); + +/*! \} */ + + + +// === INLINE ========================================================= + +// --- General -------------------------------------------------------- + +//! Get the sign of \a x. +INLINE int sgn(int x) +{ return (x>=0) ? +1 : -1; } + +//! Tri-state sign of \a x: -1 for negative, 0 for 0, +1 for positive. +INLINE int sgn3(int x) +{ return (x>>31) - (-x>>31); } + +//! Get the maximum of \a a and \a b +INLINE int max(int a, int b) +{ return (a > b) ? (a) : (b); } + +//! Get the minimum of \a a and \a b +INLINE int min(int a, int b) +{ return (a < b) ? (a) : (b); } + + +//! Range check +INLINE BOOL in_range(int x, int min, int max) +{ return (u32)(x-min) < (u32)(max-min); } + + +//! Truncates \a x to stay in range [\a min, \a max> +/*! \return Truncated value of \a x. +* \note \a max is exclusive! +*/ +INLINE int clamp(int x, int min, int max) +{ return (x>=max) ? (max-1) : ( (x, +* it'll be placed inside again with the same distance +* to the 'wall', but on the other side. Example for lower +* border: y = \a min - (\a x- \a min) = 2*\a min + \a x. +* \return Reflected value of \a x. +* \note \a max is exclusive! +*/ +INLINE int reflect(int x, int min, int max) +{ return (x>=max) ? (2*(max-1)-x) : ( (x +INLINE int wrap(int x, int min, int max) +{ return (x>=max) ? (x+min-max) : ( (x>FIX_SHIFT; } + +//! Get the unsigned fractional part of a fixed point value (orly?). +INLINE u32 fx2ufrac(FIXED fx) +{ return fx&FIX_MASK; } + +//! Convert a FIXED point value to an signed integer. +INLINE int fx2int(FIXED fx) +{ return fx/FIX_SCALE; } + +//! Convert a fixed point value to floating point. +INLINE float fx2float(FIXED fx) +{ return fx/FIX_SCALEF; } + +//! Add two fixed point values +INLINE FIXED fxadd(FIXED fa, FIXED fb) +{ return fa + fb; } + +//! Subtract two fixed point values +INLINE FIXED fxsub(FIXED fa, FIXED fb) +{ return fa - fb; } + + +//! Multiply two fixed point values +INLINE FIXED fxmul(FIXED fa, FIXED fb) +{ return (fa*fb)>>FIX_SHIFT; } + +//! Divide two fixed point values. +INLINE FIXED fxdiv(FIXED fa, FIXED fb) +{ return ((fa)*FIX_SCALE)/(fb); } + + +//! Multiply two fixed point values using 64bit math. +INLINE FIXED fxmul64(FIXED fa, FIXED fb) +{ return (((s64)fa)*fb)>>FIX_SHIFT; } + + +//! Divide two fixed point values using 64bit math. +INLINE FIXED fxdiv64(FIXED fa, FIXED fb) +{ return ( ((s64)fa)<>7)&0x1FF]; } + +//! Look-up a cosine value (2π = 0x10000) +/*! \param theta Angle in [0,FFFFh] range +* \return .12f cosine value +*/ +INLINE s32 lu_cos(uint theta) +{ return sin_lut[((theta>>7)+128)&0x1FF]; } + +//! Look-up a division value between 0 and 255 +/*! \param x reciprocal to look up. +* \return 1/x (.16f) +*/ +INLINE uint lu_div(uint x) +{ return div_lut[x]; } + + +//! Linear interpolator for 32bit LUTs. +/*! A lut is essentially the discrete form of a function, f(x). +* You can get values for non-integer \e x via (linear) +* interpolation between f(x) and f(x+1). +* \param lut The LUT to interpolate from. +* \param x Fixed point number to interpolate at. +* \param shift Number of fixed-point bits of \a x. +*/ +INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift) +{ + int xa, ya, yb; + xa=x>>shift; + ya= lut[xa]; yb= lut[xa+1]; + return ya + ( (yb-ya)*(x-(xa<>shift ); +} + +//! As lu_lerp32, but for 16bit LUTs. +INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift) +{ + int xa, ya, yb; + xa=x>>shift; + ya= lut[xa]; yb= lut[xa+1]; + return ya + ( (yb-ya)*(x-(xa<>shift ); +} + + +// --- Point ---------------------------------------------------------- + +//! Initialize \a pd to (\a x, \a y) +INLINE POINT *pt_set(POINT *pd, int x, int y) +{ + pd->x= x; pd->y= y; + return pd; +} + +//! Point addition: \a pd = \a pa + \a pb +INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb) +{ + pd->x= pa->x + pb->x; + pd->y= pa->x + pb->y; + return pd; +} + +//! Point subtraction: \a pd = \a pa - \a pb +INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb) +{ + pd->x= pa->x - pb->x; + pd->y= pa->x - pb->y; + return pd; +} + +//! Point scale: \a pd = \a c * \a pa +INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c) +{ + pd->x= pa->x*c; + pd->y= pa->y*c; + return pd; +} + +//! Point increment: \a pd += \a pb +INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb) +{ + pd->x += pb->y; + pd->y += pb->y; + return pd; +} + +//! Point decrement: \a pd -= \a pb +INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb) +{ + pd->x -= pb->y; + pd->y -= pb->y; + return pd; +} + +//! Point scale: \a pd *= \a c +INLINE POINT *pt_scale_eq(POINT *pd, int c) +{ + pd->x *= c; + pd->y *= c; + return pd; +} + +//! Point 'cross'-product: \a pa \htmlonly × \endhtmlonly \a pb +/*! Actually, there's no such thing as a 2D cross-product, but you could +* extend it to 3D and get the value of its z-component, +* which can be used for a test for parallelism. +*/ +INLINE int pt_cross(const POINT *pa, const POINT *pb) +{ return pa->x * pb->y - pa->y * pb->x; } + + +//! Point 'dot'-product:\a pa \htmlonly · \endhtmlonly \a pb +INLINE int pt_dot(const POINT *pa, const POINT *pb) +{ return pa->x * pb->x + pa->y * pb->y; } + + + +// --- Rect ----------------------------------------------------------- + +//! Initialize a rectangle. +/*! \param l Left side. +* \param t Top side. +* \param r Right side. +* \param b Bottom side. +*/ +INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b) +{ + rc->left= l; rc->top= t; rc->right= r; rc->bottom= b; + return rc; +} + +//! Initialize a rectangle, with sizes inside of max boundaries. +/*! \param x Left side. +* \param y Top side. +* \param w Width. +* \param h Height. +*/ +INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h) +{ + rc->left= x; rc->top= y; rc->right= x+w; rc->bottom= y+h; + return rc; +} + +//! Get rectangle width. +INLINE int rc_width(const RECT *rc) +{ return rc->right - rc->left; } + +//! Get rectangle height +INLINE int rc_height(const RECT *rc) +{ return rc->bottom - rc->top; } + +//! Move rectangle to (\a x, \a y) position. +INLINE RECT *rc_set_pos(RECT *rc, int x, int y) +{ + rc->right += x-rc->left; rc->left= x; + rc->bottom += y-rc->top; rc->top= y; + return rc; +} + +//! Reside rectangle. +INLINE RECT *rc_set_size(RECT *rc, int w, int h) +{ + rc->right= rc->left+w; rc->bottom= rc->top+h; + return rc; +} + +//! Move rectangle by (\a dx, \a dy). +INLINE RECT *rc_move(RECT *rc, int dx, int dy) +{ + rc->left += dx; rc->top += dy; + rc->right += dx; rc->bottom += dy; + return rc; +} + +//! Increase size by \a dw horizontally and \a dh vertically. +INLINE RECT *rc_inflate(RECT *rc, int dw, int dh) +{ + rc->left -= dw; rc->top -= dh; + rc->right += dw; rc->bottom += dh; + return rc; +} + +//! Increase sizes on all sides by values of rectangle \a dr. +INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr) +{ + rc->left += dr->left; rc->top += dr->top; + rc->right += dr->right; rc->bottom += dr->bottom; + return rc; +} + + +// --- Vector --------------------------------------------------------- + +//! Initialize a vector +INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z) +{ + vd->x= x; vd->y= y; vd->z= z; + return vd; +} + +//! Add vectors: \b d = \b a + \b b; +INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb) +{ + vd->x= va->x + vb->x; + vd->y= va->y + vb->y; + vd->z= va->z + vb->z; + return vd; +} + +//! Subtract vectors: \b d = \b a - \b b; +INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb) +{ + vd->x= va->x - vb->x; + vd->y= va->y - vb->y; + vd->z= va->z - vb->z; + return vd; +} + +//! Multiply vectors elements: \b d = \b S(ax, ay, az) �\b b +INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb) +{ + vd->x= fxmul(va->x, vb->x); + vd->y= fxmul(va->y, vb->y); + vd->z= fxmul(va->z, vb->z); + return vd; +} + +//! Scale vector: \b d = c*\b a +INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c) +{ + vd->x= fxmul(va->x, c); + vd->y= fxmul(va->y, c); + vd->z= fxmul(va->z, c); + return vd; +} + +//! Dot-product: d = \b a �\b b +INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb) +{ + FIXED dot; + dot = fxmul(va->x, vb->x); + dot += fxmul(va->y, vb->y); + dot += fxmul(va->z, vb->z); + return dot; +} + +//! Increment vector: \b d += \b b; +INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb) +{ vd->x += vb->x; vd->y += vb->y; vd->z += vb->z; return vd; } + +//! Decrease vector: \b d -= \b b; +INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb) +{ vd->x -= vb->x; vd->y -= vb->y; vd->z -= vb->z; return vd; } + +//! Multiply vectors elements: \b d = \b S(dx, dy, dz) �\b b +INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb) +{ + vd->x= fxmul(vd->x, vb->x); + vd->y= fxmul(vd->y, vb->y); + vd->z= fxmul(vd->z, vb->z); + return vd; +} + +//! Scale vector: \b d = c*\b d +INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c) +{ + vd->x= fxmul(vd->x, c); + vd->y= fxmul(vd->y, c); + vd->z= fxmul(vd->z, c); + return vd; +} + +#endif // TONC_MATH diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_math_stub.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_math_stub.h new file mode 100644 index 00000000..0e0f85ba --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_math_stub.h @@ -0,0 +1,702 @@ +// +// Created by Wouter Groeneveld on 14/12/18. +// + +#ifndef GBA_SPRITE_ENGINE_PROJECT_TONC_MATH_STUB_H +#define GBA_SPRITE_ENGINE_PROJECT_TONC_MATH_STUB_H + +// +// Mathematical functions +// +//! \file tonc_math.h +//! \author J Vijn +//! \date 20060508 - 20060908 +// +// === NOTES === + + +#include +#include "tonc_types.h" + +// --- Doxygen modules --- + +/*! \defgroup grpMathBase Base math +* \brief Basic math macros and functions like MIN, MAX +* \ingroup grpMath +*/ + +/*! \defgroup grpMathFixed Fixed point math +* \ingroup grpMath +*/ + +/*! \defgroup grpMathLut Look-up tables +* \brief Tonc's internal look-up tables and related routines. +* \ingroup grpMath +*/ + +/*! \defgroup grpMathPoint Point functions +* \ingroup grpMath +*/ + +/*! \defgroup grpMathVector Vector functions +* \ingroup grpMath +*/ + +/*! \defgroup grpMathRect Rect functions +* \ingroup grpMath +*/ + + +// -------------------------------------------------------------------- +// GENERAL +// -------------------------------------------------------------------- + + +/*! \addtogroup grpMathBase */ +/*! \{ */ + +// Also available as inline functions + +//! \name core math macros +//\{ + +#ifndef ABS +//! Get the absolute value of \a x +#define ABS(x) ( (x)>=0 ? (x) : -(x) ) +#endif // ABS + +#ifndef SGN +//! Get the sign of \a x. +#define SGN(x) ( (x)>=0 ? 1 : -1 ) +#define SGN2 SGN +#endif // SGN + +#ifndef SGN3 +//! Tri-state sign: -1 for negative, 0 for 0, +1 for positive. +#define SGN3(x) ( (x)>0 ? 1 : ( (x)<0 ? -1 : 0) ) +#endif // SGN3 + +#ifndef MAX + +//! Get the maximum of \a a and \a b +#define MAX(a, b) ( ((a) > (b)) ? (a) : (b) ) + +//! Get the minimum of \a a and \a b +#define MIN(a, b) ( ((a) < (b)) ? (a) : (b) ) +#endif // MAX + +#ifndef SWAP +//! In-place swap. +#define SWAP2(a, b) do { a=(a)-(b); b=(a)+(b); a=(b)-(a); } while(0) + +#define SWAP SWAP2 + +//Alternative: +//#define SWAP2(a, b) ( (b) ^= ((a) ^= ((b) ^= (a))) ) + +//! Swaps \a a and \a b, using \a tmp as a temporary +#define SWAP3(a, b, tmp) do { (tmp)=(a); (a)=(b); (b)=(tmp); } while(0) +#endif // SWAP + + +INLINE int sgn(int x); +INLINE int sgn3(int x); +INLINE int max(int a, int b); +INLINE int min(int a, int b); + +//\} + + +//! \name Boundary response macros +//\{ + + +//! Range check +#define IN_RANGE(x, min, max) ( ((x)>=(min)) && ((x)<(max)) ) + +//! Truncates \a x to stay in range [\a min, \a max> +/*! \return Truncated value of \a x. +* \note \a max is exclusive! +*/ +#define CLAMP(x, min, max) \ + ( (x)>=(max) ? ((max)-1) : ( ((x)<(min)) ? (min) : (x) ) ) + +//! Reflects \a x at boundaries \a min and \a max +/*! If \a x is outside the range [\a min, \a max>, +* it'll be placed inside again with the same distance +* to the 'wall', but on the other side. Example for lower +* border: y = \a min - (\a x- \a min) = 2*\a min + \a x. +* \return Reflected value of \a x. +* \note \a max is exclusive! +*/ +#define REFLECT(x, min, max) \ + ( (x)>=(max) ? 2*((max)-1)-(x) : ( ((x)<(min)) ? 2*(min)-(x) : (x) ) ) + +//! Wraps \a x to stay in range [\a min, \a max> +#define WRAP(x, min, max) \ + ( (x)>=(max) ? (x)+(min)-(max) : ( ((x)<(min)) ? (x)+(max)-(min) : (x) ) ) + + +INLINE BOOL in_range(int x, int min, int max); +INLINE int clamp(int x, int min, int max); +INLINE int reflect(int x, int min, int max); +INLINE int wrap(int x, int min, int max); + +//\} + +/* \} */ + + +// -------------------------------------------------------------------- +// FIXED POINT +// -------------------------------------------------------------------- + + +/*! \addtogroup grpMathFixed */ +/*! \{ */ + +#define FIX_SHIFT 8 +#define FIX_SCALE ( 1<fp and m = (n+a-1)/a (i.e., rounding up) +* \li Maximum safe numerator \a x: x < n/(m*a-n) +* \li Minimum n for known \a x: n > x*(a-1) +*/ +#define FX_RECIMUL(x, a, fp) ( ((x)*((1<<(fp))+(a)-1)/(a))>>(fp) ) + +INLINE FIXED int2fx(int d); +INLINE FIXED float2fx(float f); +INLINE u32 fx2uint(FIXED fx); +INLINE u32 fx2ufrac(FIXED fx); +INLINE int fx2int(FIXED fx); +INLINE float fx2float(FIXED fx); +INLINE FIXED fxadd(FIXED fa, FIXED fb); +INLINE FIXED fxsub(FIXED fa, FIXED fb); +INLINE FIXED fxmul(FIXED fa, FIXED fb); +INLINE FIXED fxdiv(FIXED fa, FIXED fb); + +INLINE FIXED fxmul64(FIXED fa, FIXED fb); +INLINE FIXED fxdiv64(FIXED fa, FIXED fb); + +/*! \} */ + +// === LUT ============================================================ + + +/*! \addtogroup grpMathLut */ +/*! \{ */ + +#define SIN_LUT_SIZE 514 // 512 for main lut, 2 extra for lerp +#define DIV_LUT_SIZE 257 // 256 for main lut, 1 extra for lerp + +INLINE s32 lu_sin(uint theta); +INLINE s32 lu_cos(uint theta); +INLINE uint lu_div(uint x); + +INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift); +INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift); + +/*! \} */ + +// === POINT ========================================================== + +struct RECT; + +//! \addtogroup grpMathPoint +//! \{ + +//! 2D Point struct +typedef struct POINT { int x, y; } POINT, POINT32; + + +// --- Point functions --- +INLINE POINT *pt_set(POINT *pd, int x, int y); +INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb); +INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb); +INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c); + +INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb); +INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb); +INLINE POINT *pt_scale_eq(POINT *pd, int c); + +INLINE int pt_cross(const POINT *pa, const POINT *pb); +INLINE int pt_dot(const POINT *pa, const POINT *pb); + +int pt_in_rect(const POINT *pt, const struct RECT *rc); + +//! \} + + +// === RECT =========================================================== + +/*! \addtogroup grpMathRect */ +/*! \{ */ + +//! Rectangle struct +typedef struct RECT +{ + int left, top; + int right, bottom; +} RECT, RECT32; + +INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b); +INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h); +INLINE int rc_width(const RECT *rc); +INLINE int rc_height(const RECT *rc); +INLINE RECT *rc_set_pos(RECT *rc, int x, int y); +INLINE RECT *rc_set_size(RECT *rc, int w, int h); +INLINE RECT *rc_move(RECT *rc, int dx, int dy); +INLINE RECT *rc_inflate(RECT *rc, int dw, int dh); +INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr); + +RECT *rc_normalize(RECT *rc); + +/*! \} */ + + +// === VECTOR ========================================================= + +/*! \addtogroup grpMathVector */ +/*! \{ */ + +//! Vector struct +typedef struct VECTOR { FIXED x, y, z; } VECTOR; + + +INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z); +INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb); +INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb); +INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb); +INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c); +INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb); + +INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb); +INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb); +INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb); +INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c); + +VECTOR *vec_cross(VECTOR *vd, const VECTOR *va, const VECTOR *vb); + +/*! \} */ + + + +// === INLINE ========================================================= + +// --- General -------------------------------------------------------- + +//! Get the sign of \a x. +INLINE int sgn(int x) +{ return (x>=0) ? +1 : -1; } + +//! Tri-state sign of \a x: -1 for negative, 0 for 0, +1 for positive. +INLINE int sgn3(int x) +{ return (x>>31) - (-x>>31); } + +//! Get the maximum of \a a and \a b +INLINE int max(int a, int b) +{ return (a > b) ? (a) : (b); } + +//! Get the minimum of \a a and \a b +INLINE int min(int a, int b) +{ return (a < b) ? (a) : (b); } + + +//! Range check +INLINE BOOL in_range(int x, int min, int max) +{ return (u32)(x-min) < (u32)(max-min); } + + +//! Truncates \a x to stay in range [\a min, \a max> +/*! \return Truncated value of \a x. +* \note \a max is exclusive! +*/ +INLINE int clamp(int x, int min, int max) +{ return (x>=max) ? (max-1) : ( (x, +* it'll be placed inside again with the same distance +* to the 'wall', but on the other side. Example for lower +* border: y = \a min - (\a x- \a min) = 2*\a min + \a x. +* \return Reflected value of \a x. +* \note \a max is exclusive! +*/ +INLINE int reflect(int x, int min, int max) +{ return (x>=max) ? (2*(max-1)-x) : ( (x +INLINE int wrap(int x, int min, int max) +{ return (x>=max) ? (x+min-max) : ( (x>FIX_SHIFT; } + +//! Get the unsigned fractional part of a fixed point value (orly?). +INLINE u32 fx2ufrac(FIXED fx) +{ return fx&FIX_MASK; } + +//! Convert a FIXED point value to an signed integer. +INLINE int fx2int(FIXED fx) +{ return fx/FIX_SCALE; } + +//! Convert a fixed point value to floating point. +INLINE float fx2float(FIXED fx) +{ return fx/FIX_SCALEF; } + +//! Add two fixed point values +INLINE FIXED fxadd(FIXED fa, FIXED fb) +{ return fa + fb; } + +//! Subtract two fixed point values +INLINE FIXED fxsub(FIXED fa, FIXED fb) +{ return fa - fb; } + + +//! Multiply two fixed point values +INLINE FIXED fxmul(FIXED fa, FIXED fb) +{ return (fa*fb)>>FIX_SHIFT; } + +//! Divide two fixed point values. +INLINE FIXED fxdiv(FIXED fa, FIXED fb) +{ return ((fa)*FIX_SCALE)/(fb); } + + +//! Multiply two fixed point values using 64bit math. +INLINE FIXED fxmul64(FIXED fa, FIXED fb) +{ return (((s64)fa)*fb)>>FIX_SHIFT; } + + +//! Divide two fixed point values using 64bit math. +INLINE FIXED fxdiv64(FIXED fa, FIXED fb) +{ return ( ((s64)fa)<x). +* You can get values for non-integer \e x via (linear) +* interpolation between f(x) and f(x+1). +* \param lut The LUT to interpolate from. +* \param x Fixed point number to interpolate at. +* \param shift Number of fixed-point bits of \a x. +*/ +INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift) +{ + int xa, ya, yb; + xa=x>>shift; + ya= lut[xa]; yb= lut[xa+1]; + return ya + ( (yb-ya)*(x-(xa<>shift ); +} + +//! As lu_lerp32, but for 16bit LUTs. +INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift) +{ + int xa, ya, yb; + xa=x>>shift; + ya= lut[xa]; yb= lut[xa+1]; + return ya + ( (yb-ya)*(x-(xa<>shift ); +} + + +// --- Point ---------------------------------------------------------- + +//! Initialize \a pd to (\a x, \a y) +INLINE POINT *pt_set(POINT *pd, int x, int y) +{ + pd->x= x; pd->y= y; + return pd; +} + +//! Point addition: \a pd = \a pa + \a pb +INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb) +{ + pd->x= pa->x + pb->x; + pd->y= pa->x + pb->y; + return pd; +} + +//! Point subtraction: \a pd = \a pa - \a pb +INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb) +{ + pd->x= pa->x - pb->x; + pd->y= pa->x - pb->y; + return pd; +} + +//! Point scale: \a pd = \a c * \a pa +INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c) +{ + pd->x= pa->x*c; + pd->y= pa->y*c; + return pd; +} + +//! Point increment: \a pd += \a pb +INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb) +{ + pd->x += pb->y; + pd->y += pb->y; + return pd; +} + +//! Point decrement: \a pd -= \a pb +INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb) +{ + pd->x -= pb->y; + pd->y -= pb->y; + return pd; +} + +//! Point scale: \a pd *= \a c +INLINE POINT *pt_scale_eq(POINT *pd, int c) +{ + pd->x *= c; + pd->y *= c; + return pd; +} + +//! Point 'cross'-product: \a pa \htmlonly × \endhtmlonly \a pb +/*! Actually, there's no such thing as a 2D cross-product, but you could +* extend it to 3D and get the value of its z-component, +* which can be used for a test for parallelism. +*/ +INLINE int pt_cross(const POINT *pa, const POINT *pb) +{ return pa->x * pb->y - pa->y * pb->x; } + + +//! Point 'dot'-product:\a pa \htmlonly · \endhtmlonly \a pb +INLINE int pt_dot(const POINT *pa, const POINT *pb) +{ return pa->x * pb->x + pa->y * pb->y; } + + + +// --- Rect ----------------------------------------------------------- + +//! Initialize a rectangle. +/*! \param l Left side. +* \param t Top side. +* \param r Right side. +* \param b Bottom side. +*/ +INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b) +{ + rc->left= l; rc->top= t; rc->right= r; rc->bottom= b; + return rc; +} + +//! Initialize a rectangle, with sizes inside of max boundaries. +/*! \param x Left side. +* \param y Top side. +* \param w Width. +* \param h Height. +*/ +INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h) +{ + rc->left= x; rc->top= y; rc->right= x+w; rc->bottom= y+h; + return rc; +} + +//! Get rectangle width. +INLINE int rc_width(const RECT *rc) +{ return rc->right - rc->left; } + +//! Get rectangle height +INLINE int rc_height(const RECT *rc) +{ return rc->bottom - rc->top; } + +//! Move rectangle to (\a x, \a y) position. +INLINE RECT *rc_set_pos(RECT *rc, int x, int y) +{ + rc->right += x-rc->left; rc->left= x; + rc->bottom += y-rc->top; rc->top= y; + return rc; +} + +//! Reside rectangle. +INLINE RECT *rc_set_size(RECT *rc, int w, int h) +{ + rc->right= rc->left+w; rc->bottom= rc->top+h; + return rc; +} + +//! Move rectangle by (\a dx, \a dy). +INLINE RECT *rc_move(RECT *rc, int dx, int dy) +{ + rc->left += dx; rc->top += dy; + rc->right += dx; rc->bottom += dy; + return rc; +} + +//! Increase size by \a dw horizontally and \a dh vertically. +INLINE RECT *rc_inflate(RECT *rc, int dw, int dh) +{ + rc->left -= dw; rc->top -= dh; + rc->right += dw; rc->bottom += dh; + return rc; +} + +//! Increase sizes on all sides by values of rectangle \a dr. +INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr) +{ + rc->left += dr->left; rc->top += dr->top; + rc->right += dr->right; rc->bottom += dr->bottom; + return rc; +} + + +// --- Vector --------------------------------------------------------- + +//! Initialize a vector +INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z) +{ + vd->x= x; vd->y= y; vd->z= z; + return vd; +} + +//! Add vectors: \b d = \b a + \b b; +INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb) +{ + vd->x= va->x + vb->x; + vd->y= va->y + vb->y; + vd->z= va->z + vb->z; + return vd; +} + +//! Subtract vectors: \b d = \b a - \b b; +INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb) +{ + vd->x= va->x - vb->x; + vd->y= va->y - vb->y; + vd->z= va->z - vb->z; + return vd; +} + +//! Multiply vectors elements: \b d = \b S(ax, ay, az) �\b b +INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb) +{ + vd->x= fxmul(va->x, vb->x); + vd->y= fxmul(va->y, vb->y); + vd->z= fxmul(va->z, vb->z); + return vd; +} + +//! Scale vector: \b d = c*\b a +INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c) +{ + vd->x= fxmul(va->x, c); + vd->y= fxmul(va->y, c); + vd->z= fxmul(va->z, c); + return vd; +} + +//! Dot-product: d = \b a �\b b +INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb) +{ + FIXED dot; + dot = fxmul(va->x, vb->x); + dot += fxmul(va->y, vb->y); + dot += fxmul(va->z, vb->z); + return dot; +} + +//! Increment vector: \b d += \b b; +INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb) +{ vd->x += vb->x; vd->y += vb->y; vd->z += vb->z; return vd; } + +//! Decrease vector: \b d -= \b b; +INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb) +{ vd->x -= vb->x; vd->y -= vb->y; vd->z -= vb->z; return vd; } + +//! Multiply vectors elements: \b d = \b S(dx, dy, dz) �\b b +INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb) +{ + vd->x= fxmul(vd->x, vb->x); + vd->y= fxmul(vd->y, vb->y); + vd->z= fxmul(vd->z, vb->z); + return vd; +} + +//! Scale vector: \b d = c*\b d +INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c) +{ + vd->x= fxmul(vd->x, c); + vd->y= fxmul(vd->y, c); + vd->z= fxmul(vd->z, c); + return vd; +} + + +#endif //GBA_SPRITE_ENGINE_PROJECT_TONC_MATH_STUB_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_memdef.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_memdef.h new file mode 100644 index 00000000..46392cf6 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_memdef.h @@ -0,0 +1,962 @@ +// +// Memory map defines. All of them +// +//! \file tonc_memdef.h +//! \author J Vijn +//! \date 20060508 - 20080521 +// +/* === NOTES === + * 20080521 : comms items taken from libgba +*/ + +#ifndef TONC_MEMDEF +#define TONC_MEMDEF + +/*! \defgroup grpMemBits Memory map bit(fields) + \ingroup grpMemmap + \brief List of all bit(field) definitions of memory mapped items. +*/ + +// --- Prefixes --- +// REG_DISPCNT : DCNT +// REG_DISPSTAT : DSTAT +// REG_BGxCNT : BG +// REG_WIN_x : WIN +// REG_MOSAIC : MOS +// REG_BLDCNT : BLD +// REG_SND1SWEEP : SSW +// REG_SNDxCNT, : SSQR +// REG_SNDxFREQ, : SFREQ +// REG_SNDDMGCNT : SDMG +// REG_SNDDSCNT : SDS +// REG_SNDSTAT : SSTAT +// REG_DMAxCNT : DMA +// REG_TMxCNT : TM +// REG_SIOCNT : SIO(N/M/U) +// REG_RCNT : R / GPIO +// REG_KEYINPUT : KEY +// REG_KEYCNT : KCNT +// REG_IE, REG_IF : IRQ +// REG_WSCNT : WS +// Regular SE : SE +// OAM attr 0 : ATTR0 +// OAM attr 1 : ATTR1 +// OAM attr 2 : ATTR2 + + +// --- REG_DISPCNT ----------------------------------------------------- + +/*! \defgroup grpVideoDCNT Display Control Flags + \ingroup grpMemBits + \brief Bits for REG_DISPCNT +*/ +/*! \{ */ + +#define DCNT_MODE0 0 //!< Mode 0; bg 0-4: reg +#define DCNT_MODE1 0x0001 //!< Mode 1; bg 0-1: reg; bg 2: affine +#define DCNT_MODE2 0x0002 //!< Mode 2; bg 2-3: affine +#define DCNT_MODE3 0x0003 //!< Mode 3; bg2: 240x160\@16 bitmap +#define DCNT_MODE4 0x0004 //!< Mode 4; bg2: 240x160\@8 bitmap +#define DCNT_MODE5 0x0005 //!< Mode 5; bg2: 160x128\@16 bitmap +#define DCNT_GB 0x0008 //!< (R) GBC indicator +#define DCNT_PAGE 0x0010 //!< Page indicator +#define DCNT_OAM_HBL 0x0020 //!< Allow OAM updates in HBlank +#define DCNT_OBJ_2D 0 //!< OBJ-VRAM as matrix +#define DCNT_OBJ_1D 0x0040 //!< OBJ-VRAM as array +#define DCNT_BLANK 0x0080 //!< Force screen blank +#define DCNT_BG0 0x0100 //!< Enable bg 0 +#define DCNT_BG1 0x0200 //!< Enable bg 1 +#define DCNT_BG2 0x0400 //!< Enable bg 2 +#define DCNT_BG3 0x0800 //!< Enable bg 3 +#define DCNT_OBJ 0x1000 //!< Enable objects +#define DCNT_WIN0 0x2000 //!< Enable window 0 +#define DCNT_WIN1 0x4000 //!< Enable window 1 +#define DCNT_WINOBJ 0x8000 //!< Enable object window + +#define DCNT_MODE_MASK 0x0007 +#define DCNT_MODE_SHIFT 0 +#define DCNT_MODE(n) ((n)< + pal_bg_bank[y][x] = color color y*16+x ( COLOR ) +*/ +#define pal_bg_bank ((PALBANK*)MEM_PAL) + +//! Object palette matrix. +/*! pal_obj_bank[y] = bank y ( COLOR[ ] )
+ pal_obj_bank[y][x] = color y*16+x ( COLOR ) +*/ +#define pal_obj_bank ((PALBANK*)MEM_PAL_OBJ) + +//\} // End Palette + + +//! \name VRAM +//\{ + +//! Charblocks, 4bpp tiles. +/*! tile_mem[y] = charblock y ( TILE[ ] )
+ tile_mem[y][x] = block y, tile x ( TILE ) +*/ +#define tile_mem ( (CHARBLOCK*)MEM_VRAM) + +//! Charblocks, 8bpp tiles. +/*! tile_mem[y] = charblock y ( TILE[ ] )
+ tile_mem[y][x] = block y, tile x ( TILE ) +*/ +#define tile8_mem ((CHARBLOCK8*)MEM_VRAM) + +//! Object charblocks, 4bpp tiles. +/*! tile_mem[y] = charblock y ( TILE[ ] )
+ tile_mem[y][x] = block y, tile x ( TILE ) +*/ +#define tile_mem_obj ( (CHARBLOCK*)MEM_VRAM_OBJ) + +//! Object charblocks, 4bpp tiles. +/*! tile_mem[y] = charblock y ( TILE[ ] )
+ tile_mem[y][x] = block y, tile x ( TILE ) +*/ +#define tile8_mem_obj ((CHARBLOCK8*)MEM_VRAM_OBJ) + +//! Screenblocks as arrays +/*! se_mem[y] = screenblock y ( SCR_ENTRY[ ] )
+* se_mem[y][x] = screenblock y, entry x ( SCR_ENTRY ) +*/ +#define se_mem ((SCREENBLOCK*)MEM_VRAM) + + +//! Screenblock as matrices +/*! se_mat[s] = screenblock s ( SCR_ENTRY[ ][ ] )
+ se_mat[s][y][x] = screenblock s, entry (x,y) ( SCR_ENTRY ) +*/ +#define se_mat ((SCREENMAT*)MEM_VRAM) + +//! Main mode 3/5 frame as an array +/*! vid_mem[i] = pixel i ( COLOR ) +*/ +#define vid_mem ((COLOR*)MEM_VRAM) + +//! Mode 3 frame as a matrix +/*! m3_mem[y][x] = pixel (x, y) ( COLOR ) +*/ +#define m3_mem ((M3LINE*)MEM_VRAM) + + +//! Mode 4 first page as a matrix +/*! m4_mem[y][x] = pixel (x, y) ( u8 ) +* \note This is a byte-buffer. Not to be used for writing. +*/ +#define m4_mem ((M4LINE*)MEM_VRAM) + +//! Mode 5 first page as a matrix +/*! m5_mem[y][x] = pixel (x, y) ( COLOR ) +*/ +#define m5_mem ((M5LINE*)MEM_VRAM) + +//! First page array +#define vid_mem_front ((COLOR*)MEM_VRAM) + +//! Second page array +#define vid_mem_back ((COLOR*)MEM_VRAM_BACK) + +//! Mode 4 second page as a matrix +/*! m4_mem[y][x] = pixel (x, y) ( u8 ) +* \note This is a byte-buffer. Not to be used for writing. +*/ +#define m4_mem_back ((M4LINE*)MEM_VRAM_BACK) + +//! Mode 5 second page as a matrix +/*! m5_mem[y][x] = pixel (x, y) ( COLOR ) +*/ +#define m5_mem_back ((M5LINE*)MEM_VRAM_BACK) + +//\} // End VRAM + + +//! \name OAM +//\{ + +//! Object attribute memory +/*! oam_mem[i] = object i ( OBJ_ATTR ) +*/ +#define oam_mem ((OBJ_ATTR*)MEM_OAM) +#define obj_mem ((OBJ_ATTR*)MEM_OAM) + +//! Object affine memory +/*! obj_aff_mem[i] = object matrix i ( OBJ_AFFINE ) +*/ +#define obj_aff_mem ((OBJ_AFFINE*)MEM_OAM) + +//\} // End OAM + +//! \name ROM +//\{ + +//! ROM pointer +#define rom_mem ((u16*)MEM_ROM) + +//\} + +//! \name SRAM +//\{ + +//! SRAM pointer +#define sram_mem ((u8*)MEM_SRAM) + +//\} + +/*! \} */ + + +// -------------------------------------------------------------------- +// REGISTER LIST +// -------------------------------------------------------------------- + + +/*! \addtogroup grpReg + \ingroup grpMemmap +*/ +/*! \{ */ + +//! \name IWRAM 'registers' +//\{ + +// 0300:7ff[y] is mirrored at 03ff:fff[y], which is why this works out: +#define REG_IFBIOS *(vu16*)(REG_BASE-0x0008) //!< IRQ ack for IntrWait functions +#define REG_RESET_DST *(vu16*)(REG_BASE-0x0006) //!< Destination for after SoftReset +#define REG_ISR_MAIN *(fnptr*)(REG_BASE-0x0004) //!< IRQ handler address +//\} + +//! \name Display registers +//\{ +#define REG_DISPCNT *(vu32*)(REG_BASE+0x0000) //!< Display control +#define REG_DISPSTAT *(vu16*)(REG_BASE+0x0004) //!< Display status +#define REG_VCOUNT *(vu16*)(REG_BASE+0x0006) //!< Scanline count +//\} + +//! \name Background control registers +//\{ +#define REG_BGCNT ((vu16*)(REG_BASE+0x0008)) //!< Bg control array + +#define REG_BG0CNT *(vu16*)(REG_BASE+0x0008) //!< Bg0 control +#define REG_BG1CNT *(vu16*)(REG_BASE+0x000A) //!< Bg1 control +#define REG_BG2CNT *(vu16*)(REG_BASE+0x000C) //!< Bg2 control +#define REG_BG3CNT *(vu16*)(REG_BASE+0x000E) //!< Bg3 control +//\} + +//! \name Regular background scroll registers. (write only!) +//\{ +#define REG_BG_OFS ((BG_POINT*)(REG_BASE+0x0010)) //!< Bg scroll array + +#define REG_BG0HOFS *(vu16*)(REG_BASE+0x0010) //!< Bg0 horizontal scroll +#define REG_BG0VOFS *(vu16*)(REG_BASE+0x0012) //!< Bg0 vertical scroll +#define REG_BG1HOFS *(vu16*)(REG_BASE+0x0014) //!< Bg1 horizontal scroll +#define REG_BG1VOFS *(vu16*)(REG_BASE+0x0016) //!< Bg1 vertical scroll +#define REG_BG2HOFS *(vu16*)(REG_BASE+0x0018) //!< Bg2 horizontal scroll +#define REG_BG2VOFS *(vu16*)(REG_BASE+0x001A) //!< Bg2 vertical scroll +#define REG_BG3HOFS *(vu16*)(REG_BASE+0x001C) //!< Bg3 horizontal scroll +#define REG_BG3VOFS *(vu16*)(REG_BASE+0x001E) //!< Bg3 vertical scroll +//\} + +//! \name Affine background parameters. (write only!) +//\{ +#define REG_BG_AFFINE ((BG_AFFINE*)(REG_BASE+0x0000)) //!< Bg affine array + +#define REG_BG2PA *(vs16*)(REG_BASE+0x0020) //!< Bg2 matrix.pa +#define REG_BG2PB *(vs16*)(REG_BASE+0x0022) //!< Bg2 matrix.pb +#define REG_BG2PC *(vs16*)(REG_BASE+0x0024) //!< Bg2 matrix.pc +#define REG_BG2PD *(vs16*)(REG_BASE+0x0026) //!< Bg2 matrix.pd +#define REG_BG2X *(vs32*)(REG_BASE+0x0028) //!< Bg2 x scroll +#define REG_BG2Y *(vs32*)(REG_BASE+0x002C) //!< Bg2 y scroll +#define REG_BG3PA *(vs16*)(REG_BASE+0x0030) //!< Bg3 matrix.pa. +#define REG_BG3PB *(vs16*)(REG_BASE+0x0032) //!< Bg3 matrix.pb +#define REG_BG3PC *(vs16*)(REG_BASE+0x0034) //!< Bg3 matrix.pc +#define REG_BG3PD *(vs16*)(REG_BASE+0x0036) //!< Bg3 matrix.pd +#define REG_BG3X *(vs32*)(REG_BASE+0x0038) //!< Bg3 x scroll +#define REG_BG3Y *(vs32*)(REG_BASE+0x003C) //!< Bg3 y scroll +//\} + +//! \name Windowing registers +//\{ +#define REG_WIN0H *(vu16*)(REG_BASE+0x0040) //!< win0 right, left (0xLLRR) +#define REG_WIN1H *(vu16*)(REG_BASE+0x0042) //!< win1 right, left (0xLLRR) +#define REG_WIN0V *(vu16*)(REG_BASE+0x0044) //!< win0 bottom, top (0xTTBB) +#define REG_WIN1V *(vu16*)(REG_BASE+0x0046) //!< win1 bottom, top (0xTTBB) +#define REG_WININ *(vu16*)(REG_BASE+0x0048) //!< win0, win1 control +#define REG_WINOUT *(vu16*)(REG_BASE+0x004A) //!< winOut, winObj control +//\} + +//! \name Alternate Windowing registers +//\{ +#define REG_WIN0R *(vu8*)(REG_BASE+0x0040) //!< Win 0 right +#define REG_WIN0L *(vu8*)(REG_BASE+0x0041) //!< Win 0 left +#define REG_WIN1R *(vu8*)(REG_BASE+0x0042) //!< Win 1 right +#define REG_WIN1L *(vu8*)(REG_BASE+0x0043) //!< Win 1 left + +#define REG_WIN0B *(vu8*)(REG_BASE+0x0044) //!< Win 0 bottom +#define REG_WIN0T *(vu8*)(REG_BASE+0x0045) //!< Win 0 top +#define REG_WIN1B *(vu8*)(REG_BASE+0x0046) //!< Win 1 bottom +#define REG_WIN1T *(vu8*)(REG_BASE+0x0047) //!< Win 1 top + +#define REG_WIN0CNT *(vu8*)(REG_BASE+0x0048) //!< window 0 control +#define REG_WIN1CNT *(vu8*)(REG_BASE+0x0049) //!< window 1 control +#define REG_WINOUTCNT *(vu8*)(REG_BASE+0x004A) //!< Out window control +#define REG_WINOBJCNT *(vu8*)(REG_BASE+0x004B) //!< Obj window control +//\} + + +//! \name Graphic effects +//\{ +#define REG_MOSAIC *(vu32*)(REG_BASE+0x004C) //!< Mosaic control +#define REG_BLDCNT *(vu16*)(REG_BASE+0x0050) //!< Alpha control +#define REG_BLDALPHA *(vu16*)(REG_BASE+0x0052) //!< Fade level +#define REG_BLDY *(vu16*)(REG_BASE+0x0054) //!< Blend levels +//\} + + +// === SOUND REGISTERS === +// sound regs, partially following pin8gba's nomenclature + +//! \name Channel 1: Square wave with sweep +//\{ +#define REG_SND1SWEEP *(vu16*)(REG_BASE+0x0060) //!< Channel 1 Sweep +#define REG_SND1CNT *(vu16*)(REG_BASE+0x0062) //!< Channel 1 Control +#define REG_SND1FREQ *(vu16*)(REG_BASE+0x0064) //!< Channel 1 frequency +//\} + +//! \name Channel 2: Simple square wave +//\{ +#define REG_SND2CNT *(vu16*)(REG_BASE+0x0068) //!< Channel 2 control +#define REG_SND2FREQ *(vu16*)(REG_BASE+0x006C) //!< Channel 2 frequency +//\} + +//! \name Channel 3: Wave player +//\{ +#define REG_SND3SEL *(vu16*)(REG_BASE+0x0070) //!< Channel 3 wave select +#define REG_SND3CNT *(vu16*)(REG_BASE+0x0072) //!< Channel 3 control +#define REG_SND3FREQ *(vu16*)(REG_BASE+0x0074) //!< Channel 3 frequency +//\} + +//! \name Channel 4: Noise generator +//\{ +#define REG_SND4CNT *(vu16*)(REG_BASE+0x0078) //!< Channel 4 control +#define REG_SND4FREQ *(vu16*)(REG_BASE+0x007C) //!< Channel 4 frequency +//\} + +//! \name Sound control +//\{ +#define REG_SNDCNT *(vu32*)(REG_BASE+0x0080) //!< Main sound control +#define REG_SNDDMGCNT *(vu16*)(REG_BASE+0x0080) //!< DMG channel control +#define REG_SNDDSCNT *(vu16*)(REG_BASE+0x0082) //!< Direct Sound control +#define REG_SNDSTAT *(vu16*)(REG_BASE+0x0084) //!< Sound status +#define REG_SNDBIAS *(vu16*)(REG_BASE+0x0088) //!< Sound bias +//\} + +//! \name Sound buffers +//\{ +#define REG_WAVE_RAM (vu32*)(REG_BASE+0x0090) //!< Channel 3 wave buffer + +#define REG_WAVE_RAM0 *(vu32*)(REG_BASE+0x0090) +#define REG_WAVE_RAM1 *(vu32*)(REG_BASE+0x0094) +#define REG_WAVE_RAM2 *(vu32*)(REG_BASE+0x0098) +#define REG_WAVE_RAM3 *(vu32*)(REG_BASE+0x009C) + +#define REG_FIFO_A *(vu32*)(REG_BASE+0x00A0) //!< DSound A FIFO +#define REG_FIFO_B *(vu32*)(REG_BASE+0x00A4) //!< DSound B FIFO +//\} + +//! \name DMA registers +//\{ +#define REG_DMA ((volatile DMA_REC*)(REG_BASE+0x00B0)) //!< DMA as DMA_REC array + +#define REG_DMA0SAD *(vu32*)(REG_BASE+0x00B0) //!< DMA 0 Source address +#define REG_DMA0DAD *(vu32*)(REG_BASE+0x00B4) //!< DMA 0 Destination address +#define REG_DMA0CNT *(vu32*)(REG_BASE+0x00B8) //!< DMA 0 Control + +#define REG_DMA1SAD *(vu32*)(REG_BASE+0x00BC) //!< DMA 1 Source address +#define REG_DMA1DAD *(vu32*)(REG_BASE+0x00C0) //!< DMA 1 Destination address +#define REG_DMA1CNT *(vu32*)(REG_BASE+0x00C4) //!< DMA 1 Control + +#define REG_DMA2SAD *(vu32*)(REG_BASE+0x00C8) //!< DMA 2 Source address +#define REG_DMA2DAD *(vu32*)(REG_BASE+0x00CC) //!< DMA 2 Destination address +#define REG_DMA2CNT *(vu32*)(REG_BASE+0x00D0) //!< DMA 2 Control + +#define REG_DMA3SAD *(vu32*)(REG_BASE+0x00D4) //!< DMA 3 Source address +#define REG_DMA3DAD *(vu32*)(REG_BASE+0x00D8) //!< DMA 3 Destination address +#define REG_DMA3CNT *(vu32*)(REG_BASE+0x00DC) //!< DMA 3 Control +//\} + +//! \name Timer registers +//\{ +#define REG_TM ((volatile TMR_REC*)(REG_BASE+0x0100)) //!< Timers as TMR_REC array + +#define REG_TM0D *(vu16*)(REG_BASE+0x0100) //!< Timer 0 data +#define REG_TM0CNT *(vu16*)(REG_BASE+0x0102) //!< Timer 0 control +#define REG_TM1D *(vu16*)(REG_BASE+0x0104) //!< Timer 1 data +#define REG_TM1CNT *(vu16*)(REG_BASE+0x0106) //!< Timer 1 control +#define REG_TM2D *(vu16*)(REG_BASE+0x0108) //!< Timer 2 data +#define REG_TM2CNT *(vu16*)(REG_BASE+0x010A) //!< Timer 2 control +#define REG_TM3D *(vu16*)(REG_BASE+0x010C) //!< Timer 3 data +#define REG_TM3CNT *(vu16*)(REG_BASE+0x010E) //!< Timer 3 control +//\} + +//! \name Serial communication +//{ +#define REG_SIOCNT *(vu16*)(REG_BASE+0x0128) //!< Serial IO control (Normal/MP/UART) + +#define REG_SIODATA ((vu32*)(REG_BASE+0x0120)) +#define REG_SIODATA32 *(vu32*)(REG_BASE+0x0120) //!< Normal/UART 32bit data +#define REG_SIODATA8 *(vu16*)(REG_BASE+0x012A) //!< Normal/UART 8bit data + +#define REG_SIOMULTI ((vu16*)(REG_BASE+0x0120)) //!< Multiplayer data array +#define REG_SIOMULTI0 *(vu16*)(REG_BASE+0x0120) //!< MP master data +#define REG_SIOMULTI1 *(vu16*)(REG_BASE+0x0122) //!< MP Slave 1 data +#define REG_SIOMULTI2 *(vu16*)(REG_BASE+0x0124) //!< MP Slave 2 data +#define REG_SIOMULTI3 *(vu16*)(REG_BASE+0x0126) //!< MP Slave 3 data + +#define REG_SIOMLT_RECV *(vu16*)(REG_BASE+0x0120) //!< MP data receiver +#define REG_SIOMLT_SEND *(vu16*)(REG_BASE+0x012A) //!< MP data sender +//\} + +//! \name Keypad registers +//\{ +#define REG_KEYINPUT *(vu16*)(REG_BASE+0x0130) //!< Key status (read only??) +#define REG_KEYCNT *(vu16*)(REG_BASE+0x0132) //!< Key IRQ control +//\} + +//! \name Joybus communication +//\{ +#define REG_RCNT *(vu16*)(REG_BASE+0x0134) //!< SIO Mode Select/General Purpose Data +#define REG_JOYCNT *(vu16*)(REG_BASE+0x0140) //!< JOY bus control +#define REG_JOY_RECV *(vu32*)(REG_BASE+0x0150) //!< JOY bus receiever +#define REG_JOY_TRANS *(vu32*)(REG_BASE+0x0154) //!< JOY bus transmitter +#define REG_JOYSTAT *(vu16*)(REG_BASE+0x0158) //!< JOY bus status +//\} + +//! \name Interrupt / System registers +//\{ +#define REG_IE *(vu16*)(REG_BASE+0x0200) //!< IRQ enable +#define REG_IF *(vu16*)(REG_BASE+0x0202) //!< IRQ status/acknowledge +#define REG_WAITCNT *(vu16*)(REG_BASE+0x0204) //!< Waitstate control +#define REG_IME *(vu16*)(REG_BASE+0x0208) //!< IRQ master enable +#define REG_PAUSE *(vu16*)(REG_BASE+0x0300) //!< Pause system (?) +//\} + +/*! \} */ + + +// -------------------------------------------------------------------- +// ALT REGISTERS +// -------------------------------------------------------------------- + + +/*! \addtogroup grpRegAlt + \ingroup grpMemmap + \brief Alternate names for some of the registers +*/ +/*! \{ */ + +#define REG_BLDMOD *(vu16*)(REG_BASE+0x0050) // alpha control +#define REG_COLEV *(vu16*)(REG_BASE+0x0052) // fade level +#define REG_COLEY *(vu16*)(REG_BASE+0x0054) // blend levels + +// sound regs as in belogic and GBATek (mostly for compatability) +#define REG_SOUND1CNT *(vu32*)(REG_BASE+0x0060) +#define REG_SOUND1CNT_L *(vu16*)(REG_BASE+0x0060) +#define REG_SOUND1CNT_H *(vu16*)(REG_BASE+0x0062) +#define REG_SOUND1CNT_X *(vu16*)(REG_BASE+0x0064) +#define REG_SOUND2CNT_L *(vu16*)(REG_BASE+0x0068) +#define REG_SOUND2CNT_H *(vu16*)(REG_BASE+0x006C) +#define REG_SOUND3CNT *(vu32*)(REG_BASE+0x0070) +#define REG_SOUND3CNT_L *(vu16*)(REG_BASE+0x0070) +#define REG_SOUND3CNT_H *(vu16*)(REG_BASE+0x0072) +#define REG_SOUND3CNT_X *(vu16*)(REG_BASE+0x0074) +#define REG_SOUND4CNT_L *(vu16*)(REG_BASE+0x0078) +#define REG_SOUND4CNT_H *(vu16*)(REG_BASE+0x007C) +#define REG_SOUNDCNT *(vu32*)(REG_BASE+0x0080) +#define REG_SOUNDCNT_L *(vu16*)(REG_BASE+0x0080) +#define REG_SOUNDCNT_H *(vu16*)(REG_BASE+0x0082) +#define REG_SOUNDCNT_X *(vu16*)(REG_BASE+0x0084) +#define REG_SOUNDBIAS *(vu16*)(REG_BASE+0x0088) + +#define REG_WAVE (vu32*)(REG_BASE+0x0090) +#define REG_FIFOA *(vu32*)(REG_BASE+0x00A0) +#define REG_FIFOB *(vu32*)(REG_BASE+0x00A4) + + +#define REG_DMA0CNT_L *(vu16*)(REG_BASE+0x00B8) // count +#define REG_DMA0CNT_H *(vu16*)(REG_BASE+0x00BA) // flags +#define REG_DMA1CNT_L *(vu16*)(REG_BASE+0x00C4) +#define REG_DMA1CNT_H *(vu16*)(REG_BASE+0x00C6) +#define REG_DMA2CNT_L *(vu16*)(REG_BASE+0x00D0) +#define REG_DMA2CNT_H *(vu16*)(REG_BASE+0x00D2) +#define REG_DMA3CNT_L *(vu16*)(REG_BASE+0x00DC) +#define REG_DMA3CNT_H *(vu16*)(REG_BASE+0x00DE) + +#define REG_TM0CNT_L *(vu16*)(REG_BASE+0x0100) +#define REG_TM0CNT_H *(vu16*)(REG_BASE+0x0102) + +#define REG_TM1CNT_L *(vu16*)(REG_BASE+0x0104) +#define REG_TM1CNT_H *(vu16*)(REG_BASE+0x0106) + +#define REG_TM2CNT_L *(vu16*)(REG_BASE+0x0108) +#define REG_TM2CNT_H *(vu16*)(REG_BASE+0x010a) + +#define REG_TM3CNT_L *(vu16*)(REG_BASE+0x010c) +#define REG_TM3CNT_H *(vu16*)(REG_BASE+0x010e) + + +#define REG_KEYS *(vu16*)(REG_BASE+0x0130) // Key status +#define REG_P1 *(vu16*)(REG_BASE+0x0130) // for backward combatibility +#define REG_P1CNT *(vu16*)(REG_BASE+0x0132) // ditto + +#define REG_SCD0 *(vu16*)(REG_BASE+0x0120) +#define REG_SCD1 *(vu16*)(REG_BASE+0x0122) +#define REG_SCD2 *(vu16*)(REG_BASE+0x0124) +#define REG_SCD3 *(vu16*)(REG_BASE+0x0126) +#define REG_SCCNT *(vu32*)(REG_BASE+0x0128) +#define REG_SCCNT_L *(vu16*)(REG_BASE+0x0128) +#define REG_SCCNT_H *(vu16*)(REG_BASE+0x012A) + +#define REG_R *(vu16*)(REG_BASE+0x0134) +#define REG_HS_CTRL *(vu16*)(REG_BASE+0x0140) +#define REG_JOYRE *(vu32*)(REG_BASE+0x0150) +#define REG_JOYRE_L *(vu16*)(REG_BASE+0x0150) +#define REG_JOYRE_H *(vu16*)(REG_BASE+0x0152) +#define REG_JOYTR *(vu32*)(REG_BASE+0x0154) +#define REG_JOYTR_L *(vu16*)(REG_BASE+0x0154) +#define REG_JOYTR_H *(vu16*)(REG_BASE+0x0156) +#define REG_JSTAT *(vu16*)(REG_BASE+0x0158) + +#define REG_WSCNT *(vu16*)(REG_BASE+0x0204) + +/*! \} */ + +#endif // TONC_MEMMAP + +// EOF diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_oam.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_oam.h new file mode 100644 index 00000000..f3212c19 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_oam.h @@ -0,0 +1,231 @@ +// +// Basic video functions +// +//! \file tonc_oam.h +//! \author J Vijn +//! \date 20060604 - 20060604 +// +// === NOTES === +// * Basic video-IO, color, background and object functionality + +#ifndef TONC_OAM +#define TONC_OAM + +#include "tonc_memmap.h" +#include "tonc_memdef.h" +#include "tonc_core.h" +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include "tonc_math_stub.h" +#else +#include "tonc_math.h" +#endif + + +// -------------------------------------------------------------------- +// OBJECTS +// -------------------------------------------------------------------- + + +//! \addtogroup grpVideoObj +/*! \{ */ + +#define OAM_CLEAR() memset32(oam_mem, 0, OAM_SIZE/4) + +// --- Prototypes ----------------------------------------------------- + +// --- Full OAM --- +void oam_init(OBJ_ATTR *obj, uint count); +INLINE void oam_copy(OBJ_ATTR *dst, const OBJ_ATTR *src, uint count); + +// --- Obj attr only --- +INLINE OBJ_ATTR *obj_set_attr(OBJ_ATTR *obj, u16 a0, u16 a1, u16 a2); +INLINE void obj_set_pos(OBJ_ATTR *obj, int x, int y); +INLINE void obj_hide(OBJ_ATTR *oatr); +INLINE void obj_unhide(OBJ_ATTR *obj, u16 mode); + +INLINE const u8 *obj_get_size(const OBJ_ATTR *obj); +INLINE int obj_get_width(const OBJ_ATTR *obj); +INLINE int obj_get_height(const OBJ_ATTR *obj); + +void obj_copy(OBJ_ATTR *dst, const OBJ_ATTR *src, uint count); +void obj_hide_multi(OBJ_ATTR *obj, u32 count); +void obj_unhide_multi(OBJ_ATTR *obj, u16 mode, uint count); + +// --- Obj affine only --- +void obj_aff_copy(OBJ_AFFINE *dst, const OBJ_AFFINE *src, uint count); + +INLINE void obj_aff_set(OBJ_AFFINE *oaff, + FIXED pa, FIXED pb, FIXED pc, FIXED pd); +INLINE void obj_aff_identity(OBJ_AFFINE *oaff); +INLINE void obj_aff_scale(OBJ_AFFINE *oaff, FIXED sx, FIXED sy); +INLINE void obj_aff_shearx(OBJ_AFFINE *oaff, FIXED hx); +INLINE void obj_aff_sheary(OBJ_AFFINE *oaff, FIXED hy); + +void obj_aff_rotate(OBJ_AFFINE *oaff, u16 alpha); +void obj_aff_rotscale(OBJ_AFFINE *oaff, FIXED sx, FIXED sy, u16 alpha); +void obj_aff_premul(OBJ_AFFINE *dst, const OBJ_AFFINE *src); +void obj_aff_postmul(OBJ_AFFINE *dst, const OBJ_AFFINE *src); + +void obj_aff_rotscale2(OBJ_AFFINE *oaff, const AFF_SRC *as); +void obj_rotscale_ex(OBJ_ATTR *obj, OBJ_AFFINE *oaff, const AFF_SRC_EX *asx); + + +// inverse (object -> screen) functions, could be useful +// inverses (prototypes) +INLINE void obj_aff_scale_inv(OBJ_AFFINE *oa, FIXED wx, FIXED wy); +INLINE void obj_aff_rotate_inv(OBJ_AFFINE *oa, u16 theta); +INLINE void obj_aff_shearx_inv(OBJ_AFFINE *oa, FIXED hx); +INLINE void obj_aff_sheary_inv(OBJ_AFFINE *oa, FIXED hy); + +/*! \} */ + + +// -------------------------------------------------------------------- +// INLINES +// -------------------------------------------------------------------- + + +/*! \addtogroup grpVideoObj */ +/*! \{ */ + +//! Set the attributes of an object. +INLINE OBJ_ATTR *obj_set_attr(OBJ_ATTR *obj, u16 a0, u16 a1, u16 a2) +{ + obj->attr0= a0; obj->attr1= a1; obj->attr2= a2; + return obj; +} + +//! Set the position of \a obj +INLINE void obj_set_pos(OBJ_ATTR *obj, int x, int y) +{ + BFN_SET(obj->attr0, y, ATTR0_Y); + BFN_SET(obj->attr1, x, ATTR1_X); +} + +//! Copies \a count OAM entries from \a src to \a dst. +INLINE void oam_copy(OBJ_ATTR *dst, const OBJ_ATTR *src, uint count) +{ memcpy32(dst, src, count*2); } + +//! Hide an object. +INLINE void obj_hide(OBJ_ATTR *obj) +{ BFN_SET2(obj->attr0, ATTR0_HIDE, ATTR0_MODE); } + +//! Unhide an object. +/*! \param obj Object to unhide. +* \param mode Object mode to unhide to. Necessary because this affects +* the affine-ness of the object. +*/ +INLINE void obj_unhide(OBJ_ATTR *obj, u16 mode) +{ BFN_SET2(obj->attr0, mode, ATTR0_MODE); } + + +//! Get object's sizes as a byte array +INLINE const u8 *obj_get_size(const OBJ_ATTR *obj) +{ return oam_sizes[obj->attr0>>14][obj->attr1>>14]; } + +//! Get object's width +INLINE int obj_get_width(const OBJ_ATTR *obj) +{ return obj_get_size(obj)[0]; } + +//! Gets object's height +INLINE int obj_get_height(const OBJ_ATTR *obj) +{ return obj_get_size(obj)[1]; } + + +// --- Affine only --- + + +//! Set the elements of an \a object affine matrix. +INLINE void obj_aff_set(OBJ_AFFINE *oaff, + FIXED pa, FIXED pb, FIXED pc, FIXED pd) +{ + oaff->pa= pa; oaff->pb= pb; + oaff->pc= pc; oaff->pd= pd; +} + +//! Set an object affine matrix to the identity matrix +INLINE void obj_aff_identity(OBJ_AFFINE *oaff) +{ + oaff->pa= 0x0100l; oaff->pb= 0; + oaff->pc= 0; oaff->pd= 0x0100; +} + +//! Set an object affine matrix for scaling. +INLINE void obj_aff_scale(OBJ_AFFINE *oaff, FIXED sx, FIXED sy) +{ + oaff->pa= sx; oaff->pb= 0; + oaff->pb= 0; oaff->pd= sy; +} + +INLINE void obj_aff_shearx(OBJ_AFFINE *oaff, FIXED hx) +{ + oaff->pa= 0x0100; oaff->pb= hx; + oaff->pc= 0; oaff->pd= 0x0100; +} + +INLINE void obj_aff_sheary(OBJ_AFFINE *oaff, FIXED hy) +{ + oaff->pa= 0x0100; oaff->pb= 0; + oaff->pc= hy; oaff->pd= 0x0100; +} + + +// --- Inverse operations --- + +INLINE void obj_aff_scale_inv(OBJ_AFFINE *oaff, FIXED wx, FIXED wy) +{ obj_aff_scale(oaff, ((1<<24)/wx)>>8, ((1<<24)/wy)>>8); } + +INLINE void obj_aff_rotate_inv(OBJ_AFFINE *oaff, u16 theta) +{ obj_aff_rotate(oaff, -theta); } + +INLINE void obj_aff_shearx_inv(OBJ_AFFINE *oaff, FIXED hx) +{ obj_aff_shearx(oaff, -hx); } + +INLINE void obj_aff_sheary_inv(OBJ_AFFINE *oaff, FIXED hy) +{ obj_aff_sheary(oaff, -hy); } + + +/*! \} */ + + +void obj_aff_copy(OBJ_AFFINE *dst, const OBJ_AFFINE *src, u32 count) +{ + int ii; + for(ii=0; iipa= src->pa; + dst->pb= src->pb; + dst->pc= src->pc; + dst->pd= src->pd; + src++; + dst++; + } +} + +//! Set obj matrix to counter-clockwise rotation. +/*! + \param oaff Object affine struct to set. + \param alpha CCW angle. full-circle is 10000h. +*/ +void obj_aff_rotate(OBJ_AFFINE *oaff, u16 alpha) +{ + int ss= lu_sin(alpha)>>4, cc= lu_cos(alpha)>>4; + oaff->pa= cc; oaff->pb= -ss; + oaff->pc= ss; oaff->pd= cc; +} + +//! Post-multiply \a dst by \a src: D= D*S +void obj_aff_postmul(OBJ_AFFINE *dst, const OBJ_AFFINE *src) +{ + FIXED tmp_a, tmp_b, tmp_c, tmp_d; + tmp_a= dst->pa; tmp_b= dst->pb; + tmp_c= dst->pc; tmp_d= dst->pd; + + dst->pa= (tmp_a*src->pa + tmp_b*src->pc)>>8; + dst->pb= (tmp_a*src->pb + tmp_b*src->pd)>>8; + dst->pc= (tmp_c*src->pa + tmp_d*src->pc)>>8; + dst->pd= (tmp_c*src->pb + tmp_d*src->pd)>>8; +} + +#endif // TONC_OAM + diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_types.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_types.h new file mode 100644 index 00000000..73fd0606 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba/tonc_types.h @@ -0,0 +1,377 @@ +// +// Basic structs and typedefs +// +//! \file tonc_types.h +//! \author J Vijn +//! \date 20060508 - 20080111 +// +// === NOTES === +// * When doing anything, always, ALWAYS!!! check the type. +// Especially when you're combining things from different sources. +// Look around on the forum and count the number of times people +// have copied, say, from a u32 source to a u16 destination. + + +#ifndef TONC_TYPES +#define TONC_TYPES + + +/*! \defgroup grpTypes Types and attributes */ + + +// -------------------------------------------------------------------- +// GCC ATTRIBUTES +// -------------------------------------------------------------------- + + +/*! \defgroup grpTypeAttr Type attributes +* \ingroup grpTypes +*/ +/*! \{ */ + +// If you want your data in specific sections, add this +// to your variables or functions. +// Example: +// +// //Declaration +// IWRAM_CODE void function(int x, int y, etc); +// +// //Definition +// IWRAM_CODE void function(int x, int y, etc) +// { +// // code +// } + + +//! Put variable in IWRAM (default). +#define IWRAM_DATA __attribute__((section(".iwram"))) + +//! Put variable in EWRAM. +#define EWRAM_DATA __attribute__((section(".ewram"))) + +//! Put non-initialized variable in EWRAM. +#define EWRAM_BSS __attribute__((section(".sbss"))) + +//! Put function in IWRAM. +#define IWRAM_CODE __attribute__((section(".iwram"), long_call)) + +//! Put function in EWRAM. +#define EWRAM_CODE __attribute__((section(".ewram"), long_call)) + +//! Force a variable to an \a n-byte boundary +#define ALIGN(n) __attribute__((aligned(n))) + +//! Force word alignment. +/*! \note In the old days, GCC aggregates were always word aligned. + In the EABI environment (devkitPro r19 and higher), they are + aligned to their widest member. While technically a good thing, + it may cause problems for struct-copies. If you have aggregates + that can multiples of 4 in size but don't have word members, + consider using this attribute to make struct-copies possible again. +*/ +#define ALIGN4 __attribute__((aligned(4))) + +//! Pack aggregate members +/*! By default, members in aggregates are aligned to their native + boundaries. Adding this prevents that. It will slow access though. +*/ +#define PACKED __attribute__((packed)) + +//! Deprecated notice. +/*! Indicates that this function/type/variable should not be used anymore. + Replacements are (usually) present somewhere as well. +*/ +#define DEPRECATED __attribute__((deprecated)) + +//! Inline function declarator +/*! `inline' inlines the function when -O > 0 when called, + but also creates a body for the function itself + `static' removes the body as well +*/ +#define INLINE static inline + +/* \} */ + + +// -------------------------------------------------------------------- +// TYPES +// -------------------------------------------------------------------- + + +// === primary typedefs =============================================== + +/*! \defgroup grpTypePrim Primary types + \ingroup grpTypes +*/ +/*! \{ */ + +/*! \name Base types + Basic signed and unsigned types for 8, 16, 32 and 64-bit integers. +
    +
  • s# : signed #-bit integer.
  • +
  • u#/u{type} : unsigned #-bit integer.
  • +
  • e{type} : enum'ed #-bit integer.
  • + +
+*/ +//\{ +typedef unsigned char u8, byte, uchar, echar; +typedef unsigned short u16, hword, ushort, eshort; +typedef unsigned int u32, word, uint, eint; +typedef unsigned long long u64; + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +//\} + +/*! \name Volatile types +* Volatile types for registers +*/ +//\{ +typedef volatile u8 vu8; +typedef volatile u16 vu16; +typedef volatile u32 vu32; +typedef volatile u64 vu64; + +typedef volatile s8 vs8; +typedef volatile s16 vs16; +typedef volatile s32 vs32; +typedef volatile s64 vs64; +//\} + +/*! \name Const types +* Const types for const function aprameters +*/ +//\{ +typedef const u8 cu8; +typedef const u16 cu16; +typedef const u32 cu32; +typedef const u64 cu64; + +typedef const s8 cs8; +typedef const s16 cs16; +typedef const s32 cs32; +typedef const s64 cs64; +//\} + +//! 8-word type for fast struct-copies +typedef struct { u32 data[8]; } BLOCK; + +//! Type for consting a string as well as the pointer than points to it. +typedef const char * const CSTR; + +/* \} */ + + +// === secondary typedefs ============================================= + +/*! \defgroup grpTypeSec Secondary types +* \ingroup grpTypes +*/ +/*! \{ */ + +typedef s32 FIXED; //!< Fixed point type +typedef u16 COLOR; //!< Type for colors +typedef u16 SCR_ENTRY, SE; //!< Type for screen entries +typedef u8 SCR_AFF_ENTRY, SAE; //!< Type for affine screen entries + +//! 4bpp tile type, for easy indexing and copying of 4-bit tiles +typedef struct { u32 data[8]; } TILE, TILE4; + +//! 8bpp tile type, for easy indexing and 8-bit tiles +typedef struct { u32 data[16]; } TILE8; + + +#ifndef __cplusplus +//! Boolean type +typedef enum { false, true } bool; +#endif + +#ifndef BOOL +typedef u8 BOOL; // C++ bool == u8 too, that's why +#define TRUE 1 +#define FALSE 0 +#endif + + +// --- function pointer --- + +typedef void (*fnptr)(void); //!< void foo() function pointer +typedef void (*fn_v_i)(int); //!< void foo(int x) function pointer +typedef int (*fn_i_i)(int); //!< int foo(int x) function pointer + + +//! \name affine structs +//\{ +//! Simple scale-rotation source struct. +/*! This can be used with ObjAffineSet, and several of tonc's +* affine functions +*/ +typedef struct AFF_SRC +{ + s16 sx; //!< Horizontal zoom (8.8f) + s16 sy; //!< Vertical zoom (8.8f) + u16 alpha; //!< Counter-clockwise angle ( range [0, 0xFFFF] ) +} ALIGN4 AFF_SRC, ObjAffineSource; + + +//! Extended scale-rotate source struct +/*! This is used to scale/rotate around an arbitrary point. See +* tonc's main text for all the details. +*/ +typedef struct AFF_SRC_EX +{ + s32 tex_x; //!< Texture-space anchor, x coordinate (.8f) + s32 tex_y; //!< Texture-space anchor, y coordinate (.8f) + s16 scr_x; //!< Screen-space anchor, x coordinate (.0f) + s16 scr_y; //!< Screen-space anchor, y coordinate (.0f) + s16 sx; //!< Horizontal zoom (8.8f) + s16 sy; //!< Vertical zoom (8.8f) + u16 alpha; //!< Counter-clockwise angle ( range [0, 0xFFFF] ) +} ALIGN4 AFF_SRC_EX, BgAffineSource; + +//! Simple scale-rotation destination struct, BG version. +/*! This is a P-matrix with continuous elements, like the BG matrix. +* It can be used with ObjAffineSet. +*/ +typedef struct AFF_DST +{ + s16 pa, pb; + s16 pc, pd; +} ALIGN4 AFF_DST, ObjAffineDest; + +//! Extended scale-rotate destination struct +/*! This contains the P-matrix and a fixed-point offset , the +* combination can be used to rotate around an arbitrary point. +* Mainly intended for BgAffineSet, but the struct cna be used +* for object transforms too. +*/ +typedef struct AFF_DST_EX +{ + s16 pa, pb; + s16 pc, pd; + s32 dx, dy; +} ALIGN4 AFF_DST_EX, BgAffineDest; + +//\} + +/* \} */ + + +// === memory map structs ============================================ + +/*! \defgroup grpTypeTert Tertiary types +* These types are used for memory mapping of VRAM, affine registers +* and other areas that would benefit from logical memory mapping. +* \ingroup grpTypes +*/ +/*! \{ */ + + +//! \name IO register types +//\{ + +//! Regular bg points; range: :0010 - :001F +typedef struct POINT16 { s16 x, y; } ALIGN4 POINT16, BG_POINT; + +//! Affine parameters for backgrounds; range : 0400:0020 - 0400:003F +typedef struct AFF_DST_EX BG_AFFINE; + +//! DMA struct; range: 0400:00B0 - 0400:00DF +typedef struct DMA_REC +{ + const void *src; + void *dst; + u32 cnt; +} DMA_REC; + +//! Timer struct, range: 0400:0100 - 0400:010F +/*! \note The attribute is required, because union's counted as u32 otherwise. +*/ +typedef struct TMR_REC +{ + union { u16 start, count; } PACKED; + u16 cnt; +} ALIGN4 TMR_REC; + +//\} + + +//! \name PAL types +//\{ + +//! Palette bank type, for 16-color palette banks +typedef COLOR PALBANK[16]; + +//\} + + +/*! \name VRAM array types +* These types allow VRAM access as arrays or matrices in their +* most natural types. +*/ +//\{ +typedef SCR_ENTRY SCREENLINE[32]; +typedef SCR_ENTRY SCREENMAT[32][32]; +typedef SCR_ENTRY SCREENBLOCK[1024]; + +typedef COLOR M3LINE[240]; +typedef u8 M4LINE[240]; // NOTE: u8, not u16!! +typedef COLOR M5LINE[160]; + +typedef TILE CHARBLOCK[512]; +typedef TILE8 CHARBLOCK8[256]; + +//\} + + +/*! \name OAM structs +* \note These OBJ_ATTR and OBJ_AFFINE structs are interlaced in OAM. +* When using affine objs, struct/DMA/mem copies will give bad results. +*/ +//\{ + +//! Object attributes. +/*! \note attribute 3 is padding for the interlace with OBJ_AFFINE. If +* not using affine objects, it can be used as a free field +*/ +typedef struct OBJ_ATTR +{ + u16 attr0; + u16 attr1; + u16 attr2; + s16 fill; +} ALIGN4 OBJ_ATTR; + + +//! Object affine parameters. +/*! \note most fields are padding for the interlace with OBJ_ATTR. +*/ +typedef struct OBJ_AFFINE +{ + u16 fill0[3]; s16 pa; + u16 fill1[3]; s16 pb; + u16 fill2[3]; s16 pc; + u16 fill3[3]; s16 pd; +} ALIGN4 OBJ_AFFINE; + +//\} + + +/*! \} */ + + +// -------------------------------------------------------------------- +// DEFINES +// -------------------------------------------------------------------- + + +#ifndef NULL +#define NULL (void*)0 +#endif + + +#endif // TONC_TYPES + diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba_engine.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba_engine.h new file mode 100644 index 00000000..ab44b4d3 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gba_engine.h @@ -0,0 +1,104 @@ +// +// Created by Wouter Groeneveld on 28/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_GBAENGINE_H +#define GBA_SPRITE_ENGINE_GBAENGINE_H + +#pragma GCC system_header + +#include +#include +#include +#include + +#include "scene.h" +#include "sound_control.h" +#include "timer.h" + +#define GBA_SCREEN_WIDTH 240 +#define GBA_SCREEN_HEIGHT 160 + +class GBAEngine { + private: + // WHY raw pointers? the engine does the transition and cleanup work itself + Scene* currentScene; + Scene* sceneToTransitionTo; + SceneEffect* currentEffectForTransition; + + bool disableTextBg; + SpriteManager spriteManager; + + static std::unique_ptr timer; + static std::unique_ptr activeChannelA; + static std::unique_ptr activeChannelB; + + void vsync(); + void cleanupPreviousScene(); + void enqueueSound(const s8* data, + int totalSamples, + int sampleRate, + SoundChannel channel); + + void enableTimer0AndVBlank(); + void disableTimer0AndVBlank(); + static void startOnVBlank() { REG_IME = 1; } + static void stopOnVBlank() { REG_IME = 0; } + static void onVBlank(); + + public: + GBAEngine(); + + Timer* getTimer(); + void setScene(Scene* scene); + void dynamicallyAddSprite(Sprite* s) { spriteManager.add(s); } + void transitionIntoScene(Scene* scene, SceneEffect* effect); + bool isTransitioning() { return currentEffectForTransition != nullptr; } + void disableText() { this->disableTextBg = true; } + void enableText() { this->disableTextBg = false; } + + void dequeueAllSounds(); + void enqueueMusic(const s8* data, int totalSamples, int sampleRate = 16000) { + enqueueSound(data, totalSamples, sampleRate, ChannelA); + } + void enqueueSound(const s8* data, int totalSamples, int sampleRate = 16000) { + enqueueSound(data, totalSamples, sampleRate, ChannelB); + } + + u16 readKeys(); + + inline void update() { + // main update loop, in while(true) {}. + // WARNING - keep amount of instructions as minimal as possible in here! + if (sceneToTransitionTo) { + currentEffectForTransition->update(); + + if (currentEffectForTransition->isDone()) { + setScene(sceneToTransitionTo); + } + } + + u16 keys = readKeys(); + // main scene update loop call. This *might* take a while. + currentScene->tick(keys); + + // Intentionally commented out: asking the scene for sprites() rebuilds the + // vector each time Causing a big performance hit. Instead, you should call + // updateSpritesInScene() yourself! if(currentScene->sprites().size() != + // spriteManager.getSpriteSize()) { + // updateSpritesInScene(); + // } + + // VSync disabled (you should handle it externally) + // vsync(); + spriteManager.render(); + } + + void updateSpritesInScene(); + void delay(int times) { + for (int i = 0; i < times; i++) { + } + } +}; + +#endif // GBA_SPRITE_ENGINE_GBAENGINE_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gbavector.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gbavector.h new file mode 100644 index 00000000..a0615d44 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/gbavector.h @@ -0,0 +1,36 @@ +// +// Created by Wouter Groeneveld on 14/12/18. +// + +#ifndef GBA_SPRITE_ENGINE_PROJECT_MATH_H +#define GBA_SPRITE_ENGINE_PROJECT_MATH_H + +#pragma GCC system_header + +#include + +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include +#else +#include +#endif + +#include + +class GBAVector { + private: + VECTOR v; + + public: + GBAVector() : v({}) {} + GBAVector(VECTOR v) : v(v) {} + + std::deque bresenhamLineTo(VECTOR dest); + VECTOR rotateAsCenter(VECTOR point, uint angle); + + std::string to_string() { + return "(" + std::to_string(v.x) + "," + std::to_string(v.y) + ")"; + } +}; + +#endif // GBA_SPRITE_ENGINE_PROJECT_MATH_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/palette/combined_palette.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/palette/combined_palette.h new file mode 100644 index 00000000..721a7351 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/palette/combined_palette.h @@ -0,0 +1,31 @@ +// +// Created by Wouter Groeneveld on 05/08/18. +// + +#ifndef GBA_SPRITE_ENGINE_COMBINED_PALETTE_H +#define GBA_SPRITE_ENGINE_COMBINED_PALETTE_H + +#pragma GCC system_header + +class PaletteManager; + +class CombinedPalette { + private: + // WHY use references here? lifetimes not bound, not owned by CombinedPalette + PaletteManager& palette1; + PaletteManager& palette2; + + void changeBrightness(PaletteManager& palette, + int bank, + int index, + int intensity); + + public: + CombinedPalette(PaletteManager& one, PaletteManager& two) + : palette1(one), palette2(two) {} + CombinedPalette(const CombinedPalette& other) = delete; + + void changeBrightness(int intensity); +}; + +#endif // GBA_SPRITE_ENGINE_COMBINED_PALETTE_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/palette/palette_manager.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/palette/palette_manager.h new file mode 100644 index 00000000..407f3e67 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/palette/palette_manager.h @@ -0,0 +1,79 @@ +// +// Created by Wouter Groeneveld on 27/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_PALETTE_MANAGER_H +#define GBA_SPRITE_ENGINE_PALETTE_MANAGER_H + +#pragma GCC system_header + +#include +#include + +#include "combined_palette.h" + +#define PALETTE_BANK_SIZE 16 +#define PALETTE_MAX_SIZE 256 + +int getBits(int number, int k, int p); + +class CombinedPalette; + +class PaletteManager { + protected: + const COLOR* data; + int size; + + virtual void* paletteAddress() = 0; + virtual PALBANK* paletteBank() = 0; + + public: + PaletteManager(); + PaletteManager(const COLOR paletteData[]) + : PaletteManager(paletteData, PALETTE_MAX_SIZE) {} + PaletteManager(const COLOR paletteData[], int size); + + CombinedPalette* operator+(const PaletteManager& other); + + void persist(); + void persistToBank(int bank); + COLOR change(int bank, int index, COLOR newColor); + COLOR get(int bank, int index) { return paletteBank()[bank][index]; } + void changeBrightness(int intensity); + + static COLOR modify(COLOR color, int intensity); + static COLOR color(u32 r, u32 g, u32 b); + static u32 red(COLOR r); + static u32 green(COLOR r); + static u32 blue(COLOR r); +}; + +class BackgroundPaletteManager : public PaletteManager { + protected: + void* paletteAddress() override { return pal_bg_mem; } + + PALBANK* paletteBank() override { return pal_bg_bank; } + + public: + BackgroundPaletteManager() : PaletteManager() {} + BackgroundPaletteManager(const COLOR paletteData[]) + : PaletteManager(paletteData) {} + BackgroundPaletteManager(const COLOR paletteData[], int size) + : PaletteManager(paletteData, size) {} +}; + +class ForegroundPaletteManager : public PaletteManager { + protected: + void* paletteAddress() override { return pal_obj_mem; } + + PALBANK* paletteBank() override { return pal_obj_bank; } + + public: + ForegroundPaletteManager() : PaletteManager() {} + ForegroundPaletteManager(const COLOR paletteData[]) + : PaletteManager(paletteData) {} + ForegroundPaletteManager(const COLOR paletteData[], int size) + : PaletteManager(paletteData, size) {} +}; + +#endif // GBA_SPRITE_ENGINE_PALETTE_MANAGER_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/scene.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/scene.h new file mode 100644 index 00000000..b0855bf6 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/scene.h @@ -0,0 +1,53 @@ +// +// Created by Wouter Groeneveld on 28/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_SCRENE_H +#define GBA_SPRITE_ENGINE_SCRENE_H + +#pragma GCC system_header + +#include +#include +#include +#include +#include + +class GBAEngine; + +class Scene { + protected: + std::unique_ptr foregroundPalette; + std::unique_ptr backgroundPalette; + std::shared_ptr engine; + + void addSprite(Sprite* sprite); + + public: + ForegroundPaletteManager* getForegroundPalette() { + return foregroundPalette.get(); + } + BackgroundPaletteManager* getBackgroundPalette() { + return backgroundPalette.get(); + } + + // WHY raw pointers? they're unwrapped unique_ptrs managed by the scene + // implementation - will be cleaned up in engine + virtual std::vector sprites() = 0; + virtual std::vector backgrounds() = 0; + + virtual void load() = 0; + virtual void tick(u16 keys) = 0; + + Scene(std::shared_ptr engine) + : engine(engine), + foregroundPalette(std::unique_ptr( + new ForegroundPaletteManager())), + backgroundPalette(std::unique_ptr( + new BackgroundPaletteManager())) {} + virtual ~Scene() { + // scenes should manage their own resources - use std::unique_ptr + } +}; + +#endif // GBA_SPRITE_ENGINE_SCRENE_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sound_control.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sound_control.h new file mode 100644 index 00000000..93836ef9 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sound_control.h @@ -0,0 +1,74 @@ +// +// Created by Wouter Groeneveld on 07/08/18. +// + +#ifndef GBA_SPRITE_ENGINE_SOUND_H +#define GBA_SPRITE_ENGINE_SOUND_H + +#pragma GCC system_header + +#include +#include +#include +#include + +#define CLOCK 16777216 +#define CYCLES_PER_BLANK 280806 +#define OVERFLOW_16_BIT_VALUE 65536 +#define DISPLAY_INTERRUPT_VBLANK_ENABLE 0x08 +#define INTERRUPT_VBLANK 0x1 +#define DMA_SYNC_TO_TIMER 0x30000000 + +#define IRQ_CALLBACK ((volatile unsigned int*)0x3007FFC) + +enum SoundChannel { ChannelA, ChannelB }; + +class SoundControl { + private: + vu32* DMAControl; // ex. ®_DMA1CNT + vu32* DMASourceAddress; // ex. ®_DMA1SAD + vu32* DMADestinationAddress; // ex. ®_DMA1DAD + vu32* FiFoBuffer; // ex. ®_FIFOA + u16 controlFlags; + u32 vblanksRemaning; // updated each vblank, counts down to 0 + u32 vblanksTotal; // calculated once when enqueueing + + const void* data; + + public: + SoundControl(vu32* dma, vu32* src, vu32* dest, vu32* fifo, u16 flags) + : DMAControl(dma), + DMASourceAddress(src), + DMADestinationAddress(dest), + FiFoBuffer(fifo), + controlFlags(flags), + vblanksRemaning(0), + vblanksTotal(0) {} + + u16 getControlFlags() { return controlFlags; } + u32 getVBlanksRemaning() { return vblanksRemaning; } + void reset(); + void step() { vblanksRemaning--; } + bool done() { return vblanksRemaning <= 0; } + u32 getVBlanksTotal() { return vblanksTotal; } + + void disable() { + *(DMAControl) = 0; + vblanksRemaning = 0; + REG_SNDDSCNT &= ~(controlFlags); + }; + void enable() { + *DMAControl = + DMA_DST_FIXED | DMA_REPEAT | DMA_32 | DMA_SYNC_TO_TIMER | DMA_ENABLE; + }; + void accept(const void* data, int totalSamples, int ticksPerSample); + + static std::unique_ptr channelAControl(); + static std::unique_ptr channelBControl(); + + static std::unique_ptr soundControl(SoundChannel channel) { + return channel == ChannelA ? channelAControl() : channelBControl(); + }; +}; + +#endif // GBA_SPRITE_ENGINE_SOUND_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/affine_sprite.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/affine_sprite.h new file mode 100644 index 00000000..b4935f50 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/affine_sprite.h @@ -0,0 +1,41 @@ +// +// Created by Wouter Groeneveld on 28/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_AFFINE_SPRITE_H +#define GBA_SPRITE_ENGINE_AFFINE_SPRITE_H + +#pragma GCC system_header + +#include "sprite.h" + +class SpriteManager; + +class AffineSprite : public Sprite { + private: + int affIndex; + std::unique_ptr affine; + void setTransformationMatrix(OBJ_AFFINE* matrix); + + void rebuildOamAttr1ForAffineIndex(); + + protected: + void buildOam(int tileIndex) override; + void syncOam() override; + + public: + void setAffineIndex(int index) { this->affIndex = index; } + void identity(); + void rotate(u16 alpha); + explicit AffineSprite(const AffineSprite& other); + explicit AffineSprite(const void* imgData, + int imgSize, + int xC, + int yC, + SpriteSize spriteSize); + OBJ_AFFINE* getMatrix() { return affine.get(); } + + friend class SpriteManager; +}; + +#endif // GBA_SPRITE_ENGINE_AFFINE_SPRITE_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/sprite.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/sprite.h new file mode 100644 index 00000000..9a41be6d --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/sprite.h @@ -0,0 +1,330 @@ +// +// Created by Wouter Groeneveld on 26/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_SPRITE_H +#define GBA_SPRITE_ENGINE_SPRITE_H + +#pragma GCC system_header + +// inline-functions--- +#include +#include +#define GBA_SCREEN_WIDTH 240 +#define GBA_SCREEN_HEIGHT 160 +// ------------------- + +#include + +#include +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include +#else +#include +#endif +#include + +#define COLOR_MODE_16 0 +#define COLOR_MODE_256 1 +#define GFX_MODE 0 +#define MOSAIC_MODE 1 +#define AFFINE_FLAG_NONE_SET_YET 0 +#define HORIZONTAL_FLIP_FLAG 0 +#define VERTICAL_FLIP_FLAG 0 + +#define FLIP_VERTICAL_CLEAR 0xdfff +#define FLIP_HORIZONTAL_CLEAR 0xefff +#define OAM_TILE_OFFSET_CLEAR 0xfc00 +#define OAM_TILE_OFFSET_NEW 0x03ff + +enum SpriteSize { + SIZE_8_8, + SIZE_16_16, + SIZE_32_32, + SIZE_64_64, + SIZE_16_8, + SIZE_32_8, + SIZE_32_16, + SIZE_64_32, + SIZE_8_16, + SIZE_8_32, + SIZE_16_32, + SIZE_32_64 +}; + +class SpriteManager; + +class Sprite { + private: + inline void updateAnimation(); + inline void syncPosition(); + + protected: + const void* data; + int x, y; + u8 animation_offset; + u32 priority, w, h, size_bits, shape_bits; + u32 imageSize, tileIndex; + SpriteSize spriteSize; + u8 animationDelay, numberOfFrames, beginFrame, currentFrame, previousFrame, + animationCounter; + bool animating; + + inline void syncAnimation(); + inline void syncOam(); + inline void buildOam(int tileIndex); + inline void setAttributesBasedOnSize(SpriteSize size); + + public: + OBJ_ATTR oam; + bool enabled = true; + explicit Sprite(const Sprite& other); + explicit Sprite(const void* imageData, + int imageSize, + int x, + int y, + SpriteSize size); + virtual ~Sprite() {} + + inline const void* getData() { return data; } + inline void setData(void* newData) { data = newData; } + inline u32 getImageSize() { return imageSize; } + inline void setImageSize(u32 size) { imageSize = size; } + + inline void setPriority(u32 priority) { this->priority = priority; } + inline void makeAnimated(int beginFrame, + int numberOfFrames, + int animationDelay); + inline void setBeginFrame(int frame) { this->beginFrame = frame; } + inline void animateToFrame(int frame) { this->currentFrame = frame; } + inline void animate() { this->animating = true; } + inline void stopAnimating() { this->animating = false; } + inline void update(); + + inline void moveTo(int x, int y); + inline void moveTo(VECTOR location); + inline bool collidesWith(Sprite& s2); + + inline void flipVertically(bool flip); + inline void flipHorizontally(bool flip); + + inline u32 getTileIndex() { return tileIndex; } + inline VECTOR getPos() { return {x, y}; } + inline GBAVector getPosAsVector() { return GBAVector(getPos()); } + inline VECTOR getCenter() { return {x + w / 2, y + h / 2}; } + inline int getX() { return x; } + inline int getY() { return y; } + inline u32 getWidth() { return w; } + inline u32 getHeight() { return h; } + inline u32 getAnimationDelay() { return animationDelay; } + inline u32 getNumberOfFrames() { return numberOfFrames; } + inline u32 getCurrentFrame() { return currentFrame; } + inline bool isAnimating() { return animating; }; + inline bool isOffScreen(); + + friend class SpriteManager; +}; + +inline void Sprite::moveTo(VECTOR location) { + moveTo(location.x, location.y); +} + +inline void Sprite::moveTo(int x, int y) { + if (x == this->x && y == this->y) + return; + + this->x = x; + this->y = y; +} + +inline bool Sprite::isOffScreen() { + return x < 0 || x > GBA_SCREEN_WIDTH || y < 0 || y > GBA_SCREEN_HEIGHT; +} + +inline void Sprite::flipHorizontally(bool flip) { + if (flip) { + oam.attr1 |= ATTR1_HFLIP; + } else { + oam.attr1 &= FLIP_HORIZONTAL_CLEAR; + } +} + +inline void Sprite::flipVertically(bool flip) { + if (flip) { + oam.attr1 |= ATTR1_VFLIP; + } else { + oam.attr1 &= FLIP_VERTICAL_CLEAR; + } +} + +inline void Sprite::makeAnimated(int beginFrame, + int numberOfFrames, + int animationDelay) { + previousFrame = -1; + setBeginFrame(beginFrame); + animateToFrame(beginFrame); + this->numberOfFrames = numberOfFrames; + this->animationDelay = animationDelay; + animate(); +} + +inline void Sprite::syncPosition() { + oam.attr0 = (oam.attr0 & ~ATTR0_Y_MASK) | (y & ATTR0_Y_MASK); + oam.attr1 = (oam.attr1 & ~ATTR1_X_MASK) | (x & ATTR1_X_MASK); + oam.attr2 = + (oam.attr2 & ~ATTR2_PRIO_MASK) | (ATTR2_PRIO(priority) & ATTR2_PRIO_MASK); +} + +inline void Sprite::syncAnimation() { + if (previousFrame == currentFrame) + return; + + int newTileIndex = + this->tileIndex + (currentFrame * (this->animation_offset * 2)); + oam.attr2 &= OAM_TILE_OFFSET_CLEAR; + oam.attr2 |= (newTileIndex & OAM_TILE_OFFSET_NEW); + + previousFrame = currentFrame; +} + +inline void Sprite::syncOam() { + syncPosition(); + syncAnimation(); +} + +inline void Sprite::updateAnimation() { + if (!animating) + return; + + animationCounter++; + if (animationCounter > animationDelay) { + currentFrame++; + if (currentFrame > (numberOfFrames - 1) + beginFrame) { + currentFrame = beginFrame; + } + if (currentFrame < beginFrame) { + currentFrame = beginFrame; + } + + animationCounter = 0; + } +} + +inline void Sprite::update() { + updateAnimation(); + syncOam(); +} + +inline void Sprite::setAttributesBasedOnSize(SpriteSize size) { + switch (size) { + case SIZE_8_8: + size_bits = 0; + shape_bits = 0; + w = 8; + h = 8; + animation_offset = 1; + break; + case SIZE_16_16: + size_bits = 1; + shape_bits = 0; + w = 16; + h = 16; + animation_offset = 4; + break; + case SIZE_32_32: + size_bits = 2; + shape_bits = 0; + w = 32; + h = 32; + animation_offset = 16; + break; + case SIZE_64_64: + size_bits = 3; + shape_bits = 0; + w = 64; + h = 64; + animation_offset = 64; + break; + case SIZE_16_8: + size_bits = 0; + shape_bits = 1; + w = 16; + h = 8; + animation_offset = 2; + break; + case SIZE_32_8: + size_bits = 1; + shape_bits = 1; + w = 32; + h = 8; + animation_offset = 4; + break; + case SIZE_32_16: + size_bits = 2; + shape_bits = 1; + w = 32; + h = 16; + animation_offset = 8; + break; + case SIZE_64_32: + size_bits = 3; + shape_bits = 1; + w = 64; + h = 32; + animation_offset = 32; + break; + case SIZE_8_16: + size_bits = 0; + shape_bits = 2; + w = 8; + h = 16; + animation_offset = 2; + break; + case SIZE_8_32: + size_bits = 1; + shape_bits = 2; + w = 8; + h = 32; + animation_offset = 4; + break; + case SIZE_16_32: + size_bits = 2; + shape_bits = 2; + w = 16; + h = 32; + animation_offset = 8; + break; + case SIZE_32_64: + size_bits = 3; + shape_bits = 2; + w = 32; + h = 64; + animation_offset = 32; + break; + } +} + +inline bool Sprite::collidesWith(Sprite& s2) { + const Sprite& s1 = *this; + + if (s1.x < s2.x + s2.w && s1.x + s1.w > s2.x && s1.y < s2.y + s2.h && + s1.h + s1.y > s2.y) { + return true; + } + return false; +} + +inline void Sprite::buildOam(int tileIndex) { + this->tileIndex = tileIndex; + + oam.attr0 = ATTR0_Y(this->y & 0x00FF) | ATTR0_MODE(0) | (GFX_MODE << 10) | + (MOSAIC_MODE << 12) | (COLOR_MODE_256 << 13) | + (this->shape_bits << 14); + oam.attr1 = (this->x & 0x01FF) | (AFFINE_FLAG_NONE_SET_YET << 9) | + (HORIZONTAL_FLIP_FLAG << 12) | (VERTICAL_FLIP_FLAG << 13) | + (this->size_bits << 14); + + oam.attr2 = ATTR2_ID(tileIndex) | ATTR2_PRIO(priority) | ATTR2_PALBANK(0); +} + +#endif // GBA_SPRITE_ENGINE_SPRITE_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/sprite_builder.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/sprite_builder.h new file mode 100644 index 00000000..6d406c57 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/sprite_builder.h @@ -0,0 +1,100 @@ +// +// Created by Wouter Groeneveld on 28/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_SPRITE_BUILDER_H +#define GBA_SPRITE_ENGINE_SPRITE_BUILDER_H + +#pragma GCC system_header + +#include "sprite.h" + +template +class SpriteBuilder { + private: + u32 imageSize; + const void* imageData; + u32 x, y; + u32 beginFrame, numberOfFrames, animationDelay; + SpriteSize size; + + void setProperties(T* sprite); + void reset() { + imageSize = x = y = beginFrame = numberOfFrames = animationDelay = 0; + imageData = nullptr; + size = SIZE_16_16; + } + + public: + SpriteBuilder() { reset(); } + SpriteBuilder& withData(const void* imageData, int imageSize) { + this->imageData = imageData; + this->imageSize = imageSize; + return *this; + } + SpriteBuilder& withLocation(u32 x, u32 y) { + this->x = x; + this->y = y; + return *this; + } + SpriteBuilder& withSize(const SpriteSize& size) { + this->size = size; + return *this; + } + SpriteBuilder& withAnimated(int numberOfFrames, int animationDelay) { + this->beginFrame = 0; + this->numberOfFrames = numberOfFrames; + this->animationDelay = animationDelay; + return *this; + } + SpriteBuilder& withAnimated(int beginFrame, + int numberOfFrames, + int animationDelay) { + this->beginFrame = beginFrame; + this->numberOfFrames = numberOfFrames; + this->animationDelay = animationDelay; + return *this; + } + T build(); + std::unique_ptr buildPtr(); + std::unique_ptr buildWithDataOf(const Sprite& other); +}; + +template +std::unique_ptr SpriteBuilder::buildWithDataOf(const Sprite& other) { + auto s = new T(other); + s->moveTo(this->x, this->y); + setProperties(s); + + reset(); + return std::unique_ptr(s); +} + +template +void SpriteBuilder::setProperties(T* s) { + if (this->numberOfFrames > 0) { + s->makeAnimated(this->beginFrame, this->numberOfFrames, + this->animationDelay); + } +} + +template +std::unique_ptr SpriteBuilder::buildPtr() { + auto s = + new T(this->imageData, this->imageSize, this->x, this->y, this->size); + setProperties(s); + + reset(); + return std::unique_ptr(s); +} + +template +T SpriteBuilder::build() { + T s(this->imageData, this->imageSize, this->x, this->y, this->size); + setProperties(&s); + + reset(); + return s; +} + +#endif // GBA_SPRITE_ENGINE_SPRITE_BUILDER_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/sprite_manager.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/sprite_manager.h new file mode 100644 index 00000000..e7d2ae57 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/sprites/sprite_manager.h @@ -0,0 +1,54 @@ +// +// Created by Wouter Groeneveld on 26/07/18. +// + +#ifndef GBA_SPRITE_ENGINE_SPRITE_MANAGER_H +#define GBA_SPRITE_ENGINE_SPRITE_MANAGER_H + +#pragma GCC system_header + +#include + +#include + +#include "sprite.h" + +class SpriteManager { + private: + bool initialized; + std::vector sprites; + + inline void copyOverSpriteOAMToVRAM() { + int i = 0; + + for (auto sprite : this->sprites) { + if (sprite->enabled) { + sprite->update(); + oam_mem[i] = sprite->oam; + } + + i++; + } + } + + void copyOverImageDataToVRAM(Sprite* s); + void copyOverImageDataToVRAM(); + + public: + int getSpriteSize() { return sprites.size(); } + + void hideAll(); + void add(Sprite* sprite); + void set(std::vector sprites); + void persist(); // copies over image and palette data to VRAM, modifies + // sprite OAM indiches + + inline void + render() { // copies over OAM buffer to OAM RAM, called in game loop + // WARNING - This is called every time in the main update loop; keep amount + // of instructions as minimal as possible in here! + copyOverSpriteOAMToVRAM(); + } +}; + +#endif // GBA_SPRITE_ENGINE_SPRITE_MANAGER_H diff --git a/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/timer.h b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/timer.h new file mode 100644 index 00000000..c40d7746 --- /dev/null +++ b/examples/full/lib/libgba-sprite-engine/include/libgba-sprite-engine/timer.h @@ -0,0 +1,43 @@ +// +// Created by Wouter Groeneveld on 06/12/18. +// + +#ifndef GBA_SPRITE_ENGINE_PROJECT_TIMER_H +#define GBA_SPRITE_ENGINE_PROJECT_TIMER_H + +#pragma GCC system_header + +#include + +class Timer { + private: + int microsecs, msecs, secs, minutes, hours; + bool active; + + public: + Timer() : active(false) { reset(); } + + void reset(); + void start(); + void toggle() { + if (isActive()) + stop(); + else + start(); + } + bool isActive() { return active; } + void stop(); + void onvblank(); + + std::string to_string(); + + int getTotalMsecs(); + int getMsecs() { return msecs; } + int getSecs() { return secs; } + int getMinutes() { return minutes; } + int getHours() { return hours; } + + friend std::ostream& operator<<(std::ostream& os, Timer& timer); +}; + +#endif // GBA_SPRITE_ENGINE_PROJECT_TIMER_H diff --git a/examples/full/lib/libgba-sprite-engine/lib/libgba-sprite-engine.a b/examples/full/lib/libgba-sprite-engine/lib/libgba-sprite-engine.a new file mode 100644 index 00000000..113eb2d7 Binary files /dev/null and b/examples/full/lib/libgba-sprite-engine/lib/libgba-sprite-engine.a differ diff --git a/examples/full/src/main.cpp b/examples/full/src/main.cpp new file mode 100644 index 00000000..c58acae6 --- /dev/null +++ b/examples/full/src/main.cpp @@ -0,0 +1,84 @@ +#include + +#include "../lib/LinkConnection.h" +#include "../lib/libgba-sprite-engine/include/libgba-sprite-engine/gba_engine.h" +#include "scenes/TestScene.h" +#include "utils/SceneUtils.h" + +void setUpInterrupts(); +void printTutorial(); +static std::shared_ptr engine{new GBAEngine()}; +static std::unique_ptr testScene{new TestScene(engine)}; +LinkConnection* linkConnection = new LinkConnection(false); + +int main() { + setUpInterrupts(); + + engine->setScene(testScene.get()); + + printTutorial(); + + while (true) { + u16 keys = ~REG_KEYS & KEY_ANY; + + if ((keys & KEY_DOWN) && linkConnection->isActive()) { + linkConnection->deactivate(); + DEBULOG("! stopped"); + } + // enable and disable + if ((keys & KEY_START) && !linkConnection->isActive()) { + linkConnection->activate(); + DEBULOG("! started"); + } + + // log player count at important REG_SIOCNT bits + TextStream::instance().setText( + "P" + asStr(linkConnection->linkState->currentPlayerId) + "/" + + asStr(linkConnection->linkState->playerCount) + "-R" + + asStr(isBitHigh(REG_SIOCNT, LINK_BIT_READY)) + "-S" + + asStr(isBitHigh(REG_SIOCNT, LINK_BIT_START)) + "-E" + + asStr(isBitHigh(REG_SIOCNT, LINK_BIT_ERROR)) + "-I" + + asStr(linkConnection->linkState->_IRQFlag), + 0, 11); + + engine->update(); + + VBlankIntrWait(); + } + + return 0; +} + +inline void ISR_reset() { + RegisterRamReset(RESET_REG | RESET_VRAM); + SoftReset(); +} + +inline void setUpInterrupts() { + irq_init(NULL); + + // VBlank + irq_add(II_VBLANK, LINK_ISR_VBLANK); + + // Link connection + irq_add(II_SERIAL, LINK_ISR_SERIAL); + + // A+B+START+SELECT + REG_KEYCNT = 0b1100000000001111; + irq_add(II_KEYPAD, ISR_reset); +} + +void printTutorial() { + DEBULOG("gba-link-connection demo"); + DEBULOG(""); + DEBULOG("START: turn on connection"); + DEBULOG("(on connection, p1 sends 999)"); + DEBULOG(""); + DEBULOG("A: send 555 once per frame"); + DEBULOG("B: send counter once"); + DEBULOG("L: send 1, then 2"); + DEBULOG("R: send 43981, then 257"); + DEBULOG("SELECT: force lag (9k lines)"); + DEBULOG("DOWN: turn off connection"); + DEBULOG(""); +} diff --git a/examples/full/src/scenes/TestScene.cpp b/examples/full/src/scenes/TestScene.cpp new file mode 100644 index 00000000..340f6c26 --- /dev/null +++ b/examples/full/src/scenes/TestScene.cpp @@ -0,0 +1,99 @@ +#include "TestScene.h" + +#include + +#include "../lib/LinkConnection.h" +#include "utils/InputHandler.h" +#include "utils/SceneUtils.h" + +TestScene::TestScene(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 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()); + +inline void send(u16 data) { + DEBULOG("-> " + asStr(data)); + linkConnection->send(data); +} + +std::vector TestScene::backgrounds() { + return {}; +} + +std::vector TestScene::sprites() { + std::vector sprites; + return sprites; +} + +void TestScene::load() { + SCENE_init(); + BACKGROUND_enable(true, false, false, false); +} + +void TestScene::tick(u16 keys) { + if (engine->isTransitioning()) + return; + + // check keys + aHandler->setIsPressed(keys & KEY_A); + bHandler->setIsPressed(keys & KEY_B); + lHandler->setIsPressed(keys & KEY_L); + rHandler->setIsPressed(keys & KEY_R); + selectHandler->setIsPressed(keys & KEY_SELECT); + + // log events + auto linkState = linkConnection->linkState.get(); + if (!isConnected && linkState->isConnected()) { + isConnected = true; + initialized = false; + DEBULOG("! connected (" + asStr(linkState->playerCount) + " players)"); + } + if (isConnected && !linkState->isConnected()) { + isConnected = false; + DEBULOG("! disconnected"); + } + if (selectHandler->hasBeenPressedNow()) { + DEBULOG("! lagging..."); + SCENE_wait(9000); + } + + // get data + u16 value = LINK_NO_DATA; + if (!initialized && linkConnection->linkState->currentPlayerId == 1) { + initialized = true; + value = 999; + } + if (aHandler->getIsPressed()) + value = 555; + if (bHandler->hasBeenPressedNow()) { + counter++; + value = counter; + } + + // send data + if (lHandler->hasBeenPressedNow()) { + send(1); + send(2); + } else if (rHandler->hasBeenPressedNow()) { + send(43981); + send(257); + } else if (value != LINK_NO_DATA) + send(value); + + // process received data + if (linkState->isConnected()) + for (u32 i = 0; i < linkState->playerCount; i++) + while (linkState->hasMessage(i)) { + u16 message = linkState->readMessage(i); + if (i != linkState->currentPlayerId) + DEBULOG("<-p" + asStr(i) + ": " + asStr(message)); + } +} diff --git a/examples/full/src/scenes/TestScene.h b/examples/full/src/scenes/TestScene.h new file mode 100644 index 00000000..90b4067f --- /dev/null +++ b/examples/full/src/scenes/TestScene.h @@ -0,0 +1,30 @@ +#ifndef TEST_SCENE_H +#define TEST_SCENE_H + +#include +#include +#include +#include + +#include + +class TestScene : public Scene { + public: + u16 data = 0; + TestScene(std::shared_ptr engine); + + std::vector backgrounds() override; + std::vector sprites() override; + + void load() override; + void tick(u16 keys) override; + + private: + u32 counter = 0; + bool isConnected = false; + bool initialized = false; + + void log(std::string text); +}; + +#endif // TEST_SCENE_H diff --git a/examples/full/src/utils/BackgroundUtils.h b/examples/full/src/utils/BackgroundUtils.h new file mode 100644 index 00000000..5435f6b3 --- /dev/null +++ b/examples/full/src/utils/BackgroundUtils.h @@ -0,0 +1,14 @@ +#ifndef BACKGROUND_UTILS_H +#define BACKGROUND_UTILS_H + +#include +#include + +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; +} + +#endif // BACKGROUND_UTILS_H diff --git a/examples/full/src/utils/InputHandler.h b/examples/full/src/utils/InputHandler.h new file mode 100644 index 00000000..c820e179 --- /dev/null +++ b/examples/full/src/utils/InputHandler.h @@ -0,0 +1,34 @@ +#ifndef INPUT_HANDLER_H +#define INPUT_HANDLER_H + +#include + +class InputHandler { + public: + InputHandler() { this->isPressed = false; } + + 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->isPressed && isPressed; + bool isNewReleaseEvent = this->isPressed && !isPressed; + this->isPressed = isPressed; + + this->isNewPressEvent = isNewPressEvent; + this->isNewReleaseEvent = isNewReleaseEvent; + } + + protected: + bool isPressed = false; + bool isNewPressEvent = false; + bool isNewReleaseEvent; + bool handledFlag = false; +}; + +#endif // INPUT_HANDLER_H diff --git a/examples/full/src/utils/SceneUtils.cpp b/examples/full/src/utils/SceneUtils.cpp new file mode 100644 index 00000000..05d39b81 --- /dev/null +++ b/examples/full/src/utils/SceneUtils.cpp @@ -0,0 +1,14 @@ +#include "SceneUtils.h" + +#include + +int DEBULOG_LINE = 2; + +void DEBULOG(std::string string) { + TextStream::instance().setText(string, DEBULOG_LINE, -3); + DEBULOG_LINE++; + for (u32 i = DEBULOG_LINE; i < 20; i++) + TextStream::instance().setText(" ", i, -3); + if (DEBULOG_LINE >= 20) + DEBULOG_LINE = 2; +} diff --git a/examples/full/src/utils/SceneUtils.h b/examples/full/src/utils/SceneUtils.h new file mode 100644 index 00000000..6f93dc0a --- /dev/null +++ b/examples/full/src/utils/SceneUtils.h @@ -0,0 +1,50 @@ +#ifndef SCENE_UTILS_H +#define SCENE_UTILS_H + +#include + +#include + +#include "BackgroundUtils.h" +#include "SpriteUtils.h" + +const u32 TEXT_MIDDLE_COL = 12; + +extern int DEBULOG_LINE; +void DEBULOG(std::string string); + +inline std::string asStr(u16 data) { + return std::to_string(data); +} + +inline bool isBitHigh(u16 data, u8 bit) { + return (data >> bit) & 1; +} + +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 lines = 0; + u32 vCount = REG_VCOUNT; + + while (lines < verticalLines) { + if (REG_VCOUNT != vCount) { + lines++; + vCount = REG_VCOUNT; + } + }; +} + +#endif // SCENE_UTILS_H diff --git a/examples/full/src/utils/SpriteUtils.h b/examples/full/src/utils/SpriteUtils.h new file mode 100644 index 00000000..69da8a81 --- /dev/null +++ b/examples/full/src/utils/SpriteUtils.h @@ -0,0 +1,11 @@ +#ifndef SPRITE_UTILS_H +#define SPRITE_UTILS_H + +#include +#include + +inline void SPRITE_disable() { + REG_DISPCNT = REG_DISPCNT & ~DCNT_OBJ; +} + +#endif // SPRITE_UTILS_H diff --git a/lib/LinkConnection.h b/lib/LinkConnection.h new file mode 100644 index 00000000..d1dd3722 --- /dev/null +++ b/lib/LinkConnection.h @@ -0,0 +1,274 @@ +#ifndef LINK_CONNECTION_H +#define LINK_CONNECTION_H + +#include +#include +#include + +#include +#include + +#define LINK_MAX_PLAYERS 4 +#define LINK_DISCONNECTED 0xFFFF +#define LINK_NO_DATA 0x0 +#define LINK_TRANSFER_VCOUNT_WAIT 2 +#define LINK_DEFAULT_TIMEOUT 3 +#define LINK_DEFAULT_BUFFER_SIZE 60 +#define LINK_BIT_SLAVE 2 +#define LINK_BIT_READY 3 +#define LINK_BITS_PLAYER_ID 4 +#define LINK_BIT_ERROR 6 +#define LINK_BIT_START 7 +#define LINK_BIT_MULTIPLAYER 13 +#define LINK_BIT_IRQ 14 +#define LINK_BIT_GENERAL_PURPOSE_LOW 14 +#define LINK_BIT_GENERAL_PURPOSE_HIGH 15 +#define LINK_SET_HIGH(REG, BIT) REG |= 1 << BIT +#define LINK_SET_LOW(REG, BIT) REG &= ~(1 << BIT) + +// A Link Cable connection for Multi-player mode. + +// Usage: +// - 1) Include this header in your main.cpp file and add: +// LinkConnection* linkConnection = new LinkConnection(); +// - 2) Add the interrupt service routines: +// irq_add(II_VBLANK, LINK_ISR_VBLANK); +// irq_add(II_SERIAL, LINK_ISR_SERIAL); +// - 3) Send/read messages by using: +// linkConnection->send(...); +// linkConnection->linkState + +// `data` restrictions: +// 0xFFFF and 0x0 are reserved values, so don't use them +// (they mean 'disconnected' and 'no data' respectively) + +void LINK_ISR_VBLANK(); +void LINK_ISR_SERIAL(); +u16 LINK_QUEUE_POP(std::queue& q); +void LINK_QUEUE_CLEAR(std::queue& q); + +struct LinkState { + u8 playerCount; + u8 currentPlayerId; + std::queue _incomingMessages[LINK_MAX_PLAYERS]; + std::queue _outgoingMessages; + bool _IRQFlag; + u32 _IRQTimeout; + + bool isConnected() { + return playerCount > 1 && currentPlayerId < playerCount; + } + + bool hasMessage(u8 playerId) { + if (playerId >= playerCount) + return false; + + return !_incomingMessages[playerId].empty(); + } + + u16 readMessage(u8 playerId) { + return LINK_QUEUE_POP(_incomingMessages[playerId]); + } +}; + +class LinkConnection { + public: + enum BaudRate { + BAUD_RATE_0, // 9600 bps + BAUD_RATE_1, // 38400 bps + BAUD_RATE_2, // 57600 bps + BAUD_RATE_3 // 115200 bps + }; + std::unique_ptr linkState{new LinkState()}; + + explicit LinkConnection(bool startNow = true, + BaudRate baudRate = BAUD_RATE_3, + u32 timeout = LINK_DEFAULT_TIMEOUT, + u32 bufferSize = LINK_DEFAULT_BUFFER_SIZE) { + this->baudRate = baudRate; + this->timeout = timeout; + this->bufferSize = bufferSize; + + if (startNow) + activate(); + else + stop(); + } + + bool isActive() { return isEnabled; } + + void activate() { + isEnabled = true; + reset(); + } + + void deactivate() { + isEnabled = false; + resetState(); + stop(); + } + + void send(u16 data) { + if (data == LINK_DISCONNECTED || data == LINK_NO_DATA) + return; + + push(linkState->_outgoingMessages, data); + } + + bool isReady() { + return isBitHigh(LINK_BIT_READY) && !isBitHigh(LINK_BIT_ERROR); + } + + void _onVBlank() { + if (!isEnabled || resetIfNeeded()) + return; + + if (!linkState->_IRQFlag) { + linkState->_IRQTimeout++; + + if (linkState->_IRQTimeout >= timeout) + reset(); + else if (isMaster()) + transfer(LINK_NO_DATA, true); + } + + linkState->_IRQFlag = false; + } + + void _onSerial() { + if (!isEnabled || resetIfNeeded()) + return; + + linkState->_IRQFlag = true; + linkState->_IRQTimeout = 0; + + linkState->playerCount = 0; + linkState->currentPlayerId = + (REG_SIOCNT & (0b11 << LINK_BITS_PLAYER_ID)) >> LINK_BITS_PLAYER_ID; + + for (u32 i = 0; i < LINK_MAX_PLAYERS; i++) { + u16 data = REG_SIOMULTI[i]; + + if (data != LINK_DISCONNECTED) { + if (data != LINK_NO_DATA) + push(linkState->_incomingMessages[i], data); + linkState->playerCount++; + } else + LINK_QUEUE_CLEAR(linkState->_incomingMessages[i]); + } + + if (linkState->isConnected()) + sendPendingData(); + } + + private: + BaudRate baudRate; + u32 timeout; + u32 bufferSize; + bool isEnabled = false; + + void sendPendingData() { + transfer(LINK_QUEUE_POP(linkState->_outgoingMessages)); + } + + void transfer(u16 data, bool force = false) { + bool shouldNotify = isMaster() && (data != LINK_NO_DATA || force); + + if (shouldNotify) + setBitLow(LINK_BIT_START); + + wait(LINK_TRANSFER_VCOUNT_WAIT); + REG_SIOMLT_SEND = data; + + if (shouldNotify) + setBitHigh(LINK_BIT_START); + } + + bool resetIfNeeded() { + if (!isReady()) { + reset(); + return true; + } + + return false; + } + + void reset() { + resetState(); + stop(); + start(); + } + + void resetState() { + linkState->playerCount = 0; + linkState->currentPlayerId = 0; + for (u32 i = 0; i < LINK_MAX_PLAYERS; i++) + LINK_QUEUE_CLEAR(linkState->_incomingMessages[i]); + LINK_QUEUE_CLEAR(linkState->_outgoingMessages); + linkState->_IRQFlag = false; + linkState->_IRQTimeout = 0; + } + + void stop() { + LINK_SET_LOW(REG_RCNT, LINK_BIT_GENERAL_PURPOSE_LOW); + LINK_SET_HIGH(REG_RCNT, LINK_BIT_GENERAL_PURPOSE_HIGH); + } + + void start() { + LINK_SET_LOW(REG_RCNT, LINK_BIT_GENERAL_PURPOSE_HIGH); + REG_SIOCNT = baudRate; + REG_SIOMLT_SEND = 0; + setBitHigh(LINK_BIT_MULTIPLAYER); + setBitHigh(LINK_BIT_IRQ); + } + + void push(std::queue& q, u16 value) { + if (q.size() >= bufferSize) + LINK_QUEUE_POP(q); + + q.push(value); + } + + void wait(u32 verticalLines) { + u32 lines = 0; + u32 vCount = REG_VCOUNT; + + while (lines < verticalLines) { + if (REG_VCOUNT != vCount) { + lines++; + vCount = REG_VCOUNT; + } + }; + } + + bool isMaster() { return !isBitHigh(LINK_BIT_SLAVE); } + bool isBitHigh(u8 bit) { return (REG_SIOCNT >> bit) & 1; } + void setBitHigh(u8 bit) { LINK_SET_HIGH(REG_SIOCNT, bit); } + void setBitLow(u8 bit) { LINK_SET_LOW(REG_SIOCNT, bit); } +}; + +extern LinkConnection* linkConnection; + +inline void LINK_ISR_VBLANK() { + linkConnection->_onVBlank(); +} + +inline void LINK_ISR_SERIAL() { + linkConnection->_onSerial(); +} + +inline u16 LINK_QUEUE_POP(std::queue& q) { + if (q.empty()) + return LINK_NO_DATA; + + u16 value = q.front(); + q.pop(); + return value; +} + +inline void LINK_QUEUE_CLEAR(std::queue& q) { + while (!q.empty()) + LINK_QUEUE_POP(q); +} + +#endif // LINK_CONNECTION_H diff --git a/src/lib/LinkConnection.h b/src/lib/LinkConnection.h deleted file mode 100644 index 5d72bde7..00000000 --- a/src/lib/LinkConnection.h +++ /dev/null @@ -1,152 +0,0 @@ -#ifndef LINK_CONNECTION_H -#define LINK_CONNECTION_H - -#include -#include - -#define LINK_MAX_PLAYERS 4 -#define LINK_NO_DATA 0xffff -#define LINK_HEART_BIT 0xf -#define LINK_HEART_BIT_UNKNOWN 2 -#define LINK_BIT_SLAVE 2 -#define LINK_BIT_READY 3 -#define LINK_BITS_PLAYER_ID 4 -#define LINK_BIT_ERROR 6 -#define LINK_BIT_START 7 -#define LINK_BIT_MULTIPLAYER 13 -#define LINK_BIT_IRQ 14 - -// A Link Cable connection for Multi-player mode. - -// Usage: -// - 1) Include this header in your main.cpp file and add: -// LinkConnection* linkConnection = new LinkConnection(); -// - 2) Add the interrupt service routine: -// irq_add(II_SERIAL, ISR_serial); -// - 3) Add to your update loop: -// auto linkState = linkConnection->tick(data); -// - 4) Use `linkState` to process updates - -// `data` restrictions: -// - 0xFFFF means 'disconnected', so don't use it -// - Bit 0xF will be ignored: it'll be used as a heartbeat - -void ISR_serial(); -u16 _withHeartBit(u16 data, bool heartBit); -bool _isBitHigh(u16 data, u8 bit); - -struct LinkState { - u8 playerCount; - u8 currentPlayerId; - u16 data[4]; - u8 _heartBits[4]; - u32 _tick; - u32 _lastIRQTick; - - bool isConnected() { return playerCount > 1; } - bool hasData(u8 playerId) { return data[playerId] != LINK_NO_DATA; } - - bool _isOutOfSync() { - return isConnected() && - (_lastIRQTick != _tick || currentPlayerId >= playerCount); - } - void _sync() { _lastIRQTick = _tick; } - void _reset() { - playerCount = 0; - currentPlayerId = 0; - for (u32 i = 0; i < LINK_MAX_PLAYERS; i++) { - data[i] = LINK_NO_DATA; - _heartBits[i] = LINK_HEART_BIT_UNKNOWN; - } - _tick = 0; - _lastIRQTick = 0; - } -}; - -class LinkConnection { - public: - enum BaudRate { - BAUD_RATE_0, // 9600 bps - BAUD_RATE_1, // 38400 bps - BAUD_RATE_2, // 57600 bps - BAUD_RATE_3 // 115200 bps - }; - struct LinkState _linkState; - - explicit LinkConnection(BaudRate baudRate = BAUD_RATE_3) { - _linkState._reset(); - REG_RCNT = 0; - REG_SIOCNT = (u8)baudRate; - setBitHigh(LINK_BIT_MULTIPLAYER); - setBitHigh(LINK_BIT_IRQ); - } - - LinkState tick(u16 data) { - bool shouldForceReset = !isBitHigh(LINK_BIT_READY) || - isBitHigh(LINK_BIT_ERROR) || - _linkState._isOutOfSync(); - - if (shouldForceReset) { - resetCommunicationCircuit(); - _linkState._reset(); - return _linkState; - } - - REG_SIOMLT_SEND = _withHeartBit(data, heartBit); - _linkState._tick++; - heartBit = !heartBit; - - if (!isBitHigh(LINK_BIT_SLAVE)) - setBitHigh(LINK_BIT_START); - - return _linkState; - } - - private: - bool heartBit = 0; - - void resetCommunicationCircuit() { - REG_RCNT = 0xf; - REG_RCNT = 0; - } - - bool isBitHigh(u8 bit) { return _isBitHigh(REG_SIOCNT, bit); } - void setBitHigh(u8 bit) { REG_SIOCNT |= 1 << bit; } -}; - -extern LinkConnection* linkConnection; - -inline void ISR_serial() { - linkConnection->_linkState.playerCount = 0; - linkConnection->_linkState.currentPlayerId = - (REG_SIOCNT & (0b11 << LINK_BITS_PLAYER_ID)) >> LINK_BITS_PLAYER_ID; - - for (u32 i = 0; i < LINK_MAX_PLAYERS; i++) { - auto data = REG_SIOMULTI[i]; - u8 oldHeartBit = linkConnection->_linkState._heartBits[i]; - u8 newHeartBit = _isBitHigh(data, LINK_HEART_BIT); - bool isConnectionAlive = - data != LINK_NO_DATA && - (oldHeartBit == LINK_HEART_BIT_UNKNOWN || oldHeartBit != newHeartBit); - - linkConnection->_linkState.data[i] = - isConnectionAlive ? _withHeartBit(data, 0) : LINK_NO_DATA; - linkConnection->_linkState._heartBits[i] = - isConnectionAlive ? newHeartBit : LINK_HEART_BIT_UNKNOWN; - - if (linkConnection->_linkState.hasData(i)) - linkConnection->_linkState.playerCount++; - } - - linkConnection->_linkState._sync(); -} - -inline bool _isBitHigh(u16 data, u8 bit) { - return (data >> bit) & 1; -} - -inline u16 _withHeartBit(u16 data, bool heartBit) { - return (data & ~(1 << LINK_HEART_BIT)) | (heartBit << LINK_HEART_BIT); -} - -#endif // LINK_CONNECTION_H diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 7c24d20a..00000000 --- a/src/main.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include - -#include - -// (0) Include the header -#include "lib/LinkConnection.h" - -void log(std::string text); - -// (1) Create a LinkConnection instance -LinkConnection* linkConnection = new LinkConnection(); - -void init() { - REG_DISPCNT = DCNT_MODE0 | DCNT_BG0; - tte_init_se_default(0, BG_CBB(0) | BG_SBB(31)); - - irq_init(NULL); - irq_add(II_VBLANK, NULL); - - // (2) Add the interrupt service routine - irq_add(II_SERIAL, ISR_serial); -} - -int main() { - init(); - - while (1) { - // (3) Run the `tick` method in your update loop - u16 dataToBeSent = ~REG_KEYS & KEY_ANY; // (keys state) - auto linkState = linkConnection->tick(dataToBeSent); - - // (4) Process results - std::string output = ""; - if (linkState.isConnected()) { - output += "Players: " + std::to_string(linkState.playerCount) + "\n"; - - for (u32 i = 0; i < linkState.playerCount; i++) - output += "Player " + std::to_string(i) + ": " + - std::to_string(linkState.data[i]) + "\n"; - - output += "Self pID: " + std::to_string(linkState.currentPlayerId); - } else - output += std::string("Waiting..."); - log(output); - - VBlankIntrWait(); - } - - return 0; -} - -void log(std::string text) { - tte_erase_screen(); - tte_write("#{P:0,0}"); - tte_write(text.c_str()); -} \ No newline at end of file