├── .gitignore ├── LICENSE ├── Makefile └── source ├── bios.h ├── main.iwram.c └── si.iwram.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.elf 3 | *.gba 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2016-2021, Extrems' Corner.org 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | # Clear the implicit built in rules 3 | #--------------------------------------------------------------------------------- 4 | .SUFFIXES: 5 | #--------------------------------------------------------------------------------- 6 | ifeq ($(strip $(DEVKITARM)),) 7 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) 8 | endif 9 | 10 | include $(DEVKITARM)/gba_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output, if this ends with _mb a multiboot image is generated 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | #--------------------------------------------------------------------------------- 19 | TARGET := $(shell basename $(CURDIR))_mb 20 | BUILD := build 21 | SOURCES := source 22 | DATA := 23 | GRAPHICS := gfx 24 | INCLUDES := 25 | 26 | #--------------------------------------------------------------------------------- 27 | # options for code generation 28 | #--------------------------------------------------------------------------------- 29 | ARCH := -mthumb -mthumb-interwork 30 | 31 | CFLAGS := -g -Wall -O3\ 32 | -mcpu=arm7tdmi -mtune=arm7tdmi\ 33 | -fomit-frame-pointer\ 34 | -ffast-math \ 35 | $(ARCH) 36 | 37 | CFLAGS += $(INCLUDE) 38 | 39 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 40 | 41 | ASFLAGS := $(ARCH) 42 | LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $@).map 43 | 44 | #--------------------------------------------------------------------------------- 45 | # any extra libraries we wish to link with the project 46 | #--------------------------------------------------------------------------------- 47 | LIBS := -lgba 48 | 49 | #--------------------------------------------------------------------------------- 50 | # list of directories containing libraries, this must be the top level containing 51 | # include and lib 52 | #--------------------------------------------------------------------------------- 53 | LIBDIRS := $(LIBGBA) 54 | 55 | #--------------------------------------------------------------------------------- 56 | # no real need to edit anything past this point unless you need to add additional 57 | # rules for different file extensions 58 | #--------------------------------------------------------------------------------- 59 | ifneq ($(BUILD),$(notdir $(CURDIR))) 60 | #--------------------------------------------------------------------------------- 61 | 62 | export OUTPUT := $(CURDIR)/$(TARGET) 63 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 64 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 65 | 66 | export DEPSDIR := $(CURDIR)/$(BUILD) 67 | 68 | #--------------------------------------------------------------------------------- 69 | # automatically build a list of object files for our project 70 | #--------------------------------------------------------------------------------- 71 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 72 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 73 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 74 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 75 | BMPFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.bmp))) 76 | 77 | #--------------------------------------------------------------------------------- 78 | # use CXX for linking C++ projects, CC for standard C 79 | #--------------------------------------------------------------------------------- 80 | ifeq ($(strip $(CPPFILES)),) 81 | #--------------------------------------------------------------------------------- 82 | export LD := $(CC) 83 | #--------------------------------------------------------------------------------- 84 | else 85 | #--------------------------------------------------------------------------------- 86 | export LD := $(CXX) 87 | #--------------------------------------------------------------------------------- 88 | endif 89 | #--------------------------------------------------------------------------------- 90 | 91 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 92 | $(BMPFILES:.bmp=.o) \ 93 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 94 | 95 | #--------------------------------------------------------------------------------- 96 | # build a list of include paths 97 | #--------------------------------------------------------------------------------- 98 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 99 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 100 | -I$(CURDIR)/$(BUILD) 101 | 102 | #--------------------------------------------------------------------------------- 103 | # build a list of library paths 104 | #--------------------------------------------------------------------------------- 105 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 106 | 107 | .PHONY: $(BUILD) clean 108 | 109 | #--------------------------------------------------------------------------------- 110 | $(BUILD): 111 | @[ -d $@ ] || mkdir -p $@ 112 | @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 113 | 114 | all : $(BUILD) 115 | #--------------------------------------------------------------------------------- 116 | clean: 117 | @echo clean ... 118 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba 119 | 120 | #--------------------------------------------------------------------------------- 121 | else 122 | 123 | DEPENDS := $(OFILES:.o=.d) 124 | 125 | #--------------------------------------------------------------------------------- 126 | # main targets 127 | #--------------------------------------------------------------------------------- 128 | $(OUTPUT).gba : $(OUTPUT).elf 129 | 130 | $(OUTPUT).elf : $(OFILES) 131 | 132 | #--------------------------------------------------------------------------------- 133 | %.gba: %.elf 134 | @$(OBJCOPY) -O binary $< $@ 135 | @echo built ... $(notdir $@) 136 | @gbafix -tCONTROLLER -cVGBA -mEC -r$(shell git rev-list --count HEAD) $@ 137 | 138 | #--------------------------------------------------------------------------------- 139 | # The bin2o rule should be copied and modified 140 | # for each extension used in the data directories 141 | #--------------------------------------------------------------------------------- 142 | 143 | #--------------------------------------------------------------------------------- 144 | # This rule links in binary data with the .bin extension 145 | #--------------------------------------------------------------------------------- 146 | %.bin.o : %.bin 147 | #--------------------------------------------------------------------------------- 148 | @echo $(notdir $<) 149 | @$(bin2o) 150 | 151 | #--------------------------------------------------------------------------------- 152 | # This rule links in binary data with the .raw extension 153 | #--------------------------------------------------------------------------------- 154 | %.raw.o : %.raw 155 | #--------------------------------------------------------------------------------- 156 | @echo $(notdir $<) 157 | @$(bin2o) 158 | 159 | #--------------------------------------------------------------------------------- 160 | # This rule creates assembly source files using grit 161 | # grit takes an image file and a .grit describing how the file is to be processed 162 | # add additional rules like this for each image extension 163 | # you use in the graphics folders 164 | #--------------------------------------------------------------------------------- 165 | %.s %.h : %.bmp %.grit 166 | #--------------------------------------------------------------------------------- 167 | grit $< -fts -o$* 168 | 169 | -include $(DEPENDS) 170 | 171 | #--------------------------------------------------------------------------------- 172 | endif 173 | #--------------------------------------------------------------------------------- 174 | -------------------------------------------------------------------------------- /source/bios.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2021, Extrems' Corner.org 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef GBA_BIOS_H 28 | #define GBA_BIOS_H 29 | 30 | #include 31 | 32 | #define RESET_EWRAM 0x01 33 | #define RESET_IWRAM 0x02 34 | #define RESET_PALETTE 0x04 35 | #define RESET_VRAM 0x08 36 | #define RESET_OAM 0x10 37 | #define RESET_ALL_RAM 0x1F 38 | #define RESET_SIO_REG 0x20 39 | #define RESET_SOUND_REG 0x40 40 | #define RESET_REG 0x80 41 | #define RESET_ALL_REG 0xE0 42 | #define RESET_ALL 0xFF 43 | 44 | #define HALT 0x00 45 | #define STOP 0x80 46 | 47 | struct Div { 48 | int32_t quot; 49 | int32_t rem; 50 | uint32_t quot_abs; 51 | }; 52 | 53 | struct BitUnPack { 54 | uint16_t size; 55 | uint8_t in_bits; 56 | uint8_t out_bits; 57 | uint32_t bias : 31; 58 | uint32_t bias_zero : 1; 59 | }; 60 | 61 | #if defined(__thumb__) 62 | 63 | static inline void RegisterRamReset(uint8_t flag) 64 | { 65 | register int r0 asm("r0") = flag; 66 | asm volatile("svc 0x01" : "+r" (r0) :: "r1", "r2", "r3", "memory"); 67 | } 68 | 69 | static inline void Halt(void) 70 | { 71 | asm volatile("svc 0x02" ::: "r0", "r1", "r2", "r3", "memory"); 72 | } 73 | 74 | static inline void Stop(void) 75 | { 76 | asm volatile("svc 0x03" ::: "r0", "r1", "r2", "r3", "memory"); 77 | } 78 | 79 | static inline void VBlankIntrWait(void) 80 | { 81 | asm volatile("svc 0x05" ::: "r0", "r1", "r2", "r3", "memory"); 82 | } 83 | 84 | static inline struct Div Div(int32_t num, int32_t denom) 85 | { 86 | register int r0 asm("r0") = num; 87 | register int r1 asm("r1") = denom; 88 | register int r3 asm("r3"); 89 | asm volatile("svc 0x06" : "+r" (r0), "+r" (r1), "=r" (r3) :: "r2", "memory"); 90 | return (struct Div){r0, r1, r3}; 91 | } 92 | 93 | static inline void BitUnPack(const void *src, void *dst, struct BitUnPack *bup) 94 | { 95 | register int r0 asm("r0") = (int)src; 96 | register int r1 asm("r1") = (int)dst; 97 | register int r2 asm("r2") = (int)bup; 98 | asm volatile("svc 0x10" : "+r" (r2) : "r" (r0), "r" (r1) : "r3", "memory"); 99 | } 100 | 101 | static inline void LZ77UnCompVram(const void *src, void *dst) 102 | { 103 | register int r0 asm("r0") = (int)src; 104 | register int r1 asm("r1") = (int)dst; 105 | asm volatile("svc 0x12" :: "r" (r0), "r" (r1) : "r2", "r3", "memory"); 106 | } 107 | 108 | static inline void SoundBias(uint32_t bias) 109 | { 110 | register int r0 asm("r0") = bias; 111 | asm volatile("svc 0x19" :: "r" (r0) : "r1", "r2", "r3", "memory"); 112 | } 113 | 114 | static inline void HardReset(void) 115 | { 116 | asm volatile("svc 0x26" ::: "r0", "r1", "r2", "r3", "memory"); 117 | } 118 | 119 | static inline void CustomHalt(uint8_t flag) 120 | { 121 | register int r2 asm("r2") = flag; 122 | asm volatile("svc 0x27" :: "r" (r2) : "r0", "r1", "r3", "memory"); 123 | } 124 | 125 | #else 126 | 127 | static inline void RegisterRamReset(uint8_t flag) 128 | { 129 | register int r0 asm("r0") = flag; 130 | asm volatile("svc 0x010000" : "+r" (r0) :: "r1", "r2", "r3", "memory"); 131 | } 132 | 133 | static inline void Halt(void) 134 | { 135 | asm volatile("mov lr, pc; mov pc, #0x000001A0" ::: "r2", "ip", "lr", "memory"); 136 | } 137 | 138 | static inline void Stop(void) 139 | { 140 | asm volatile("mov lr, pc; mov pc, #0x000001A8" ::: "r2", "ip", "lr", "memory"); 141 | } 142 | 143 | static inline void VBlankIntrWait(void) 144 | { 145 | asm volatile("svc 0x050000" ::: "r0", "r1", "r2", "r3", "memory"); 146 | } 147 | 148 | static inline struct Div Div(int32_t num, int32_t denom) 149 | { 150 | register int r0 asm("r0") = num; 151 | register int r1 asm("r1") = denom; 152 | register int r3 asm("r3"); 153 | asm volatile("svc 0x060000" : "+r" (r0), "+r" (r1), "=r" (r3) :: "r2", "memory"); 154 | return (struct Div){r0, r1, r3}; 155 | } 156 | 157 | static inline void BitUnPack(const void *src, void *dst, struct BitUnPack *bup) 158 | { 159 | register int r0 asm("r0") = (int)src; 160 | register int r1 asm("r1") = (int)dst; 161 | register int r2 asm("r2") = (int)bup; 162 | asm volatile("svc 0x100000" : "+r" (r2) : "r" (r0), "r" (r1) : "r3", "memory"); 163 | } 164 | 165 | static inline void LZ77UnCompVram(const void *src, void *dst) 166 | { 167 | register int r0 asm("r0") = (int)src; 168 | register int r1 asm("r1") = (int)dst; 169 | asm volatile("svc 0x120000" :: "r" (r0), "r" (r1) : "r2", "r3", "memory"); 170 | } 171 | 172 | static inline void SoundBias(uint32_t bias) 173 | { 174 | register int r0 asm("r0") = bias; 175 | asm volatile("svc 0x190000" :: "r" (r0) : "r1", "r2", "r3", "memory"); 176 | } 177 | 178 | static inline void HardReset(void) 179 | { 180 | asm volatile("svc 0x260000" ::: "r0", "r1", "r2", "r3", "memory"); 181 | } 182 | 183 | static inline void CustomHalt(uint8_t flag) 184 | { 185 | register int r2 asm("r2") = flag; 186 | asm volatile("mov lr, pc; mov pc, #0x000001AC" :: "r" (r2) : "ip", "lr", "memory"); 187 | } 188 | 189 | #endif 190 | 191 | #endif /* GBA_BIOS_H */ 192 | -------------------------------------------------------------------------------- /source/main.iwram.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2021, Extrems' Corner.org 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include "bios.h" 35 | 36 | #define struct struct __attribute__((packed, scalar_storage_order("big-endian"))) 37 | 38 | #define ROM ((volatile int16_t *)0x08000000) 39 | #define ROM_GPIODATA *((volatile int16_t *)0x080000C4) 40 | #define ROM_GPIODIR *((volatile int16_t *)0x080000C6) 41 | #define ROM_GPIOCNT *((volatile int16_t *)0x080000C8) 42 | 43 | //#define ANALOG 44 | 45 | enum { 46 | CMD_ID = 0x00, 47 | CMD_STATUS = 0x40, 48 | CMD_ORIGIN, 49 | CMD_RECALIBRATE, 50 | CMD_STATUS_LONG, 51 | CMD_RESET = 0xFF 52 | }; 53 | 54 | enum { 55 | MOTOR_STOP = 0, 56 | MOTOR_RUMBLE, 57 | MOTOR_STOP_HARD 58 | }; 59 | 60 | struct buttons { 61 | uint16_t : 1; 62 | uint16_t unknown : 1; 63 | uint16_t get_origin : 1; 64 | uint16_t start : 1; 65 | uint16_t y : 1; 66 | uint16_t x : 1; 67 | uint16_t b : 1; 68 | uint16_t a : 1; 69 | uint16_t use_origin : 1; 70 | uint16_t l : 1; 71 | uint16_t r : 1; 72 | uint16_t z : 1; 73 | uint16_t up : 1; 74 | uint16_t down : 1; 75 | uint16_t right : 1; 76 | uint16_t left : 1; 77 | }; 78 | 79 | static struct { 80 | uint16_t type; 81 | struct { 82 | uint8_t : 1; 83 | uint8_t unknown : 1; 84 | uint8_t get_origin : 1; 85 | uint8_t motor : 2; 86 | uint8_t mode : 3; 87 | } status; 88 | } id; 89 | 90 | static struct { 91 | struct buttons buttons; 92 | struct { uint8_t x, y; } stick; 93 | union { 94 | struct { 95 | struct { uint8_t x : 8, y : 8; } substick; 96 | struct { uint8_t l : 4, r : 4; } trigger; 97 | struct { uint8_t a : 4, b : 4; } button; 98 | } mode0; 99 | 100 | struct { 101 | struct { uint8_t x : 4, y : 4; } substick; 102 | struct { uint8_t l : 8, r : 8; } trigger; 103 | struct { uint8_t a : 4, b : 4; } button; 104 | } mode1; 105 | 106 | struct { 107 | struct { uint8_t x : 4, y : 4; } substick; 108 | struct { uint8_t l : 4, r : 4; } trigger; 109 | struct { uint8_t a : 8, b : 8; } button; 110 | } mode2; 111 | 112 | struct { 113 | struct { uint8_t x, y; } substick; 114 | struct { uint8_t l, r; } trigger; 115 | } mode3; 116 | 117 | struct { 118 | struct { uint8_t x, y; } substick; 119 | struct { uint8_t a, b; } button; 120 | } mode4; 121 | 122 | struct { 123 | struct { uint8_t x, y; } substick; 124 | struct { uint8_t l, r; } trigger; 125 | struct { uint8_t a, b; } button; 126 | }; 127 | }; 128 | } status; 129 | 130 | static struct { 131 | struct buttons buttons; 132 | struct { uint8_t x, y; } stick; 133 | struct { uint8_t x, y; } substick; 134 | struct { uint8_t l, r; } trigger; 135 | struct { uint8_t a, b; } button; 136 | } origin = { 137 | .buttons = { .get_origin = true, .use_origin = true }, 138 | .stick = { 128, 128 }, 139 | .substick = { 128, 128 }, 140 | }; 141 | 142 | static uint8_t buffer[128]; 143 | 144 | static enum { 145 | RUMBLE_NONE = 0, 146 | RUMBLE_GBA, 147 | RUMBLE_NDS, 148 | RUMBLE_NDS_EZCARD, 149 | RUMBLE_NDS_SLIDE, 150 | } rumble; 151 | 152 | static bool has_motor(void) 153 | { 154 | switch (ROM[0x59]) { 155 | case 0x59: 156 | switch (ROM[0xFFFFFF]) { 157 | case ~0x0002: 158 | rumble = RUMBLE_NDS; 159 | return true; 160 | case ~0x0101: 161 | rumble = RUMBLE_NDS_SLIDE; 162 | return true; 163 | } 164 | break; 165 | case 0x96: 166 | switch (ROM[0x56] & 0xFF) { 167 | case 'R': case 'V': 168 | rumble = RUMBLE_GBA; 169 | return true; 170 | } 171 | break; 172 | case ~0x0002: 173 | rumble = RUMBLE_NDS_EZCARD; 174 | return true; 175 | } 176 | 177 | rumble = RUMBLE_NONE; 178 | return false; 179 | } 180 | 181 | static void set_motor(bool enable) 182 | { 183 | switch (rumble) { 184 | case RUMBLE_NONE: 185 | break; 186 | case RUMBLE_GBA: 187 | ROM_GPIODIR = 1 << 3; 188 | ROM_GPIODATA = enable << 3; 189 | break; 190 | case RUMBLE_NDS: 191 | if (enable) 192 | DMA3COPY(SRAM, SRAM, DMA_VBLANK | DMA_REPEAT | 1) 193 | else 194 | REG_DMA3CNT &= ~DMA_REPEAT; 195 | break; 196 | case RUMBLE_NDS_EZCARD: 197 | ROM[0x000800] = enable << 1; 198 | break; 199 | case RUMBLE_NDS_SLIDE: 200 | *ROM = enable << 8; 201 | break; 202 | } 203 | } 204 | 205 | void SISetResponse(const void *buf, unsigned bits); 206 | int SIGetCommand(void *buf, unsigned bits); 207 | 208 | int main(void) 209 | { 210 | RegisterRamReset(RESET_ALL_REG); 211 | 212 | REG_IE = IRQ_SERIAL | IRQ_TIMER2 | IRQ_TIMER1 | IRQ_TIMER0; 213 | REG_IF = REG_IF; 214 | 215 | REG_RCNT = R_GPIO | GPIO_IRQ | GPIO_SO_IO | GPIO_SO; 216 | 217 | REG_TM0CNT_L = -67; 218 | REG_TM1CNT_H = TIMER_START | TIMER_IRQ | TIMER_COUNT; 219 | REG_TM0CNT_H = TIMER_START; 220 | 221 | SoundBias(0); 222 | Halt(); 223 | 224 | while (true) { 225 | int length = SIGetCommand(buffer, sizeof(buffer) * 8 + 1); 226 | if (length < 8 + 1) continue; 227 | 228 | unsigned buttons = ~REG_KEYINPUT; 229 | origin.buttons.a = !!(buttons & KEY_A); 230 | origin.buttons.b = !!(buttons & KEY_B); 231 | origin.buttons.unknown = 232 | origin.buttons.z = !!(buttons & KEY_SELECT); 233 | origin.buttons.start = !!(buttons & KEY_START); 234 | #ifndef ANALOG 235 | origin.buttons.right = !!(buttons & KEY_RIGHT); 236 | origin.buttons.left = !!(buttons & KEY_LEFT); 237 | origin.buttons.up = !!(buttons & KEY_UP); 238 | origin.buttons.down = !!(buttons & KEY_DOWN); 239 | #endif 240 | origin.buttons.r = !!(buttons & KEY_R); 241 | origin.buttons.l = !!(buttons & KEY_L); 242 | 243 | id.status.get_origin = origin.buttons.get_origin; 244 | id.status.unknown = origin.buttons.unknown; 245 | 246 | switch (buffer[0]) { 247 | case CMD_RESET: 248 | id.status.motor = MOTOR_STOP; 249 | case CMD_ID: 250 | if (length == 8 + 1) { 251 | if (has_motor()) 252 | id.type = 0x0900; 253 | else 254 | id.type = 0x2900; 255 | 256 | SISetResponse(&id, 24); 257 | } 258 | break; 259 | case CMD_STATUS: 260 | if (length == 24 + 1) { 261 | unsigned mode = buffer[1] & 0b111; 262 | unsigned motor = buffer[2] & 0b11; 263 | 264 | id.status.mode = mode; 265 | if (motor <= MOTOR_STOP_HARD) 266 | id.status.motor = motor; 267 | 268 | status.buttons = origin.buttons; 269 | status.stick.x = origin.stick.x; 270 | status.stick.y = origin.stick.y; 271 | #ifdef ANALOG 272 | if (buttons & KEY_RIGHT) 273 | status.stick.x = origin.stick.x + 100; 274 | else if (buttons & KEY_LEFT) 275 | status.stick.x = origin.stick.x - 100; 276 | if (buttons & KEY_UP) 277 | status.stick.y = origin.stick.y + 100; 278 | else if (buttons & KEY_DOWN) 279 | status.stick.y = origin.stick.y - 100; 280 | #endif 281 | 282 | switch (id.status.mode) { 283 | default: 284 | status.mode0.substick.x = origin.substick.x; 285 | status.mode0.substick.y = origin.substick.y; 286 | status.mode0.trigger.l = (buttons & KEY_L ? 200 : origin.trigger.l) >> 4; 287 | status.mode0.trigger.r = (buttons & KEY_R ? 200 : origin.trigger.r) >> 4; 288 | status.mode0.button.a = (buttons & KEY_A ? 200 : origin.button.a) >> 4; 289 | status.mode0.button.b = (buttons & KEY_B ? 200 : origin.button.b) >> 4; 290 | break; 291 | case 1: 292 | status.mode1.substick.x = origin.substick.x >> 4; 293 | status.mode1.substick.y = origin.substick.y >> 4; 294 | status.mode1.trigger.l = (buttons & KEY_L ? 200 : origin.trigger.l); 295 | status.mode1.trigger.r = (buttons & KEY_R ? 200 : origin.trigger.r); 296 | status.mode1.button.a = (buttons & KEY_A ? 200 : origin.button.a) >> 4; 297 | status.mode1.button.b = (buttons & KEY_B ? 200 : origin.button.b) >> 4; 298 | break; 299 | case 2: 300 | status.mode2.substick.x = origin.substick.x >> 4; 301 | status.mode2.substick.y = origin.substick.y >> 4; 302 | status.mode2.trigger.l = (buttons & KEY_L ? 200 : origin.trigger.l) >> 4; 303 | status.mode2.trigger.r = (buttons & KEY_R ? 200 : origin.trigger.r) >> 4; 304 | status.mode2.button.a = (buttons & KEY_A ? 200 : origin.button.a); 305 | status.mode2.button.b = (buttons & KEY_B ? 200 : origin.button.b); 306 | break; 307 | case 3: 308 | status.mode3.substick.x = origin.substick.x; 309 | status.mode3.substick.y = origin.substick.y; 310 | status.mode3.trigger.l = (buttons & KEY_L ? 200 : origin.trigger.l); 311 | status.mode3.trigger.r = (buttons & KEY_R ? 200 : origin.trigger.r); 312 | break; 313 | case 4: 314 | status.mode4.substick.x = origin.substick.x; 315 | status.mode4.substick.y = origin.substick.y; 316 | status.mode4.button.a = (buttons & KEY_A ? 200 : origin.button.a); 317 | status.mode4.button.b = (buttons & KEY_B ? 200 : origin.button.b); 318 | break; 319 | } 320 | 321 | SISetResponse(&status, 64); 322 | } 323 | break; 324 | case CMD_ORIGIN: 325 | if (length == 8 + 1) { 326 | origin.buttons.get_origin = false; 327 | SISetResponse(&origin, 80); 328 | } 329 | break; 330 | case CMD_RECALIBRATE: 331 | if (length == 24 + 1) { 332 | origin.buttons.get_origin = false; 333 | SISetResponse(&origin, 80); 334 | } 335 | break; 336 | case CMD_STATUS_LONG: 337 | if (length == 24 + 1) { 338 | unsigned motor = buffer[2] & 0b11; 339 | 340 | if (motor <= MOTOR_STOP_HARD) 341 | id.status.motor = motor; 342 | 343 | status.buttons = origin.buttons; 344 | status.stick.x = origin.stick.x; 345 | status.stick.y = origin.stick.y; 346 | #ifdef ANALOG 347 | if (buttons & KEY_RIGHT) 348 | status.stick.x = origin.stick.x + 100; 349 | else if (buttons & KEY_LEFT) 350 | status.stick.x = origin.stick.x - 100; 351 | if (buttons & KEY_UP) 352 | status.stick.y = origin.stick.y + 100; 353 | else if (buttons & KEY_DOWN) 354 | status.stick.y = origin.stick.y - 100; 355 | #endif 356 | status.substick.x = origin.substick.x; 357 | status.substick.y = origin.substick.y; 358 | status.trigger.l = (buttons & KEY_L ? 200 : origin.trigger.l); 359 | status.trigger.r = (buttons & KEY_R ? 200 : origin.trigger.r); 360 | status.button.a = (buttons & KEY_A ? 200 : origin.button.a); 361 | status.button.b = (buttons & KEY_B ? 200 : origin.button.b); 362 | 363 | SISetResponse(&status, 80); 364 | } 365 | break; 366 | } 367 | 368 | set_motor(id.status.motor == MOTOR_RUMBLE); 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /source/si.iwram.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016-2021, Extrems' Corner.org 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "bios.h" 32 | 33 | void SISetResponse(const void *buf, unsigned bits) 34 | { 35 | unsigned byte = 0, bit = 0; 36 | 37 | do { 38 | if (bit++ % 8 == 0) 39 | byte = *(uint8_t *)buf++; 40 | byte <<= 1; 41 | 42 | if (byte & 0x100) { 43 | asm volatile ( 44 | "strb %0, %2 \n" 45 | "nop \n" 46 | "nop \n" 47 | "nop \n" 48 | "nop \n" 49 | "nop \n" 50 | "nop \n" 51 | "nop \n" 52 | "nop \n" 53 | "nop \n" 54 | "nop \n" 55 | "nop \n" 56 | "nop \n" 57 | "nop \n" 58 | "nop \n" 59 | "nop \n" 60 | "strb %1, %2 \n" 61 | "nop \n" 62 | "nop \n" 63 | "nop \n" 64 | "nop \n" 65 | "nop \n" 66 | "nop \n" 67 | "nop \n" 68 | "nop \n" 69 | "nop \n" 70 | "nop \n" 71 | "nop \n" 72 | "nop \n" 73 | "nop \n" 74 | "nop \n" 75 | "nop \n" 76 | "strb %1, %2 \n" 77 | "nop \n" 78 | "nop \n" 79 | "nop \n" 80 | "nop \n" 81 | "nop \n" 82 | "nop \n" 83 | "nop \n" 84 | "nop \n" 85 | "nop \n" 86 | "nop \n" 87 | "nop \n" 88 | "nop \n" 89 | "nop \n" 90 | "nop \n" 91 | "nop \n" 92 | "strb %1, %2 \n" 93 | "nop \n" 94 | "nop \n" 95 | "nop \n" 96 | "nop \n" 97 | : 98 | : "r" (GPIO_SO_IO), "r" (GPIO_SO_IO | GPIO_SO), 99 | "m" (REG_RCNT) 100 | : "memory" 101 | ); 102 | } else { 103 | asm volatile ( 104 | "strb %0, %2 \n" 105 | "nop \n" 106 | "nop \n" 107 | "nop \n" 108 | "nop \n" 109 | "nop \n" 110 | "nop \n" 111 | "nop \n" 112 | "nop \n" 113 | "nop \n" 114 | "nop \n" 115 | "nop \n" 116 | "nop \n" 117 | "nop \n" 118 | "nop \n" 119 | "nop \n" 120 | "strb %0, %2 \n" 121 | "nop \n" 122 | "nop \n" 123 | "nop \n" 124 | "nop \n" 125 | "nop \n" 126 | "nop \n" 127 | "nop \n" 128 | "nop \n" 129 | "nop \n" 130 | "nop \n" 131 | "nop \n" 132 | "nop \n" 133 | "nop \n" 134 | "nop \n" 135 | "nop \n" 136 | "strb %0, %2 \n" 137 | "nop \n" 138 | "nop \n" 139 | "nop \n" 140 | "nop \n" 141 | "nop \n" 142 | "nop \n" 143 | "nop \n" 144 | "nop \n" 145 | "nop \n" 146 | "nop \n" 147 | "nop \n" 148 | "nop \n" 149 | "nop \n" 150 | "nop \n" 151 | "nop \n" 152 | "strb %1, %2 \n" 153 | "nop \n" 154 | "nop \n" 155 | "nop \n" 156 | "nop \n" 157 | : 158 | : "r" (GPIO_SO_IO), "r" (GPIO_SO_IO | GPIO_SO), 159 | "m" (REG_RCNT) 160 | : "memory" 161 | ); 162 | } 163 | } while (bit < bits); 164 | 165 | asm volatile ( 166 | "nop \n" 167 | "nop \n" 168 | "nop \n" 169 | "strb %0, %2 \n" 170 | "nop \n" 171 | "nop \n" 172 | "nop \n" 173 | "nop \n" 174 | "nop \n" 175 | "nop \n" 176 | "nop \n" 177 | "nop \n" 178 | "nop \n" 179 | "nop \n" 180 | "nop \n" 181 | "nop \n" 182 | "nop \n" 183 | "nop \n" 184 | "nop \n" 185 | "strb %1, %2 \n" 186 | : 187 | : "r" (GPIO_SO_IO), "r" (GPIO_SO_IO | GPIO_SO), 188 | "m" (REG_RCNT) 189 | : "memory" 190 | ); 191 | } 192 | 193 | int SIGetCommand(void *buf, unsigned bits) 194 | { 195 | unsigned byte = 0, bit = 0; 196 | unsigned irq; 197 | 198 | REG_TM2CNT_H = REG_TM0CNT_H = 0; 199 | REG_IF = irq = REG_IF; 200 | REG_TM2CNT_H = TIMER_START | TIMER_IRQ | 3; 201 | 202 | do { 203 | CustomHalt(irq & IRQ_TIMER2 ? STOP : HALT); 204 | REG_TM0CNT_H = 0; 205 | REG_IF = irq = REG_IF; 206 | REG_TM0CNT_H = TIMER_START | TIMER_IRQ; 207 | 208 | if (irq & IRQ_SERIAL) { 209 | byte <<= 1; 210 | byte |= !!((REG_RCNT | REG_RCNT | REG_RCNT) & GPIO_SI); 211 | 212 | if (++bit % 8 == 0) 213 | *(uint8_t *)buf++ = byte; 214 | } else if (irq & IRQ_TIMER0) 215 | break; 216 | } while (bit < bits); 217 | 218 | return bit; 219 | } 220 | --------------------------------------------------------------------------------