├── .gitignore ├── .gitmodules ├── COPYING ├── Makefile ├── README.md ├── bootloader ├── .gitignore ├── Makefile ├── arm9code │ └── mpu_reset.s ├── load.ld └── source │ ├── arm9clear.arm.c │ ├── arm9mpu_reset.s │ ├── boot.c │ ├── boot.h │ ├── io_dldi.h │ ├── io_dldi.s │ ├── load_crt0.s │ ├── minifat.c │ ├── minifat.h │ ├── sdmmc.c │ └── sdmmc.h ├── bootstub ├── Makefile └── bootstub.s ├── gfx ├── font.grit ├── font.png ├── font6x8.grit ├── font6x8.png ├── hbmenu_banner.grit ├── hbmenu_banner.png ├── hbmenu_consolebg.grit └── hbmenu_consolebg.png ├── license.txt ├── resources └── hbmenu_banner.xcf ├── source ├── args.cpp ├── args.h ├── excpt_stub.c ├── file_browse.cpp ├── file_browse.h ├── iconTitle.cpp ├── iconTitle.h ├── main.cpp ├── nds_loader_arm9.c └── nds_loader_arm9.h └── testfiles ├── argvTest.nds ├── test1.argv └── test2.argv /.gitignore: -------------------------------------------------------------------------------- 1 | *.DAT 2 | *.dat 3 | *.NDS 4 | *.nds 5 | *.elf 6 | *.bin 7 | *.bz2 8 | *.dsi 9 | *.cia 10 | *.zip 11 | SCFW.SC 12 | BootStrap/arm7/build 13 | BootStrap/arm9/build 14 | data 15 | build 16 | hbmenu 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nds-exception-stub"] 2 | path = nds-exception-stub 3 | url = https://github.com/devkitPro/nds-exception-stub.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | 3 | GNU GENERAL PUBLIC LICENSE 4 | Version 2, June 1991 5 | 6 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 7 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 8 | Everyone is permitted to copy and distribute verbatim copies 9 | of this license document, but changing it is not allowed. 10 | 11 | Preamble 12 | 13 | The licenses for most software are designed to take away your 14 | freedom to share and change it. By contrast, the GNU General Public 15 | License is intended to guarantee your freedom to share and change free 16 | software--to make sure the software is free for all its users. This 17 | General Public License applies to most of the Free Software 18 | Foundation's software and to any other program whose authors commit to 19 | using it. (Some other Free Software Foundation software is covered by 20 | the GNU Library General Public License instead.) You can apply it to 21 | your programs, too. 22 | 23 | When we speak of free software, we are referring to freedom, not 24 | price. Our General Public Licenses are designed to make sure that you 25 | have the freedom to distribute copies of free software (and charge for 26 | this service if you wish), that you receive source code or can get it 27 | if you want it, that you can change the software or use pieces of it 28 | in new free programs; and that you know you can do these things. 29 | 30 | To protect your rights, we need to make restrictions that forbid 31 | anyone to deny you these rights or to ask you to surrender the rights. 32 | These restrictions translate to certain responsibilities for you if you 33 | distribute copies of the software, or if you modify it. 34 | 35 | For example, if you distribute copies of such a program, whether 36 | gratis or for a fee, you must give the recipients all the rights that 37 | you have. You must make sure that they, too, receive or can get the 38 | source code. And you must show them these terms so they know their 39 | rights. 40 | 41 | We protect your rights with two steps: (1) copyright the software, and 42 | (2) offer you this license which gives you legal permission to copy, 43 | distribute and/or modify the software. 44 | 45 | Also, for each author's protection and ours, we want to make certain 46 | that everyone understands that there is no warranty for this free 47 | software. If the software is modified by someone else and passed on, we 48 | want its recipients to know that what they have is not the original, so 49 | that any problems introduced by others will not reflect on the original 50 | authors' reputations. 51 | 52 | Finally, any free program is threatened constantly by software 53 | patents. We wish to avoid the danger that redistributors of a free 54 | program will individually obtain patent licenses, in effect making the 55 | program proprietary. To prevent this, we have made it clear that any 56 | patent must be licensed for everyone's free use or not licensed at all. 57 | 58 | The precise terms and conditions for copying, distribution and 59 | modification follow. 60 | 61 | GNU GENERAL PUBLIC LICENSE 62 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 63 | 64 | 0. This License applies to any program or other work which contains 65 | a notice placed by the copyright holder saying it may be distributed 66 | under the terms of this General Public License. The "Program", below, 67 | refers to any such program or work, and a "work based on the Program" 68 | means either the Program or any derivative work under copyright law: 69 | that is to say, a work containing the Program or a portion of it, 70 | either verbatim or with modifications and/or translated into another 71 | language. (Hereinafter, translation is included without limitation in 72 | the term "modification".) Each licensee is addressed as "you". 73 | 74 | Activities other than copying, distribution and modification are not 75 | covered by this License; they are outside its scope. The act of 76 | running the Program is not restricted, and the output from the Program 77 | is covered only if its contents constitute a work based on the 78 | Program (independent of having been made by running the Program). 79 | Whether that is true depends on what the Program does. 80 | 81 | 1. You may copy and distribute verbatim copies of the Program's 82 | source code as you receive it, in any medium, provided that you 83 | conspicuously and appropriately publish on each copy an appropriate 84 | copyright notice and disclaimer of warranty; keep intact all the 85 | notices that refer to this License and to the absence of any warranty; 86 | and give any other recipients of the Program a copy of this License 87 | along with the Program. 88 | 89 | You may charge a fee for the physical act of transferring a copy, and 90 | you may at your option offer warranty protection in exchange for a fee. 91 | 92 | 2. You may modify your copy or copies of the Program or any portion 93 | of it, thus forming a work based on the Program, and copy and 94 | distribute such modifications or work under the terms of Section 1 95 | above, provided that you also meet all of these conditions: 96 | 97 | a) You must cause the modified files to carry prominent notices 98 | stating that you changed the files and the date of any change. 99 | 100 | b) You must cause any work that you distribute or publish, that in 101 | whole or in part contains or is derived from the Program or any 102 | part thereof, to be licensed as a whole at no charge to all third 103 | parties under the terms of this License. 104 | 105 | c) If the modified program normally reads commands interactively 106 | when run, you must cause it, when started running for such 107 | interactive use in the most ordinary way, to print or display an 108 | announcement including an appropriate copyright notice and a 109 | notice that there is no warranty (or else, saying that you provide 110 | a warranty) and that users may redistribute the program under 111 | these conditions, and telling the user how to view a copy of this 112 | License. (Exception: if the Program itself is interactive but 113 | does not normally print such an announcement, your work based on 114 | the Program is not required to print an announcement.) 115 | 116 | These requirements apply to the modified work as a whole. If 117 | identifiable sections of that work are not derived from the Program, 118 | and can be reasonably considered independent and separate works in 119 | themselves, then this License, and its terms, do not apply to those 120 | sections when you distribute them as separate works. But when you 121 | distribute the same sections as part of a whole which is a work based 122 | on the Program, the distribution of the whole must be on the terms of 123 | this License, whose permissions for other licensees extend to the 124 | entire whole, and thus to each and every part regardless of who wrote it. 125 | 126 | Thus, it is not the intent of this section to claim rights or contest 127 | your rights to work written entirely by you; rather, the intent is to 128 | exercise the right to control the distribution of derivative or 129 | collective works based on the Program. 130 | 131 | In addition, mere aggregation of another work not based on the Program 132 | with the Program (or with a work based on the Program) on a volume of 133 | a storage or distribution medium does not bring the other work under 134 | the scope of this License. 135 | 136 | 3. You may copy and distribute the Program (or a work based on it, 137 | under Section 2) in object code or executable form under the terms of 138 | Sections 1 and 2 above provided that you also do one of the following: 139 | 140 | a) Accompany it with the complete corresponding machine-readable 141 | source code, which must be distributed under the terms of Sections 142 | 1 and 2 above on a medium customarily used for software interchange; or, 143 | 144 | b) Accompany it with a written offer, valid for at least three 145 | years, to give any third party, for a charge no more than your 146 | cost of physically performing source distribution, a complete 147 | machine-readable copy of the corresponding source code, to be 148 | distributed under the terms of Sections 1 and 2 above on a medium 149 | customarily used for software interchange; or, 150 | 151 | c) Accompany it with the information you received as to the offer 152 | to distribute corresponding source code. (This alternative is 153 | allowed only for noncommercial distribution and only if you 154 | received the program in object code or executable form with such 155 | an offer, in accord with Subsection b above.) 156 | 157 | The source code for a work means the preferred form of the work for 158 | making modifications to it. For an executable work, complete source 159 | code means all the source code for all modules it contains, plus any 160 | associated interface definition files, plus the scripts used to 161 | control compilation and installation of the executable. However, as a 162 | special exception, the source code distributed need not include 163 | anything that is normally distributed (in either source or binary 164 | form) with the major components (compiler, kernel, and so on) of the 165 | operating system on which the executable runs, unless that component 166 | itself accompanies the executable. 167 | 168 | If distribution of executable or object code is made by offering 169 | access to copy from a designated place, then offering equivalent 170 | access to copy the source code from the same place counts as 171 | distribution of the source code, even though third parties are not 172 | compelled to copy the source along with the object code. 173 | 174 | 4. You may not copy, modify, sublicense, or distribute the Program 175 | except as expressly provided under this License. Any attempt 176 | otherwise to copy, modify, sublicense or distribute the Program is 177 | void, and will automatically terminate your rights under this License. 178 | However, parties who have received copies, or rights, from you under 179 | this License will not have their licenses terminated so long as such 180 | parties remain in full compliance. 181 | 182 | 5. You are not required to accept this License, since you have not 183 | signed it. However, nothing else grants you permission to modify or 184 | distribute the Program or its derivative works. These actions are 185 | prohibited by law if you do not accept this License. Therefore, by 186 | modifying or distributing the Program (or any work based on the 187 | Program), you indicate your acceptance of this License to do so, and 188 | all its terms and conditions for copying, distributing or modifying 189 | the Program or works based on it. 190 | 191 | 6. Each time you redistribute the Program (or any work based on the 192 | Program), the recipient automatically receives a license from the 193 | original licensor to copy, distribute or modify the Program subject to 194 | these terms and conditions. You may not impose any further 195 | restrictions on the recipients' exercise of the rights granted herein. 196 | You are not responsible for enforcing compliance by third parties to 197 | this License. 198 | 199 | 7. If, as a consequence of a court judgment or allegation of patent 200 | infringement or for any other reason (not limited to patent issues), 201 | conditions are imposed on you (whether by court order, agreement or 202 | otherwise) that contradict the conditions of this License, they do not 203 | excuse you from the conditions of this License. If you cannot 204 | distribute so as to satisfy simultaneously your obligations under this 205 | License and any other pertinent obligations, then as a consequence you 206 | may not distribute the Program at all. For example, if a patent 207 | license would not permit royalty-free redistribution of the Program by 208 | all those who receive copies directly or indirectly through you, then 209 | the only way you could satisfy both it and this License would be to 210 | refrain entirely from distribution of the Program. 211 | 212 | If any portion of this section is held invalid or unenforceable under 213 | any particular circumstance, the balance of the section is intended to 214 | apply and the section as a whole is intended to apply in other 215 | circumstances. 216 | 217 | It is not the purpose of this section to induce you to infringe any 218 | patents or other property right claims or to contest validity of any 219 | such claims; this section has the sole purpose of protecting the 220 | integrity of the free software distribution system, which is 221 | implemented by public license practices. Many people have made 222 | generous contributions to the wide range of software distributed 223 | through that system in reliance on consistent application of that 224 | system; it is up to the author/donor to decide if he or she is willing 225 | to distribute software through any other system and a licensee cannot 226 | impose that choice. 227 | 228 | This section is intended to make thoroughly clear what is believed to 229 | be a consequence of the rest of this License. 230 | 231 | 8. If the distribution and/or use of the Program is restricted in 232 | certain countries either by patents or by copyrighted interfaces, the 233 | original copyright holder who places the Program under this License 234 | may add an explicit geographical distribution limitation excluding 235 | those countries, so that distribution is permitted only in or among 236 | countries not thus excluded. In such case, this License incorporates 237 | the limitation as if written in the body of this License. 238 | 239 | 9. The Free Software Foundation may publish revised and/or new versions 240 | of the General Public License from time to time. Such new versions will 241 | be similar in spirit to the present version, but may differ in detail to 242 | address new problems or concerns. 243 | 244 | Each version is given a distinguishing version number. If the Program 245 | specifies a version number of this License which applies to it and "any 246 | later version", you have the option of following the terms and conditions 247 | either of that version or of any later version published by the Free 248 | Software Foundation. If the Program does not specify a version number of 249 | this License, you may choose any version ever published by the Free Software 250 | Foundation. 251 | 252 | 10. If you wish to incorporate parts of the Program into other free 253 | programs whose distribution conditions are different, write to the author 254 | to ask for permission. For software which is copyrighted by the Free 255 | Software Foundation, write to the Free Software Foundation; we sometimes 256 | make exceptions for this. Our decision will be guided by the two goals 257 | of preserving the free status of all derivatives of our free software and 258 | of promoting the sharing and reuse of software generally. 259 | 260 | NO WARRANTY 261 | 262 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 263 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 264 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 265 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 266 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 267 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 268 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 269 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 270 | REPAIR OR CORRECTION. 271 | 272 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 273 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 274 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 275 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 276 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 277 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 278 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 279 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 280 | POSSIBILITY OF SUCH DAMAGES. 281 | 282 | END OF TERMS AND CONDITIONS 283 | 284 | How to Apply These Terms to Your New Programs 285 | 286 | If you develop a new program, and you want it to be of the greatest 287 | possible use to the public, the best way to achieve this is to make it 288 | free software which everyone can redistribute and change under these terms. 289 | 290 | To do so, attach the following notices to the program. It is safest 291 | to attach them to the start of each source file to most effectively 292 | convey the exclusion of warranty; and each file should have at least 293 | the "copyright" line and a pointer to where the full notice is found. 294 | 295 | 296 | Copyright (C) 297 | 298 | This program is free software; you can redistribute it and/or modify 299 | it under the terms of the GNU General Public License as published by 300 | the Free Software Foundation; either version 2 of the License, or 301 | (at your option) any later version. 302 | 303 | This program is distributed in the hope that it will be useful, 304 | but WITHOUT ANY WARRANTY; without even the implied warranty of 305 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 306 | GNU General Public License for more details. 307 | 308 | You should have received a copy of the GNU General Public License 309 | along with this program; if not, write to the Free Software 310 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 311 | 312 | 313 | Also add information on how to contact you by electronic and paper mail. 314 | 315 | If the program is interactive, make it output a short notice like this 316 | when it starts in an interactive mode: 317 | 318 | Gnomovision version 69, Copyright (C) year name of author 319 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 320 | This is free software, and you are welcome to redistribute it 321 | under certain conditions; type `show c' for details. 322 | 323 | The hypothetical commands `show w' and `show c' should show the appropriate 324 | parts of the General Public License. Of course, the commands you use may 325 | be called something other than `show w' and `show c'; they could even be 326 | mouse-clicks or menu items--whatever suits your program. 327 | 328 | You should also get your employer (if you work as a programmer) or your 329 | school, if any, to sign a "copyright disclaimer" for the program, if 330 | necessary. Here is a sample; alter the names: 331 | 332 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 333 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 334 | 335 | , 1 April 1989 336 | Ty Coon, President of Vice 337 | 338 | This General Public License does not permit incorporating your program into 339 | proprietary programs. If your program is a subroutine library, you may 340 | consider it more useful to permit linking proprietary applications with the 341 | library. If this is what you want to do, use the GNU Library General 342 | Public License instead of this License. 343 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | .SECONDARY: 5 | 6 | ifeq ($(strip $(DEVKITARM)),) 7 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 8 | endif 9 | 10 | include $(DEVKITARM)/ds_rules 11 | 12 | export HBMENU_MAJOR := 0 13 | export HBMENU_MINOR := 10 14 | export HBMENU_PATCH := 0 15 | 16 | 17 | VERSION := $(HBMENU_MAJOR).$(HBMENU_MINOR).$(HBMENU_PATCH) 18 | #--------------------------------------------------------------------------------- 19 | # TARGET is the name of the output 20 | # BUILD is the directory where object files & intermediate files will be placed 21 | # SOURCES is a list of directories containing source code 22 | # INCLUDES is a list of directories containing extra header files 23 | # DATA is a list of directories containing binary files embedded using bin2o 24 | # GRAPHICS is a list of directories containing image files to be converted with grit 25 | #--------------------------------------------------------------------------------- 26 | TARGET := gbar3-frontend 27 | BUILD := build 28 | SOURCES := source 29 | INCLUDES := include 30 | DATA := data 31 | GRAPHICS := gfx 32 | 33 | GBARUNNER3_PATH ?= DEFAULT 34 | 35 | #--------------------------------------------------------------------------------- 36 | # options for code generation 37 | #--------------------------------------------------------------------------------- 38 | ARCH := -mthumb -mthumb-interwork 39 | 40 | CFLAGS := -g -Wall -O2 \ 41 | -ffunction-sections -fdata-sections \ 42 | -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ 43 | -ffast-math \ 44 | $(ARCH) 45 | 46 | ifeq ($(GBARUNNER3_PATH),TWLMENU) 47 | CFLAGS += -DWITH_TWLMENU 48 | else ifeq ($(GBARUNNER3_PATH),AOS) 49 | CFLAGS += -DWITH_AOS 50 | endif 51 | 52 | CFLAGS += $(INCLUDE) -DARM9 53 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 54 | 55 | ASFLAGS := -g $(ARCH) 56 | LDFLAGS = -specs=ds_arm9.specs -g -Wl,--gc-sections $(ARCH) -Wl,-Map,$(notdir $*.map) 57 | 58 | #--------------------------------------------------------------------------------- 59 | # any extra libraries we wish to link with the project (order is important) 60 | #--------------------------------------------------------------------------------- 61 | LIBS := -lfat -lnds9 62 | 63 | 64 | #--------------------------------------------------------------------------------- 65 | # list of directories containing libraries, this must be the top level containing 66 | # include and lib 67 | #--------------------------------------------------------------------------------- 68 | LIBDIRS := $(LIBNDS) 69 | 70 | #--------------------------------------------------------------------------------- 71 | # no real need to edit anything past this point unless you need to add additional 72 | # rules for different file extensions 73 | #--------------------------------------------------------------------------------- 74 | ifneq ($(BUILD),$(notdir $(CURDIR))) 75 | #--------------------------------------------------------------------------------- 76 | 77 | export OUTPUT := $(CURDIR)/$(TARGET) 78 | 79 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 80 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 81 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 82 | 83 | export DEPSDIR := $(CURDIR)/$(BUILD) 84 | 85 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 86 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 87 | PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) 88 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 89 | BINFILES := load.bin bootstub.bin exceptionstub.bin 90 | 91 | #--------------------------------------------------------------------------------- 92 | # use CXX for linking C++ projects, CC for standard C 93 | #--------------------------------------------------------------------------------- 94 | ifeq ($(strip $(CPPFILES)),) 95 | #--------------------------------------------------------------------------------- 96 | export LD := $(CC) 97 | #--------------------------------------------------------------------------------- 98 | else 99 | #--------------------------------------------------------------------------------- 100 | export LD := $(CXX) 101 | #--------------------------------------------------------------------------------- 102 | endif 103 | #--------------------------------------------------------------------------------- 104 | 105 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 106 | $(PNGFILES:.png=.o) \ 107 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 108 | 109 | export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ 110 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 111 | -I$(CURDIR)/$(BUILD) 112 | 113 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 114 | 115 | icons := $(wildcard *.bmp) 116 | 117 | ifneq (,$(findstring $(TARGET).bmp,$(icons))) 118 | export GAME_ICON := $(CURDIR)/$(TARGET).bmp 119 | else 120 | ifneq (,$(findstring icon.bmp,$(icons))) 121 | export GAME_ICON := $(CURDIR)/icon.bmp 122 | endif 123 | endif 124 | 125 | .PHONY: bootloader bootstub exceptionstub $(BUILD) clean 126 | 127 | all: bootloader bootstub exceptionstub $(BUILD) 128 | 129 | #--------------------------------------------------------------------------------- 130 | $(BUILD): bootloader bootstub exceptionstub 131 | @[ -d $@ ] || mkdir -p $@ 132 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 133 | 134 | #--------------------------------------------------------------------------------- 135 | clean: 136 | @echo clean ... 137 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds $(TARGET).arm9 data 138 | @$(MAKE) -C bootloader clean 139 | @$(MAKE) -C bootstub clean 140 | @$(MAKE) -C nds-exception-stub clean 141 | 142 | data: 143 | @mkdir -p data 144 | 145 | bootloader: data 146 | @$(MAKE) -C bootloader LOADBIN=$(CURDIR)/data/load.bin 147 | 148 | exceptionstub: data 149 | @$(MAKE) -C nds-exception-stub STUBBIN=$(CURDIR)/data/exceptionstub.bin 150 | 151 | bootstub: data 152 | @$(MAKE) -C bootstub 153 | 154 | #--------------------------------------------------------------------------------- 155 | else 156 | 157 | #--------------------------------------------------------------------------------- 158 | # main targets 159 | #--------------------------------------------------------------------------------- 160 | $(OUTPUT).nds : $(OUTPUT).elf 161 | $(OUTPUT).elf : $(OFILES) 162 | 163 | #--------------------------------------------------------------------------------- 164 | %.bin.o : %.bin 165 | #--------------------------------------------------------------------------------- 166 | @echo $(notdir $<) 167 | $(bin2o) 168 | 169 | #--------------------------------------------------------------------------------- 170 | # This rule creates assembly source files using grit 171 | # grit takes an image file and a .grit describing how the file is to be processed 172 | # add additional rules like this for each image extension 173 | # you use in the graphics folders 174 | #--------------------------------------------------------------------------------- 175 | %.s %.h : %.png %.grit 176 | #--------------------------------------------------------------------------------- 177 | grit $< -fts -o$* 178 | 179 | -include $(DEPSDIR)/*.d 180 | 181 | #--------------------------------------------------------------------------------------- 182 | endif 183 | #--------------------------------------------------------------------------------------- 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Homebrew Menu 2 | The Homebrew Menu is a simple launcher menu which can be used to replace the menu on several DS flashcards. This menu supports the argv protocol needed for devkitARM compiled applications to make use of nitrofs as well as providing a soft reset feature where applications can exit back to the menu. Exiting to the menu is as simple as returning from main() or calling exit(0). 3 | 4 | Currently the menu is a simple text based interface but we intend to improve this in the near future. 5 | 6 | Installation on your card is a simple matter of copying BOOT.NDS and the appropriate bootstrap file from the hbmenu folder in this archive. At present we have bootstrap launchers for the original R4, ezflash 5, dstt and Acekard 2(i). Copying all the files will give you a single SD card which will boot the Homebrew Menu on all 4 cards. 7 | 8 | The bootstrap.cia file can be installed on a 3DS using FBI. This requires boot.nds on your SD card. 9 | 10 | Place your homebrew games in the /nds folder and have fun. 11 | 12 | ## Passing arguments 13 | The Homebrew Menu supports passing arguments to launched .nds files via .argv files. The testfiles folder has an nds file which lists arguments and some sample .argv files. These are simple text files which start with the name of the nds file to run and a list of arguments to pass to the application. Here's a quick sample .argv file. 14 | ```shell 15 | # This is a comment 16 | # commands can be placed on multiple lines 17 | # first the name of the nds file to run 18 | # this can include a path relative to the .argv file 19 | argvTest.nds 20 | 21 | # a single argument 22 | argument1 23 | 24 | # multiple arguments separated by spaces 25 | argument2 argument3 26 | 27 | # multiple arguments separated by tabs 28 | argument4 argument5 29 | 30 | # or even separated by both tabs and spaces 31 | argument6 argument7 argument8 32 | ``` 33 | 34 | ## File associations 35 | Data files can be associated with NDS files via .ext files. These are like .argv files, but instead of being run directly they are named after the file extension of the data file and run when that data file is opened. For example, placing a file named `txt.ext` in the `/nds` folder with the contents `/nds/notes.nds` will open the `notes.nds` app when a `.txt` file is selected. 36 | 37 | # License 38 | Note: While the GPL license allows you to distribute modified versions of this program it would be appreciated if any improvements are contributed to devkitPro. Ultimately the community as a whole is better served by having a single official source for tools, applications and libraries. 39 | 40 | The latest sources may be obtained from devkitPro git using the command: `git clone git@github.com:devkitPro/nds-hb-menu.git` 41 | 42 | ``` 43 | Copyright (C) 2005 - 2017 44 | Michael "Chishm" Chisholm 45 | Dave "WinterMute" Murphy 46 | 47 | This program is free software; you can redistribute it and/or 48 | modify it under the terms of the GNU General Public License 49 | as published by the Free Software Foundation; either version 2 50 | of the License, or (at your option) any later version. 51 | 52 | This program is distributed in the hope that it will be useful, 53 | but WITHOUT ANY WARRANTY; without even the implied warranty of 54 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 55 | GNU General Public License for more details. 56 | 57 | You should have received a copy of the GNU General Public License 58 | along with this program; if not, write to the Free Software 59 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 60 | ``` 61 | -------------------------------------------------------------------------------- /bootloader/.gitignore: -------------------------------------------------------------------------------- 1 | *build 2 | load.bin 3 | load.elf 4 | -------------------------------------------------------------------------------- /bootloader/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) 6 | endif 7 | 8 | -include $(DEVKITARM)/ds_rules 9 | 10 | #--------------------------------------------------------------------------------- 11 | # BUILD is the directory where object files & intermediate files will be placed 12 | # SOURCES is a list of directories containing source code 13 | # INCLUDES is a list of directories containing extra header files 14 | #--------------------------------------------------------------------------------- 15 | TARGET := load 16 | BUILD ?= build 17 | SOURCES := source source/patches 18 | INCLUDES := build 19 | SPECS := specs 20 | 21 | 22 | #--------------------------------------------------------------------------------- 23 | # options for code generation 24 | #--------------------------------------------------------------------------------- 25 | ARCH := -mthumb -mthumb-interwork -march=armv4t -mtune=arm7tdmi 26 | 27 | CFLAGS := -g -Wall -Os \ 28 | -ffunction-sections -fdata-sections \ 29 | $(ARCH) 30 | 31 | CFLAGS += $(INCLUDE) $(EXTRA_CFLAGS) -DARM7 32 | 33 | ASFLAGS := -g $(ARCH) $(EXTRA_CFLAGS) $(INCLUDE) 34 | LDFLAGS = -nostartfiles -T $(TOPDIR)/load.ld -Wl,--no-warn-rwx-segments,--nmagic,--gc-sections -g $(ARCH) -Wl,-Map,$(TARGET).map 35 | 36 | LIBS := -lnds7 -lcalico_ds7 37 | 38 | #--------------------------------------------------------------------------------- 39 | # list of directories containing libraries, this must be the top level containing 40 | # include and lib 41 | #--------------------------------------------------------------------------------- 42 | LIBDIRS := $(LIBNDS) 43 | 44 | 45 | #--------------------------------------------------------------------------------- 46 | # no real need to edit anything past this point unless you need to add additional 47 | # rules for different file extensions 48 | #--------------------------------------------------------------------------------- 49 | ifneq ($(BUILD),$(notdir $(CURDIR))) 50 | #--------------------------------------------------------------------------------- 51 | 52 | export TOPDIR := $(CURDIR) 53 | export LOADBIN ?= $(CURDIR)/$(TARGET).bin 54 | export LOADELF := $(CURDIR)/$(TARGET).elf 55 | export DEPSDIR := $(CURDIR)/$(BUILD) 56 | 57 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) 58 | 59 | export CC := $(PREFIX)gcc 60 | export CXX := $(PREFIX)g++ 61 | export AR := $(PREFIX)ar 62 | export OBJCOPY := $(PREFIX)objcopy 63 | 64 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 65 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 66 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 67 | 68 | export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 69 | 70 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 71 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 72 | -I$(CURDIR)/$(BUILD) 73 | 74 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 75 | 76 | #--------------------------------------------------------------------------------- 77 | # use CC for linking standard C 78 | #--------------------------------------------------------------------------------- 79 | export LD := $(CC) 80 | #--------------------------------------------------------------------------------- 81 | 82 | .PHONY: $(BUILD) clean 83 | 84 | #--------------------------------------------------------------------------------- 85 | $(BUILD): 86 | @[ -d $@ ] || mkdir -p $@ 87 | @$(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile 88 | 89 | #--------------------------------------------------------------------------------- 90 | clean: 91 | @echo clean ... 92 | @rm -fr $(BUILD) *.elf *.bin 93 | 94 | 95 | #--------------------------------------------------------------------------------- 96 | else 97 | 98 | DEPENDS := $(OFILES:.o=.d) 99 | 100 | #--------------------------------------------------------------------------------- 101 | # main targets 102 | #--------------------------------------------------------------------------------- 103 | $(LOADBIN) : $(LOADELF) 104 | @$(OBJCOPY) -O binary $< $@ 105 | @echo built ... $(notdir $@) 106 | 107 | 108 | $(LOADELF) : $(OFILES) 109 | @echo linking $(notdir $@) 110 | @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ 111 | 112 | arm9mpu_reset.o: mpu_reset.bin 113 | 114 | mpu_reset.bin: mpu_reset.elf 115 | $(OBJCOPY) -O binary $< $@ 116 | 117 | mpu_reset.elf: $(TOPDIR)/arm9code/mpu_reset.s 118 | $(CC) $(ASFLAGS) -Ttext=0 -x assembler-with-cpp -nostartfiles -nostdlib $< -o $@ 119 | 120 | -include $(DEPENDS) 121 | #--------------------------------------------------------------------------------------- 122 | endif 123 | #--------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /bootloader/arm9code/mpu_reset.s: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2006 - 2015 Dave Murphy (WinterMute) 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 2 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | 17 | */ 18 | 19 | #include 20 | 21 | .text 22 | .align 4 23 | 24 | .arm 25 | 26 | .arch armv5te 27 | .cpu arm946e-s 28 | 29 | @--------------------------------------------------------------------------------- 30 | .global _start 31 | .type _start STT_FUNC 32 | @--------------------------------------------------------------------------------- 33 | _start: 34 | @--------------------------------------------------------------------------------- 35 | @ Switch off MPU 36 | mrc p15, 0, r0, c1, c0, 0 37 | bic r0, r0, #PROTECT_ENABLE 38 | mcr p15, 0, r0, c1, c0, 0 39 | 40 | 41 | adr r12, mpu_initial_data 42 | ldmia r12, {r0-r10} 43 | 44 | mcr p15, 0, r0, c2, c0, 0 45 | mcr p15, 0, r0, c2, c0, 1 46 | mcr p15, 0, r1, c3, c0, 0 47 | mcr p15, 0, r2, c5, c0, 2 48 | mcr p15, 0, r3, c5, c0, 3 49 | mcr p15, 0, r4, c6, c0, 0 50 | mcr p15, 0, r5, c6, c1, 0 51 | mcr p15, 0, r6, c6, c3, 0 52 | mcr p15, 0, r7, c6, c4, 0 53 | mcr p15, 0, r8, c6, c6, 0 54 | mcr p15, 0, r9, c6, c7, 0 55 | mcr p15, 0, r10, c9, c1, 0 56 | 57 | mov r0, #0 58 | mcr p15, 0, r0, c6, c2, 0 @ PU Protection Unit Data/Unified Region 2 59 | mcr p15, 0, r0, c6, c5, 0 @ PU Protection Unit Data/Unified Region 5 60 | 61 | mrc p15, 0, r0, c9, c1, 0 @ DTCM 62 | mov r0, r0, lsr #12 @ base 63 | mov r0, r0, lsl #12 @ size 64 | add r0, r0, #0x4000 @ dtcm top 65 | 66 | sub r0, r0, #4 @ irq vector 67 | mov r1, #0 68 | str r1, [r0] 69 | sub r0, r0, #4 @ IRQ1 Check Bits 70 | str r1, [r0] 71 | 72 | sub r0, r0, #128 73 | bic r0, r0, #7 74 | 75 | msr cpsr_c, #0xd3 @ svc mode 76 | mov sp, r0 77 | sub r0, r0, #128 78 | msr cpsr_c, #0xd2 @ irq mode 79 | mov sp, r0 80 | sub r0, r0, #128 81 | msr cpsr_c, #0xdf @ system mode 82 | mov sp, r0 83 | 84 | @ enable cache & tcm 85 | mrc p15, 0, r0, c1, c0, 0 86 | ldr r1,= ITCM_ENABLE | DTCM_ENABLE | ICACHE_ENABLE | DCACHE_ENABLE 87 | orr r0,r0,r1 88 | mcr p15, 0, r0, c1, c0, 0 89 | 90 | ldr r10, =0x2FFFE04 91 | ldr r0, =0xE59FF018 92 | str r0, [r10] 93 | add r1, r10, #0x20 94 | str r10, [r1] 95 | bx r10 96 | 97 | .pool 98 | 99 | mpu_initial_data: 100 | .word 0x00000042 @ p15,0,c2,c0,0..1,r0 ;PU Cachability Bits for Data/Unified+Instruction Protection Region 101 | .word 0x00000002 @ p15,0,c3,c0,0,r1 ;PU Write-Bufferability Bits for Data Protection Regions 102 | .word 0x15111011 @ p15,0,c5,c0,2,r2 ;PU Extended Access Permission Data/Unified Protection Region 103 | .word 0x05100011 @ p15,0,c5,c0,3,r3 ;PU Extended Access Permission Instruction Protection Region 104 | .word 0x04000033 @ p15,0,c6,c0,0,r4 ;PU Protection Unit Data/Unified Region 0 105 | .word 0x0200002b @ p15,0,c6,c1,0,r5 ;PU Protection Unit Data/Unified Region 1 4MB 106 | .word 0x08000035 @ p15,0,c6,c3,0,r6 ;PU Protection Unit Data/Unified Region 3 107 | .word 0x0300001b @ p15,0,c6,c4,0,r7 ;PU Protection Unit Data/Unified Region 4 108 | .word 0xffff001d @ p15,0,c6,c6,0,r8 ;PU Protection Unit Data/Unified Region 6 109 | .word 0x02fff017 @ p15,0,c6,c7,0,r9 ;PU Protection Unit Data/Unified Region 7 4KB 110 | .word 0x0300000a @ p15,0,c9,c1,0,r10 ;TCM Data TCM Base and Virtual Size 111 | -------------------------------------------------------------------------------- /bootloader/load.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | ENTRY(_start) 4 | 5 | MEMORY { 6 | wram : ORIGIN = 0x037f8000, LENGTH = 0x17000 7 | vram : ORIGIN = 0x06000000, LENGTH = 128K 8 | } 9 | 10 | PROVIDE_HIDDEN( __sys_start = ORIGIN(wram) + LENGTH(wram) ); 11 | PROVIDE_HIDDEN( __sp_usr = __sys_start + 0xe80 ); 12 | PROVIDE_HIDDEN( __sp_irq = __sp_usr + 0x100 ); 13 | PROVIDE_HIDDEN( __sp_svc = __sp_irq + 0x40 ); 14 | PROVIDE_HIDDEN( __irq_flags2 = __sp_svc + 0x00 ); 15 | PROVIDE_HIDDEN( __scfg_buf = __sp_svc + 0x04 ); 16 | PROVIDE_HIDDEN( __dma_fill = __sp_svc + 0x20 ); 17 | PROVIDE_HIDDEN( __irq_flags = __sp_svc + 0x38 ); 18 | PROVIDE_HIDDEN( __irq_vector = __sp_svc + 0x3c ); 19 | 20 | SECTIONS 21 | { 22 | .init : 23 | { 24 | __text_start = . ; 25 | KEEP (*(.init)) 26 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 27 | } >vram = 0xff 28 | 29 | .plt : 30 | { 31 | *(.plt) 32 | } >vram = 0xff 33 | 34 | .text : /* ALIGN (4): */ 35 | { 36 | 37 | *(.text*) 38 | *(.stub) 39 | /* .gnu.warning sections are handled specially by elf32.em. */ 40 | *(.gnu.warning) 41 | *(.gnu.linkonce.t*) 42 | *(.glue_7) 43 | *(.glue_7t) 44 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 45 | } >vram = 0xff 46 | 47 | .fini : 48 | { 49 | KEEP (*(.fini)) 50 | } >vram =0xff 51 | 52 | __text_end = . ; 53 | 54 | .rodata : 55 | { 56 | *(.rodata) 57 | *all.rodata*(*) 58 | *(.roda) 59 | *(.rodata.*) 60 | *(.gnu.linkonce.r*) 61 | SORT(CONSTRUCTORS) 62 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 63 | } >vram = 0xff 64 | 65 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >vram 66 | __exidx_start = .; 67 | .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >vram 68 | __exidx_end = .; 69 | 70 | /* Ensure the __preinit_array_start label is properly aligned. We 71 | could instead move the label definition inside the section, but 72 | the linker would then create the section even if it turns out to 73 | be empty, which isn't pretty. */ 74 | . = ALIGN(32 / 8); 75 | PROVIDE (__preinit_array_start = .); 76 | .preinit_array : { KEEP (*(.preinit_array)) } >vram = 0xff 77 | PROVIDE (__preinit_array_end = .); 78 | PROVIDE (__init_array_start = .); 79 | .init_array : { KEEP (*(.init_array)) } >vram = 0xff 80 | PROVIDE (__init_array_end = .); 81 | PROVIDE (__fini_array_start = .); 82 | .fini_array : { KEEP (*(.fini_array)) } >vram = 0xff 83 | PROVIDE (__fini_array_end = .); 84 | 85 | .ctors : 86 | { 87 | /* gcc uses crtbegin.o to find the start of the constructors, so 88 | we make sure it is first. Because this is a wildcard, it 89 | doesn't matter if the user does not actually link against 90 | crtbegin.o; the linker won't look for a file to match a 91 | wildcard. The wildcard also means that it doesn't matter which 92 | directory crtbegin.o is in. */ 93 | KEEP (*crtbegin.o(.ctors)) 94 | KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) 95 | KEEP (*(SORT(.ctors.*))) 96 | KEEP (*(.ctors)) 97 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 98 | } >vram = 0xff 99 | 100 | .dtors : 101 | { 102 | KEEP (*crtbegin.o(.dtors)) 103 | KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) 104 | KEEP (*(SORT(.dtors.*))) 105 | KEEP (*(.dtors)) 106 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 107 | } >vram = 0xff 108 | 109 | .eh_frame : 110 | { 111 | KEEP (*(.eh_frame)) 112 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 113 | } >vram = 0xff 114 | 115 | .gcc_except_table : 116 | { 117 | *(.gcc_except_table) 118 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 119 | } >vram = 0xff 120 | .jcr : { KEEP (*(.jcr)) } >vram = 0 121 | .got : { *(.got.plt) *(.got) } >vram = 0 122 | 123 | 124 | .vram ALIGN(4) : 125 | { 126 | __vram_start = ABSOLUTE(.) ; 127 | *(.vram) 128 | *vram.*(.text) 129 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 130 | __vram_end = ABSOLUTE(.) ; 131 | } >vram = 0xff 132 | 133 | 134 | .data ALIGN(4) : { 135 | __data_start = ABSOLUTE(.); 136 | *(.data) 137 | *(.data.*) 138 | *(.gnu.linkonce.d*) 139 | CONSTRUCTORS 140 | . = ALIGN(4); 141 | __data_end = ABSOLUTE(.) ; 142 | } >vram = 0xff 143 | 144 | 145 | 146 | .bss ALIGN(4) : 147 | { 148 | __bss_start = ABSOLUTE(.); 149 | __bss_start__ = ABSOLUTE(.); 150 | *(.dynbss) 151 | *(.gnu.linkonce.b*) 152 | *(.bss*) 153 | *(COMMON) 154 | . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ 155 | } >vram 156 | 157 | __bss_end = . ; 158 | __bss_end__ = . ; 159 | 160 | _end = . ; 161 | __end__ = . ; 162 | PROVIDE (end = _end); 163 | 164 | /* Stabs debugging sections. */ 165 | .stab 0 : { *(.stab) } 166 | .stabstr 0 : { *(.stabstr) } 167 | .stab.excl 0 : { *(.stab.excl) } 168 | .stab.exclstr 0 : { *(.stab.exclstr) } 169 | .stab.index 0 : { *(.stab.index) } 170 | .stab.indexstr 0 : { *(.stab.indexstr) } 171 | .comment 0 : { *(.comment) } 172 | /* DWARF debug sections. 173 | Symbols in the DWARF debugging sections are relative to the beginning 174 | of the section so we begin them at 0. */ 175 | /* DWARF 1 */ 176 | .debug 0 : { *(.debug) } 177 | .line 0 : { *(.line) } 178 | /* GNU DWARF 1 extensions */ 179 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 180 | .debug_sfnames 0 : { *(.debug_sfnames) } 181 | /* DWARF 1.1 and DWARF 2 */ 182 | .debug_aranges 0 : { *(.debug_aranges) } 183 | .debug_pubnames 0 : { *(.debug_pubnames) } 184 | /* DWARF 2 */ 185 | .debug_info 0 : { *(.debug_info) } 186 | .debug_abbrev 0 : { *(.debug_abbrev) } 187 | .debug_line 0 : { *(.debug_line) } 188 | .debug_frame 0 : { *(.debug_frame) } 189 | .debug_str 0 : { *(.debug_str) } 190 | .debug_loc 0 : { *(.debug_loc) } 191 | .debug_macinfo 0 : { *(.debug_macinfo) } 192 | /* SGI/MIPS DWARF 2 extensions */ 193 | .debug_weaknames 0 : { *(.debug_weaknames) } 194 | .debug_funcnames 0 : { *(.debug_funcnames) } 195 | .debug_typenames 0 : { *(.debug_typenames) } 196 | .debug_varnames 0 : { *(.debug_varnames) } 197 | .stack 0x80000 : { _stack = .; *(.stack) } 198 | /* These must appear regardless of . */ 199 | } 200 | -------------------------------------------------------------------------------- /bootloader/source/arm9clear.arm.c: -------------------------------------------------------------------------------- 1 | #define ARM9 2 | #undef ARM7 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "boot.h" 15 | 16 | /*------------------------------------------------------------------------- 17 | resetMemory2_ARM9 18 | Clears the ARM9's DMA channels and resets video memory 19 | Written by Darkain. 20 | Modified by Chishm: 21 | * Changed MultiNDS specific stuff 22 | --------------------------------------------------------------------------*/ 23 | void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9 (void) 24 | { 25 | register int i; 26 | 27 | //clear out ARM9 DMA channels 28 | for (i=0; i<4; i++) { 29 | DMA_CR(i) = 0; 30 | DMA_SRC(i) = 0; 31 | DMA_DEST(i) = 0; 32 | TIMER_CR(i) = 0; 33 | TIMER_DATA(i) = 0; 34 | } 35 | 36 | VRAM_CR = (VRAM_CR & 0xffff0000) | 0x00008080 ; 37 | 38 | vu16 *mainregs = (vu16*)0x04000000; 39 | vu16 *subregs = (vu16*)0x04001000; 40 | 41 | for (i=0; i<43; i++) { 42 | mainregs[i] = 0; 43 | subregs[i] = 0; 44 | } 45 | 46 | REG_DISPSTAT = 0; 47 | 48 | VRAM_A_CR = 0; 49 | VRAM_B_CR = 0; 50 | // Don't mess with the ARM7's VRAM 51 | // VRAM_C_CR = 0; 52 | VRAM_D_CR = 0; 53 | VRAM_E_CR = 0; 54 | VRAM_F_CR = 0; 55 | VRAM_G_CR = 0; 56 | VRAM_H_CR = 0; 57 | VRAM_I_CR = 0; 58 | REG_POWERCNT = 0x820F; 59 | 60 | //set shared ram to ARM7 61 | WRAM_CR = 0x03; 62 | 63 | // Return to passme loop 64 | *((vu32*)0x02FFFE04) = (u32)0xE59FF018; // ldr pc, 0x02FFFE24 65 | *((vu32*)0x02FFFE24) = (u32)0x02FFFE04; // Set ARM9 Loop address 66 | 67 | asm volatile( 68 | "\tbx %0\n" 69 | : : "r" (0x02FFFE04) 70 | ); 71 | while(1); 72 | } 73 | 74 | /*------------------------------------------------------------------------- 75 | startBinary_ARM9 76 | Jumps to the ARM9 NDS binary in sync with the display and ARM7 77 | Written by Darkain. 78 | Modified by Chishm: 79 | * Removed MultiNDS specific stuff 80 | --------------------------------------------------------------------------*/ 81 | void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9 (void) 82 | { 83 | if ((*(u8*)0x2FFE012 == *(u8*)0x2FFFE12) && (*(u8*)0x2FFE012 > 0)) { 84 | *(vu32*)REG_MBK1 = *(u32*)0x02FFE180; 85 | *(vu32*)REG_MBK2 = *(u32*)0x02FFE184; 86 | *(vu32*)REG_MBK3 = *(u32*)0x02FFE188; 87 | *(vu32*)REG_MBK4 = *(u32*)0x02FFE18C; 88 | *(vu32*)REG_MBK5 = *(u32*)0x02FFE190; 89 | REG_MBK6 = *(u32*)0x02FFE194; 90 | REG_MBK7 = *(u32*)0x02FFE198; 91 | REG_MBK8 = *(u32*)0x02FFE19C; 92 | REG_MBK9 = *(u32*)0x02FFE1AC; 93 | WRAM_CR = *(u8*)0x02FFE1AF; 94 | } 95 | 96 | REG_IME=0; 97 | REG_EXMEMCNT = 0xE880; 98 | // set ARM9 load address to 0 and wait for it to change again 99 | ARM9_START_FLAG = 0; 100 | while(REG_VCOUNT!=191); 101 | while(REG_VCOUNT==191); 102 | while ( ARM9_START_FLAG != 1 ); 103 | VoidFn arm9code = *(VoidFn*)(0x2FFFE24); 104 | arm9code(); 105 | while(1); 106 | } 107 | 108 | -------------------------------------------------------------------------------- /bootloader/source/arm9mpu_reset.s: -------------------------------------------------------------------------------- 1 | .arm 2 | .global mpu_reset, mpu_reset_end 3 | 4 | mpu_reset: 5 | .incbin "mpu_reset.bin" 6 | mpu_reset_end: 7 | -------------------------------------------------------------------------------- /bootloader/source/boot.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | boot.c 3 | 4 | BootLoader 5 | Loads a file into memory and runs it 6 | 7 | All resetMemory and startBinary functions are based 8 | on the MultiNDS loader by Darkain. 9 | Original source available at: 10 | http://cvs.sourceforge.net/viewcvs.py/ndslib/ndslib/examples/loader/boot/main.cpp 11 | 12 | License: 13 | Copyright (C) 2005 Michael "Chishm" Chisholm 14 | 15 | This program is free software; you can redistribute it and/or 16 | modify it under the terms of the GNU General Public License 17 | as published by the Free Software Foundation; either version 2 18 | of the License, or (at your option) any later version. 19 | 20 | This program is distributed in the hope that it will be useful, 21 | but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | GNU General Public License for more details. 24 | 25 | You should have received a copy of the GNU General Public License 26 | along with this program; if not, write to the Free Software 27 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 28 | 29 | If you use this code, please give due credit and email me about your 30 | project at chishm@hotmail.com 31 | 32 | Helpful information: 33 | This code runs from VRAM bank C on ARM7 34 | ------------------------------------------------------------------*/ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include "boot.h" 46 | #include "io_dldi.h" 47 | #include "sdmmc.h" 48 | #include "minifat.h" 49 | 50 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 51 | // Important things 52 | #define TEMP_MEM MM_ENV_FREE_D000 53 | #define ARM9_START_ADDRESS (*(vu32*)&g_envAppNdsHeader->arm9_entrypoint) 54 | #define DEFAULT_BOOT_NAME "BOOT.NDS" 55 | 56 | extern volatile uptr __irq_vector; 57 | 58 | extern unsigned long _start; 59 | extern unsigned long storedFileCluster; 60 | extern unsigned long wantToPatchDLDI; 61 | extern unsigned long argStart; 62 | extern unsigned long argSize; 63 | extern unsigned long dsiSD; 64 | extern unsigned long dsiMode; 65 | 66 | static uptr temp_arm9_start_address; 67 | 68 | static char boot_nds[] = "fat:/boot.nds"; 69 | static unsigned long argbuf[4]; 70 | 71 | static MiniFat fatState; 72 | 73 | /*------------------------------------------------------------------------- 74 | passArgs_ARM7 75 | Copies the command line arguments to the end of the ARM9 binary, 76 | then sets a flag in memory for the loaded NDS to use 77 | --------------------------------------------------------------------------*/ 78 | static void passArgs_ARM7 (void) { 79 | void* argSrc; 80 | void* argDst; 81 | 82 | if (!argStart || !argSize) { 83 | char *arg = boot_nds; 84 | argSize = __builtin_strlen(boot_nds); 85 | 86 | if (dsiSD) { 87 | arg++; 88 | arg[0] = 's'; 89 | arg[1] = 'd'; 90 | } 91 | __builtin_memcpy(argbuf,arg,argSize+1); 92 | argSrc = argbuf; 93 | } else { 94 | argSrc = (void*)(argStart + (uptr)&_start); 95 | } 96 | 97 | argDst = (void*)((g_envAppNdsHeader->arm9_ram_address + g_envAppNdsHeader->arm9_size + 3) & ~3); // Word aligned 98 | 99 | if (dsiMode && (g_envAppNdsHeader->unitcode & BIT(1)) && g_envAppTwlHeader->arm9i_size) 100 | { 101 | void* argDst2 = (void*)((g_envAppTwlHeader->arm9i_ram_address + g_envAppTwlHeader->arm9i_size + 3) & ~3); // Word aligned 102 | if (argDst2 > argDst) 103 | argDst = argDst2; 104 | } 105 | 106 | armCopyMem32(argDst, argSrc, (argSize + 3) &~ 3); 107 | 108 | g_envNdsArgvHeader->magic = ENV_NDS_ARGV_MAGIC; 109 | g_envNdsArgvHeader->args_str = argDst; 110 | g_envNdsArgvHeader->args_str_size = argSize; 111 | } 112 | 113 | static void initMBK_dsiMode(void) { 114 | *(vu32*)REG_MBK1 = *(u32*)0x02FFE180; 115 | *(vu32*)REG_MBK2 = *(u32*)0x02FFE184; 116 | *(vu32*)REG_MBK3 = *(u32*)0x02FFE188; 117 | *(vu32*)REG_MBK4 = *(u32*)0x02FFE18C; 118 | *(vu32*)REG_MBK5 = *(u32*)0x02FFE190; 119 | REG_MBK6 = *(u32*)0x02FFE1A0; 120 | REG_MBK7 = *(u32*)0x02FFE1A4; 121 | REG_MBK8 = *(u32*)0x02FFE1A8; 122 | REG_MBK9 = *(u32*)0x02FFE1AC; 123 | } 124 | 125 | /*------------------------------------------------------------------------- 126 | resetMemory_ARM7 127 | Clears all of the NDS's RAM that is visible to the ARM7 128 | Written by Darkain. 129 | Modified by Chishm: 130 | * Added STMIA clear mem loop 131 | --------------------------------------------------------------------------*/ 132 | static void resetMemory_ARM7 (void) 133 | { 134 | // Reset the interrupt controller & related BIOS variables 135 | REG_IME = 0; 136 | REG_IE = 0; 137 | REG_IF = ~0; 138 | __irq_vector = 0; 139 | __irq_flags = ~0; 140 | __irq_flags2 = ~0; 141 | 142 | REG_POWCNT = POWCNT_SOUND; //turn off power to stuff 143 | 144 | for (unsigned i=0; i<16; i++) { 145 | SCHANNEL_CR(i) = 0; 146 | SCHANNEL_TIMER(i) = 0; 147 | SCHANNEL_SOURCE(i) = 0; 148 | SCHANNEL_LENGTH(i) = 0; 149 | } 150 | 151 | REG_SOUNDCNT = 0; 152 | 153 | //clear out ARM7 DMA channels and timers 154 | for (unsigned i=0; i<4; i++) { 155 | DMA_CR(i) = 0; 156 | DMA_SRC(i) = 0; 157 | DMA_DEST(i) = 0; 158 | TIMER_CR(i) = 0; 159 | TIMER_DATA(i) = 0; 160 | } 161 | 162 | // Clear most of ARM7 exclusive WRAM 163 | extern char __sys_start[]; 164 | armFillMem32((void*)MM_A7WRAM, 0, __sys_start - (char*)MM_A7WRAM); 165 | 166 | // Clear most of main RAM 167 | uptr main_ram_clr_begin = dsiMode ? MM_ENV_TWL_AUTOLOAD_EXT : MM_MAINRAM; 168 | uptr main_ram_clr_end = MM_ENV_HB_BOOTSTUB - (dsiMode ? 0 : (MM_MAINRAM_SZ_TWL-MM_MAINRAM_SZ_NTR)); 169 | armFillMem32((void*)main_ram_clr_begin, 0, main_ram_clr_end-main_ram_clr_begin); 170 | 171 | // Repair ARM7 mirror of SCFG regs if they have been previously cleared out 172 | if (dsiMode && !__scfg_buf.ext && !__scfg_buf.other) { 173 | __scfg_buf.ext = g_scfgBackup->ext; 174 | __scfg_buf.other = g_scfgBackup->other; 175 | } 176 | 177 | // XX: Previously we would read user settings from NVRAM here. However, 178 | // either the previously loaded app or the app we want to load are 179 | // guaranteed to be hbmenu, which already does this on startup by virtue 180 | // of being compiled with a modern enough version of libnds. 181 | 182 | // Repair AES keyslot used by NAND encryption 183 | if (dsiMode) { 184 | REG_AES_SLOTxY(3).data[2] = 0x202DDD1D; 185 | REG_AES_SLOTxY(3).data[3] = 0xE1A00005; 186 | aesBusyWaitReady(); 187 | } 188 | } 189 | 190 | static void loadBinary_ARM7 (u32 fileCluster) 191 | { 192 | EnvNdsHeader ndsHeader; 193 | 194 | // read NDS header 195 | minifatRead(&fatState, fileCluster, &ndsHeader, 0, sizeof(ndsHeader)); 196 | 197 | // Load binaries into memory 198 | minifatRead(&fatState, fileCluster, (void*)ndsHeader.arm9_ram_address, ndsHeader.arm9_rom_offset, ndsHeader.arm9_size); 199 | minifatRead(&fatState, fileCluster, (void*)ndsHeader.arm7_ram_address, ndsHeader.arm7_rom_offset, ndsHeader.arm7_size); 200 | 201 | // first copy the header to its proper location, excluding 202 | // the ARM9 start address, so as not to start it 203 | temp_arm9_start_address = ndsHeader.arm9_entrypoint; // Store for later 204 | ndsHeader.arm9_entrypoint = 0; 205 | dmaCopyWords(3, &ndsHeader, g_envAppNdsHeader, sizeof(EnvNdsHeader)); 206 | 207 | if (dsiMode && (ndsHeader.unitcode & BIT(1))) 208 | { 209 | // Read full TWL header 210 | minifatRead(&fatState, fileCluster, g_envAppTwlHeader, 0, sizeof(EnvTwlHeader)); 211 | 212 | // Load TWL binaries into memory 213 | if (g_envAppTwlHeader->arm9i_size) 214 | minifatRead(&fatState, fileCluster, (void*)g_envAppTwlHeader->arm9i_ram_address, g_envAppTwlHeader->arm9i_rom_offset, g_envAppTwlHeader->arm9i_size); 215 | if (g_envAppTwlHeader->arm7i_size) 216 | minifatRead(&fatState, fileCluster, (void*)g_envAppTwlHeader->arm7i_ram_address, g_envAppTwlHeader->arm7i_rom_offset, g_envAppTwlHeader->arm7i_size); 217 | 218 | initMBK_dsiMode(); 219 | } 220 | } 221 | 222 | /*------------------------------------------------------------------------- 223 | startBinary_ARM7 224 | Jumps to the ARM7 NDS binary in sync with the display and ARM9 225 | Written by Darkain. 226 | Modified by Chishm: 227 | * Removed MultiNDS specific stuff 228 | --------------------------------------------------------------------------*/ 229 | static void startBinary_ARM7 (void) { 230 | REG_IME=0; 231 | while(REG_VCOUNT!=191); 232 | while(REG_VCOUNT==191); 233 | // copy NDS ARM9 start address into the header, starting ARM9 234 | ARM9_START_ADDRESS = temp_arm9_start_address; 235 | ARM9_START_FLAG = 1; 236 | // Start ARM7 237 | VoidFn arm7code = (VoidFn)g_envAppNdsHeader->arm7_entrypoint; 238 | arm7code(); 239 | } 240 | 241 | extern const char mpu_reset[]; 242 | extern const char mpu_reset_end[]; 243 | 244 | int main (void) { 245 | #ifdef NO_DLDI 246 | dsiSD = true; 247 | dsiMode = true; 248 | #endif 249 | 250 | bool ok = false; 251 | MiniFatDiscReadFn readFn; 252 | 253 | #ifndef NO_SDMMC 254 | if (dsiSD && dsiMode) { 255 | sdmmc_controller_init(true); 256 | ok = sdmmc_sdcard_init() == 0; 257 | readFn = sdmmc_sdcard_readsectors; 258 | } 259 | #ifndef NO_DLDI 260 | else 261 | #endif 262 | #endif 263 | #ifndef NO_DLDI 264 | { 265 | ok = _io_dldi.startup(); 266 | readFn = _io_dldi.readSectors; 267 | } 268 | #endif 269 | 270 | u32 fileCluster = storedFileCluster; 271 | // Init card 272 | if(!ok || !minifatInit(&fatState, readFn, 0)) 273 | { 274 | return -1; 275 | } 276 | if (fileCluster < MINIFAT_CLUSTER_FIRST) /* Invalid file cluster specified */ 277 | { 278 | MiniFatDirEnt ent; 279 | fileCluster = minifatFind(&fatState, 0, DEFAULT_BOOT_NAME, &ent); 280 | if (fileCluster < MINIFAT_CLUSTER_FIRST || (ent.attrib & MINIFAT_ATTRIB_DIR)) 281 | { 282 | return -1; 283 | } 284 | } 285 | 286 | // ARM9 clears its memory part 2 287 | // copy ARM9 function to RAM, and make the ARM9 jump to it 288 | armCopyMem32((void*)TEMP_MEM, resetMemory2_ARM9, resetMemory2_ARM9_size); 289 | ARM9_START_ADDRESS = TEMP_MEM; // Make ARM9 jump to the function 290 | // Wait until the ARM9 has completed its task 291 | while (ARM9_START_ADDRESS == TEMP_MEM); 292 | 293 | // ARM9 sets up mpu 294 | // copy ARM9 function to RAM, and make the ARM9 jump to it 295 | armCopyMem32((void*)TEMP_MEM, mpu_reset, mpu_reset_end - mpu_reset); 296 | ARM9_START_ADDRESS = TEMP_MEM; // Make ARM9 jump to the function 297 | // Wait until the ARM9 has completed its task 298 | while (ARM9_START_ADDRESS == TEMP_MEM); 299 | 300 | // Get ARM7 to clear RAM 301 | resetMemory_ARM7(); 302 | 303 | // ARM9 enters a wait loop 304 | // copy ARM9 function to RAM, and make the ARM9 jump to it 305 | armCopyMem32((void*)TEMP_MEM, startBinary_ARM9, startBinary_ARM9_size); 306 | ARM9_START_ADDRESS = TEMP_MEM; // Make ARM9 jump to the function 307 | 308 | // Load the NDS file 309 | loadBinary_ARM7(fileCluster); 310 | 311 | #ifndef NO_DLDI 312 | // Patch with DLDI if desired 313 | if (wantToPatchDLDI) { 314 | dldiPatchBinary((void*)g_envAppNdsHeader->arm9_ram_address, g_envAppNdsHeader->arm9_size, &_dldi_start); 315 | } 316 | #endif 317 | 318 | #ifndef NO_SDMMC 319 | if (dsiSD && dsiMode) { 320 | sdmmc_controller_init(true); 321 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFDu; 322 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDDu; 323 | *(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 0; 324 | } 325 | #endif 326 | 327 | // Pass command line arguments to loaded program 328 | passArgs_ARM7(); 329 | 330 | startBinary_ARM7(); 331 | 332 | return 0; 333 | } 334 | -------------------------------------------------------------------------------- /bootloader/source/boot.h: -------------------------------------------------------------------------------- 1 | #ifndef _BOOT_H_ 2 | #define _BOOT_H_ 3 | 4 | #define resetMemory2_ARM9_size 0x400 5 | void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9(); 6 | #define startBinary_ARM9_size 0x200 7 | void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9 (); 8 | #define ARM9_START_FLAG (*(vu8*)0x02FFFDFB) 9 | 10 | #endif // _BOOT_H_ 11 | -------------------------------------------------------------------------------- /bootloader/source/io_dldi.h: -------------------------------------------------------------------------------- 1 | /* 2 | io_dldi.h 3 | 4 | Reserved space for post-compilation adding of an extra driver 5 | 6 | Copyright (c) 2006 Michael "Chishm" Chisholm 7 | 8 | Redistribution and use in source and binary forms, with or without modification, 9 | are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 3. The name of the author may not be used to endorse or promote products derived 17 | from this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 20 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | 2006-12-22 - Chishm 30 | * Original release 31 | */ 32 | 33 | #ifndef IO_DLDI_H 34 | #define IO_DLDI_H 35 | 36 | #include 37 | 38 | // export interface 39 | extern DLDI_INTERFACE _dldi_start ; 40 | extern DISC_INTERFACE _io_dldi ; 41 | 42 | #endif // define IO_DLDI_H 43 | -------------------------------------------------------------------------------- /bootloader/source/io_dldi.s: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | 3 | Copyright (C) 2005 Michael "Chishm" Chisholm 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | If you use this code, please give due credit and email me about your 20 | project at chishm@hotmail.com 21 | ------------------------------------------------------------------*/ 22 | @--------------------------------------------------------------------------------- 23 | .align 4 24 | .arm 25 | .global _dldi_start 26 | .global _io_dldi 27 | @--------------------------------------------------------------------------------- 28 | .equ FEATURE_MEDIUM_CANREAD, 0x00000001 29 | .equ FEATURE_MEDIUM_CANWRITE, 0x00000002 30 | .equ FEATURE_SLOT_GBA, 0x00000010 31 | .equ FEATURE_SLOT_NDS, 0x00000020 32 | 33 | 34 | _dldi_start: 35 | #ifndef NO_DLDI 36 | 37 | @--------------------------------------------------------------------------------- 38 | @ Driver patch file standard header -- 16 bytes 39 | #ifdef STANDARD_DLDI 40 | .word 0xBF8DA5ED @ Magic number to identify this region 41 | #else 42 | .word 0xBF8DA5EE @ Magic number to identify this region 43 | #endif 44 | .asciz " Chishm" @ Identifying Magic string (8 bytes with null terminator) 45 | .byte 0x01 @ Version number 46 | .byte 0x0e @ 16KiB @ Log [base-2] of the size of this driver in bytes. 47 | .byte 0x00 @ Sections to fix 48 | .byte 0x0e @ 16KiB @ Log [base-2] of the allocated space in bytes. 49 | 50 | @--------------------------------------------------------------------------------- 51 | @ Text identifier - can be anything up to 47 chars + terminating null -- 16 bytes 52 | .align 4 53 | .asciz "Loader (No interface)" 54 | 55 | @--------------------------------------------------------------------------------- 56 | @ Offsets to important sections within the data -- 32 bytes 57 | .align 6 58 | .word _dldi_start @ data start 59 | .word _dldi_end @ data end 60 | .word 0x00000000 @ Interworking glue start -- Needs address fixing 61 | .word 0x00000000 @ Interworking glue end 62 | .word 0x00000000 @ GOT start -- Needs address fixing 63 | .word 0x00000000 @ GOT end 64 | .word 0x00000000 @ bss start -- Needs setting to zero 65 | .word 0x00000000 @ bss end 66 | @--------------------------------------------------------------------------------- 67 | @ IO_INTERFACE data -- 32 bytes 68 | _io_dldi: 69 | .ascii "DLDI" @ ioType 70 | .word 0x00000000 @ Features 71 | .word _DLDI_startup @ 72 | .word _DLDI_isInserted @ 73 | .word _DLDI_readSectors @ Function pointers to standard device driver functions 74 | .word _DLDI_writeSectors @ 75 | .word _DLDI_clearStatus @ 76 | .word _DLDI_shutdown @ 77 | 78 | 79 | @--------------------------------------------------------------------------------- 80 | 81 | _DLDI_startup: 82 | _DLDI_isInserted: 83 | _DLDI_readSectors: 84 | _DLDI_writeSectors: 85 | _DLDI_clearStatus: 86 | _DLDI_shutdown: 87 | mov r0, #0x00 @ Return false for every function 88 | bx lr 89 | 90 | 91 | 92 | @--------------------------------------------------------------------------------- 93 | .align 94 | .pool 95 | 96 | .space (_dldi_start + 16384) - . @ Fill to 16KiB 97 | 98 | _dldi_end: 99 | .end 100 | @--------------------------------------------------------------------------------- 101 | #else 102 | @--------------------------------------------------------------------------------- 103 | @ IO_INTERFACE data -- 32 bytes 104 | _io_dldi: 105 | .ascii "DLDI" @ ioType 106 | .word 0x00000000 @ Features 107 | .word _DLDI_startup @ 108 | .word _DLDI_isInserted @ 109 | .word _DLDI_readSectors @ Function pointers to standard device driver functions 110 | .word _DLDI_writeSectors @ 111 | .word _DLDI_clearStatus @ 112 | .word _DLDI_shutdown @ 113 | 114 | _DLDI_startup: 115 | _DLDI_isInserted: 116 | _DLDI_readSectors: 117 | _DLDI_writeSectors: 118 | _DLDI_clearStatus: 119 | _DLDI_shutdown: 120 | mov r0, #0x00 @ Return false for every function 121 | bx lr 122 | 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /bootloader/source/load_crt0.s: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | 3 | Copyright (C) 2005 Michael "Chishm" Chisholm 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | If you use this code, please give due credit and email me about your 20 | project at chishm@hotmail.com 21 | ------------------------------------------------------------------*/ 22 | 23 | @--------------------------------------------------------------------------------- 24 | .section ".init" 25 | .global _start 26 | .global storedFileCluster 27 | .global wantToPatchDLDI 28 | .global argStart 29 | .global argSize 30 | .global dsiSD 31 | .global dsiMode 32 | @--------------------------------------------------------------------------------- 33 | .align 4 34 | .arm 35 | @--------------------------------------------------------------------------------- 36 | _start: 37 | @--------------------------------------------------------------------------------- 38 | b startUp 39 | 40 | storedFileCluster: 41 | .word 0 @ default BOOT.NDS 42 | wantToPatchDLDI: 43 | .word 0x00000001 @ by default patch the DLDI section of the loaded NDS 44 | @ Used for passing arguments to the loaded app 45 | argStart: 46 | .word _end - _start 47 | argSize: 48 | .word 0x00000000 49 | dldiOffset: 50 | .word _dldi_start - _start 51 | dsiSD: 52 | .word 0 53 | dsiMode: 54 | .word 0 55 | 56 | startUp: 57 | mov r0, #0x04000000 58 | mov r1, #0 59 | str r1, [r0,#0x208] @ REG_IME 60 | str r1, [r0,#0x210] @ REG_IE 61 | str r1, [r0,#0x218] @ REG_AUXIE 62 | 63 | mov r0, #0x12 @ Switch to IRQ Mode 64 | msr cpsr, r0 65 | ldr sp, =__sp_irq @ Set IRQ stack 66 | 67 | mov r0, #0x13 @ Switch to SVC Mode 68 | msr cpsr, r0 69 | ldr sp, =__sp_svc @ Set SVC stack 70 | 71 | mov r0, #0x1F @ Switch to System Mode 72 | msr cpsr, r0 73 | ldr sp, =__sp_usr @ Set user stack 74 | 75 | ldr r0, =__bss_start @ Clear BSS section to 0x00 76 | ldr r1, =__bss_end 77 | sub r1, r1, r0 78 | bl ClearMem 79 | 80 | mov r0, #0 @ int argc 81 | mov r1, #0 @ char *argv[] 82 | ldr r3, =main 83 | bl _blx_r3_stub @ jump to user code 84 | 85 | @ If the user ever returns, restart 86 | b _start 87 | 88 | @--------------------------------------------------------------------------------- 89 | _blx_r3_stub: 90 | @--------------------------------------------------------------------------------- 91 | bx r3 92 | 93 | @--------------------------------------------------------------------------------- 94 | @ Clear memory to 0x00 if length != 0 95 | @ r0 = Start Address 96 | @ r1 = Length 97 | @--------------------------------------------------------------------------------- 98 | ClearMem: 99 | @--------------------------------------------------------------------------------- 100 | mov r2, #3 @ Round down to nearest word boundary 101 | add r1, r1, r2 @ Shouldn't be needed 102 | bics r1, r1, r2 @ Clear 2 LSB (and set Z) 103 | bxeq lr @ Quit if copy size is 0 104 | 105 | mov r2, #0 106 | ClrLoop: 107 | stmia r0!, {r2} 108 | subs r1, r1, #4 109 | bne ClrLoop 110 | bx lr 111 | 112 | @--------------------------------------------------------------------------------- 113 | @ Copy memory if length != 0 114 | @ r1 = Source Address 115 | @ r2 = Dest Address 116 | @ r4 = Dest Address + Length 117 | @--------------------------------------------------------------------------------- 118 | CopyMemCheck: 119 | @--------------------------------------------------------------------------------- 120 | sub r3, r4, r2 @ Is there any data to copy? 121 | @--------------------------------------------------------------------------------- 122 | @ Copy memory 123 | @ r1 = Source Address 124 | @ r2 = Dest Address 125 | @ r3 = Length 126 | @--------------------------------------------------------------------------------- 127 | CopyMem: 128 | @--------------------------------------------------------------------------------- 129 | mov r0, #3 @ These commands are used in cases where 130 | add r3, r3, r0 @ the length is not a multiple of 4, 131 | bics r3, r3, r0 @ even though it should be. 132 | bxeq lr @ Length is zero, so exit 133 | CIDLoop: 134 | ldmia r1!, {r0} 135 | stmia r2!, {r0} 136 | subs r3, r3, #4 137 | bne CIDLoop 138 | bx lr 139 | 140 | @--------------------------------------------------------------------------------- 141 | .align 142 | .pool 143 | .end 144 | @--------------------------------------------------------------------------------- 145 | -------------------------------------------------------------------------------- /bootloader/source/minifat.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | 3 | minifat.c -- Minimal FAT filesystem driver 4 | 5 | Copyright (c) 2023 fincs 6 | Loosely based on code by Michael "Chishm" Chisholm 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any 10 | damages arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any 13 | purpose, including commercial applications, and to alter it and 14 | redistribute it freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you 17 | must not claim that you wrote the original software. If you use 18 | this software in a product, an acknowledgment in the product 19 | documentation would be appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and 21 | must not be misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source 23 | distribution. 24 | 25 | ------------------------------------------------------------------*/ 26 | 27 | #include "minifat.h" 28 | #include 29 | 30 | #ifdef MINIFAT_DEBUG 31 | #include 32 | #else 33 | #define dietPrint(...) ((void)0) 34 | #endif 35 | 36 | // First FAT cluster number used to indicate invalid/unusable cluster numbers 37 | #define FAT12_BAD_CLUSTER 0x0ff7 38 | #define FAT16_BAD_CLUSTER 0xfff7 39 | #define FAT32_BAD_CLUSTER 0x0ffffff7 40 | 41 | // Maximum number of clusters representable in the given FAT type 42 | #define MAX_CLUSTERS(_type) (_type##_BAD_CLUSTER-MINIFAT_CLUSTER_FIRST) 43 | 44 | // MBR partition information 45 | typedef struct { 46 | u8 status; 47 | u8 startHead; 48 | u16 startCySc; 49 | u8 type; 50 | u8 endHead; 51 | u16 endCySc; 52 | u16 lbaStartLo; 53 | u16 lbaStartHi; 54 | u16 lbaSizeLo; 55 | u16 lbaSizeHi; 56 | } PARTITION; 57 | 58 | _Static_assert(sizeof(PARTITION) == 0x10); 59 | 60 | // BIOS Parameter Block 61 | typedef struct { 62 | u16 bytesPerSector; 63 | u8 sectorsPerCluster; 64 | u16 reservedSectors; 65 | u8 numFATs; 66 | u16 rootEntries; 67 | u16 numSectorsSmall; 68 | u8 mediaDesc; 69 | u16 sectorsPerFAT; 70 | u16 sectorsPerTrk; 71 | u16 numHeads; 72 | u32 numHiddenSectors; 73 | u32 numSectors; 74 | } MK_PACKED BIOS_BPB; 75 | 76 | // Boot Sector - must be packed 77 | typedef struct { 78 | u8 jmpBoot[3]; 79 | u8 OEMName[8]; 80 | BIOS_BPB bpb; 81 | 82 | union { // Different types of extended BIOS Parameter Block for FAT16 and FAT32 83 | struct { 84 | // Ext BIOS Parameter Block for FAT16 85 | u8 driveNumber; 86 | u8 reserved1; 87 | u8 extBootSig; 88 | u32 volumeID; 89 | u8 volumeLabel[11]; 90 | u8 fileSysType[8]; 91 | // Bootcode 92 | u8 bootCode[448]; 93 | } MK_PACKED fat16; 94 | 95 | struct { 96 | // FAT32 extended block 97 | u32 sectorsPerFAT32; 98 | u16 extFlags; 99 | u16 fsVer; 100 | u32 rootClus; 101 | u16 fsInfo; 102 | u16 bkBootSec; 103 | u8 reserved[12]; 104 | // Ext BIOS Parameter Block for FAT16 105 | u8 driveNumber; 106 | u8 reserved1; 107 | u8 extBootSig; 108 | u32 volumeID; 109 | u8 volumeLabel[11]; 110 | u8 fileSysType[8]; 111 | // Bootcode 112 | u8 bootCode[420]; 113 | } MK_PACKED fat32; 114 | } extBlock; 115 | 116 | u16 bootSig; 117 | } BOOT_SEC; 118 | 119 | _Static_assert(sizeof(BOOT_SEC) == MINIFAT_SECTOR_SZ); 120 | 121 | MK_INLINE u32 _minifatClustToSec(MiniFat* fat, u32 cluster) 122 | { 123 | return fat->dataStart + ((cluster-MINIFAT_CLUSTER_FIRST) << fat->clusterShift); 124 | } 125 | 126 | MK_CONSTEXPR char _minifatToUpper(char c) 127 | { 128 | return c >= 'a' && c <= 'z' ? c + 'A' - 'a' : c; 129 | } 130 | 131 | MK_CONSTEXPR bool _minifatIsNonZeroPo2(unsigned x) 132 | { 133 | return x != 0 && (x & (x-1)) == 0; 134 | } 135 | 136 | static bool _minifatLoadBuffer(MiniFat* fat, u32 sector) 137 | { 138 | bool ok = true; 139 | if (fat->bufferPos != sector) { 140 | ok = fat->discReadFn(sector, 1, fat->buffer); 141 | if (ok) { 142 | fat->bufferPos = sector; 143 | } 144 | } 145 | return ok; 146 | } 147 | 148 | static bool _minifatInitParams(MiniFat* fat) 149 | { 150 | BOOT_SEC* buf = (BOOT_SEC*)fat->buffer; 151 | 152 | // Check for a valid boot signature 153 | if (buf->bootSig != 0xaa55) { 154 | dietPrint("Bad boot sig\n"); 155 | return false; 156 | } 157 | 158 | // Check for a valid Microsoft VBR 159 | unsigned jmp = buf->jmpBoot[0]; 160 | if (jmp != 0xeb && jmp != 0xe9 && jmp != 0xe8) { 161 | dietPrint("Bad jump opcode\n"); 162 | goto _isUnknown; 163 | } 164 | 165 | /* Not needed 166 | static const u8 fat32_ident[] = { 'F', 'A', 'T', '3', '2', ' ', ' ', ' ' }; 167 | if (memcmp(buf->extBlock.fat32.fileSysType, fat32_ident, 8) == 0) { 168 | //... etc 169 | } 170 | */ 171 | 172 | // Retrieve FAT size 173 | u32 sectorsPerFat = buf->bpb.sectorsPerFAT; 174 | if (!sectorsPerFat) { 175 | sectorsPerFat = buf->extBlock.fat32.sectorsPerFAT32; 176 | } 177 | 178 | // Validate basic FAT VBR parameters 179 | if ( 180 | buf->bpb.bytesPerSector != MINIFAT_SECTOR_SZ || // Only supporting 512-byte sectors 181 | !_minifatIsNonZeroPo2(buf->bpb.sectorsPerCluster) || 182 | buf->bpb.reservedSectors == 0 || 183 | (buf->bpb.numFATs != 1 && buf->bpb.numFATs != 2) || 184 | (buf->bpb.numSectorsSmall < 0x80 && buf->bpb.numSectors < 0x10000) || 185 | sectorsPerFat == 0 186 | ) { 187 | dietPrint("Bad FAT validation\n"); 188 | goto _isUnknown; 189 | } 190 | 191 | // Calculate total volume size in sectors 192 | u32 totalSectors = buf->bpb.numSectorsSmall; 193 | if (!totalSectors) { 194 | totalSectors = buf->bpb.numSectors; 195 | } 196 | 197 | // Initialize FAT parameters 198 | fat->clusterSectors = buf->bpb.sectorsPerCluster; 199 | fat->clusterShift = 0; 200 | fat->fatStart = fat->bufferPos + buf->bpb.reservedSectors; 201 | fat->rootDirStart = fat->fatStart + buf->bpb.numFATs*sectorsPerFat; 202 | fat->dataStart = fat->rootDirStart + (buf->bpb.rootEntries*sizeof(MiniFatDirEnt) + MINIFAT_SECTOR_SZ-1) / MINIFAT_SECTOR_SZ; 203 | 204 | // Calculate cluster sector size shift (log2) 205 | while ((1U << fat->clusterShift) != fat->clusterSectors) { 206 | fat->clusterShift ++; 207 | } 208 | 209 | // Calculate number of clusters 210 | u32 numClusters = (fat->bufferPos + totalSectors - fat->dataStart) >> fat->clusterShift; 211 | dietPrint("! FAT num clus 0x%lx\n", numClusters); 212 | 213 | // Discriminate FAT version according to number of clusters 214 | // XX: Note that the official FAT specification is off-by-one here. 215 | // In any case, well-behaved formatting tools will avoid volume sizes 216 | // dangerously close to the problematic decision boundaries. 217 | if (numClusters > MAX_CLUSTERS(FAT32)) { 218 | goto _isUnknown; 219 | } else if (numClusters > MAX_CLUSTERS(FAT16)) { 220 | // FAT32 - validate parameters 221 | if ( 222 | buf->bpb.rootEntries != 0 || 223 | buf->extBlock.fat32.fsVer != 0 || 224 | buf->extBlock.fat32.rootClus < MINIFAT_CLUSTER_FIRST || 225 | buf->extBlock.fat32.rootClus >= (MINIFAT_CLUSTER_FIRST + numClusters) 226 | ) { 227 | dietPrint("Bad FAT32 params\n"); 228 | goto _isUnknown; 229 | } 230 | 231 | // Select the correct FAT if mirroring is disabled 232 | // XX: FatFs does not implement this for some reason 233 | if (buf->extBlock.fat32.extFlags & 0x80) { 234 | fat->fatStart += (buf->extBlock.fat32.extFlags & 0x0f)*sectorsPerFat; 235 | } 236 | 237 | fat->fsType = MiniFatType_Fat32; 238 | fat->rootDirCluster = buf->extBlock.fat32.rootClus; 239 | fat->rootDirStart = _minifatClustToSec(fat, fat->rootDirCluster); 240 | } else { 241 | // FAT12/16 - validate parameters 242 | if (buf->bpb.rootEntries == 0) { 243 | dietPrint("Bad FAT12/16 params\n"); 244 | goto _isUnknown; 245 | } 246 | 247 | fat->fsType = numClusters > MAX_CLUSTERS(FAT12) ? MiniFatType_Fat16 : MiniFatType_Fat12; 248 | fat->rootDirCluster = MINIFAT_CLUSTER_FREE; // FAT12/16 store the root dir outside of the data area 249 | } 250 | 251 | return true; 252 | 253 | _isUnknown: 254 | fat->fsType = MiniFatType_Unknown; 255 | return true; 256 | } 257 | 258 | static bool _minifatInitPartition(MiniFat* fat, u32 sector) 259 | { 260 | return 261 | sector != 0 && 262 | _minifatLoadBuffer(fat, sector) && 263 | _minifatInitParams(fat) && 264 | fat->fsType != MiniFatType_Unknown; 265 | } 266 | 267 | bool minifatInit(MiniFat* fat, MiniFatDiscReadFn discReadFn, unsigned partitionIndex) 268 | { 269 | if (partitionIndex > 4) { 270 | return false; 271 | } 272 | 273 | fat->discReadFn = discReadFn; 274 | fat->bufferPos = UINT32_MAX; 275 | 276 | // Read and parse first sector of card 277 | if (!_minifatLoadBuffer(fat, 0) || !_minifatInitParams(fat)) { 278 | return false; 279 | } 280 | 281 | // If we have a valid boot sector but not a valid filesystem, check MBR 282 | if (fat->fsType == MiniFatType_Unknown) { 283 | // Extract partition offsets from MBR 284 | u32 partOffsets[4]; 285 | for (unsigned i = 0; i < 4; i ++) { 286 | PARTITION* part = &((PARTITION*)&fat->buffer[0x1be])[i]; 287 | 288 | // Fail if MBR is malformed 289 | if (part->status != 0x80 && part->status != 0x00) { 290 | return false; 291 | } 292 | 293 | // Ignore unpopulated/extended partitions 294 | if (part->type == 0x00 || part->type == 0x05 || part->type == 0x0f) { 295 | partOffsets[i] = 0; 296 | } else { 297 | partOffsets[i] = part->lbaStartLo | (part->lbaStartHi << 16); 298 | } 299 | } 300 | 301 | // Try to initialize the specified partition, or the first partition that succeeds 302 | bool ok = false; 303 | if (partitionIndex) { 304 | ok = _minifatInitPartition(fat, partOffsets[partitionIndex-1]); 305 | } else for (unsigned i = 0; !ok && i < 4; i ++) { 306 | ok = _minifatInitPartition(fat, partOffsets[i]); 307 | } 308 | 309 | // If above did not successfully initialize a partition, fail 310 | if (!ok) { 311 | return false; 312 | } 313 | } 314 | 315 | dietPrint("! FAT at 0x%lx type%u\n", fat->bufferPos, fat->fsType); 316 | dietPrint(" clusSect=%u clusShift=%u\n", fat->clusterSectors, fat->clusterShift); 317 | return true; 318 | } 319 | 320 | u32 minifatNextCluster(MiniFat* fat, u32 cluster) 321 | { 322 | u32 sector; 323 | u32 offset; 324 | u32 ret = MINIFAT_CLUSTER_FREE; 325 | 326 | if (cluster < MINIFAT_CLUSTER_FIRST) { 327 | return cluster; 328 | } 329 | 330 | switch (fat->fsType) { 331 | default: 332 | case MiniFatType_Unknown: 333 | break; 334 | 335 | case MiniFatType_Fat12: 336 | sector = fat->fatStart + (((cluster * 3) / 2) / MINIFAT_SECTOR_SZ); 337 | offset = ((cluster * 3) / 2) % MINIFAT_SECTOR_SZ; 338 | if (!_minifatLoadBuffer(fat, sector)) { 339 | break; 340 | } 341 | 342 | ret = fat->buffer[offset]; 343 | offset++; 344 | 345 | if (offset >= MINIFAT_SECTOR_SZ) { 346 | offset = 0; 347 | sector++; 348 | } 349 | 350 | if (!_minifatLoadBuffer(fat, sector)) { 351 | break; 352 | } 353 | 354 | ret |= fat->buffer[offset] << 8; 355 | 356 | if (cluster & 1) { 357 | ret >>= 4; 358 | } else { 359 | ret &= 0x0fff; 360 | } 361 | 362 | if (ret >= FAT12_BAD_CLUSTER) { 363 | ret = MINIFAT_CLUSTER_EOF; 364 | } 365 | break; 366 | 367 | case MiniFatType_Fat16: 368 | sector = fat->fatStart + ((cluster << 1) / MINIFAT_SECTOR_SZ); 369 | offset = cluster % (MINIFAT_SECTOR_SZ >> 1); 370 | if (!_minifatLoadBuffer(fat, sector)) { 371 | break; 372 | } 373 | 374 | // read the next cluster value 375 | ret = ((u16*)fat->buffer)[offset]; 376 | 377 | if (ret >= FAT16_BAD_CLUSTER) { 378 | ret = MINIFAT_CLUSTER_EOF; 379 | } 380 | break; 381 | 382 | case MiniFatType_Fat32: 383 | sector = fat->fatStart + ((cluster << 2) / MINIFAT_SECTOR_SZ); 384 | offset = cluster % (MINIFAT_SECTOR_SZ >> 2); 385 | if (!_minifatLoadBuffer(fat, sector)) { 386 | break; 387 | } 388 | 389 | // read the next cluster value 390 | ret = ((u32*)fat->buffer)[offset] & 0x0fffffff; 391 | 392 | if (ret >= FAT32_BAD_CLUSTER) { 393 | ret = MINIFAT_CLUSTER_EOF; 394 | } 395 | break; 396 | } 397 | 398 | return ret; 399 | } 400 | 401 | MK_INLINE void _minifatMake8dot3(char* buf, const char* name) 402 | { 403 | unsigned i = 0; 404 | 405 | // Copy filename 406 | for (; i < 8 && *name && *name != '.'; i ++, name ++) { 407 | buf[i] = _minifatToUpper(*name); 408 | } 409 | 410 | // Pad filename 411 | for (; i < 8; i ++) { 412 | buf[i] = ' '; 413 | } 414 | 415 | // Skip extension dot 416 | if (*name == '.') name++; 417 | 418 | // Copy extension 419 | for (; i < 8+3 && *name; i ++, name ++) { 420 | buf[i] = _minifatToUpper(*name); 421 | } 422 | 423 | // Pad extension 424 | for (; i < 8+3; i ++) { 425 | buf[i] = ' '; 426 | } 427 | } 428 | 429 | u32 minifatFind(MiniFat* fat, u32 dirCluster, const char* name, MiniFatDirEnt* out_entry) 430 | { 431 | char name8dot3[8+3]; 432 | _minifatMake8dot3(name8dot3, name); 433 | 434 | u32 baseSector; 435 | if (dirCluster < MINIFAT_CLUSTER_FIRST) { 436 | dirCluster = fat->rootDirCluster; 437 | baseSector = fat->rootDirStart; 438 | } else { 439 | baseSector = _minifatClustToSec(fat, dirCluster); 440 | } 441 | 442 | u32 sectorOff = 0; 443 | for (;;) { 444 | if (!_minifatLoadBuffer(fat, baseSector + sectorOff)) { 445 | return MINIFAT_CLUSTER_EOF; 446 | } 447 | 448 | MiniFatDirEnt* entries = (void*)fat->buffer; 449 | for (unsigned i = 0; i < MINIFAT_SECTOR_SZ / sizeof(MiniFatDirEnt); i ++) { 450 | MiniFatDirEnt* entry = &entries[i]; 451 | 452 | if (entry->name[0] == 0x00) { 453 | return MINIFAT_CLUSTER_EOF; 454 | } 455 | 456 | if (entry->name[0] == 0xe5 || (entry->attrib & MINIFAT_ATTRIB_VOLUME)) { 457 | continue; 458 | } 459 | 460 | if (__builtin_memcmp(entry->name_ext, name8dot3, 8+3) == 0) { 461 | if (out_entry) *out_entry = *entry; 462 | return entry->startCluster | (entry->startClusterHigh << 16); 463 | } 464 | } 465 | 466 | sectorOff ++; 467 | 468 | if (dirCluster >= MINIFAT_CLUSTER_FIRST) { 469 | if (sectorOff == fat->clusterSectors) { 470 | dirCluster = minifatNextCluster(fat, dirCluster); 471 | if (dirCluster < MINIFAT_CLUSTER_FIRST) { 472 | return MINIFAT_CLUSTER_EOF; 473 | } 474 | 475 | baseSector = _minifatClustToSec(fat, dirCluster); 476 | sectorOff = 0; 477 | } 478 | } else if (baseSector + sectorOff == fat->dataStart) { 479 | return MINIFAT_CLUSTER_EOF; 480 | } 481 | } 482 | } 483 | 484 | MK_INLINE bool _minifatLargeRead(MiniFat* fat, void** pBuffer, u32* pTotalReadLen, u32 readPos, u32 readLen) 485 | { 486 | bool ok = fat->discReadFn(readPos, readLen, *pBuffer); 487 | if (ok) { 488 | u32 readBytes = readLen * MINIFAT_SECTOR_SZ; 489 | *pBuffer = (u8*)*pBuffer + readBytes; 490 | *pTotalReadLen += readBytes; 491 | } 492 | return ok; 493 | } 494 | 495 | u32 minifatRead(MiniFat* fat, u32 objCluster, void* buffer, u32 offset, u32 length) 496 | { 497 | const u32 clusterSectors = fat->clusterSectors; 498 | const u32 clusterBytes = MINIFAT_SECTOR_SZ << fat->clusterShift; 499 | 500 | // All offsets must be word aligned 501 | if (((uptr)buffer | offset | length) & 3) { 502 | return 0; 503 | } 504 | 505 | // Skip over initial clusters 506 | while (objCluster >= MINIFAT_CLUSTER_FIRST && offset >= clusterBytes) { 507 | offset -= clusterBytes; 508 | objCluster = minifatNextCluster(fat, objCluster); 509 | } 510 | 511 | // Fail early if EOF 512 | if (objCluster < MINIFAT_CLUSTER_FIRST) { 513 | return 0; 514 | } 515 | 516 | // Initialize variables 517 | u32 totalReadLen = 0; 518 | u32 baseSector = _minifatClustToSec(fat, objCluster); 519 | u32 sectorOff = offset / MINIFAT_SECTOR_SZ; 520 | offset &= MINIFAT_SECTOR_SZ-1; 521 | 522 | // Read the head if needed 523 | if (offset || length < MINIFAT_SECTOR_SZ) { 524 | if (!_minifatLoadBuffer(fat, baseSector+sectorOff)) { 525 | return 0; 526 | } 527 | 528 | u32 maxLen = MINIFAT_SECTOR_SZ - offset; 529 | totalReadLen = length < maxLen ? length : maxLen; 530 | armCopyMem32(buffer, &fat->buffer[offset], totalReadLen); 531 | 532 | buffer = (u8*)buffer + totalReadLen; 533 | sectorOff ++; 534 | length -= totalReadLen; 535 | } 536 | 537 | // Main body read loop, iterating through clusters 538 | u32 accumReadPos = 0; 539 | u32 accumReadLen = 0; 540 | while (length >= MINIFAT_SECTOR_SZ) { 541 | u32 maxSectors = clusterSectors - sectorOff; 542 | u32 remSectors = length / MINIFAT_SECTOR_SZ; 543 | 544 | u32 readLen = remSectors < maxSectors ? remSectors : maxSectors; 545 | if_likely (readLen) { 546 | u32 readPos = baseSector+sectorOff; 547 | if_likely (accumReadLen) { 548 | u32 nextReadPos = accumReadPos + accumReadLen; 549 | if_likely (nextReadPos == readPos) { 550 | accumReadLen += readLen; 551 | } else if (!_minifatLargeRead(fat, &buffer, &totalReadLen, accumReadPos, accumReadLen)) { 552 | return totalReadLen; 553 | } else { 554 | goto _startNewRead; 555 | } 556 | } else { 557 | _startNewRead: 558 | accumReadPos = readPos; 559 | accumReadLen = readLen; 560 | } 561 | 562 | sectorOff += readLen; 563 | length -= readLen * MINIFAT_SECTOR_SZ; 564 | } 565 | 566 | if (length && sectorOff >= clusterSectors) { 567 | objCluster = minifatNextCluster(fat, objCluster); 568 | if (objCluster < MINIFAT_CLUSTER_FIRST) { 569 | return totalReadLen; 570 | } 571 | 572 | baseSector = _minifatClustToSec(fat, objCluster); 573 | sectorOff = 0; 574 | } 575 | } 576 | 577 | if (accumReadLen && !_minifatLargeRead(fat, &buffer, &totalReadLen, accumReadPos, accumReadLen)) { 578 | return totalReadLen; 579 | } 580 | 581 | // Read the tail if needed 582 | if (length) { 583 | if (!_minifatLoadBuffer(fat, baseSector+sectorOff)) { 584 | return totalReadLen; 585 | } 586 | 587 | armCopyMem32(buffer, &fat->buffer[0], length); 588 | totalReadLen += length; 589 | } 590 | 591 | return totalReadLen; 592 | } 593 | -------------------------------------------------------------------------------- /bootloader/source/minifat.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | 3 | minifat.h -- Minimal FAT filesystem driver 4 | 5 | Copyright (c) 2023 fincs 6 | Loosely based on code by Michael "Chishm" Chisholm 7 | 8 | This software is provided 'as-is', without any express or implied 9 | warranty. In no event will the authors be held liable for any 10 | damages arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any 13 | purpose, including commercial applications, and to alter it and 14 | redistribute it freely, subject to the following restrictions: 15 | 16 | 1. The origin of this software must not be misrepresented; you 17 | must not claim that you wrote the original software. If you use 18 | this software in a product, an acknowledgment in the product 19 | documentation would be appreciated but is not required. 20 | 2. Altered source versions must be plainly marked as such, and 21 | must not be misrepresented as being the original software. 22 | 3. This notice may not be removed or altered from any source 23 | distribution. 24 | 25 | ------------------------------------------------------------------*/ 26 | /*! \file minifat.h 27 | \brief Minimal FAT filesystem driver 28 | */ 29 | 30 | #pragma once 31 | #include 32 | 33 | #define MINIFAT_SECTOR_SZ 512 //!< Size of a sector in bytes 34 | 35 | #define MINIFAT_CLUSTER_FREE 0 //!< Free cluster 36 | #define MINIFAT_CLUSTER_EOF 1 //!< End-of-file marker 37 | #define MINIFAT_CLUSTER_FIRST 2 //!< First valid cluster 38 | 39 | #define MINIFAT_ATTRIB_ARCHIVE 0x20 //!< Archive attribute 40 | #define MINIFAT_ATTRIB_DIR 0x10 //!< Directory attribute 41 | #define MINIFAT_ATTRIB_LFN 0x0f //!< Long File Name attribute combination 42 | #define MINIFAT_ATTRIB_VOLUME 0x08 //!< Volume attribute 43 | #define MINIFAT_ATTRIB_HIDDEN 0x02 //!< Hidden attribute 44 | #define MINIFAT_ATTRIB_SYSTEM 0x04 //!< System attribute 45 | #define MINIFAT_ATTRIB_READONLY 0x01 //!< Read-only attribute 46 | 47 | //! Disc read callback function type 48 | typedef bool (*MiniFatDiscReadFn)(u32 sector, u32 numSectors, void* buffer); 49 | 50 | //! FAT filesystem type 51 | typedef enum MiniFatType { 52 | MiniFatType_Unknown, 53 | MiniFatType_Fat12, 54 | MiniFatType_Fat16, 55 | MiniFatType_Fat32, 56 | } MiniFatType; 57 | 58 | //! FAT filesystem driver state 59 | typedef struct MiniFat { 60 | alignas(4) u8 buffer[MINIFAT_SECTOR_SZ]; 61 | 62 | MiniFatDiscReadFn discReadFn; 63 | u32 bufferPos; 64 | 65 | MiniFatType fsType; 66 | u8 clusterSectors; 67 | u8 clusterShift; 68 | 69 | u32 fatStart; 70 | u32 rootDirStart; 71 | u32 rootDirCluster; 72 | u32 dataStart; 73 | } MiniFat; 74 | 75 | //! FAT directory entry 76 | typedef struct MiniFatDirEnt { 77 | union { 78 | char name_ext[8+3]; 79 | struct { 80 | char name[8]; 81 | char ext[3]; 82 | }; 83 | }; 84 | u8 attrib; 85 | u8 reserved; 86 | u8 cTime_ms; 87 | u16 cTime; 88 | u16 cDate; 89 | u16 aDate; 90 | u16 startClusterHigh; 91 | u16 mTime; 92 | u16 mDate; 93 | u16 startCluster; 94 | u32 fileSize; 95 | } MiniFatDirEnt; 96 | 97 | //! Initialize 98 | bool minifatInit(MiniFat* fat, MiniFatDiscReadFn discReadFn, unsigned partitionIndex); 99 | 100 | //! Return 101 | u32 minifatNextCluster(MiniFat* fat, u32 cluster); 102 | 103 | //! Find 104 | u32 minifatFind(MiniFat* fat, u32 dirCluster, const char* name, MiniFatDirEnt* out_entry); 105 | 106 | //! Read 107 | u32 minifatRead(MiniFat* fat, u32 objCluster, void* buffer, u32 offset, u32 length); 108 | -------------------------------------------------------------------------------- /bootloader/source/sdmmc.c: -------------------------------------------------------------------------------- 1 | #ifndef NO_SDMMC 2 | #include 3 | #include 4 | #include "sdmmc.h" 5 | 6 | static struct mmcdevice deviceSD; 7 | 8 | //--------------------------------------------------------------------------------- 9 | static int geterror(struct mmcdevice *ctx) { 10 | //--------------------------------------------------------------------------------- 11 | //if(ctx->error == 0x4) return -1; 12 | //else return 0; 13 | return (ctx->error << 29) >> 31; 14 | } 15 | 16 | 17 | //--------------------------------------------------------------------------------- 18 | static void setTarget(struct mmcdevice *ctx) { 19 | //--------------------------------------------------------------------------------- 20 | sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber); 21 | setckl(ctx->clk); 22 | if (ctx->SDOPT == 0) { 23 | sdmmc_mask16(REG_SDOPT, 0, 0x8000); 24 | } else { 25 | sdmmc_mask16(REG_SDOPT, 0x8000, 0); 26 | } 27 | 28 | } 29 | 30 | 31 | //--------------------------------------------------------------------------------- 32 | static void sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args) { 33 | //--------------------------------------------------------------------------------- 34 | int i; 35 | bool getSDRESP = (cmd << 15) >> 31; 36 | uint16_t flags = (cmd << 15) >> 31; 37 | const bool readdata = cmd & 0x20000; 38 | const bool writedata = cmd & 0x40000; 39 | 40 | if(readdata || writedata) 41 | { 42 | flags |= TMIO_STAT0_DATAEND; 43 | } 44 | 45 | ctx->error = 0; 46 | while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working? 47 | sdmmc_write16(REG_SDIRMASK0,0); 48 | sdmmc_write16(REG_SDIRMASK1,0); 49 | sdmmc_write16(REG_SDSTATUS0,0); 50 | sdmmc_write16(REG_SDSTATUS1,0); 51 | sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); 52 | sdmmc_write16(REG_SDCMDARG1,args >> 16); 53 | sdmmc_write16(REG_SDCMD,cmd &0xFFFF); 54 | 55 | uint32_t size = ctx->size; 56 | uint16_t *dataPtr = (uint16_t*)ctx->data; 57 | uint32_t *dataPtr32 = (uint32_t*)ctx->data; 58 | 59 | bool useBuf = ( NULL != dataPtr ); 60 | bool useBuf32 = (useBuf && (0 == (3 & ((uint32_t)dataPtr)))); 61 | 62 | uint16_t status0 = 0; 63 | 64 | while(1) { 65 | volatile uint16_t status1 = sdmmc_read16(REG_SDSTATUS1); 66 | volatile uint16_t ctl32 = sdmmc_read16(REG_SDDATACTL32); 67 | if((ctl32 & 0x100)) 68 | { 69 | if(readdata) { 70 | if(useBuf) { 71 | sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); 72 | if(size > 0x1FF) { 73 | if(useBuf32) { 74 | for(i = 0; i<0x200; i+=4) { 75 | *dataPtr32++ = sdmmc_read32(REG_SDFIFO32); 76 | } 77 | } else { 78 | for(i = 0; i<0x200; i+=2) { 79 | *dataPtr++ = sdmmc_read16(REG_SDFIFO); 80 | } 81 | } 82 | size -= 0x200; 83 | } 84 | } 85 | 86 | sdmmc_mask16(REG_SDDATACTL32, 0x800, 0); 87 | } 88 | } 89 | 90 | if(status1 & TMIO_MASK_GW) { 91 | ctx->error |= 4; 92 | break; 93 | } 94 | 95 | if(!(status1 & TMIO_STAT1_CMD_BUSY)) { 96 | status0 = sdmmc_read16(REG_SDSTATUS0); 97 | if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) { 98 | ctx->error |= 0x1; 99 | } 100 | if(status0 & TMIO_STAT0_DATAEND) { 101 | ctx->error |= 0x2; 102 | } 103 | 104 | if((status0 & flags) == flags) 105 | break; 106 | } 107 | } 108 | ctx->stat0 = sdmmc_read16(REG_SDSTATUS0); 109 | ctx->stat1 = sdmmc_read16(REG_SDSTATUS1); 110 | sdmmc_write16(REG_SDSTATUS0,0); 111 | sdmmc_write16(REG_SDSTATUS1,0); 112 | 113 | if(getSDRESP != 0) { 114 | ctx->ret[0] = sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16); 115 | ctx->ret[1] = sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16); 116 | ctx->ret[2] = sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16); 117 | ctx->ret[3] = sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16); 118 | } 119 | } 120 | 121 | 122 | //--------------------------------------------------------------------------------- 123 | int sdmmc_cardinserted() { 124 | //--------------------------------------------------------------------------------- 125 | return 1; //sdmmc_cardready; 126 | } 127 | 128 | //--------------------------------------------------------------------------------- 129 | void sdmmc_controller_init(bool force) { 130 | //--------------------------------------------------------------------------------- 131 | deviceSD.isSDHC = 0; 132 | deviceSD.SDOPT = 0; 133 | deviceSD.res = 0; 134 | deviceSD.initarg = 0; 135 | deviceSD.clk = 0x80; 136 | deviceSD.devicenumber = 0; 137 | 138 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xF7FFu; 139 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xEFFFu; 140 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u; 141 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL) = (*(vu16*)(SDMMC_BASE + REG_SDDATACTL) & 0xFFDD) | 2; 142 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFFu; 143 | *(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDFu; 144 | *(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 512; 145 | *(vu16*)(SDMMC_BASE + REG_SDBLKCOUNT32) = 1; 146 | *(vu16*)(SDMMC_BASE + REG_SDRESET) &= 0xFFFEu; 147 | *(vu16*)(SDMMC_BASE + REG_SDRESET) |= 1u; 148 | *(vu16*)(SDMMC_BASE + REG_SDIRMASK0) |= TMIO_MASK_ALL; 149 | *(vu16*)(SDMMC_BASE + REG_SDIRMASK1) |= TMIO_MASK_ALL>>16; 150 | *(vu16*)(SDMMC_BASE + 0x0fc) |= 0xDBu; //SDCTL_RESERVED7 151 | *(vu16*)(SDMMC_BASE + 0x0fe) |= 0xDBu; //SDCTL_RESERVED8 152 | *(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu; 153 | *(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x20; 154 | *(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EE; 155 | *(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu; 156 | *(vu16*)(SDMMC_BASE + REG_SDBLKLEN) = 512; 157 | *(vu16*)(SDMMC_BASE + REG_SDSTOP) = 0; 158 | 159 | } 160 | 161 | //--------------------------------------------------------------------------------- 162 | int sdmmc_sdcard_init() { 163 | //--------------------------------------------------------------------------------- 164 | setTarget(&deviceSD); 165 | swiDelay(0xF000); 166 | sdmmc_send_command(&deviceSD,0,0); 167 | sdmmc_send_command(&deviceSD,0x10408,0x1AA); 168 | u32 temp = (deviceSD.error & 0x1) << 0x1E; 169 | 170 | u32 temp2 = 0; 171 | do { 172 | do { 173 | sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10); 174 | sdmmc_send_command(&deviceSD,0x10769,0x00FF8000 | temp); 175 | temp2 = 1; 176 | } while ( !(deviceSD.error & 1) ); 177 | 178 | } while((deviceSD.ret[0] & 0x80000000) == 0); 179 | 180 | if(!((deviceSD.ret[0] >> 30) & 1) || !temp) 181 | temp2 = 0; 182 | 183 | deviceSD.isSDHC = temp2; 184 | 185 | sdmmc_send_command(&deviceSD,0x10602,0); 186 | if (deviceSD.error & 0x4) return -1; 187 | 188 | sdmmc_send_command(&deviceSD,0x10403,0); 189 | if (deviceSD.error & 0x4) return -1; 190 | deviceSD.initarg = deviceSD.ret[0] >> 0x10; 191 | 192 | sdmmc_send_command(&deviceSD,0x10609,deviceSD.initarg << 0x10); 193 | if (deviceSD.error & 0x4) return -1; 194 | 195 | deviceSD.clk = 1; 196 | setckl(1); 197 | 198 | sdmmc_send_command(&deviceSD,0x10507,deviceSD.initarg << 0x10); 199 | if (deviceSD.error & 0x4) return -1; 200 | 201 | sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10); 202 | if (deviceSD.error & 0x4) return -1; 203 | 204 | sdmmc_send_command(&deviceSD,0x1076A,0x0); 205 | if (deviceSD.error & 0x4) return -1; 206 | 207 | sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10); 208 | if (deviceSD.error & 0x4) return -1; 209 | 210 | deviceSD.SDOPT = 1; 211 | sdmmc_send_command(&deviceSD,0x10446,0x2); 212 | if (deviceSD.error & 0x4) return -1; 213 | 214 | sdmmc_send_command(&deviceSD,0x1040D,deviceSD.initarg << 0x10); 215 | if (deviceSD.error & 0x4) return -1; 216 | 217 | sdmmc_send_command(&deviceSD,0x10410,0x200); 218 | if (deviceSD.error & 0x4) return -1; 219 | deviceSD.clk |= 0x200; 220 | 221 | return 0; 222 | 223 | } 224 | 225 | //--------------------------------------------------------------------------------- 226 | bool sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out) { 227 | //--------------------------------------------------------------------------------- 228 | if (deviceSD.isSDHC == 0) 229 | sector_no <<= 9; 230 | setTarget(&deviceSD); 231 | sdmmc_write16(REG_SDSTOP,0x100); 232 | 233 | sdmmc_write16(REG_SDBLKCOUNT32,numsectors); 234 | sdmmc_write16(REG_SDBLKLEN32,0x200); 235 | 236 | sdmmc_write16(REG_SDBLKCOUNT,numsectors); 237 | deviceSD.data = out; 238 | deviceSD.size = numsectors << 9; 239 | sdmmc_send_command(&deviceSD,0x33C12,sector_no); 240 | return geterror(&deviceSD) == 0; 241 | } 242 | #endif -------------------------------------------------------------------------------- /bootloader/source/sdmmc.h: -------------------------------------------------------------------------------- 1 | #ifndef __SDMMC_H__ 2 | #define __SDMMC_H__ 3 | 4 | #include 5 | 6 | #define DATA32_SUPPORT 7 | 8 | #define SDMMC_BASE 0x04004800 9 | 10 | 11 | #define REG_SDCMD 0x00 12 | #define REG_SDPORTSEL 0x02 13 | #define REG_SDCMDARG 0x04 14 | #define REG_SDCMDARG0 0x04 15 | #define REG_SDCMDARG1 0x06 16 | #define REG_SDSTOP 0x08 17 | #define REG_SDRESP 0x0c 18 | #define REG_SDBLKCOUNT 0x0a 19 | 20 | #define REG_SDRESP0 0x0c 21 | #define REG_SDRESP1 0x0e 22 | #define REG_SDRESP2 0x10 23 | #define REG_SDRESP3 0x12 24 | #define REG_SDRESP4 0x14 25 | #define REG_SDRESP5 0x16 26 | #define REG_SDRESP6 0x18 27 | #define REG_SDRESP7 0x1a 28 | 29 | #define REG_SDSTATUS0 0x1c 30 | #define REG_SDSTATUS1 0x1e 31 | 32 | #define REG_SDIRMASK0 0x20 33 | #define REG_SDIRMASK1 0x22 34 | #define REG_SDCLKCTL 0x24 35 | 36 | #define REG_SDBLKLEN 0x26 37 | #define REG_SDOPT 0x28 38 | #define REG_SDFIFO 0x30 39 | 40 | #define REG_SDDATACTL 0xd8 41 | #define REG_SDRESET 0xe0 42 | #define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not? 43 | 44 | #define REG_SDDATACTL32 0x100 45 | #define REG_SDBLKLEN32 0x104 46 | #define REG_SDBLKCOUNT32 0x108 47 | #define REG_SDFIFO32 0x10C 48 | 49 | #define REG_CLK_AND_WAIT_CTL 0x138 50 | #define REG_RESET_SDIO 0x1e0 51 | //The below defines are from linux kernel drivers/mmc tmio_mmc.h. 52 | /* Definitions for values the CTRL_STATUS register can take. */ 53 | #define TMIO_STAT0_CMDRESPEND 0x0001 54 | #define TMIO_STAT0_DATAEND 0x0004 55 | #define TMIO_STAT0_CARD_REMOVE 0x0008 56 | #define TMIO_STAT0_CARD_INSERT 0x0010 57 | #define TMIO_STAT0_SIGSTATE 0x0020 58 | #define TMIO_STAT0_WRPROTECT 0x0080 59 | #define TMIO_STAT0_CARD_REMOVE_A 0x0100 60 | #define TMIO_STAT0_CARD_INSERT_A 0x0200 61 | #define TMIO_STAT0_SIGSTATE_A 0x0400 62 | 63 | #define TMIO_STAT1_CMD_IDX_ERR 0x0001 64 | #define TMIO_STAT1_CRCFAIL 0x0002 65 | #define TMIO_STAT1_STOPBIT_ERR 0x0004 66 | #define TMIO_STAT1_DATATIMEOUT 0x0008 67 | #define TMIO_STAT1_RXOVERFLOW 0x0010 68 | #define TMIO_STAT1_TXUNDERRUN 0x0020 69 | #define TMIO_STAT1_CMDTIMEOUT 0x0040 70 | #define TMIO_STAT1_RXRDY 0x0100 71 | #define TMIO_STAT1_TXRQ 0x0200 72 | #define TMIO_STAT1_ILL_FUNC 0x2000 73 | #define TMIO_STAT1_CMD_BUSY 0x4000 74 | #define TMIO_STAT1_ILL_ACCESS 0x8000 75 | 76 | #define SDMC_NORMAL 0x00000000 77 | #define SDMC_ERR_COMMAND 0x00000001 78 | #define SDMC_ERR_CRC 0x00000002 79 | #define SDMC_ERR_END 0x00000004 80 | #define SDMC_ERR_TIMEOUT 0x00000008 81 | #define SDMC_ERR_FIFO_OVF 0x00000010 82 | #define SDMC_ERR_FIFO_UDF 0x00000020 83 | #define SDMC_ERR_WP 0x00000040 84 | #define SDMC_ERR_ABORT 0x00000080 85 | #define SDMC_ERR_FPGA_TIMEOUT 0x00000100 86 | #define SDMC_ERR_PARAM 0x00000200 87 | #define SDMC_ERR_R1_STATUS 0x00000800 88 | #define SDMC_ERR_NUM_WR_SECTORS 0x00001000 89 | #define SDMC_ERR_RESET 0x00002000 90 | #define SDMC_ERR_ILA 0x00004000 91 | #define SDMC_ERR_INFO_DETECT 0x00008000 92 | 93 | #define SDMC_STAT_ERR_UNKNOWN 0x00080000 94 | #define SDMC_STAT_ERR_CC 0x00100000 95 | #define SDMC_STAT_ERR_ECC_FAILED 0x00200000 96 | #define SDMC_STAT_ERR_CRC 0x00800000 97 | #define SDMC_STAT_ERR_OTHER 0xf9c70008 98 | 99 | #define TMIO_MASK_ALL 0x837f031d 100 | 101 | #define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \ 102 | TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR) 103 | 104 | #define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND) 105 | #define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND) 106 | 107 | typedef struct mmcdevice { 108 | u8* data; 109 | u32 size; 110 | u32 error; 111 | u16 stat0; 112 | u16 stat1; 113 | u32 ret[4]; 114 | u32 initarg; 115 | u32 isSDHC; 116 | u32 clk; 117 | u32 SDOPT; 118 | u32 devicenumber; 119 | u32 total_size; //size in sectors of the device 120 | u32 res; 121 | } mmcdevice; 122 | 123 | enum { 124 | MMC_DEVICE_SDCARD, 125 | MMC_DEVICE_NAND, 126 | }; 127 | 128 | void sdmmc_controller_init(bool force_init); 129 | void sdmmc_initirq(); 130 | int sdmmc_cardinserted(); 131 | 132 | int sdmmc_sdcard_init(); 133 | int sdmmc_nand_init(); 134 | void sdmmc_get_cid(int devicenumber, u32 *cid); 135 | 136 | static inline void sdmmc_nand_cid( u32 *cid) { 137 | sdmmc_get_cid(MMC_DEVICE_NAND,cid); 138 | } 139 | 140 | static inline void sdmmc_sdcard_cid( u32 *cid) { 141 | sdmmc_get_cid(MMC_DEVICE_SDCARD,cid); 142 | } 143 | 144 | bool sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out); 145 | /* 146 | int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, void *in); 147 | int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, void *out); 148 | int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, void *in); 149 | */ 150 | 151 | extern u32 sdmmc_cid[]; 152 | extern int sdmmc_curdevice; 153 | 154 | //--------------------------------------------------------------------------------- 155 | static inline u16 sdmmc_read16(u16 reg) { 156 | //--------------------------------------------------------------------------------- 157 | return *(vu16*)(SDMMC_BASE + reg); 158 | } 159 | 160 | //--------------------------------------------------------------------------------- 161 | static inline void sdmmc_write16(u16 reg, u16 val) { 162 | //--------------------------------------------------------------------------------- 163 | *(vu16*)(SDMMC_BASE + reg) = val; 164 | } 165 | 166 | //--------------------------------------------------------------------------------- 167 | static inline u32 sdmmc_read32(u16 reg) { 168 | //--------------------------------------------------------------------------------- 169 | return *(vu32*)(SDMMC_BASE + reg); 170 | } 171 | 172 | //--------------------------------------------------------------------------------- 173 | static inline void sdmmc_write32(u16 reg, u32 val) { 174 | //--------------------------------------------------------------------------------- 175 | *(vu32*)(SDMMC_BASE + reg) = val; 176 | } 177 | 178 | //--------------------------------------------------------------------------------- 179 | static inline void sdmmc_mask16(u16 reg, u16 clear, u16 set) { 180 | //--------------------------------------------------------------------------------- 181 | u16 val = sdmmc_read16(reg); 182 | val &= ~clear; 183 | val |= set; 184 | sdmmc_write16(reg, val); 185 | } 186 | 187 | 188 | //--------------------------------------------------------------------------------- 189 | static inline void setckl(u32 data) { 190 | //--------------------------------------------------------------------------------- 191 | sdmmc_mask16(REG_SDCLKCTL, 0x100, 0); 192 | sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF); 193 | sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100); 194 | } 195 | 196 | #endif 197 | -------------------------------------------------------------------------------- /bootstub/Makefile: -------------------------------------------------------------------------------- 1 | include $(DEVKITARM)/base_tools 2 | 3 | TARGET := bootstub 4 | 5 | ../data/$(TARGET).bin: $(TARGET).elf 6 | $(OBJCOPY) -O binary $< $@ 7 | 8 | $(TARGET).elf: $(TARGET).s Makefile 9 | $(CC) -Wl,-Ttext=0 -x assembler-with-cpp -nostartfiles -nostdlib $(TARGET).s -o $@ 10 | 11 | 12 | clean: 13 | rm -f $(TARGET).elf $(TARGET).bin 14 | -------------------------------------------------------------------------------- /bootstub/bootstub.s: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | 3 | Copyright (C) 2010 Dave "WinterMute" Murphy 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | 19 | ------------------------------------------------------------------*/ 20 | .global _start 21 | 22 | //----------------------------------------------------------------- 23 | _start: 24 | //----------------------------------------------------------------- 25 | .ascii "bootstub" 26 | .word hook7from9 - _start 27 | .word hook9from7 - _start 28 | _loader_size: 29 | .word 0 30 | 31 | .arch armv4t 32 | .cpu arm7tdmi 33 | 34 | //----------------------------------------------------------------- 35 | hook9from7: 36 | //----------------------------------------------------------------- 37 | ldr r0, arm9bootaddr 38 | adr r1, hook7from9 39 | str r1, [r0] 40 | 41 | mov r3, #0x04000000 42 | ldr r0, resetcode 43 | str r0, [r3, #0x188] 44 | add r3, r3, #0x180 45 | 46 | adr r0, waitcode_start 47 | ldr r1, arm7base 48 | adr r2, waitcode_end 49 | 1: ldr r4, [r0],#4 50 | str r4, [r1],#4 51 | cmp r2, r0 52 | bne 1b 53 | 54 | ldr r1, arm7base 55 | bx r1 56 | 57 | //----------------------------------------------------------------- 58 | waitcode_start: 59 | //----------------------------------------------------------------- 60 | push {lr} 61 | mov r2, #1 62 | bl waitsync 63 | 64 | mov r0, #0x100 65 | strh r0, [r3] 66 | 67 | mov r2, #0 68 | bl waitsync 69 | 70 | mov r0, #0 71 | strh r0, [r3] 72 | pop {lr} 73 | 74 | bx lr 75 | 76 | waitsync: 77 | ldrh r0, [r3] 78 | and r0, r0, #0x000f 79 | cmp r0, r2 80 | bne waitsync 81 | bx lr 82 | waitcode_end: 83 | 84 | arm7base: 85 | .word 0x037f8000 86 | arm7bootaddr: 87 | .word 0x02FFFE34 88 | arm9bootaddr: 89 | .word 0x02FFFE24 90 | tcmpudisable: 91 | .word 0x2078 92 | 93 | resetcode: 94 | .word 0x0c04000c 95 | 96 | //----------------------------------------------------------------- 97 | hook7from9: 98 | //----------------------------------------------------------------- 99 | mov r12, #0x04000000 100 | strb r12, [r12,#0x208] 101 | 102 | .arch armv5te 103 | .cpu arm946e-s 104 | 105 | ldr r1, tcmpudisable @ disable TCM and protection unit 106 | mcr p15, 0, r1, c1, c0 107 | 108 | @ Disable cache 109 | mov r0, #0 110 | mcr p15, 0, r0, c7, c5, 0 @ Instruction cache 111 | mcr p15, 0, r0, c7, c6, 0 @ Data cache 112 | mcr p15, 0, r0, c3, c0, 0 @ write buffer 113 | 114 | @ Wait for write buffer to empty 115 | mcr p15, 0, r0, c7, c10, 4 116 | 117 | add r3, r12, #0x180 @ r3 = 4000180 118 | 119 | mov r0,#0x80 120 | strb r0,[r3,#0x242-0x180] 121 | 122 | adr r0, _loader 123 | ldr r2, _loader_size 124 | mov r1, #0x06800000 125 | add r1, r1, #0x40000 126 | add r2, r0, r2 127 | _copyloader: 128 | ldr r4, [r0], #4 129 | str r4, [r1], #4 130 | cmp r0, r2 131 | blt _copyloader 132 | 133 | mov r0,#0x82 134 | strb r0,[r3,#0x242-0x180] 135 | 136 | ldrh r0,[r3,#0x204-0x180] 137 | orr r0,r0,#(1<<11) | (1<<7) 138 | strh r0,[r3,#0x204-0x180] 139 | 140 | 141 | ldr r0, arm7bootaddr 142 | mov r1, #0x06000000 143 | str r1, [r0] 144 | 145 | ldr r0, resetcode 146 | str r0, [r12, #0x188] 147 | 148 | mov r2, #1 149 | bl waitsync 150 | 151 | mov r0, #0x100 152 | strh r0, [r3] 153 | 154 | mov r2, #0 155 | bl waitsync 156 | 157 | mov r0, #0 158 | strh r0, [r3] 159 | 160 | // set up and enter passme loop 161 | 162 | ldr r0,arm9branchaddr 163 | ldr r1,branchinst 164 | str r1,[r0] 165 | str r0,[r0,#0x20] 166 | 167 | bx r0 168 | 169 | branchinst: 170 | .word 0xE59FF018 171 | 172 | arm9branchaddr: 173 | .word 0x02fffe04 174 | 175 | 176 | _loader: 177 | -------------------------------------------------------------------------------- /gfx/font.grit: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------- 2 | # graphics in tile format 3 | #------------------------------------------------------- 4 | -gt 5 | 6 | #------------------------------------------------------- 7 | # output first 16 colors of the palette 8 | #------------------------------------------------------- 9 | -pw16 10 | 11 | #------------------------------------------------------- 12 | # no tile reduction 13 | #------------------------------------------------------- 14 | -mR! 15 | 16 | #------------------------------------------------------- 17 | # no map output 18 | #------------------------------------------------------- 19 | -m! 20 | 21 | #------------------------------------------------------- 22 | # graphics bit depth is 4 (16 color) 23 | #------------------------------------------------------- 24 | -gB4 25 | 26 | -------------------------------------------------------------------------------- /gfx/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flashcarts/gbar3-frontend/7e604ae33dc6c5fd60ebd5d52aa0d210d3ea9b80/gfx/font.png -------------------------------------------------------------------------------- /gfx/font6x8.grit: -------------------------------------------------------------------------------- 1 | -gB8 2 | -gTFFFFFF 3 | # use lz77 compression 4 | -gzl 5 | -------------------------------------------------------------------------------- /gfx/font6x8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flashcarts/gbar3-frontend/7e604ae33dc6c5fd60ebd5d52aa0d210d3ea9b80/gfx/font6x8.png -------------------------------------------------------------------------------- /gfx/hbmenu_banner.grit: -------------------------------------------------------------------------------- 1 | -W3 2 | # disable alpha and set opaque bit for all pixels 3 | -gT! 4 | 5 | # use lz77 compression 6 | -gzl 7 | 8 | # 16 bit bitmap 9 | -gB16 10 | -------------------------------------------------------------------------------- /gfx/hbmenu_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flashcarts/gbar3-frontend/7e604ae33dc6c5fd60ebd5d52aa0d210d3ea9b80/gfx/hbmenu_banner.png -------------------------------------------------------------------------------- /gfx/hbmenu_consolebg.grit: -------------------------------------------------------------------------------- 1 | # 8 bit bitmap 2 | -gB8 3 | 4 | # bitmap format 5 | -gb -------------------------------------------------------------------------------- /gfx/hbmenu_consolebg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flashcarts/gbar3-frontend/7e604ae33dc6c5fd60ebd5d52aa0d210d3ea9b80/gfx/hbmenu_consolebg.png -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2005 - 2013 2 | Michael "Chishm" Chisholm 3 | Dave "WinterMute" Murphy 4 | Claudio "sverx" 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License 8 | as published by the Free Software Foundation; either version 2 9 | of the License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US 19 | -------------------------------------------------------------------------------- /resources/hbmenu_banner.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flashcarts/gbar3-frontend/7e604ae33dc6c5fd60ebd5d52aa0d210d3ea9b80/resources/hbmenu_banner.xcf -------------------------------------------------------------------------------- /source/args.cpp: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2017 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | Claudio "sverx" 6 | Michael "mtheall" Theall 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | ------------------------------------------------------------------*/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #include "args.h" 36 | 37 | using namespace std; 38 | 39 | static const string GBA_EXT = ".gba"; 40 | static const string NDS_EXT = ".nds"; 41 | static const string ARG_EXT = ".argv"; 42 | static const string EXT_EXT = ".ext"; 43 | static const char EXT_DIR[] = "/nds"; 44 | static const char SEPARATORS[] = "\n\r\t "; 45 | 46 | /* Checks if s1 ends with s2, ignoring case. 47 | Returns true if it does, false otherwise. 48 | */ 49 | static bool strCaseEnd(const string& s1, const string& s2) { 50 | return (s1.size() >= s2.size() && 51 | strcasecmp(s1.c_str() + s1.size() - s2.size(), s2.c_str()) == 0); 52 | } 53 | 54 | /* Parses the contents of the file given by filename into argarray. Arguments 55 | are tokenized based on whitespace. 56 | */ 57 | static bool parseArgFileAll(const string& filename, vector& argarray) { 58 | FILE *argfile = fopen(filename.c_str(), "rb"); 59 | if (!argfile) { 60 | return false; 61 | } 62 | 63 | char *line = NULL; 64 | size_t lineSize = 0; 65 | while (__getline(&line, &lineSize, argfile) >= 0) { 66 | // Find comment and end string there 67 | char *pstr = strchr(line, '#'); 68 | if (pstr) { 69 | *pstr = '\0'; 70 | } 71 | 72 | // Tokenize arguments 73 | char *saveptr; 74 | pstr = strtok_r(line, SEPARATORS, &saveptr); 75 | 76 | while (pstr) { 77 | argarray.emplace_back(pstr); 78 | pstr = strtok_r(NULL, SEPARATORS, &saveptr); 79 | } 80 | } 81 | 82 | if (line) { 83 | free(line); 84 | } 85 | 86 | fclose(argfile); 87 | 88 | return argarray.size() > 0; 89 | } 90 | 91 | /* Parses the argument file given by filename and returns the NDS file that it 92 | * points to. 93 | */ 94 | static bool parseArgFileNds(const std::string& filename, std::string& ndsPath) { 95 | bool success = false; 96 | FILE *argfile = fopen(filename.c_str(), "rb"); 97 | if (!argfile) { 98 | return false; 99 | } 100 | 101 | char *line = NULL; 102 | size_t lineSize = 0; 103 | while (__getline(&line, &lineSize, argfile) >= 0) { 104 | char *pstr = NULL; 105 | 106 | // Find comment and end string there 107 | pstr = strchr(line, '#'); 108 | if (pstr) { 109 | *pstr = '\0'; 110 | } 111 | 112 | // Tokenize arguments 113 | char *saveptr; 114 | pstr = strtok_r(line, SEPARATORS, &saveptr); 115 | 116 | if (pstr) { 117 | // Only want the first token, which should be the NDS file name 118 | ndsPath = pstr; 119 | success = true; 120 | break; 121 | } 122 | } 123 | 124 | if (line) { 125 | free(line); 126 | } 127 | 128 | fclose(argfile); 129 | 130 | return success; 131 | } 132 | 133 | /* Converts a plain filename into an absolute path. If it's already an absolute 134 | * path, it is returned as-is. If basePath is NULL, the current working directory 135 | * is used. 136 | * Returns true on success, false on failure. 137 | */ 138 | static bool toAbsPath(const string& filename, const char* basePath, string& filePath) { 139 | // Copy existing absolute or empty paths 140 | if (filename.size() == 0 || filename[0] == '/') { 141 | filePath = filename; 142 | return true; 143 | } 144 | 145 | if (basePath == NULL) { 146 | // Get current working directory (uses C-strings) 147 | vector cwd(PATH_MAX); 148 | if (getcwd (cwd.data(), cwd.size()) == NULL) { 149 | // Path was too long, abort 150 | return false; 151 | } 152 | // Copy CWD into path 153 | filePath = cwd.data(); 154 | } else { 155 | // Just copy the base path 156 | filePath = basePath; 157 | } 158 | 159 | // Ensure there's a path separator 160 | if (filePath.back() != '/') { 161 | filePath += '/'; 162 | } 163 | 164 | // Now append the filename 165 | filePath += filename; 166 | 167 | return true; 168 | } 169 | 170 | /* Convert a dataFilePath to the path of the ext file that specifies the 171 | * handler. 172 | * Returns true on success, false on failure 173 | */ 174 | static bool toExtPath(const string& dataFilePath, string& extFilePath) { 175 | // Figure out what the file extension is 176 | size_t extPos = dataFilePath.rfind('.'); 177 | if (extPos == string::npos) { 178 | return false; 179 | } 180 | 181 | extPos += 1; 182 | if (extPos >= dataFilePath.size()) { 183 | return false; 184 | } 185 | 186 | // Construct handler path from extension. Handlers are in the EXT_DIR and 187 | // end with EXT_EXT. 188 | const string ext = dataFilePath.substr(extPos); 189 | if (!toAbsPath(ext, EXT_DIR, extFilePath)) { 190 | return false; 191 | } 192 | 193 | extFilePath += EXT_EXT; 194 | 195 | return true; 196 | } 197 | 198 | bool argsNdsPath(const std::string& filePath, std::string& ndsPath) { 199 | if (strCaseEnd(filePath, NDS_EXT)) { 200 | ndsPath = filePath; 201 | return true; 202 | } else if (strCaseEnd(filePath, ARG_EXT)) { 203 | return parseArgFileNds(filePath, ndsPath); 204 | } else if (strCaseEnd(filePath, GBA_EXT)) { 205 | ndsPath = GBARUNNER3_PATH; 206 | return true; 207 | } else { 208 | // This is a data file associated with a handler NDS by an ext file 209 | string extPath; 210 | if (!toExtPath(filePath, extPath)) { 211 | return false; 212 | } 213 | string ndsRelPath; 214 | if (!parseArgFileNds(extPath, ndsRelPath)) { 215 | return false; 216 | } 217 | // Handler is in EXT_DIR 218 | return toAbsPath(ndsRelPath, EXT_DIR, ndsPath); 219 | } 220 | 221 | return false; 222 | } 223 | 224 | bool argsFillArray(const string& filePath, vector& argarray) { 225 | // Ensure argarray is empty 226 | argarray.clear(); 227 | 228 | if (strCaseEnd(filePath, NDS_EXT)) { 229 | string absPath; 230 | if (!toAbsPath(filePath, NULL, absPath)) { 231 | return false; 232 | } 233 | argarray.push_back(move(absPath)); 234 | } else if (strCaseEnd(filePath, ARG_EXT)) { 235 | if (!parseArgFileAll(filePath, argarray)) { 236 | return false; 237 | } 238 | // Ensure argv[0] is absolute path 239 | string absPath; 240 | if (!toAbsPath(argarray[0], NULL, absPath)) { 241 | return false; 242 | } 243 | argarray[0] = absPath; 244 | } else if (strCaseEnd(filePath, GBA_EXT)) { 245 | string absPath; 246 | if(!toAbsPath(filePath, NULL, absPath)) { 247 | return false; 248 | } 249 | if(isDSiMode()) 250 | argarray.push_back(GBARUNNER3_PATH_SD); 251 | else 252 | argarray.push_back(GBARUNNER3_PATH); 253 | argarray.push_back(move(absPath)); 254 | } else { 255 | // This is a data file associated with a handler NDS by an ext file 256 | string extPath; 257 | 258 | if (!toExtPath(filePath, extPath)) { 259 | return false; 260 | } 261 | 262 | // Read the arg file for the extension handler 263 | if (!parseArgFileAll(extPath, argarray)) { 264 | return false; 265 | } 266 | 267 | // Extension handler relative path is relative to EXT_DIR, not CWD 268 | string absPath; 269 | if (!toAbsPath(argarray[0], EXT_DIR, absPath)) { 270 | return false; 271 | } 272 | argarray[0] = absPath; 273 | 274 | // Add the data filename to the end. Its path is relative to CWD. 275 | if (!toAbsPath(filePath, NULL, absPath)) { 276 | return false; 277 | } 278 | argarray.push_back(move(absPath)); 279 | } 280 | 281 | return argarray.size() > 0 && strCaseEnd(argarray[0], NDS_EXT); 282 | } 283 | 284 | vector argsGetExtensionList() { 285 | vector extensionList; 286 | 287 | // Always supported files: NDS binaries and predefined argument lists 288 | extensionList.push_back(NDS_EXT); 289 | extensionList.push_back(ARG_EXT); 290 | extensionList.push_back(GBA_EXT); 291 | 292 | // Get a list of extension files: argument lists associated with a file type 293 | DIR *dir = opendir (EXT_DIR); 294 | if (dir) { 295 | for (struct dirent* dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) { 296 | // Add the name component of all files ending with EXT_EXT to the list 297 | if (dirent->d_type != DT_REG) { 298 | continue; 299 | } 300 | 301 | if (dirent->d_name[0] != '.' && strCaseEnd(dirent->d_name, EXT_EXT)) { 302 | size_t extPos = strlen(dirent->d_name) - EXT_EXT.size(); 303 | dirent->d_name[extPos] = '\0'; 304 | extensionList.push_back(dirent->d_name); 305 | } 306 | } 307 | closedir(dir); 308 | } 309 | 310 | return extensionList; 311 | } 312 | -------------------------------------------------------------------------------- /source/args.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2017 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | Claudio "sverx" 6 | Michael "mtheall" Theall 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | ------------------------------------------------------------------*/ 23 | 24 | #ifndef ARGS_H 25 | #define ARGS_H 26 | 27 | #include 28 | #include 29 | 30 | #ifdef WITH_AOS 31 | #define GBARUNNER3_PATH "/__rpg/APP/GBARunner3.nds" 32 | #endif 33 | #ifdef WITH_TWLMENU 34 | #define GBARUNNER3_PATH "/_nds/TWiLightMenu/emulators/GBARunner3.nds" 35 | #endif 36 | #ifndef GBARUNNER3_PATH 37 | #define GBARUNNER3_PATH "/GBARunner3.nds" 38 | #endif 39 | 40 | #define GBARUNNER3_PATH_SD "sd:" GBARUNNER3_PATH 41 | 42 | /* Convert a file path of any type (e.g. .nds or .argv) into a path to the NDS 43 | * file to be opened. The returned path may be absolute or relative to the 44 | * current working directory. 45 | * Returns true on success, false on failure. 46 | */ 47 | bool argsNdsPath(const std::string& filePath, std::string& ndsPath); 48 | 49 | /* Convert a file path of any type into an argument array by filling the array 50 | * that is passed in. The first argument will be the full path to an NDS file. 51 | * Returns true on success, false on failure. 52 | */ 53 | bool argsFillArray(const std::string& filePath, std::vector& argarray); 54 | 55 | /* Return a list of all file extensions that can be browsed and opened. 56 | */ 57 | std::vector argsGetExtensionList(); 58 | 59 | #endif // ARGS_H 60 | -------------------------------------------------------------------------------- /source/excpt_stub.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "nds_loader_arm9.h" 5 | #include "exceptionstub_bin.h" 6 | 7 | typedef struct excptStub_t { 8 | u32 dummy; 9 | void* bss_start; 10 | void* bss_end; 11 | VoidFn excpt_vector; 12 | } excptStub_t; 13 | 14 | void installExcptStub(void) 15 | { 16 | excptStub_t* exceptionstub = (excptStub_t*)0x2ffa000; 17 | armCopyMem32(exceptionstub,exceptionstub_bin,exceptionstub_bin_size); 18 | if (exceptionstub->bss_start != exceptionstub->bss_end) { 19 | armFillMem32(exceptionstub->bss_start, 0, (char*)exceptionstub->bss_end - (char*)exceptionstub->bss_start); 20 | } 21 | 22 | EXCEPTION_VECTOR = exceptionstub->excpt_vector; 23 | } 24 | -------------------------------------------------------------------------------- /source/file_browse.cpp: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2017 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | Claudio "sverx" 6 | 7 | This program is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; either version 2 10 | of the License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | ------------------------------------------------------------------*/ 22 | 23 | #include "file_browse.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include "iconTitle.h" 34 | 35 | #define SCREEN_COLS 30 36 | #define ENTRIES_PER_SCREEN 20 37 | #define ENTRIES_START_ROW 2 38 | #define ENTRY_PAGE_LENGTH 10 39 | 40 | using namespace std; 41 | 42 | struct DirEntry { 43 | string name; 44 | bool isDirectory; 45 | } ; 46 | 47 | bool nameEndsWith (const string& name, const vector extensionList) { 48 | 49 | if (name.size() == 0) return false; 50 | if (name.front() == '.') return false; 51 | 52 | if (extensionList.size() == 0) return true; 53 | 54 | for (int i = 0; i < (int)extensionList.size(); i++) { 55 | const string ext = extensionList.at(i); 56 | if ( strcasecmp (name.c_str() + name.size() - ext.size(), ext.c_str()) == 0) return true; 57 | } 58 | return false; 59 | } 60 | 61 | bool dirEntryPredicate (const DirEntry& lhs, const DirEntry& rhs) { 62 | 63 | if (!lhs.isDirectory && rhs.isDirectory) { 64 | return false; 65 | } 66 | if (lhs.isDirectory && !rhs.isDirectory) { 67 | return true; 68 | } 69 | return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0; 70 | } 71 | 72 | void getDirectoryContents (vector& dirContents, const vector extensionList) { 73 | struct stat st; 74 | 75 | dirContents.clear(); 76 | 77 | DIR *pdir = opendir ("."); 78 | 79 | if (pdir == NULL) { 80 | iprintf ("Unable to open the directory.\n"); 81 | } else { 82 | 83 | while(true) { 84 | DirEntry dirEntry; 85 | 86 | struct dirent* pent = readdir(pdir); 87 | if(pent == NULL) break; 88 | 89 | stat(pent->d_name, &st); 90 | dirEntry.name = pent->d_name; 91 | dirEntry.isDirectory = (st.st_mode & S_IFDIR) ? true : false; 92 | 93 | if (dirEntry.name.compare(".") != 0 && (dirEntry.isDirectory || nameEndsWith(dirEntry.name, extensionList))) { 94 | dirContents.push_back (dirEntry); 95 | } 96 | 97 | } 98 | 99 | closedir(pdir); 100 | } 101 | 102 | sort(dirContents.begin(), dirContents.end(), dirEntryPredicate); 103 | } 104 | 105 | void getDirectoryContents (vector& dirContents) { 106 | vector extensionList; 107 | getDirectoryContents (dirContents, extensionList); 108 | } 109 | 110 | void showDirectoryContents (const vector& dirContents, int startRow) { 111 | char path[PATH_MAX]; 112 | 113 | 114 | getcwd(path, PATH_MAX); 115 | 116 | // Clear the screen 117 | consoleClear(); 118 | 119 | // Print the path 120 | if (strlen(path) < SCREEN_COLS) { 121 | iprintf ("%s", path); 122 | } else { 123 | iprintf ("%s", path + strlen(path) - SCREEN_COLS); 124 | } 125 | 126 | // Move to 2nd row 127 | iprintf ("\x1b[1;0H"); 128 | // Print line of dashes 129 | iprintf ("------------------------------"); 130 | 131 | // Print directory listing 132 | for (int i = 0; i < ((int)dirContents.size() - startRow) && i < ENTRIES_PER_SCREEN; i++) { 133 | const DirEntry* entry = &dirContents.at(i + startRow); 134 | char entryName[SCREEN_COLS + 1]; 135 | 136 | // Set row 137 | iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW); 138 | 139 | if (entry->isDirectory) { 140 | strncpy (entryName, entry->name.c_str(), SCREEN_COLS); 141 | entryName[SCREEN_COLS - 3] = '\0'; 142 | iprintf (" [%s]", entryName); 143 | } else { 144 | strncpy (entryName, entry->name.c_str(), SCREEN_COLS); 145 | entryName[SCREEN_COLS - 1] = '\0'; 146 | iprintf (" %s", entryName); 147 | } 148 | } 149 | } 150 | 151 | string browseForFile (const vector& extensionList) { 152 | int pressed = 0; 153 | int screenOffset = 0; 154 | int fileOffset = 0; 155 | vector dirContents; 156 | 157 | getDirectoryContents (dirContents, extensionList); 158 | showDirectoryContents (dirContents, screenOffset); 159 | 160 | while (pmMainLoop()) { 161 | // Clear old cursors 162 | for (int i = ENTRIES_START_ROW; i < ENTRIES_PER_SCREEN + ENTRIES_START_ROW; i++) { 163 | iprintf ("\x1b[%d;0H ", i); 164 | } 165 | // Show cursor 166 | iprintf ("\x1b[%d;0H*", fileOffset - screenOffset + ENTRIES_START_ROW); 167 | 168 | iconTitleUpdate (dirContents.at(fileOffset).isDirectory, dirContents.at(fileOffset).name); 169 | 170 | // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do 171 | do { 172 | scanKeys(); 173 | pressed = keysDownRepeat(); 174 | swiWaitForVBlank(); 175 | if (!pmMainLoop()) { 176 | return ""; 177 | } 178 | } while (!pressed); 179 | 180 | if (pressed & KEY_UP) fileOffset -= 1; 181 | if (pressed & KEY_DOWN) fileOffset += 1; 182 | if (pressed & KEY_LEFT) fileOffset -= ENTRY_PAGE_LENGTH; 183 | if (pressed & KEY_RIGHT) fileOffset += ENTRY_PAGE_LENGTH; 184 | 185 | if (fileOffset < 0) fileOffset = dirContents.size() - 1; // Wrap around to bottom of list 186 | if (fileOffset > ((int)dirContents.size() - 1)) fileOffset = 0; // Wrap around to top of list 187 | 188 | // Scroll screen if needed 189 | if (fileOffset < screenOffset) { 190 | screenOffset = fileOffset; 191 | showDirectoryContents (dirContents, screenOffset); 192 | } 193 | if (fileOffset > screenOffset + ENTRIES_PER_SCREEN - 1) { 194 | screenOffset = fileOffset - ENTRIES_PER_SCREEN + 1; 195 | showDirectoryContents (dirContents, screenOffset); 196 | } 197 | 198 | if (pressed & KEY_A) { 199 | DirEntry* entry = &dirContents.at(fileOffset); 200 | if (entry->isDirectory) { 201 | iprintf("Entering directory\n"); 202 | // Enter selected directory 203 | chdir (entry->name.c_str()); 204 | getDirectoryContents (dirContents, extensionList); 205 | screenOffset = 0; 206 | fileOffset = 0; 207 | showDirectoryContents (dirContents, screenOffset); 208 | } else { 209 | // Clear the screen 210 | consoleClear(); 211 | // Return the chosen file 212 | return entry->name; 213 | } 214 | } 215 | 216 | if (pressed & KEY_B) { 217 | // Go up a directory 218 | chdir (".."); 219 | getDirectoryContents (dirContents, extensionList); 220 | screenOffset = 0; 221 | fileOffset = 0; 222 | showDirectoryContents (dirContents, screenOffset); 223 | } 224 | } 225 | 226 | return ""; 227 | } 228 | -------------------------------------------------------------------------------- /source/file_browse.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2017 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License 8 | as published by the Free Software Foundation; either version 2 9 | of the License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | ------------------------------------------------------------------*/ 21 | 22 | #ifndef FILE_BROWSE_H 23 | #define FILE_BROWSE_H 24 | 25 | #include 26 | #include 27 | 28 | std::string browseForFile (const std::vector& extensionList); 29 | 30 | 31 | 32 | #endif //FILE_BROWSE_H 33 | -------------------------------------------------------------------------------- /source/iconTitle.cpp: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2013 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | Claudio "sverx" 6 | Michael "mtheall" Theall 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 2 11 | of the License, or (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21 | 22 | ------------------------------------------------------------------*/ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "args.h" 30 | #include "hbmenu_banner.h" 31 | #include "font6x8.h" 32 | 33 | #define TITLE_POS_X (2*8) 34 | #define TITLE_POS_Y (17*8) 35 | 36 | #define ICON_POS_X 32 37 | #define ICON_POS_Y 80 38 | 39 | #define TEXT_WIDTH ((32-4)*8/6) 40 | 41 | static int bg2, bg3; 42 | static u16 *sprite; 43 | static tNDSBanner banner; 44 | 45 | static inline void writecharRS (int row, int col, u16 car) { 46 | // get map pointer 47 | u16 *gfx = bgGetMapPtr(bg2); 48 | // get old pair of values from VRAM 49 | u16 oldval = gfx[row*(512/8/2)+(col/2)]; 50 | 51 | // clear the half we will update 52 | oldval &= (col%2) ? 0x00FF : 0xFF00; 53 | // apply the updated half 54 | oldval |= (col%2) ? (car<<8) : car; 55 | 56 | // write back to VRAM 57 | gfx[row*(512/8/2)+col/2] = oldval; 58 | } 59 | 60 | static inline void writeRow (int rownum, const char* text) { 61 | int i,len,p=0; 62 | len=strlen(text); 63 | 64 | if (len>TEXT_WIDTH) 65 | len=TEXT_WIDTH; 66 | 67 | // clear left part 68 | for (i=0;i<(TEXT_WIDTH-len)/2;i++) 69 | writecharRS (rownum, i, 0); 70 | 71 | // write centered text 72 | for (i=(TEXT_WIDTH-len)/2;i<((TEXT_WIDTH-len)/2+len);i++) 73 | writecharRS (rownum, i, text[p++]-' '); 74 | 75 | // clear right part 76 | for (i=((TEXT_WIDTH-len)/2+len);i>> HBMenu+ <<<==="); 134 | writeRow (2,"(this text should disappear..."); 135 | writeRow (3,"...otherwise, trouble!)"); 136 | } 137 | 138 | 139 | void iconTitleUpdate (int isdir, const std::string& name) { 140 | writeRow (0, name.c_str()); 141 | writeRow (1, ""); 142 | writeRow (2, ""); 143 | writeRow (3, ""); 144 | 145 | if (isdir) { 146 | // text 147 | writeRow (2, "[directory]"); 148 | // icon 149 | clearIcon(); 150 | } else { 151 | std::string ndsPath; 152 | if (!argsNdsPath(name, ndsPath)) { 153 | writeRow(2, "(invalid argv or NDS file!)"); 154 | clearIcon(); 155 | return; 156 | } 157 | 158 | unsigned int Icon_title_offset; 159 | 160 | // open file for reading info 161 | FILE *fp = fopen (ndsPath.c_str(), "rb"); 162 | 163 | if (!fp) { 164 | // text 165 | writeRow (2,"(can't open file!)"); 166 | // icon 167 | clearIcon(); 168 | fclose (fp); 169 | return; 170 | } 171 | 172 | if (fseek (fp, offsetof(tNDSHeader, bannerOffset), SEEK_SET) != 0 || 173 | fread (&Icon_title_offset, sizeof(int), 1, fp) != 1) { 174 | // text 175 | writeRow (2, "(can't read file!)"); 176 | // icon 177 | clearIcon(); 178 | fclose (fp); 179 | return; 180 | } 181 | 182 | if (Icon_title_offset == 0) { 183 | // text 184 | writeRow (2, "(no title/icon)"); 185 | // icon 186 | clearIcon(); 187 | fclose (fp); 188 | return; 189 | } 190 | 191 | if (fseek (fp, Icon_title_offset, SEEK_SET) != 0 || 192 | fread (&banner, sizeof(banner), 1, fp) != 1) { 193 | // text 194 | writeRow (2,"(can't read icon/title!)"); 195 | // icon 196 | clearIcon(); 197 | fclose (fp); 198 | return; 199 | } 200 | 201 | // close file! 202 | fclose (fp); 203 | 204 | // Do something special for GBA stuff 205 | if (ndsPath == GBARUNNER3_PATH) { 206 | writeRow (2, "GameBoy Advance ROM"); 207 | } else { 208 | // turn unicode into ascii (kind of) 209 | // and convert 0x0A into 0x00 210 | char *p = (char*)banner.titles[0]; 211 | for (size_t i = 0; i < sizeof(banner.titles[0]); i = i+2) { 212 | if ((p[i] == 0x0A) || (p[i] == 0xFF)) 213 | p[i/2] = 0; 214 | else 215 | p[i/2] = p[i]; 216 | } 217 | 218 | // text 219 | for (size_t i = 0; i < 3; ++i) { 220 | writeRow(i+1, p); 221 | p += strlen(p) + 1; 222 | } 223 | } 224 | 225 | // icon 226 | DC_FlushAll(); 227 | dmaCopy(banner.icon, sprite, sizeof(banner.icon)); 228 | dmaCopy(banner.palette, SPRITE_PALETTE, sizeof(banner.palette)); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /source/iconTitle.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2013 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | Claudio "sverx" 6 | 7 | This program is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; either version 2 10 | of the License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | ------------------------------------------------------------------*/ 22 | #include 23 | 24 | void iconTitleInit (void); 25 | void iconTitleUpdate (int isdir, const std::string& name); 26 | -------------------------------------------------------------------------------- /source/main.cpp: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2013 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | Claudio "sverx" 6 | 7 | This program is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; either version 2 10 | of the License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | ------------------------------------------------------------------*/ 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include "args.h" 32 | #include "file_browse.h" 33 | #include "font.h" 34 | #include "hbmenu_consolebg.h" 35 | #include "iconTitle.h" 36 | #include "nds_loader_arm9.h" 37 | 38 | 39 | using namespace std; 40 | 41 | void InitGUI(void) { 42 | iconTitleInit(); 43 | videoSetModeSub(MODE_4_2D); 44 | vramSetBankC(VRAM_C_SUB_BG); 45 | int bgSub = bgInitSub(3, BgType_Bmp8, BgSize_B8_256x256, 1, 0); 46 | PrintConsole *console = consoleInit(0, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 6, false, false); 47 | dmaCopy(hbmenu_consolebgBitmap, bgGetGfxPtr(bgSub), 256*256); 48 | ConsoleFont font; 49 | font.gfx = (u16*)fontTiles; 50 | font.pal = (u16*)fontPal; 51 | font.numChars = 95; 52 | font.numColors = (fontPalLen / 2); 53 | font.bpp = 4; 54 | font.asciiOffset = 32; 55 | font.convertSingleColor = true; 56 | consoleSetFont(console, &font); 57 | dmaCopy(hbmenu_consolebgPal, BG_PALETTE_SUB, 256*2); 58 | BG_PALETTE_SUB[255] = RGB15(31,31,31); 59 | keysSetRepeat(25,5); 60 | consoleSetWindow(console, 1, 1, 30, 22); 61 | } 62 | 63 | //--------------------------------------------------------------------------------- 64 | void stop (void) { 65 | //--------------------------------------------------------------------------------- 66 | while (pmMainLoop()) { 67 | swiWaitForVBlank(); 68 | } 69 | } 70 | 71 | //--------------------------------------------------------------------------------- 72 | int main(int argc, char **argv) { 73 | //--------------------------------------------------------------------------------- 74 | 75 | // overwrite reboot stub identifier 76 | // so tapping power on DSi returns to DSi menu 77 | pmClearResetJumpTarget(); 78 | 79 | // install exception stub 80 | installExcptStub(); 81 | 82 | InitGUI(); 83 | 84 | if (!fatInitDefault()) { 85 | iprintf ("\n\nfatinitDefault failed!\n"); 86 | stop(); 87 | } 88 | 89 | vector extensionList = argsGetExtensionList(); 90 | 91 | if(!access("/ROMs/gba", F_OK)) { 92 | chdir("/ROMs/gba"); 93 | } 94 | else { 95 | chdir("/"); 96 | } 97 | 98 | while(pmMainLoop()) { 99 | 100 | string filename = browseForFile(extensionList); 101 | if (filename.empty()) { 102 | continue; 103 | } 104 | 105 | // Construct a command line 106 | vector argarray; 107 | if (!argsFillArray(filename, argarray)) { 108 | iprintf("Invalid NDS or arg file selected\n"); 109 | } else { 110 | iprintf("Running %s with %d parameters\n", argarray[0].c_str(), argarray.size()); 111 | 112 | // Make a copy of argarray using C strings, for the sake of runNdsFile 113 | vector c_args; 114 | for (const auto& arg: argarray) { 115 | c_args.push_back(arg.c_str()); 116 | } 117 | 118 | // Try to run the NDS file with the given arguments 119 | int err = runNdsFile(c_args[0], c_args.size(), &c_args[0]); 120 | iprintf("Start failed. Error %i\n", err); 121 | } 122 | 123 | argarray.clear(); 124 | 125 | while (pmMainLoop()) { 126 | swiWaitForVBlank(); 127 | scanKeys(); 128 | if (!(keysHeld() & KEY_A)) break; 129 | } 130 | 131 | } 132 | 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /source/nds_loader_arm9.c: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2010 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License 8 | as published by the Free Software Foundation; either version 2 9 | of the License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | ------------------------------------------------------------------*/ 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include "load_bin.h" 33 | 34 | #ifndef _NO_BOOTSTUB_ 35 | #include "bootstub_bin.h" 36 | #endif 37 | 38 | #include "nds_loader_arm9.h" 39 | 40 | /* 41 | b startUp 42 | 43 | storedFileCluster: 44 | .word 0x0FFFFFFF @ default BOOT.NDS 45 | wantToPatchDLDI: 46 | .word 0x00000001 @ by default patch the DLDI section of the loaded NDS 47 | @ Used for passing arguments to the loaded app 48 | argStart: 49 | .word _end - _start 50 | argSize: 51 | .word 0x00000000 52 | dldiOffset: 53 | .word _dldi_start - _start 54 | dsiSD: 55 | .word 0 56 | dsiMode: 57 | .word 0 58 | */ 59 | 60 | typedef struct BootLdrHeader { 61 | u32 entrypoint; 62 | u32 storedFileCluster; 63 | u32 wantToPatchDldi; 64 | u32 argStart; 65 | u32 argSize; 66 | u32 dldiOffset; 67 | u32 hasTwlSd; 68 | u32 isTwlMode; 69 | } BootLdrHeader; 70 | 71 | static bool dldiPatchLoader(BootLdrHeader* loader) 72 | { 73 | alignas(ARM_CACHE_LINE_SZ) static u8 io_dldi_data[DLDI_MAX_ALLOC_SZ]; 74 | DLDI_INTERFACE* io = (DLDI_INTERFACE*)io_dldi_data; 75 | 76 | if (!dldiDumpInternal(io)) { 77 | // No DLDI patch 78 | return false; 79 | } 80 | 81 | DLDI_INTERFACE* area = (void*)((u8*)loader + loader->dldiOffset); 82 | return dldiApplyPatch(area, io); 83 | } 84 | 85 | static eRunNdsRetCode runNds (const void* loader, u32 loaderSize, u32 cluster, int argc, const char** argv) 86 | { 87 | char* argStart; 88 | u16* argData; 89 | u16 argTempVal = 0; 90 | int argSize; 91 | const char* argChar; 92 | 93 | // Direct CPU access to VRAM bank C 94 | VRAM_C_CR = VRAM_ENABLE | VRAM_C_LCD; 95 | // Load the loader/patcher into the correct address 96 | armCopyMem32 (VRAM_C, loader, loaderSize); 97 | 98 | BootLdrHeader* hdr = (BootLdrHeader*)MM_VRAM_C; 99 | 100 | // Set the parameters for the loader 101 | hdr->storedFileCluster = cluster; 102 | hdr->isTwlMode = systemIsTwlMode(); 103 | 104 | if(argv[0][0]=='s' && argv[0][1]=='d') { 105 | hdr->wantToPatchDldi = 0; 106 | hdr->hasTwlSd = 1; 107 | } else { 108 | hdr->hasTwlSd = 0; 109 | } 110 | 111 | // Give arguments to loader 112 | argStart = (char*)MM_VRAM_C + hdr->argStart; 113 | argStart = (char*)(((int)argStart + 3) & ~3); // Align to word 114 | argData = (u16*)argStart; 115 | argSize = 0; 116 | 117 | for (; argc > 0 && *argv; ++argv, --argc) 118 | { 119 | for (argChar = *argv; *argChar != 0; ++argChar, ++argSize) 120 | { 121 | if (argSize & 1) 122 | { 123 | argTempVal |= (*argChar) << 8; 124 | *argData = argTempVal; 125 | ++argData; 126 | } 127 | else 128 | { 129 | argTempVal = *argChar; 130 | } 131 | } 132 | if (argSize & 1) 133 | { 134 | *argData = argTempVal; 135 | ++argData; 136 | } 137 | argTempVal = 0; 138 | ++argSize; 139 | } 140 | *argData = argTempVal; 141 | 142 | hdr->argStart = (uptr)argStart - MM_VRAM_C; 143 | hdr->argSize = argSize; 144 | 145 | if(hdr->wantToPatchDldi) { 146 | // Patch the loader with a DLDI for the card 147 | if (!dldiPatchLoader((BootLdrHeader*)VRAM_C)) { 148 | return RUN_NDS_PATCH_DLDI_FAILED; 149 | } 150 | } 151 | 152 | // Give the VRAM to the ARM7 153 | VRAM_C_CR = VRAM_ENABLE | VRAM_C_ARM7_0x06000000; 154 | 155 | // Reset into a passme loop 156 | *((vu32*)0x02FFFFFC) = 0; 157 | *(u32*)&g_envAppNdsHeader->title[4] = 0xE59FF018; 158 | g_envAppNdsHeader->arm9_entrypoint = (u32)&g_envAppNdsHeader->title[4]; 159 | g_envAppNdsHeader->arm7_entrypoint = 0x06000000; 160 | g_envAppTwlHeader->arm7_mbk_map_settings[0] = mbkMakeMapping(MM_TWLWRAM_MAP, MM_TWLWRAM_MAP+MM_TWLWRAM_BANK_SZ, MbkMapSize_256K); 161 | g_envExtraInfo->pm_chainload_flag = 1; 162 | 163 | exit(0); 164 | } 165 | 166 | eRunNdsRetCode runNdsFile (const char* filename, int argc, const char** argv) { 167 | struct stat st; 168 | char filePath[PATH_MAX]; 169 | int pathLen; 170 | const char* args[1]; 171 | 172 | if (stat (filename, &st) < 0) { 173 | return RUN_NDS_STAT_FAILED; 174 | } 175 | 176 | if (argc <= 0 || !argv) { 177 | // Construct a command line if we weren't supplied with one 178 | if (!getcwd (filePath, PATH_MAX)) { 179 | return RUN_NDS_GETCWD_FAILED; 180 | } 181 | pathLen = strlen (filePath); 182 | strcpy (filePath + pathLen, filename); 183 | args[0] = filePath; 184 | argv = args; 185 | } 186 | 187 | bool havedsiSD = false; 188 | 189 | if(argv[0][0]=='s' && argv[0][1]=='d') havedsiSD = true; 190 | 191 | installBootStub(havedsiSD); 192 | 193 | return runNds (load_bin, load_bin_size, st.st_ino, argc, argv); 194 | } 195 | 196 | bool installBootStub(bool havedsiSD) { 197 | #ifndef _NO_BOOTSTUB_ 198 | void* bootstub = g_envNdsBootstub; 199 | BootLdrHeader *bootloader = (BootLdrHeader*)((u8*)bootstub+bootstub_bin_size); 200 | 201 | armCopyMem32(bootstub,bootstub_bin,bootstub_bin_size); 202 | armCopyMem32(bootloader,load_bin,load_bin_size); 203 | bool ret = false; 204 | 205 | bootloader->isTwlMode = systemIsTwlMode(); 206 | if( havedsiSD) { 207 | ret = true; 208 | bootloader->wantToPatchDldi = 0; 209 | bootloader->hasTwlSd = 1; 210 | } else { 211 | ret = dldiPatchLoader(bootloader); 212 | } 213 | 214 | g_envNdsBootstub->arm9_entrypoint = (void*)((u32)bootstub+(u32)g_envNdsBootstub->arm9_entrypoint); 215 | g_envNdsBootstub->arm7_entrypoint = (void*)((u32)bootstub+(u32)g_envNdsBootstub->arm7_entrypoint); 216 | *(u32*)(g_envNdsBootstub+1) = load_bin_size; 217 | 218 | DC_FlushAll(); 219 | 220 | return ret; 221 | #else 222 | return true; 223 | #endif 224 | 225 | } 226 | -------------------------------------------------------------------------------- /source/nds_loader_arm9.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2010 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License 8 | as published by the Free Software Foundation; either version 2 9 | of the License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | ------------------------------------------------------------------*/ 21 | 22 | #ifndef NDS_LOADER_ARM9_H 23 | #define NDS_LOADER_ARM9_H 24 | 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | typedef enum { 31 | RUN_NDS_OK = 0, 32 | RUN_NDS_STAT_FAILED, 33 | RUN_NDS_GETCWD_FAILED, 34 | RUN_NDS_PATCH_DLDI_FAILED, 35 | } eRunNdsRetCode; 36 | 37 | #define LOAD_DEFAULT_NDS 0 38 | 39 | eRunNdsRetCode runNdsFile (const char* filename, int argc, const char** argv); 40 | 41 | bool installBootStub(bool havedsiSD); 42 | void installExcptStub(void); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | 48 | #endif // NDS_LOADER_ARM7_H 49 | -------------------------------------------------------------------------------- /testfiles/argvTest.nds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flashcarts/gbar3-frontend/7e604ae33dc6c5fd60ebd5d52aa0d210d3ea9b80/testfiles/argvTest.nds -------------------------------------------------------------------------------- /testfiles/test1.argv: -------------------------------------------------------------------------------- 1 | # This is a comment 2 | # This is a straightforward command line, name of nds file followed by parameters 3 | # this can include a path relative to the .argv file 4 | # absolute paths are not currently supported 5 | 6 | argvTest.nds bunch of parameters go here -------------------------------------------------------------------------------- /testfiles/test2.argv: -------------------------------------------------------------------------------- 1 | # This is a comment 2 | # lines consisting only of whitespace will also be ignored 3 | # commands can be placed on multiple lines 4 | # first the name of the nds file to run 5 | # this can include a path relative to the .argv file 6 | # absolute paths are not currently supported 7 | 8 | argvTest.nds 9 | 10 | # a single argument 11 | argument1 12 | 13 | # multiple arguments separated by spaces 14 | argument2 argument3 15 | 16 | # multiple arguments separated by tabs 17 | argument4 argument5 18 | 19 | # or even separated by both tabs and spaces 20 | argument6 argument7 argument8 21 | 22 | 23 | --------------------------------------------------------------------------------