commit 06cbab11edfedf36ca291e49454f9bec2c9707c9 Author: ApacheThunder Date: Tue Nov 12 19:26:10 2024 -0600 Initial Commit... * Requires unlocked flash. (aka EZP cart has not had fw1.06 run on it yet). EZP flash can be unlocked with EZP_Recovery_Tool however requires desoldering WP# win on flash chip to do this. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d183310 --- /dev/null +++ b/COPYING @@ -0,0 +1,342 @@ + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b1632bc --- /dev/null +++ b/Makefile @@ -0,0 +1,88 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +export TARGET := EZDS_P +export TOPDIR := $(CURDIR) + +export HBMENU_MAJOR := 1 +export HBMENU_MINOR := 0 +export HBMENU_PATCH := 2 + + +VERSION := $(HBMENU_MAJOR).$(HBMENU_MINOR).$(HBMENU_PATCH) + +# GMAE_ICON is the image used to create the game icon, leave blank to use default rule +GAME_ICON := + +# specify a directory which contains the nitro filesystem +# this is relative to the Makefile +NITRO_FILES := $(CURDIR)/NitroFS + +# These set the information text in the nds file +#GAME_TITLE := My Wonderful Homebrew +#GAME_SUBTITLE1 := built with devkitARM +#GAME_SUBTITLE2 := http://devitpro.org + +include $(DEVKITARM)/ds_rules + +.PHONY: data ndsbootloader bootstub clean + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all: ndsbootloader bootstub $(TARGET).nds + +#--------------------------------------------------------------------------------- +checkarm7: + $(MAKE) -C arm7 + +#--------------------------------------------------------------------------------- +checkarm9: + $(MAKE) -C arm9 + +#--------------------------------------------------------------------------------- +$(TARGET).nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf + ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \ + -b $(CURDIR)/icon.bmp "hbmenu;$(VERSION);http://devkitpro.org" \ + -g ABJJ 01 "EZFLASH NEW" -t banner.bin -d $(NITRO_FILES) + dlditool ntro.dldi $(TARGET).nds + +data: + @mkdir -p data + +ndsbootloader: data + $(MAKE) -C ndsbootloader LOADBIN=$(CURDIR)/data/load.bin + +# exceptionstub: data +# $(MAKE) -C exception-stub STUBBIN=$(CURDIR)/data/exceptionstub.bin + +bootstub: data + $(MAKE) -C bootstub + +#--------------------------------------------------------------------------------- +arm7/$(TARGET).elf: + $(MAKE) -C arm7 + +#--------------------------------------------------------------------------------- +arm9/$(TARGET).elf: ndsbootloader + $(MAKE) -C arm9 + +#--------------------------------------------------------------------------------- +clean: + $(MAKE) -C arm9 clean + $(MAKE) -C arm7 clean + $(MAKE) -C ndsbootloader clean + $(MAKE) -C bootstub clean + rm -rf data + rm -rf hbmenu + rm -f $(TARGET).nds + rm -f boot.nds + rm -f 00000000.app + rm -f _DS_MENU.DAT +# rm -f ACE3DS.nds +# rm -f ACE3DS.DAT + diff --git a/NitroFS/Nitro.img b/NitroFS/Nitro.img new file mode 100644 index 0000000..9bc2e5d Binary files /dev/null and b/NitroFS/Nitro.img differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..5fe9b89 --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +# EZP Dual Storage HBMenu + +This is a modified build of HBMenu with UI enhancements but setup to act as a kernel/firmware replacement to EZFlash Parellel. +This can not be used on other carts and is targed specifically for this cart and is designed to be flashed to it via EZP Recovery tool. + +The program will change boot behavior based on what button is held on boot: + +* Holding no button on boot will show file browser. +* Holding A button will auto boot into GBA-Exploader. Specifically a new custom version with SuperCard and EZFlash Omega support. +* Holding B button will reboot console into GBA mode with gbaframe.bmp as the frame graphic. +* Holding Y button will boot GodMode9i. +* Holding X button will boot MaxMediaDock's slot1 rom for booting into MaxMediaDock devices on slot-2. + +Also if an N-Card or N-Card clone USB slot-2 device is present on boot, the program will auto boot into nrio-usb-disk. A open source +file transfer program that makes use of the USB slot-2 device to connect flashcart's MicroSD storage to PC. + +The repo this build of nrio-usb-disk can be found at: https://github.com/ApacheThunder/nrio-usb-disk +The original repo this build was forked from: https://github.com/asiekierka/nrio-usb-disk + +This new app was created courtasy of Asiekirka. + + +Currently the gbaframe loader is setup to load GBA-Exploader compatible BMP files. Currently only setup to load frames from internal flash fat image. +Please refer to the fat image in NitroFS folder for that file if you wish to customize this. + +This menu has a special feature in the filebrowser. You will notice that on boot it will show the file listing for the files found inside internal FAT image +stored in NitroFS. This is made possible with a special "NTRO" dldi driver that I wrote. This allows this cart to boot without a MicroSD card inserted! + +However some actions will of coarse require the presense of a MicroSD card. (some of the included apps depend on that) + +Assuming a MicroSD card is present on boot you can use shoulder buttons to navigate to MicroSD storage when wanting to boot files from MicroSD. + + +# Notes on how the EZFLash BIN file is constructed + +The layout for the BIN file to be flashed via EZP Recovery Tool is reletively simple compared to other flashcarts. +This card does not support ntrboot due to hardcoded blowfish keys however you can flash entire 4MB flash chip with standard save commands. + +EZP Recovery Tool can be found here: https://github.com/ApacheThunder/EZP_Recovery_Tool + +This is a upgraded fork of NDS Backup Tool by Rudolph. +The full open sourced fork I've made for this tool can be found here: https://github.com/ApacheThunder/NDS_BACKUP_TOOL + + +As with other flashcarts. Arm9 secure area must be encrypted (though this program doesn't really use that region for anything). +Game code used in header is hardcoded as well due to hardcoded blowfish keys so do not attempt to change that. + +Data at 0x1000 mostly pertain to the ntrboot payload this flashcart uses on 3DS. +Though the data found at 0x1200 is a copy of data found at found at 0x108400 + +EZP for some reason mirrors this data from 0x1200 into 0x108400 when rom reads happen instead of presening any data orginally found there in the flash. + +This is easy to account for. Anytime you build a custom bin file for this cart, just copy a 0x400 chunk of data from 0x108400 over to 0x1200. + +I have not tested if the cart still functions correctly with the 3DS ntrboot stuff missing. I would recommend ensuring this is still present. + +Fortunately though this cart can't really be bricked if you flash bad data to it. (I've ensured you can still use EZP Recovery Tool regardless of what state it's in). + +Do note though if you have run official firmware 1.06 or newer on this card, 0x0 to 0x7FFF is write protected. + +To undo this you must desolder WP# of eeprom flash chip. While it's lifted, run EZP_Recovery_Tool and perform a save initialize via the restore menu. + +Doing so will make the recovery tool run a special command to reset the write protection registers on the chip which can't be done without WP# pin being lifted. + +However from my tests the cart won't mount/read the flash contents while it's in this state. So after doing the save intialize to trigger the reset of write protection, +you must solder it back down. Then go back into EZP Recovery Tool and flash a new bin file. This should now properly write the intended data to the first couple of blocks. + +Do not run official fw 1.06 or newer on the cart again in the future. Doing so will require a repeat of this process if you want to flash a new custom rom to it agian. + + +# License +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. + +The latest sources may be obtained from devkitPro git using the command: `git clone git@github.com:devkitPro/nds-hb-menu.git` + +``` + Copyright (C) 2005 - 2017 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ``` diff --git a/arm7/Makefile b/arm7/Makefile new file mode 100644 index 0000000..991b696 --- /dev/null +++ b/arm7/Makefile @@ -0,0 +1,124 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +# DATA is a list of directories containing binary files +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD := build +SOURCES := source +INCLUDES := include build +DATA := + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb-interwork + +CFLAGS := -g -Wall -O2 \ + -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer \ + -ffast-math \ + $(ARCH) $(INCLUDE) -DARM7 +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,-Map,$(notdir $*).map + +LIBS := -lnds7 + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export ARM7ELF := $(CURDIR)/$(TARGET).elf +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))\ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) + +export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) + +export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir))\ + $(foreach dir,$(LIBDIRS),-I$(dir)/include)\ + -I$(CURDIR)/$(BUILD) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(ARM7ELF) : $(OFILES) + @echo linking $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + + +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/arm7/source/biosCalls.s b/arm7/source/biosCalls.s new file mode 100644 index 0000000..8933271 --- /dev/null +++ b/arm7/source/biosCalls.s @@ -0,0 +1,15 @@ + .TEXT + .ARM + +@--------------------------------------------------------------------------------------- +.GLOBAL swiSwitchToGBAModeFixed +.func swiSwitchToGBAModeFixed +@--------------------------------------------------------------------------------------- +swiSwitchToGBAModeFixed: + mov r2,#0x40 + swi 0x1f0000 + +.endfunc + + .end + diff --git a/arm7/source/main.c b/arm7/source/main.c new file mode 100644 index 0000000..9890a62 --- /dev/null +++ b/arm7/source/main.c @@ -0,0 +1,71 @@ +#include + +volatile bool switchedMode = false; +volatile bool exitflag = false; + +// libnds messed up the original SWI bios call ASM. +// They used r0 instead of r2. This reimplementation fixes that issue for now. +extern void swiSwitchToGBAModeFixed(); + +void powerButtonCB() { exitflag = true; } + +void gbaMode() { + vu32 vr; + + REG_IME = IME_DISABLE; + for(vr = 0; vr < 0x1000; vr++); // Wait ARM9 + + // writePowerManagement(PM_BACKLIGHT_LEVEL, (backlightLevel & 0x03)); + writePowerManagement(PM_BACKLIGHT_LEVEL, (PersonalData->defaultBrightness & 0x03)); + + if (PersonalData->gbaScreen) { + writePowerManagement(PM_CONTROL_REG, PM_BACKLIGHT_BOTTOM | PM_SOUND_AMP); + } else { + writePowerManagement(PM_CONTROL_REG, PM_BACKLIGHT_TOP | PM_SOUND_AMP); + } + swiSwitchToGBAModeFixed(); + while(1); +} + +void fifoCheckHandler() { + if (!switchedMode) { + if (fifoCheckValue32(FIFO_USER_01)) { + switchedMode = true; + gbaMode(); + } + } +} + +void VblankHandler() { fifoCheckHandler(); } +void VcountHandler() { inputGetAndSend(); } + +int main(void) { + readUserSettings(); + ledBlink(0); + + irqInit(); + + initClockIRQ(); + fifoInit(); + touchInit(); + + SetYtrigger(80); + + installSystemFIFO(); + + irqSet(IRQ_VCOUNT, VcountHandler); + irqSet(IRQ_VBLANK, VblankHandler); + + irqEnable(IRQ_VBLANK | IRQ_VCOUNT); + + setPowerButtonCB(powerButtonCB); + + /*if (REG_SNDEXTCNT != 0) { + i2cWriteRegister(0x4A, 0x12, 0x00); // Press power-button for auto-reset + i2cWriteRegister(0x4A, 0x70, 0x01); // Bootflag = Warmboot/SkipHealthSafety + }*/ + + while(1)swiWaitForVBlank(); + return 0; +} + diff --git a/arm9/Makefile b/arm9/Makefile new file mode 100644 index 0000000..8232eb7 --- /dev/null +++ b/arm9/Makefile @@ -0,0 +1,143 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +# DATA is a list of directories containing binary files embedded using bin2o +# GRAPHICS is a list of directories containing image files to be converted with grit +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD := build +SOURCES := source +INCLUDES := include +DATA := ../data +GRAPHICS := gfx + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -g -Wall -O2 \ + -ffunction-sections -fdata-sections \ + -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ + -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM9 -D_NO_BOOTSTUB_ +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lfat -lnds9 + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) $(PORTLIBS) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export ARM9ELF := $(CURDIR)/$(TARGET).elf + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))\ + $(foreach dir,$(DATA),$(CURDIR)/$(dir))\ + $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) + +export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export OFILES := $(PNGFILES:.png=.o) $(OFILES_BIN) $(OFILES_SOURCES) + +export HFILES := $(PNGFILES:.png=.h) $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir))\ + $(foreach dir,$(LIBDIRS),-I$(dir)/include)\ + -I$(CURDIR)/$(BUILD) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(ARM9ELF) : $(OFILES) + @echo linking $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +# This rule creates assembly source files using grit +# grit takes an image file and a .grit describing how the file is to be processed +# add additional rules like this for each image extension +# you use in the graphics folders +#--------------------------------------------------------------------------------- +%.s %.h: %.png %.grit +#--------------------------------------------------------------------------------- + grit $< -fts -o$* + +-include $(DEPSDIR)/*.d + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/arm9/gfx/font.grit b/arm9/gfx/font.grit new file mode 100644 index 0000000..c3d1aa0 --- /dev/null +++ b/arm9/gfx/font.grit @@ -0,0 +1,25 @@ +#------------------------------------------------------- +# graphics in tile format +#------------------------------------------------------- +-gt + +#------------------------------------------------------- +# output first 16 colors of the palette +#------------------------------------------------------- +-pw16 + +#------------------------------------------------------- +# no tile reduction +#------------------------------------------------------- +-mR! + +#------------------------------------------------------- +# no map output +#------------------------------------------------------- +-m! + +#------------------------------------------------------- +# graphics bit depth is 4 (16 color) +#------------------------------------------------------- +-gB4 + diff --git a/arm9/gfx/font.png b/arm9/gfx/font.png new file mode 100644 index 0000000..5757358 Binary files /dev/null and b/arm9/gfx/font.png differ diff --git a/arm9/gfx/font6x8.grit b/arm9/gfx/font6x8.grit new file mode 100644 index 0000000..7d6a8dc --- /dev/null +++ b/arm9/gfx/font6x8.grit @@ -0,0 +1,4 @@ +-gB8 +-gTFFFFFF +# use lz77 compression +-gzl diff --git a/arm9/gfx/font6x8.png b/arm9/gfx/font6x8.png new file mode 100644 index 0000000..d62830b Binary files /dev/null and b/arm9/gfx/font6x8.png differ diff --git a/arm9/gfx/hbmenu_banner.grit b/arm9/gfx/hbmenu_banner.grit new file mode 100644 index 0000000..f50c6c1 --- /dev/null +++ b/arm9/gfx/hbmenu_banner.grit @@ -0,0 +1,9 @@ +-W3 +# disable alpha and set opaque bit for all pixels +-gT! + +# use lz77 compression +-gzl + +# 16 bit bitmap +-gB16 diff --git a/arm9/gfx/hbmenu_banner.png b/arm9/gfx/hbmenu_banner.png new file mode 100644 index 0000000..585d9a2 Binary files /dev/null and b/arm9/gfx/hbmenu_banner.png differ diff --git a/arm9/gfx/hbmenu_consolebg.grit b/arm9/gfx/hbmenu_consolebg.grit new file mode 100644 index 0000000..2666060 --- /dev/null +++ b/arm9/gfx/hbmenu_consolebg.grit @@ -0,0 +1,5 @@ +# 8 bit bitmap +-gB8 + +# bitmap format +-gb \ No newline at end of file diff --git a/arm9/gfx/hbmenu_consolebg.png b/arm9/gfx/hbmenu_consolebg.png new file mode 100644 index 0000000..8292369 Binary files /dev/null and b/arm9/gfx/hbmenu_consolebg.png differ diff --git a/arm9/include/hbNoIcon.bin b/arm9/include/hbNoIcon.bin new file mode 100644 index 0000000..f932ea9 Binary files /dev/null and b/arm9/include/hbNoIcon.bin differ diff --git a/arm9/source/args.cpp b/arm9/source/args.cpp new file mode 100644 index 0000000..d0b627c --- /dev/null +++ b/arm9/source/args.cpp @@ -0,0 +1,294 @@ +/*----------------------------------------------------------------- + Copyright (C) 2005 - 2017 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + Claudio "sverx" + Michael "mtheall" Theall + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "args.h" + +using namespace std; + +static const string NDS_EXT = ".nds"; +static const string ARG_EXT = ".argv"; +static const string EXT_EXT = ".ext"; +static const char EXT_DIR[] = "/nds"; +static const char SEPARATORS[] = "\n\r\t "; + +/* Checks if s1 ends with s2, ignoring case. + Returns true if it does, false otherwise. + */ +static bool strCaseEnd(const string& s1, const string& s2) { + return (s1.size() >= s2.size() && + strcasecmp(s1.c_str() + s1.size() - s2.size(), s2.c_str()) == 0); +} + +/* Parses the contents of the file given by filename into argarray. Arguments + are tokenized based on whitespace. + */ +static bool parseArgFileAll(const string& filename, vector& argarray) { + FILE *argfile = fopen(filename.c_str(), "rb"); + if (!argfile) { + return false; + } + + char *line = NULL; + size_t lineSize = 0; + while (__getline(&line, &lineSize, argfile) >= 0) { + // Find comment and end string there + char *pstr = strchr(line, '#'); + if (pstr) { + *pstr = '\0'; + } + + // Tokenize arguments + char *saveptr; + pstr = strtok_r(line, SEPARATORS, &saveptr); + + while (pstr) { + argarray.emplace_back(pstr); + pstr = strtok_r(NULL, SEPARATORS, &saveptr); + } + } + + if (line) { + free(line); + } + + fclose(argfile); + + return argarray.size() > 0; +} + +/* Parses the argument file given by filename and returns the NDS file that it + * points to. + */ +static bool parseArgFileNds(const std::string& filename, std::string& ndsPath) { + bool success = false; + FILE *argfile = fopen(filename.c_str(), "rb"); + if (!argfile) { + return false; + } + + char *line = NULL; + size_t lineSize = 0; + while (__getline(&line, &lineSize, argfile) >= 0) { + char *pstr = NULL; + + // Find comment and end string there + pstr = strchr(line, '#'); + if (pstr) { + *pstr = '\0'; + } + + // Tokenize arguments + char *saveptr; + pstr = strtok_r(line, SEPARATORS, &saveptr); + + if (pstr) { + // Only want the first token, which should be the NDS file name + ndsPath = pstr; + success = true; + break; + } + } + + if (line) { + free(line); + } + + fclose(argfile); + + return success; +} + +/* Converts a plain filename into an absolute path. If it's already an absolute + * path, it is returned as-is. If basePath is NULL, the current working directory + * is used. + * Returns true on success, false on failure. + */ +static bool toAbsPath(const string& filename, const char* basePath, string& filePath) { + // Copy existing absolute or empty paths + if (filename.size() == 0 || filename[0] == '/') { + filePath = filename; + return true; + } + + if (basePath == NULL) { + // Get current working directory (uses C-strings) + vector cwd(PATH_MAX); + if (getcwd (cwd.data(), cwd.size()) == NULL) { + // Path was too long, abort + return false; + } + // Copy CWD into path + filePath = cwd.data(); + } else { + // Just copy the base path + filePath = basePath; + } + + // Ensure there's a path separator + if (filePath.back() != '/') { + filePath += '/'; + } + + // Now append the filename + filePath += filename; + + return true; +} + +/* Convert a dataFilePath to the path of the ext file that specifies the + * handler. + * Returns true on success, false on failure + */ +static bool toExtPath(const string& dataFilePath, string& extFilePath) { + // Figure out what the file extension is + size_t extPos = dataFilePath.rfind('.'); + if (extPos == string::npos) { + return false; + } + + extPos += 1; + if (extPos >= dataFilePath.size()) { + return false; + } + + // Construct handler path from extension. Handlers are in the EXT_DIR and + // end with EXT_EXT. + const string ext = dataFilePath.substr(extPos); + if (!toAbsPath(ext, EXT_DIR, extFilePath)) { + return false; + } + + extFilePath += EXT_EXT; + + return true; +} + +bool argsNdsPath(const std::string& filePath, std::string& ndsPath) { + if (strCaseEnd(filePath, NDS_EXT)) { + ndsPath = filePath; + return true; + } else if (strCaseEnd(filePath, ARG_EXT)) { + return parseArgFileNds(filePath, ndsPath); + } else { + // This is a data file associated with a handler NDS by an ext file + string extPath; + if (!toExtPath(filePath, extPath)) { + return false; + } + string ndsRelPath; + if (!parseArgFileNds(extPath, ndsRelPath)) { + return false; + } + // Handler is in EXT_DIR + return toAbsPath(ndsRelPath, EXT_DIR, ndsPath); + } + + return false; +} + +bool argsFillArray(const string& filePath, vector& argarray) { + // Ensure argarray is empty + argarray.clear(); + + if (strCaseEnd(filePath, NDS_EXT)) { + string absPath; + if (!toAbsPath(filePath, NULL, absPath)) { + return false; + } + argarray.push_back(move(absPath)); + } else if (strCaseEnd(filePath, ARG_EXT)) { + if (!parseArgFileAll(filePath, argarray)) { + return false; + } + // Ensure argv[0] is absolute path + string absPath; + if (!toAbsPath(argarray[0], NULL, absPath)) { + return false; + } + argarray[0] = absPath; + } else { + // This is a data file associated with a handler NDS by an ext file + string extPath; + + if (!toExtPath(filePath, extPath)) { + return false; + } + + // Read the arg file for the extension handler + if (!parseArgFileAll(extPath, argarray)) { + return false; + } + + // Extension handler relative path is relative to EXT_DIR, not CWD + string absPath; + if (!toAbsPath(argarray[0], EXT_DIR, absPath)) { + return false; + } + argarray[0] = absPath; + + // Add the data filename to the end. Its path is relative to CWD. + if (!toAbsPath(filePath, NULL, absPath)) { + return false; + } + argarray.push_back(move(absPath)); + } + + return argarray.size() > 0 && strCaseEnd(argarray[0], NDS_EXT); +} + +vector argsGetExtensionList() { + vector extensionList; + + // Always supported files: NDS binaries and predefined argument lists + extensionList.push_back(NDS_EXT); + extensionList.push_back(ARG_EXT); + + // Get a list of extension files: argument lists associated with a file type + DIR *dir = opendir (EXT_DIR); + if (dir) { + for (struct dirent* dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) { + // Add the name component of all files ending with EXT_EXT to the list + if (dirent->d_type != DT_REG) { + continue; + } + + if (dirent->d_name[0] != '.' && strCaseEnd(dirent->d_name, EXT_EXT)) { + size_t extPos = strlen(dirent->d_name) - EXT_EXT.size(); + dirent->d_name[extPos] = '\0'; + extensionList.push_back(dirent->d_name); + } + } + closedir(dir); + } + + return extensionList; +} diff --git a/arm9/source/args.h b/arm9/source/args.h new file mode 100644 index 0000000..88584c3 --- /dev/null +++ b/arm9/source/args.h @@ -0,0 +1,47 @@ +/*----------------------------------------------------------------- + Copyright (C) 2005 - 2017 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + Claudio "sverx" + Michael "mtheall" Theall + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ + +#ifndef ARGS_H +#define ARGS_H + +#include +#include + +/* Convert a file path of any type (e.g. .nds or .argv) into a path to the NDS + * file to be opened. The returned path may be absolute or relative to the + * current working directory. + * Returns true on success, false on failure. + */ +bool argsNdsPath(const std::string& filePath, std::string& ndsPath); + +/* Convert a file path of any type into an argument array by filling the array + * that is passed in. The first argument will be the full path to an NDS file. + * Returns true on success, false on failure. + */ +bool argsFillArray(const std::string& filePath, std::vector& argarray); + +/* Return a list of all file extensions that can be browsed and opened. + */ +std::vector argsGetExtensionList(); + +#endif // ARGS_H diff --git a/arm9/source/defaultBanners.s b/arm9/source/defaultBanners.s new file mode 100644 index 0000000..dc12b92 --- /dev/null +++ b/arm9/source/defaultBanners.s @@ -0,0 +1,5 @@ + .arm + .global hbNoIcon_bin + +hbNoIcon_bin: .incbin "../include/hbNoIcon.bin" + diff --git a/arm9/source/ez5n.c b/arm9/source/ez5n.c new file mode 100644 index 0000000..a4c89a0 --- /dev/null +++ b/arm9/source/ez5n.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: CC0-1.0 +// +// SPDX-FileContributor: Antonio Niño Díaz, 2023 +// SPDX-FileContributor: lifehackerhansol, 2024 + +#include + +#include "ioezp.h" +#include "libtwl_card.h" + +#define EZChipID (u16)0x0FC2 + +// Initialize the driver. Returns true on success. +bool ez_startup(void) { return (ioEZP_GetChipID() == EZChipID); } + +// Returns true if a card is present and initialized. +bool ez_is_inserted(void) { return (ioEZP_GetChipID() == EZChipID); } + +// Reads 512 byte sectors into a buffer that may be unaligned. Returns true on +// success. +bool ez_read_sectors(uint32_t sector, uint32_t num_sectors, void *buffer) { + ioEZP_SDReadSectors(sector, num_sectors, buffer); + return true; +} + +// Writes 512 byte sectors from a buffer that may be unaligned. Returns true on +// success. +bool ez_write_sectors(uint32_t sector, uint32_t num_sectors, const void *buffer) { + ioEZP_SDWriteSectors(sector, num_sectors, buffer); + return true; +} + +// Clear error flags from the card. Returns true on success. +bool ez_clear_status(void) { return true; } + +// Shutdowns the card. This may never be called. +bool ez_shutdown(void) { return true; } + diff --git a/arm9/source/ez5n.h b/arm9/source/ez5n.h new file mode 100644 index 0000000..be16981 --- /dev/null +++ b/arm9/source/ez5n.h @@ -0,0 +1,30 @@ +#ifndef EZ5N_H +#define EZ5N_H + +#ifdef __cplusplus +extern "C" { +#endif + +bool ez_startup(void); +bool ez_is_inserted(void); +bool ez_read_sectors(uint32_t sector, uint32_t num_sectors, void *buffer); +bool ez_write_sectors(uint32_t sector, uint32_t num_sectors, const void *buffer); +bool ez_clear_status(void); +bool ez_shutdown(void); + +const DISC_INTERFACE io_ez5n = { + 0x455A354E, // "EZ5N" + (FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_SLOT_NDS), + (FN_MEDIUM_STARTUP)&ez_startup, + (FN_MEDIUM_ISINSERTED)&ez_is_inserted, + (FN_MEDIUM_READSECTORS)&ez_read_sectors, + (FN_MEDIUM_WRITESECTORS)&ez_write_sectors, + (FN_MEDIUM_CLEARSTATUS)&ez_clear_status, + (FN_MEDIUM_SHUTDOWN)&ez_shutdown +}; + +#ifdef __cplusplus +} +#endif + +#endif // EZ5N_H \ No newline at end of file diff --git a/arm9/source/file_browse.cpp b/arm9/source/file_browse.cpp new file mode 100644 index 0000000..f9cf053 --- /dev/null +++ b/arm9/source/file_browse.cpp @@ -0,0 +1,248 @@ +/*----------------------------------------------------------------- + Copyright (C) 2005 - 2017 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + Claudio "sverx" + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ + +#include "file_browse.h" +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "iconTitle.h" +#include "ez5n.h" + +#define SCREEN_COLS 30 +#define ENTRIES_PER_SCREEN 20 +#define ENTRIES_START_ROW 2 +#define ENTRY_PAGE_LENGTH 10 + +using namespace std; + +extern void gbaMode(); + +bool EZ5NFatInit = false; +bool usingInternalFat = true; + +struct DirEntry { + string name; + bool isDirectory; +}; + +bool nameEndsWith (const string& name, const vector extensionList) { + + if (name.size() == 0) return false; + if (name.front() == '.') return false; + + if (extensionList.size() == 0) return true; + + for (int i = 0; i < (int)extensionList.size(); i++) { + const string ext = extensionList.at(i); + if ( strcasecmp (name.c_str() + name.size() - ext.size(), ext.c_str()) == 0) return true; + } + return false; +} + +bool dirEntryPredicate (const DirEntry& lhs, const DirEntry& rhs) { + if (!lhs.isDirectory && rhs.isDirectory)return false; + if (lhs.isDirectory && !rhs.isDirectory)return true; + return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0; +} + +void getDirectoryContents (vector& dirContents, const vector extensionList) { + struct stat st; + + dirContents.clear(); + + DIR *pdir = opendir ("."); + + if (pdir == NULL) { + iprintf ("Unable to open the directory.\n"); + } else { + while(true) { + DirEntry dirEntry; + + struct dirent* pent = readdir(pdir); + if(pent == NULL) break; + + stat(pent->d_name, &st); + dirEntry.name = pent->d_name; + dirEntry.isDirectory = (st.st_mode & S_IFDIR) ? true : false; + + if (dirEntry.name.compare(".") != 0 && (dirEntry.isDirectory || nameEndsWith(dirEntry.name, extensionList))) { + dirContents.push_back (dirEntry); + } + } + closedir(pdir); + } + + sort(dirContents.begin(), dirContents.end(), dirEntryPredicate); +} + +void getDirectoryContents (vector& dirContents) { + vector extensionList; + getDirectoryContents (dirContents, extensionList); +} + +void showDirectoryContents (const vector& dirContents, int startRow) { + char path[PATH_MAX]; + + getcwd(path, PATH_MAX); + + // Clear the screen + consoleClear(); + + // Print the path + if (strlen(path) < SCREEN_COLS) { + iprintf ("%s", path); + } else { + iprintf ("%s", path + strlen(path) - SCREEN_COLS); + } + + // Move to 2nd row + iprintf ("\x1b[1;0H"); + // Print line of dashes + iprintf ("------------------------------"); + + // Print directory listing + for (int i = 0; i < ((int)dirContents.size() - startRow) && i < ENTRIES_PER_SCREEN; i++) { + const DirEntry* entry = &dirContents.at(i + startRow); + char entryName[SCREEN_COLS + 1]; + + // Set row + iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW); + + if (entry->isDirectory) { + strncpy (entryName, entry->name.c_str(), SCREEN_COLS); + entryName[SCREEN_COLS - 3] = '\0'; + iprintf (" [%s]", entryName); + } else { + strncpy (entryName, entry->name.c_str(), SCREEN_COLS); + entryName[SCREEN_COLS - 1] = '\0'; + iprintf (" %s", entryName); + } + } +} + +string browseForFile (const vector& extensionList) { + int pressed = 0; + int screenOffset = 0; + int fileOffset = 0; + vector dirContents; + + RESET: + + getDirectoryContents (dirContents, extensionList); + showDirectoryContents (dirContents, screenOffset); + + while (true) { + // Clear old cursors + for (int i = ENTRIES_START_ROW; i < ENTRIES_PER_SCREEN + ENTRIES_START_ROW; i++) { + iprintf ("\x1b[%d;0H ", i); + } + // Show cursor + iprintf ("\x1b[%d;0H*", fileOffset - screenOffset + ENTRIES_START_ROW); + + iconTitleUpdate (dirContents.at(fileOffset).isDirectory, dirContents.at(fileOffset).name); + + // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do + do { + scanKeys(); + pressed = keysDownRepeat(); + swiWaitForVBlank(); + } while (!pressed); + + if ((pressed & KEY_L) || (pressed & KEY_R)) { + if (usingInternalFat) { + if (!EZ5NFatInit)EZ5NFatInit = fatMountSimple("ez5n", &io_ez5n); + if (EZ5NFatInit) { + if (access("ez5n:/", F_OK) == 0)chdir("ez5n:/"); + usingInternalFat = false; + pressed = 0; + screenOffset = 0; + fileOffset = 0; + goto RESET; + } + } else { + usingInternalFat = true; + if (access("fat:/", F_OK) == 0)chdir("fat:/"); + pressed = 0; + screenOffset = 0; + fileOffset = 0; + goto RESET; + } + } + if (pressed & KEY_UP) fileOffset -= 1; + if (pressed & KEY_DOWN) fileOffset += 1; + if (pressed & KEY_LEFT) fileOffset -= ENTRY_PAGE_LENGTH; + if (pressed & KEY_RIGHT)fileOffset += ENTRY_PAGE_LENGTH; + + if (fileOffset < 0)fileOffset = dirContents.size() - 1; // Wrap around to bottom of list + if (fileOffset > ((int)dirContents.size() - 1))fileOffset = 0; // Wrap around to top of list + + // Scroll screen if needed + if (fileOffset < screenOffset) { + screenOffset = fileOffset; + showDirectoryContents (dirContents, screenOffset); + } + if (fileOffset > screenOffset + ENTRIES_PER_SCREEN - 1) { + screenOffset = fileOffset - ENTRIES_PER_SCREEN + 1; + showDirectoryContents (dirContents, screenOffset); + } + + if (pressed & KEY_A) { + DirEntry* entry = &dirContents.at(fileOffset); + if (entry->isDirectory) { + iprintf("Entering directory\n"); + // Enter selected directory + chdir (entry->name.c_str()); + getDirectoryContents (dirContents, extensionList); + screenOffset = 0; + fileOffset = 0; + showDirectoryContents (dirContents, screenOffset); + } else { + // Clear the screen + consoleClear(); + // Return the chosen file + return entry->name; + } + } + + if (pressed & KEY_B) { + // Go up a directory + chdir (".."); + getDirectoryContents (dirContents, extensionList); + screenOffset = 0; + fileOffset = 0; + showDirectoryContents (dirContents, screenOffset); + } + + if (pressed & KEY_X) { + gbaMode(); + while(1)swiWaitForVBlank(); + } + } +} + diff --git a/arm9/source/file_browse.h b/arm9/source/file_browse.h new file mode 100644 index 0000000..248d3ba --- /dev/null +++ b/arm9/source/file_browse.h @@ -0,0 +1,32 @@ +/*----------------------------------------------------------------- + Copyright (C) 2005 - 2017 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ + +#ifndef FILE_BROWSE_H +#define FILE_BROWSE_H + +#include +#include + +std::string browseForFile (const std::vector& extensionList); + + + +#endif //FILE_BROWSE_H diff --git a/arm9/source/iconTitle.cpp b/arm9/source/iconTitle.cpp new file mode 100644 index 0000000..6aed4b6 --- /dev/null +++ b/arm9/source/iconTitle.cpp @@ -0,0 +1,242 @@ +/*----------------------------------------------------------------- + Copyright (C) 2005 - 2013 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + Claudio "sverx" + Michael "mtheall" Theall + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ + +#include +#include +#include +#include + +#include "args.h" +#include "hbmenu_banner.h" +#include "font6x8.h" + +#define TITLE_POS_X (13*8) +#define TITLE_POS_Y (10*8) + +#define ICON_POS_X 26 +#define ICON_POS_Y 79 + +#define TEXT_WIDTH ((22-4)*8/6) + +static int bg2, bg3; +static u16 *sprite; +static tNDSBanner banner; + +extern tNDSBanner hbNoIcon_bin; + +static inline void writecharRS (int row, int col, u16 car) { + // get map pointer + u16 *gfx = bgGetMapPtr(bg2); + // get old pair of values from VRAM + u16 oldval = gfx[row*(512/8/2)+(col/2)]; + + // clear the half we will update + oldval &= (col%2) ? 0x00FF : 0xFF00; + // apply the updated half + oldval |= (col%2) ? (car<<8) : car; + + // write back to VRAM + gfx[row*(512/8/2)+col/2] = oldval; +} + +static inline void writeRow (int rownum, const char* text) { + int i,len,p=0; + len=strlen(text); + + if (len>TEXT_WIDTH) + len=TEXT_WIDTH; + + // clear left part + for (i=0;i<(TEXT_WIDTH-len)/2;i++) + writecharRS (rownum, i, 0); + + // write centered text + for (i=(TEXT_WIDTH-len)/2;i<((TEXT_WIDTH-len)/2+len);i++) + writecharRS (rownum, i, text[p++]-' '); + + // clear right part + for (i=((TEXT_WIDTH-len)/2+len);i>> HBMenu+ <<<==="); +} + +static void loadDefaultIcon() { + DC_FlushAll(); + dmaCopy(hbNoIcon_bin.icon, sprite, sizeof(hbNoIcon_bin.icon)); + dmaCopy(hbNoIcon_bin.palette, SPRITE_PALETTE, sizeof(hbNoIcon_bin.palette)); +} + + +void iconTitleUpdate (int isdir, const std::string& name) { + writeRow (0, name.c_str()); + writeRow (1, ""); + writeRow (2, ""); + writeRow (3, ""); + + if (isdir) { + // text + writeRow (2, "[directory]"); + // icon + clearIcon(); + loadDefaultIcon(); + } else { + std::string ndsPath; + if (!argsNdsPath(name, ndsPath)) { + writeRow(2, "(invalid argv or NDS file!)"); + clearIcon(); + loadDefaultIcon(); + return; + } + + unsigned int Icon_title_offset; + + // open file for reading info + FILE *fp = fopen (ndsPath.c_str(), "rb"); + + if (!fp) { + // text + writeRow (2,"(can't open file!)"); + // icon + clearIcon(); + loadDefaultIcon(); + fclose (fp); + return; + } + + if (fseek (fp, offsetof(tNDSHeader, bannerOffset), SEEK_SET) != 0 || fread (&Icon_title_offset, sizeof(int), 1, fp) != 1) { + // text + writeRow (2, "(can't read file!)"); + // icon + clearIcon(); + loadDefaultIcon(); + fclose (fp); + return; + } + + if (Icon_title_offset == 0) { + // text + writeRow (2, "(no title/icon)"); + // icon + clearIcon(); + loadDefaultIcon(); + fclose (fp); + return; + } + + if (fseek (fp, Icon_title_offset, SEEK_SET) != 0 || fread (&banner, sizeof(banner), 1, fp) != 1) { + // text + writeRow (2,"(can't read icon/title!)"); + // icon + clearIcon(); + loadDefaultIcon(); + fclose (fp); + return; + } + + // close file! + fclose (fp); + + // turn unicode into ascii (kind of) + // and convert 0x0A into 0x00 + char *p = (char*)banner.titles[1]; + int rowOffset = 1; + int lineReturns = 0; + for (size_t i = 0; i < sizeof(banner.titles[1]); i = i+2) { + if ((p[i] == 0x0A) || (p[i] == 0xFF)) { + p[i/2] = 0; + lineReturns++; + } else { + p[i/2] = p[i]; + } + } + + if (lineReturns < 2)rowOffset = 2; // Recenter if bennar has less 2 or less rows of text maintaining empty row gap between nds file name and nds banner. + + // text + for (size_t i = 0; i < 3; ++i) { + writeRow(i+rowOffset, p); + p += strlen(p) + 1; + } + + // icon + DC_FlushAll(); + dmaCopy(banner.icon, sprite, sizeof(banner.icon)); + dmaCopy(banner.palette, SPRITE_PALETTE, sizeof(banner.palette)); + } +} + diff --git a/arm9/source/iconTitle.h b/arm9/source/iconTitle.h new file mode 100644 index 0000000..27611ca --- /dev/null +++ b/arm9/source/iconTitle.h @@ -0,0 +1,25 @@ +/*----------------------------------------------------------------- + Copyright (C) 2005 - 2013 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + Claudio "sverx" + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ +#include + +void iconTitleInit (void); +void iconTitleUpdate (int isdir, const std::string& name); diff --git a/arm9/source/ioezp.c b/arm9/source/ioezp.c new file mode 100644 index 0000000..e932748 --- /dev/null +++ b/arm9/source/ioezp.c @@ -0,0 +1,90 @@ +/* + EZ-Flash Parallel + Card IO routines + + Copyright (C) 2023-2024 lifehackerhansol + + SPDX-License-Identifier: Zlib +*/ + +#include +#include +#include "ioezp.h" +#include "libtwl_card.h" + +static u32 ioEZP_ReadLengthCtrlValues[4] = {IOEZP_CTRL_READ_SD_512, IOEZP_CTRL_READ_SD_1024, IOEZP_CTRL_READ_SD_2048, IOEZP_CTRL_READ_SD_2048}; + +static inline void ioEZP_ReadCardData(u64 command, u32 flags, void *buffer, u32 length) +{ + card_romSetCmd(command); + card_romStartXfer(flags, false); + if ((u32)buffer & 3) + card_romCpuReadUnaligned((u8 *)buffer, length); + else + card_romCpuRead(buffer, length); +} + +static inline void ioEZP_WriteCardData(u64 command, u32 flags, const void *buffer, u32 length) +{ + card_romSetCmd(command); + card_romStartXfer(flags, false); + if ((u32)buffer & 3) + card_romCpuWriteUnaligned((u8 *)buffer, length); + else + card_romCpuWrite(buffer, length); +} + +static inline u32 ioEZP_SendCommand(const u64 command, u32 latency) +{ + card_romSetCmd(command); + card_romStartXfer((IOEZP_CTRL_SEND_CMD | MCCNT1_LATENCY1(latency)), false); + card_romWaitDataReady(); + return card_romGetData(); +} + +u32 ioEZP_GetChipID(void) +{ + return ioEZP_SendCommand((((u64)CARD_CMD_DATA_CHIPID) << 56) | 1ull, 0x200); +} + +void ioEZP_SDReadSectors(u32 sector, u32 num_sectors, void *buffer) +{ + u32 word_count; + while(num_sectors > 0) + { + word_count = num_sectors >= 4 ? 4 : num_sectors; + + // wait until data is ready + // request should return 0 when ready to access + while(ioEZP_SendCommand(IOEZP_CMD_SD_READ_REQUEST(sector, word_count), 0xC8)); + + + ioEZP_ReadCardData(IOEZP_CMD_SD_READ_DATA, ioEZP_ReadLengthCtrlValues[word_count - 1], buffer, 128 * word_count); + sector += word_count; + num_sectors -= word_count; + buffer = (u8*)buffer + (0x200 * word_count); + } +} + +void ioEZP_SDWriteSectors(u32 sector, u32 num_sectors, const void *buffer) +{ + u32 word_count; + while(num_sectors > 0) + { + word_count = num_sectors >= 4 ? 4 : num_sectors; + + // Write to buffer + for(u32 i=0; i < word_count; i++) + { + ioEZP_WriteCardData(IOEZP_CMD_SD_WRITE_BUFFER(i), (IOEZP_CTRL_WRITE_SD | MCCNT1_LEN_512), buffer, 128); + buffer = (u8*)buffer + 0x200; + } + + // Flush to disk + // Should return 0 when done + while(ioEZP_SendCommand(IOEZP_CMD_SD_WRITE_FLUSH(sector, word_count), 0x190)); + sector += word_count; + num_sectors -= word_count; + } +} + diff --git a/arm9/source/ioezp.h b/arm9/source/ioezp.h new file mode 100644 index 0000000..1242e76 --- /dev/null +++ b/arm9/source/ioezp.h @@ -0,0 +1,53 @@ +/* + EZ-Flash Parallel + Card IO routines + + Copyright (C) 2023-2024 lifehackerhansol + + SPDX-License-Identifier: Zlib +*/ + +#pragma once + +#include + +#ifndef NULL + #define NULL 0 +#endif + +// EZParallel defines +// EZParallel MCCNT1 flags +#define IOEZP_CTRL_BASE (MCCNT1_ENABLE | MCCNT1_RESET_OFF | MCCNT1_CMD_SCRAMBLE | MCCNT1_CLOCK_SCRAMBLER | MCCNT1_READ_DATA_DESCRAMBLE) +#define IOEZP_CTRL_SEND_CMD (IOEZP_CTRL_BASE | MCCNT1_LATENCY2(24) | MCCNT1_LEN_4) +#define IOEZP_CTRL_READ_SD (IOEZP_CTRL_BASE | MCCNT1_LATENCY2(26) | MCCNT1_LATENCY1(0)) +#define IOEZP_CTRL_WRITE_SD (IOEZP_CTRL_BASE | MCCNT1_DIR_WRITE | MCCNT1_LATENCY2(26) | MCCNT1_LATENCY1(0)) + +#define IOEZP_CTRL_READ_SD_512 (IOEZP_CTRL_READ_SD | MCCNT1_LEN_512) +#define IOEZP_CTRL_READ_SD_1024 (IOEZP_CTRL_READ_SD | MCCNT1_LEN_1024) +#define IOEZP_CTRL_READ_SD_2048 (IOEZP_CTRL_READ_SD | MCCNT1_LEN_2048) + +// EZParallel MCCMDs +#define IOEZP_CMD_SD_READ_DATA (0xBAull << 56) + +// this reads 4 sectors??? +static inline u64 IOEZP_CMD_SD_READ_REQUEST(u32 sector, u8 num_sectors) +{ + return (0xB9ull << 56) | ((u64)num_sectors << 32) | ((u64)sector); +} + +// Writing seems to support up to 0x800 bytes +// It appears to work from an internal buffer, and flushes afterwards. +static inline u64 IOEZP_CMD_SD_WRITE_BUFFER(u8 buffer_index) +{ + return (0xBBull << 56) | (1ull << 32) | ((u64)buffer_index); +} + +static inline u64 IOEZP_CMD_SD_WRITE_FLUSH(u32 sector, u8 num_sectors) +{ + return (0xBCull << 56) | ((u64)num_sectors << 32) | ((u64)sector); +} + +// user API +u32 ioEZP_GetChipID(); +void ioEZP_SDReadSectors(u32 sector, u32 num_sectors, void* buffer); +void ioEZP_SDWriteSectors(u32 sector, u32 num_sectors, const void* buffer); diff --git a/arm9/source/libtwl_card.c b/arm9/source/libtwl_card.c new file mode 100644 index 0000000..a35c2d7 --- /dev/null +++ b/arm9/source/libtwl_card.c @@ -0,0 +1,80 @@ +/* + This file is a part of libtwl (card.c) + + Copyright (C) 2023 Gericom + + SPDX-License-Identifier: Zlib +*/ + +#include +#include "libtwl_card.h" + +void card_romCpuRead(u32* dst, u32 words) +{ + u32* target = dst + words; + do + { + // Read data if available + if (card_romIsDataReady()) + { + u32 data = card_romGetData(); + if (dst < target) + *dst++ = data; + } + } while (card_romIsBusy()); +} + +void card_romCpuReadUnaligned(u8* dst, u32 words) +{ + u8* target = dst + (words << 2); + do + { + // Read data if available + if (card_romIsDataReady()) + { + u32 data = card_romGetData(); + if (dst < target) + { + *dst++ = data & 0xFF; + *dst++ = (data >> 8) & 0xFF; + *dst++ = (data >> 16) & 0xFF; + *dst++ = (data >> 24) & 0xFF; + } + } + } while (card_romIsBusy()); +} + +void card_romCpuWrite(const u32* src, u32 words) +{ + u32 data = 0; + const u32* target = src + words; + do + { + // Write data if ready + if (card_romIsDataReady()) + { + if (src < target) + data = *src++; + card_romSetData(data); + } + } while (card_romIsBusy()); +} + +void card_romCpuWriteUnaligned(const u8* src, u32 words) +{ + u32 data = 0; + const u8* target = src + (words << 2); + do + { + // Write data if ready + if (card_romIsDataReady()) + { + if (src < target) + { + data = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + src += 4; + } + card_romSetData(data); + } + } while (card_romIsBusy()); +} diff --git a/arm9/source/libtwl_card.h b/arm9/source/libtwl_card.h new file mode 100644 index 0000000..bbbcced --- /dev/null +++ b/arm9/source/libtwl_card.h @@ -0,0 +1,132 @@ +/* + This file is a part of libtwl (card.h) + + Copyright (C) 2023 Gericom + + SPDX-License-Identifier: Zlib +*/ + +#pragma once + +#define REG_MCCNT0 (*(vu16*)0x040001A0) +#define REG_MCD0 (*(vu16*)0x040001A2) +#define REG_MCCNT1 (*(vu32*)0x040001A4) +#define REG_MCCMD0 (*(vu32*)0x040001A8) +#define REG_MCCMD1 (*(vu32*)0x040001AC) +#define REG_MCSCR0 (*(vu32*)0x040001B0) +#define REG_MCSCR1 (*(vu32*)0x040001B4) +#define REG_MCSCR2 (*(vu32*)0x040001B8) +#define REG_MCD1 (*(vu32*)0x04100010) + +// REG_MCCNT0 + +#define MCCNT0_SPI_RATE_4_19_MHZ 0 +#define MCCNT0_SPI_RATE_2_09_MHZ 1 +#define MCCNT0_SPI_RATE_1_05_MHZ 2 +#define MCCNT0_SPI_RATE_524_KHZ 3 + +#define MCCNT0_SPI_HOLD_CS (1 << 6) +#define MCCNT0_SPI_BUSY (1 << 7) + +#define MCCNT0_MODE_MASK (1 << 13) +#define MCCNT0_MODE_ROM (0 << 13) +#define MCCNT0_MODE_SPI (1 << 13) + +#define MCCNT0_ROM_XFER_IRQ (1 << 14) +#define MCCNT0_ENABLE (1 << 15) + +// REG_MCCNT1 + +#define MCCNT1_LATENCY1_SHIFT 0 +#define MCCNT1_LATENCY1_MASK 0x1FFF +#define MCCNT1_LATENCY1(x) (x) + +#define MCCNT1_READ_DATA_DESCRAMBLE (1 << 13) +#define MCCNT1_CLOCK_SCRAMBLER (1 << 14) +#define MCCNT1_APPLY_SCRAMBLE_SEED (1 << 15) + +#define MCCNT1_LATENCY2_SHIFT 16 +#define MCCNT1_LATENCY2_MASK 0x3F0000 +#define MCCNT1_LATENCY2(x) (((x) << MCCNT1_LATENCY2_SHIFT) & MCCNT1_LATENCY2_MASK) + +#define MCCNT1_CMD_SCRAMBLE (1 << 22) + +#define MCCNT1_DATA_READY (1 << 23) + +#define MCCNT1_LEN_0 (0 << 24) +#define MCCNT1_LEN_512 (1 << 24) +#define MCCNT1_LEN_1024 (2 << 24) +#define MCCNT1_LEN_2048 (3 << 24) +#define MCCNT1_LEN_4096 (4 << 24) +#define MCCNT1_LEN_8192 (5 << 24) +#define MCCNT1_LEN_16384 (6 << 24) +#define MCCNT1_LEN_4 (7 << 24) + +#define MCCNT1_CLK_6_7_MHZ (0 << 27) +#define MCCNT1_CLK_4_2_MHZ (1 << 27) + +#define MCCNT1_LATENCY_CLK (1 << 28) + +#define MCCNT1_RESET_ON (0 << 29) +#define MCCNT1_RESET_OFF (1 << 29) + +#define MCCNT1_DIR_READ (0 << 30) +#define MCCNT1_DIR_WRITE (1 << 30) + +#define MCCNT1_ENABLE (1 << 31) + +#ifdef __cplusplus +extern "C" +{ +#endif + + static inline void card_romSetCmd(u64 cmd) + { + *(vu64*)®_MCCMD0 = __builtin_bswap64(cmd); + } + + static inline bool card_romIsDataReady(void) + { + return REG_MCCNT1 & MCCNT1_DATA_READY; + } + + static inline void card_romWaitDataReady(void) + { + while(!card_romIsDataReady()); + } + + static inline u32 card_romGetData(void) + { + return REG_MCD1; + } + + static inline void card_romSetData(u32 data) + { + REG_MCD1 = data; + } + + static inline bool card_romIsBusy(void) + { + return REG_MCCNT1 & MCCNT1_ENABLE; + } + + static inline void card_romWaitBusy(void) + { + while(card_romIsBusy()); + } + + static inline void card_romStartXfer(u32 settings, bool irq) + { + REG_MCCNT0 = (REG_MCCNT0 & ~(MCCNT0_MODE_MASK | MCCNT0_ROM_XFER_IRQ)) | MCCNT0_MODE_ROM | (irq ? MCCNT0_ROM_XFER_IRQ : 0) | MCCNT0_ENABLE; + REG_MCCNT1 = MCCNT1_ENABLE | settings; + } + + void card_romCpuRead(u32* dst, u32 len); + void card_romCpuReadUnaligned(u8* dst, u32 words); + + void card_romCpuWrite(const u32* src, u32 words); + void card_romCpuWriteUnaligned(const u8* src, u32 words); + +#ifdef __cplusplus +} +#endif diff --git a/arm9/source/main.cpp b/arm9/source/main.cpp new file mode 100644 index 0000000..caaf625 --- /dev/null +++ b/arm9/source/main.cpp @@ -0,0 +1,248 @@ +/*----------------------------------------------------------------- + Copyright (C) 2005 - 2013 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + Claudio "sverx" + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "args.h" +#include "file_browse.h" +#include "font.h" +#include "hbmenu_consolebg.h" +#include "iconTitle.h" +#include "skin.h" +#include "tonccpy.h" +#include "nrio_detect.h" +#include "nds_loader_arm9.h" + +#define BG_256_COLOR (BIT(7)) +#define FlashBase_S98 0x09000000 + +using namespace std; + +volatile bool guiEnabled = false; +volatile bool gbaGuiEnabled = false; + +// const bool BlankScreenOnBoot = true; + +static const int pathListSize = 3; +static const int framePathListSize = 4; + +static const char *PossiblePaths[3] = { "GBAExploader.nds", "/boot.nds", "/boot.dat" }; + +static const char *GBAFramePaths[4] = { + "/gbaframe.bmp", + "/GBA_SIGN/gbaframe.bmp", + "/_system_/gbaframe.bmp", + "/ttmenu/gbaframe.bmp" +}; + +void InitGUI(void) { + if (guiEnabled)return; + guiEnabled = true; + gbaGuiEnabled = false; + iconTitleInit(); + videoSetModeSub(MODE_4_2D); + vramSetBankC(VRAM_C_SUB_BG); + int bgSub = bgInitSub(3, BgType_Bmp8, BgSize_B8_256x256, 1, 0); + PrintConsole *console = consoleInit(0, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 6, false, false); + dmaCopy(hbmenu_consolebgBitmap, bgGetGfxPtr(bgSub), 256*256); + ConsoleFont font; + font.gfx = (u16*)fontTiles; + font.pal = (u16*)fontPal; + font.numChars = 95; + font.numColors = (fontPalLen / 2); + font.bpp = 4; + font.asciiOffset = 32; + font.convertSingleColor = true; + consoleSetFont(console, &font); + dmaCopy(hbmenu_consolebgPal, BG_PALETTE_SUB, 256*2); + BG_PALETTE_SUB[255] = RGB15(31,31,31); + keysSetRepeat(25,5); + consoleSetWindow(console, 1, 1, 30, 22); +} + +void InitGUIForGBA() { + if (gbaGuiEnabled)return; + gbaGuiEnabled = true; + guiEnabled = false; + videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE); + videoSetModeSub(MODE_5_2D | DISPLAY_BG3_ACTIVE); + vramSetBankA(VRAM_A_MAIN_BG_0x06000000); + vramSetBankB(VRAM_B_MAIN_BG_0x06020000); + vramSetBankC(VRAM_C_SUB_BG_0x06200000); + vramSetBankD(VRAM_D_LCD); + // for the main screen + REG_BG3CNT = BG_BMP16_256x256 | BG_BMP_BASE(0) | BG_WRAP_OFF; + REG_BG3PA = 1 << 8; //scale x + REG_BG3PB = 0; //rotation x + REG_BG3PC = 0; //rotation y + REG_BG3PD = 1 << 8; //scale y + REG_BG3X = 0; //translation x + REG_BG3Y = 0; //translation y*/ + toncset((void*)BG_BMP_RAM(0),0,0x18000); + toncset((void*)BG_BMP_RAM(8),0,0x18000); + swiWaitForVBlank(); +} + + +u16 Read_S98NOR_ID() { + *((vu16*)(FlashBase_S98)) = 0xF0; + *((vu16*)(FlashBase_S98+0x555*2)) = 0xAA; + *((vu16*)(FlashBase_S98+0x2AA*2)) = 0x55; + *((vu16*)(FlashBase_S98+0x555*2)) = 0x90; + return *((vu16*)(FlashBase_S98+0xE*2)); +} + +void SetKernelRomPage() { + *(vu16*)0x09FE0000 = 0xD200; + *(vu16*)0x08000000 = 0x1500; + *(vu16*)0x08020000 = 0xD200; + *(vu16*)0x08040000 = 0x1500; + *(vu16*)0x09880000 = 0x8002; // Kernel section of NorFlash + *(vu16*)0x09FC0000 = 0x1500; +} + +void LoadGBAFrame() { + InitGUIForGBA(); + for (int i = 0; i < framePathListSize; i++) { + if ((access(GBAFramePaths[i], F_OK) == 0) && LoadSkin(3, GBAFramePaths[i]))return; + } +} + +void gbaMode() { + sysSetCartOwner(true); + + swiWaitForVBlank(); + + if (Read_S98NOR_ID() == 0x223D)SetKernelRomPage(); + + LoadGBAFrame(); + + if(PersonalData->gbaScreen) { lcdMainOnBottom(); } else { lcdMainOnTop(); } + + sysSetCartOwner(false); + fifoSendValue32(FIFO_USER_01, 1); + REG_IME = 0; + irqDisable(IRQ_VBLANK); + while(1)swiWaitForVBlank(); +} + +int stop(void) { + while(1) { + swiWaitForVBlank(); + scanKeys(); + if (!keysHeld())break; + } + while (1) { + swiWaitForVBlank(); + scanKeys(); + if (keysDown() != 0)break; + } + return 0; +} + +int FileBrowser() { + InitGUI(); + consoleClear(); + while(1) { + swiWaitForVBlank(); + scanKeys(); + if (!keysHeld())break; + } + vector extensionList = argsGetExtensionList(); + // if (access("fat:/nds", F_OK) == 0)chdir("fat:/nds"); + while(1) { + string filename = browseForFile(extensionList); + // Construct a command line + vector argarray; + if (!argsFillArray(filename, argarray)) { + printf("Invalid NDS or arg file selected\n"); + } else { + iprintf("Running %s with %d parameters\n", argarray[0].c_str(), argarray.size()); + // Make a copy of argarray using C strings, for the sake of runNdsFile + vector c_args; + for (const auto& arg: argarray) { c_args.push_back(arg.c_str()); } + // Try to run the NDS file with the given arguments + int err = runNdsFile(c_args[0], c_args.size(), &c_args[0]); + iprintf("Start failed. Error %i\n", err); + } + argarray.clear(); + } + return 0; +} + +int main(int argc, char **argv) { + // overwrite reboot stub identifier + // so tapping power on DSi returns to DSi menu + extern u64 *fake_heap_end; + *fake_heap_end = 0; + bool fatInit = fatInitDefault(); + // if (!fatMountSimple("ez5n", &io_ez5n)) { + if (!fatInit) { + InitGUI(); + consoleClear(); + printf ("\n\n\n\n\n\n\n\n\n\n FAT init failed! \n"); + return stop(); + } + swiWaitForVBlank(); + scanKeys(); + u32 key = keysDown(); + switch (key) { + case KEY_A: { + for (int i = 0; i < pathListSize; i++) { + if (access(PossiblePaths[i], F_OK) == 0) { + runNdsFile(PossiblePaths[i], 0, NULL); + return stop(); + } + } + FileBrowser(); + } break; + case KEY_B: gbaMode(); break; + case KEY_X: { + if((access("/mmd.nds", F_OK) == 0)) { + runNdsFile("/mmd.nds", 0, NULL); + } else { + FileBrowser(); + } + } break; + case KEY_Y: { + if((access("/GodMode9i.nds", F_OK) == 0)) { runNdsFile("/GodMode9i.nds", 0, NULL); } else { FileBrowser(); } + } break; + // case 0: gbaMode(); break; + default: { + if (!(key & KEY_SELECT) && (access("/nrio-usb-disk.nds", F_OK) == 0)) { + nrio_usb_type_t usb = nrio_usb_detect(); + if (usb.board_type != 0)runNdsFile("/nrio-usb-disk.nds", 0, NULL); + } + FileBrowser(); + } break; + } + return stop(); +} + diff --git a/arm9/source/nds_loader_arm9.c b/arm9/source/nds_loader_arm9.c new file mode 100644 index 0000000..019a7bd --- /dev/null +++ b/arm9/source/nds_loader_arm9.c @@ -0,0 +1,370 @@ +/*----------------------------------------------------------------- + Copyright (C) 2005 - 2010 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ +#include +#include +#include +#include +#include + +#include +#include + +#include "load_bin.h" + +#ifndef _NO_BOOTSTUB_ +#include "bootstub_bin.h" +#include "exceptionstub_bin.h" +#endif + +#include "nds_loader_arm9.h" + +#define TMP_DATA 0x02100000 + +#define LCDC_BANK_D (u16*)0x06860000 +#define STORED_FILE_CLUSTER (*(((u32*)LCDC_BANK_D) + 1)) +#define INIT_DISC (*(((u32*)LCDC_BANK_D) + 2)) +#define WANT_TO_PATCH_DLDI (*(((u32*)LCDC_BANK_D) + 3)) + +#define STORED_FILE_CLUSTER_OFFSET 4 +#define INIT_DISC_OFFSET 8 +#define WANT_TO_PATCH_DLDI_OFFSET 12 +#define ARG_START_OFFSET 16 +#define ARG_SIZE_OFFSET 20 +#define HAVE_DSISD_OFFSET 28 +#define DSIMODE_OFFSET 32 + +typedef signed int addr_t; +typedef unsigned char data_t; + +extern bool usingInternalFat; + +#define FIX_ALL 0x01 +#define FIX_GLUE 0x02 +#define FIX_GOT 0x04 +#define FIX_BSS 0x08 + +DLDI_INTERFACE* EZ5NDLDI; + +enum DldiOffsets { + DO_magicString = 0x00, // "\xED\xA5\x8D\xBF Chishm" + DO_magicToken = 0x00, // 0xBF8DA5ED + DO_magicShortString = 0x04, // " Chishm" + DO_version = 0x0C, + DO_driverSize = 0x0D, + DO_fixSections = 0x0E, + DO_allocatedSpace = 0x0F, + + DO_friendlyName = 0x10, + + DO_text_start = 0x40, // Data start + DO_data_end = 0x44, // Data end + DO_glue_start = 0x48, // Interworking glue start -- Needs address fixing + DO_glue_end = 0x4C, // Interworking glue end + DO_got_start = 0x50, // GOT start -- Needs address fixing + DO_got_end = 0x54, // GOT end + DO_bss_start = 0x58, // bss start -- Needs setting to zero + DO_bss_end = 0x5C, // bss end + + // IO_INTERFACE data + DO_ioType = 0x60, + DO_features = 0x64, + DO_startup = 0x68, + DO_isInserted = 0x6C, + DO_readSectors = 0x70, + DO_writeSectors = 0x74, + DO_clearStatus = 0x78, + DO_shutdown = 0x7C, + DO_code = 0x80 +}; + +static addr_t readAddr (data_t *mem, addr_t offset) { return ((addr_t*)mem)[offset/sizeof(addr_t)]; } + +static void writeAddr (data_t *mem, addr_t offset, addr_t value) { ((addr_t*)mem)[offset/sizeof(addr_t)] = value; } + +static void vramcpy (void* dst, const void* src, int len) { + u16* dst16 = (u16*)dst; + u16* src16 = (u16*)src; + + for ( ; len > 0; len -= 2) { *dst16++ = *src16++; } +} + +static addr_t quickFind (const data_t* data, const data_t* search, size_t dataLen, size_t searchLen) { + const int* dataChunk = (const int*) data; + int searchChunk = ((const int*)search)[0]; + addr_t i; + addr_t dataChunkEnd = (addr_t)(dataLen / sizeof(int)); + + for ( i = 0; i < dataChunkEnd; i++) { + if (dataChunk[i] == searchChunk) { + if ((i*sizeof(int) + searchLen) > dataLen)return -1; + if (memcmp (&data[i*sizeof(int)], search, searchLen) == 0)return i*sizeof(int); + } + } + return -1; +} + +// Normal DLDI uses "\xED\xA5\x8D\xBF Chishm" +// Bootloader string is different to avoid being patched +static const data_t dldiMagicLoaderString[] = "\xEE\xA5\x8D\xBF Chishm"; // Different to a normal DLDI file + +#define DEVICE_TYPE_DLDI 0x49444C44 + +static bool dldiPatchLoader (data_t *binData, u32 binSize, bool clearBSS, bool useEZPDLDI) { + addr_t memOffset; // Offset of DLDI after the file is loaded into memory + addr_t patchOffset; // Position of patch destination in the file + addr_t relocationOffset; // Value added to all offsets within the patch to fix it properly + addr_t ddmemOffset; // Original offset used in the DLDI file + addr_t ddmemStart; // Start of range that offsets can be in the DLDI file + addr_t ddmemEnd; // End of range that offsets can be in the DLDI file + addr_t ddmemSize; // Size of range that offsets can be in the DLDI file + + addr_t addrIter; + + data_t *pDH; + data_t *pAH; + + size_t dldiFileSize = 0; + + // Find the DLDI reserved space in the file + patchOffset = quickFind (binData, dldiMagicLoaderString, binSize, sizeof(dldiMagicLoaderString)); + + if (patchOffset < 0)return false; // does not have a DLDI section + + if (useEZPDLDI) { + EZ5NDLDI = dldiLoadFromFile("fat:/ez5n.dldi"); + pDH = (data_t*)(EZ5NDLDI); + } else { + pDH = (data_t*)(io_dldi_data); + } + + pAH = &(binData[patchOffset]); + + if (*((u32*)(pDH + DO_ioType)) == DEVICE_TYPE_DLDI)return false; // No DLDI patch + + if (pDH[DO_driverSize] > pAH[DO_allocatedSpace])return false; // Not enough space for patch + + dldiFileSize = 1 << pDH[DO_driverSize]; + + memOffset = readAddr (pAH, DO_text_start); + if (memOffset == 0)memOffset = (readAddr (pAH, DO_startup) - DO_code); + ddmemOffset = readAddr (pDH, DO_text_start); + relocationOffset = (memOffset - ddmemOffset); + + ddmemStart = readAddr (pDH, DO_text_start); + ddmemSize = (1 << pDH[DO_driverSize]); + ddmemEnd = (ddmemStart + ddmemSize); + + // Remember how much space is actually reserved + pDH[DO_allocatedSpace] = pAH[DO_allocatedSpace]; + // Copy the DLDI patch into the application + vramcpy (pAH, pDH, dldiFileSize); + + // Fix the section pointers in the header + writeAddr (pAH, DO_text_start, readAddr (pAH, DO_text_start) + relocationOffset); + writeAddr (pAH, DO_data_end, readAddr (pAH, DO_data_end) + relocationOffset); + writeAddr (pAH, DO_glue_start, readAddr (pAH, DO_glue_start) + relocationOffset); + writeAddr (pAH, DO_glue_end, readAddr (pAH, DO_glue_end) + relocationOffset); + writeAddr (pAH, DO_got_start, readAddr (pAH, DO_got_start) + relocationOffset); + writeAddr (pAH, DO_got_end, readAddr (pAH, DO_got_end) + relocationOffset); + writeAddr (pAH, DO_bss_start, readAddr (pAH, DO_bss_start) + relocationOffset); + writeAddr (pAH, DO_bss_end, readAddr (pAH, DO_bss_end) + relocationOffset); + // Fix the function pointers in the header + writeAddr (pAH, DO_startup, readAddr (pAH, DO_startup) + relocationOffset); + writeAddr (pAH, DO_isInserted, readAddr (pAH, DO_isInserted) + relocationOffset); + writeAddr (pAH, DO_readSectors, readAddr (pAH, DO_readSectors) + relocationOffset); + writeAddr (pAH, DO_writeSectors, readAddr (pAH, DO_writeSectors) + relocationOffset); + writeAddr (pAH, DO_clearStatus, readAddr (pAH, DO_clearStatus) + relocationOffset); + writeAddr (pAH, DO_shutdown, readAddr (pAH, DO_shutdown) + relocationOffset); + + if (pDH[DO_fixSections] & FIX_ALL) { + // Search through and fix pointers within the data section of the file + for (addrIter = (readAddr(pDH, DO_text_start) - ddmemStart); addrIter < (readAddr(pDH, DO_data_end) - ddmemStart); addrIter++) { + if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) { + writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset); + } + } + } + + if (pDH[DO_fixSections] & FIX_GLUE) { + // Search through and fix pointers within the glue section of the file + for (addrIter = (readAddr(pDH, DO_glue_start) - ddmemStart); addrIter < (readAddr(pDH, DO_glue_end) - ddmemStart); addrIter++) { + if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) { + writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset); + } + } + } + + if (pDH[DO_fixSections] & FIX_GOT) { + // Search through and fix pointers within the Global Offset Table section of the file + for (addrIter = (readAddr(pDH, DO_got_start) - ddmemStart); addrIter < (readAddr(pDH, DO_got_end) - ddmemStart); addrIter++) { + if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) { + writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset); + } + } + } + + if (clearBSS && (pDH[DO_fixSections] & FIX_BSS)) { + // Initialise the BSS to 0, only if the disc is being re-inited + memset (&pAH[readAddr(pDH, DO_bss_start) - ddmemStart] , 0, readAddr(pDH, DO_bss_end) - readAddr(pDH, DO_bss_start)); + } + + return true; +} + +// eRunNdsRetCode runNds (const void* loader, u32 loaderSize, u32 cluster, bool initDisc, bool dldiPatchNds, int argc, const char** argv) { +eRunNdsRetCode runNds (const void* loader, u32 loaderSize, u32 cluster, bool initDisc, bool useExtDLDI, int argc, const char** argv) { + char* argStart; + u16* argData; + u16 argTempVal = 0; + int argSize; + const char* argChar; + + irqDisable(IRQ_ALL); + + // Direct CPU access to VRAM bank C + VRAM_D_CR = VRAM_ENABLE | VRAM_D_LCD; + // Load the loader/patcher into the correct address + vramcpy (LCDC_BANK_D, loader, loaderSize); + + // Set the parameters for the loader + // STORED_FILE_CLUSTER = cluster; + writeAddr ((data_t*) LCDC_BANK_D, STORED_FILE_CLUSTER_OFFSET, cluster); + // INIT_DISC = initDisc; + writeAddr ((data_t*) LCDC_BANK_D, INIT_DISC_OFFSET, initDisc); + + writeAddr ((data_t*) LCDC_BANK_D, DSIMODE_OFFSET, isDSiMode()); + + /*if(argv[0][0]=='s' && argv[0][1]=='d') { + dldiPatchNds = false; + writeAddr ((data_t*) LCDC_BANK_D, HAVE_DSISD_OFFSET, 1); + } + + // WANT_TO_PATCH_DLDI = dldiPatchNds; + // writeAddr ((data_t*) LCDC_BANK_D, WANT_TO_PATCH_DLDI_OFFSET, dldiPatchNds);*/ + writeAddr ((data_t*) LCDC_BANK_D, WANT_TO_PATCH_DLDI_OFFSET, useExtDLDI); + // Give arguments to loader + argStart = (char*)LCDC_BANK_D + readAddr((data_t*)LCDC_BANK_D, ARG_START_OFFSET); + argStart = (char*)(((int)argStart + 3) & ~3); // Align to word + argData = (u16*)argStart; + argSize = 0; + + for (; argc > 0 && *argv; ++argv, --argc) { + for (argChar = *argv; *argChar != 0; ++argChar, ++argSize) { + if (argSize & 1) { + argTempVal |= (*argChar) << 8; + *argData = argTempVal; + ++argData; + } else { + argTempVal = *argChar; + } + } + if (argSize & 1) { *argData = argTempVal; ++argData; } + argTempVal = 0; + ++argSize; + } + + *argData = argTempVal; + + writeAddr ((data_t*) LCDC_BANK_D, ARG_START_OFFSET, (addr_t)argStart - (addr_t)LCDC_BANK_D); + writeAddr ((data_t*) LCDC_BANK_D, ARG_SIZE_OFFSET, argSize); + + /*if(dldiPatchNds) { + // Patch the loader with a DLDI for the card + if (!dldiPatchLoader ((data_t*)LCDC_BANK_D, loaderSize, initDisc))return RUN_NDS_PATCH_DLDI_FAILED; + }*/ + if (!dldiPatchLoader ((data_t*)LCDC_BANK_D, loaderSize, initDisc, useExtDLDI))return RUN_NDS_PATCH_DLDI_FAILED; + + irqDisable(IRQ_ALL); + + // Give the VRAM to the ARM7 + VRAM_D_CR = VRAM_ENABLE | VRAM_D_ARM7_0x06020000; + // Reset into a passme loop + REG_EXMEMCNT |= ARM7_OWNS_ROM | ARM7_OWNS_CARD; + *((vu32*)0x02FFFFFC) = 0; + *((vu32*)0x02FFFE04) = (u32)0xE59FF018; + *((vu32*)0x02FFFE24) = (u32)0x02FFFE04; + + resetARM7(0x06020000); + + swiSoftReset(); + return RUN_NDS_OK; +} + +eRunNdsRetCode runNdsFile (const char* filename, int argc, const char** argv) { + struct stat st; + char filePath[PATH_MAX]; + int pathLen; + const char* args[1]; + + if (stat (filename, &st) < 0)return RUN_NDS_STAT_FAILED; + + if ((argc <= 0) || !argv) { + // Construct a command line if we weren't supplied with one + if (!getcwd (filePath, PATH_MAX))return RUN_NDS_GETCWD_FAILED; + pathLen = strlen (filePath); + strcpy (filePath + pathLen, filename); + args[0] = filePath; + argv = args; + } + + /*bool havedsiSD = false; + + if(argv[0][0]=='s' && argv[0][1]=='d') havedsiSD = true; + + installBootStub(havedsiSD);*/ + // return runNds (load_bin, load_bin_size, st.st_ino, true, true, argc, argv); + if (usingInternalFat) { + return runNds (load_bin, load_bin_size, st.st_ino, true, false, argc, argv); + } else { + return runNds (load_bin, load_bin_size, st.st_ino, true, true, argc, argv); + } +} + +void(*exceptionstub)(void) = (void(*)(void))0x2FFA000; + +/*bool installBootStub(bool havedsiSD) { +#ifndef _NO_BOOTSTUB_ + extern char *fake_heap_end; + struct __bootstub *bootstub = (struct __bootstub *)fake_heap_end; + u32 *bootloader = (u32*)(fake_heap_end+bootstub_bin_size); + memcpy(bootstub,bootstub_bin,bootstub_bin_size); + memcpy(bootloader,load_bin,load_bin_size); + bool ret = false; + bootloader[8] = isDSiMode(); + if(havedsiSD) { + ret = true; + bootloader[3] = 0; // don't dldi patch + bootloader[7] = 1; // use internal dsi SD code + } else { + ret = dldiPatchLoader((data_t*)bootloader, load_bin_size,false); + } + bootstub->arm9reboot = (VoidFn)(((u32)bootstub->arm9reboot)+fake_heap_end); + bootstub->arm7reboot = (VoidFn)(((u32)bootstub->arm7reboot)+fake_heap_end); + bootstub->bootsize = load_bin_size; + memcpy(exceptionstub,exceptionstub_bin,exceptionstub_bin_size); + exceptionstub(); + DC_FlushAll(); + return ret; +#else + return true; +#endif +}*/ + diff --git a/arm9/source/nds_loader_arm9.h b/arm9/source/nds_loader_arm9.h new file mode 100644 index 0000000..c2f23c2 --- /dev/null +++ b/arm9/source/nds_loader_arm9.h @@ -0,0 +1,51 @@ +/*----------------------------------------------------------------- + Copyright (C) 2005 - 2010 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ + +#ifndef NDS_LOADER_ARM9_H +#define NDS_LOADER_ARM9_H + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + RUN_NDS_OK = 0, + RUN_NDS_STAT_FAILED, + RUN_NDS_GETCWD_FAILED, + RUN_NDS_PATCH_DLDI_FAILED, +} eRunNdsRetCode; + +#define LOAD_DEFAULT_NDS 0 + +// eRunNdsRetCode runNds (const void* loader, u32 loaderSize, u32 cluster, bool initDisc, bool dldiPatchNds, int argc, const char** argv); +eRunNdsRetCode runNds (const void* loader, u32 loaderSize, u32 cluster, bool initDisc, bool useExtDLDI, int argc, const char** argv); + +eRunNdsRetCode runNdsFile (const char* filename, int argc, const char** argv); + +bool installBootStub(bool havedsiSD); + +#ifdef __cplusplus +} +#endif + +#endif // NDS_LOADER_ARM7_H + diff --git a/arm9/source/nrio_detect.c b/arm9/source/nrio_detect.c new file mode 100644 index 0000000..783a92d --- /dev/null +++ b/arm9/source/nrio_detect.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: CC0-1.0 +// +// SPDX-FileContributor: Adrian "asie" Siekierka, 2024 + +#include +#include "nrio_detect.h" + +#define EXMEMCNT_SRAM_TIME_SHIFT (0) +#define EXMEMCNT_SRAM_TIME_6_CYCLES (2 << EXMEMCNT_SRAM_TIME_SHIFT) +#define EXMEMCNT_SRAM_TIME_18_CYCLES (3 << EXMEMCNT_SRAM_TIME_SHIFT) + +#define EXMEMCNT_ROM_TIME1_SHIFT (2) +#define EXMEMCNT_ROM_TIME2_SHIFT (4) +#define EXMEMCNT_ROM_TIME1_6_CYCLES (2 << EXMEMCNT_ROM_TIME1_SHIFT) +#define EXMEMCNT_ROM_TIME1_18_CYCLES (3 << EXMEMCNT_ROM_TIME1_SHIFT) +#define EXMEMCNT_ROM_TIME2_6_CYCLES (0 << EXMEMCNT_ROM_TIME2_SHIFT) +#define EXMEMCNT_ROM_TIME2_4_CYCLES (1 << EXMEMCNT_ROM_TIME2_SHIFT) + +#define NRIO_D12_DATA (*((volatile uint16_t*) 0x9FDFFFE)) +#define NRIO_D12_CMD (*((volatile uint16_t*) 0x9FFFFFE)) + +#define NRIO_D12_CMD_READ_ID 0xFD + +static inline uint16_t nrio_d12_read_chip_id(void) { + uint16_t result; + NRIO_D12_CMD = NRIO_D12_CMD_READ_ID; + result = NRIO_D12_DATA & 0xFF; + result |= (NRIO_D12_DATA & 0xFF) << 8; + return result; +} + +#define NRIO_D14_REG(index) GBA_BUS[(index) * 0x10000] + +#define NRIO_D14_CHIP_IDL NRIO_D14_REG(0x70) +#define NRIO_D14_CHIP_IDH NRIO_D14_REG(0x72) + +static inline uint32_t nrio_d14_read_chip_id(void) { + return ((NRIO_D14_CHIP_IDH << 16) | NRIO_D14_CHIP_IDL) & 0xFFFFFF; +} + +nrio_usb_type_t nrio_usb_detect(void) { + + nrio_usb_type_t result = {0, 0, 0}; + + // Configure GBA cartridge bus + sysSetCartOwner(BUS_OWNER_ARM9); + + REG_EXMEMCNT = (REG_EXMEMCNT & ~0x1F) | EXMEMCNT_ROM_TIME1_6_CYCLES + | EXMEMCNT_ROM_TIME2_4_CYCLES | EXMEMCNT_SRAM_TIME_6_CYCLES; + + // Check if the chip is an ISP1581/82/83 + uint32_t d14_chip_id = nrio_d14_read_chip_id(); + if (d14_chip_id >= 0x158100 && d14_chip_id < 0x158400) { + result.board_type = NRIO_USB_BOARD_TYPE_D14; + result.chip_id = d14_chip_id >> 8; + result.chip_rev = d14_chip_id & 0xFF; + return result; + } + + // The D12 supports a maximum readout speed of 2 MHz. + REG_EXMEMCNT = (REG_EXMEMCNT & ~0x1F) | EXMEMCNT_ROM_TIME1_18_CYCLES + | EXMEMCNT_ROM_TIME2_6_CYCLES | EXMEMCNT_SRAM_TIME_18_CYCLES; + + // Check if the chip is an PDIUSBD12 (do other compatible chips exist?) + uint16_t d12_chip_id = nrio_d12_read_chip_id(); + uint8_t d12_open_bus = NRIO_D12_DATA; + if (!((d12_chip_id >> 8) == d12_open_bus && (d12_chip_id & 0xFF) == d12_open_bus)) { + result.board_type = NRIO_USB_BOARD_TYPE_D12; + result.chip_id = d12_chip_id; + return result; + } + + return result; +} + diff --git a/arm9/source/nrio_detect.h b/arm9/source/nrio_detect.h new file mode 100644 index 0000000..c4bd21f --- /dev/null +++ b/arm9/source/nrio_detect.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: CC0-1.0 +// +// SPDX-FileContributor: Adrian "asie" Siekierka, 2024 + +#ifndef _NRIO_DETECT_H_ +#define _NRIO_DETECT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define NRIO_USB_BOARD_TYPE_NONE 0 +#define NRIO_USB_BOARD_TYPE_D12 12 +#define NRIO_USB_BOARD_TYPE_D14 14 + +typedef struct nrio_usb_type { + uint16_t board_type; ///< Board type + uint16_t chip_id; ///< Chip ID + uint16_t chip_rev; ///< Chip revision +} nrio_usb_type_t; + +nrio_usb_type_t nrio_usb_detect(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _NRIO_DETECT_H_ */ + diff --git a/arm9/source/skin.cpp b/arm9/source/skin.cpp new file mode 100644 index 0000000..61f6ad4 --- /dev/null +++ b/arm9/source/skin.cpp @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "skin.h" + + +#define BI_RGB (0) +#define BI_RLE8 (1) +#define BI_RLE4 (2) +#define BI_Bitfields (3) + +typedef struct { + u8 bfType[2]; + u32 bfSize; + u16 bfReserved1; + u16 bfReserved2; + u32 bfOffset; + u32 biSize; + u32 biWidth; + u32 biHeight; + u16 biPlanes; + u16 biBitCount; + u32 biCopmression; + u32 biSizeImage; + u32 biXPixPerMeter; + u32 biYPixPerMeter; + u32 biClrUsed; + u32 biCirImportant; + u8 *pPalette; + u8 *pBitmap; + u32 DataWidth; +} TBMPHeader; + + +static u16 GetVariable16bit(void *pb) { + u16 res; + u8 *pb8=(u8*)pb; + + res=(u32)pb8[0] << 0; + res+=(u32)pb8[1] << 8; + + return(res); +} + +static u32 GetVariable32bit(void *pb) { + u32 res; + u8 *pb8=(u8*)pb; + + res=(u32)pb8[0] << 0; + res+=(u32)pb8[1] << 8; + res+=(u32)pb8[2] << 16; + res+=(u32)pb8[3] << 24; + + return(res); +} + + +static bool GetBMPHeader(u8 *pb,TBMPHeader *pBMPHeader) { + if(pb==NULL){ +// BMP_LoadErrorStr="SourceData Null."; + return(false); + } + if(pBMPHeader==NULL){ +// BMP_LoadErrorStr="pBMPHeader Null."; + return(false); + } + + pBMPHeader->bfType[0]=pb[0]; + pBMPHeader->bfType[1]=pb[1]; + pBMPHeader->bfSize=GetVariable32bit(&pb[2]); + pBMPHeader->bfReserved1=GetVariable16bit(&pb[6]); + pBMPHeader->bfReserved2=GetVariable16bit(&pb[8]); + pBMPHeader->bfOffset=GetVariable32bit(&pb[10]); + pBMPHeader->biSize=GetVariable32bit(&pb[14+0]); + pBMPHeader->biWidth=GetVariable32bit(&pb[14+4]); + pBMPHeader->biHeight=GetVariable32bit(&pb[14+8]); + pBMPHeader->biPlanes=GetVariable16bit(&pb[14+12]); + pBMPHeader->biBitCount=GetVariable16bit(&pb[14+14]); + pBMPHeader->biCopmression=GetVariable32bit(&pb[14+16]); + pBMPHeader->biSizeImage=GetVariable32bit(&pb[14+20]); + pBMPHeader->biXPixPerMeter=GetVariable32bit(&pb[14+24]); + pBMPHeader->biYPixPerMeter=GetVariable32bit(&pb[14+28]); + pBMPHeader->biClrUsed=GetVariable32bit(&pb[14+32]); + pBMPHeader->biCirImportant=GetVariable32bit(&pb[14+36]); + + pBMPHeader->pPalette=&pb[14+40]; + pBMPHeader->pBitmap=&pb[pBMPHeader->bfOffset]; + + pBMPHeader->DataWidth=0; + + if((pBMPHeader->bfType[0]!='B')||(pBMPHeader->bfType[1]!='M')){ +// BMP_LoadErrorStr="Error MagicID!=BM"; + return(false); + } + + if(pBMPHeader->biCopmression!=BI_RGB){ +// BMP_LoadErrorStr="Error notsupport Compression"; + return(false); + } + + if(pBMPHeader->biHeight>=0x80000000){ +// BMP_LoadErrorStr="Error notsupport OS/2 format"; + return(false); + } + + if(pBMPHeader->biPlanes!=1){ +// BMP_LoadErrorStr="Error notsupport Planes!=1"; + return(false); + } + + switch(pBMPHeader->biBitCount){ + case 1: +// BMP_LoadErrorStr="Error notsupport 1bitcolor."; + return(false); + case 4: +// BMP_LoadErrorStr="Error notsupport 4bitcolor."; + return(false); + case 8: + pBMPHeader->DataWidth=pBMPHeader->biWidth*1; + break; + case 16: +// BMP_LoadErrorStr="Error notsupport 16bitcolor."; + return(false); + case 24: + pBMPHeader->DataWidth=pBMPHeader->biWidth*3; + break; + case 32: + pBMPHeader->DataWidth=pBMPHeader->biWidth*4; + break; + default: +// BMP_LoadErrorStr="Error Unknown xxBitColor."; + return(false); + } + + if((pBMPHeader->DataWidth&3)!=0){ + pBMPHeader->DataWidth+=4-(pBMPHeader->DataWidth&3); + } + +// BMP_LoadErrorStr=""; + return(true); +} + + +static bool intLoadBM(const char *bmpfn,u16 *pbm,const u32 bmw,const u32 bmh) { + FILE *fh; + + if(pbm == NULL)return(false); + + u8 *bmdata = NULL; + u32 bmsize; + + fh=fopen(bmpfn, "rb"); + + if(fh != NULL) { + fseek(fh, 0, SEEK_END); + bmsize=ftell(fh); + fseek(fh, 0, SEEK_SET); + bmdata = (u8*)malloc(bmsize); + + fread(bmdata, 1, bmsize, fh); + fclose(fh); + } + + if(bmdata==NULL)return(false); + + TBMPHeader BMPHeader; + + if(!GetBMPHeader(bmdata,&BMPHeader)) { + free(bmdata); bmdata=NULL; + return(false); + } + + if((BMPHeader.biWidth==1)&&(BMPHeader.biHeight==1)) { + free(bmdata); + bmdata=NULL; + return false; + } + + if(BMPHeader.biBitCount==32) { + free(bmdata); + bmdata = NULL; + return false; + } + + if((BMPHeader.biWidth>3; \ + if((c&(~0x1f))!=0) c=(c<0) ? 0x00 : 0x1f; \ +} + + for(u32 y=0;y=4) + { + u32 *src32= (u32*)src, *dst32= (u32*)dst; + + count= size/4; + uint tmp= count&3; + count /= 4; + + // Duff's Device, good friend! + switch(tmp) { + do { *dst32++ = *src32++; + case 3: *dst32++ = *src32++; + case 2: *dst32++ = *src32++; + case 1: *dst32++ = *src32++; + case 0: ; } while(count--); + } + + // Check for tail + size &= 3; + if(size == 0) + return; + + src8= (u8*)src32; + dst16= (u16*)dst32; + } + else // Unaligned. + { + uint dstOfs= (u32)dst&1; + src8= (u8*)src; + dst16= (u16*)(dst-dstOfs); + + // Head: 1 byte. + if(dstOfs != 0) + { + *dst16= (*dst16 & 0xFF) | *src8++<<8; + dst16++; + if(--size==0) + return; + } + } + + // Unaligned main: copy by 2x byte. + count= size/2; + while(count--) + { + *dst16++ = src8[0] | src8[1]<<8; + src8 += 2; + } + + // Tail: 1 byte. + if(size&1) + *dst16= (*dst16 &~ 0xFF) | *src8; +} +//# toncset.c + +//! VRAM-safe memset, internal routine. +/*! This version mimics memset in functionality, with + the benefit of working for VRAM as well. It is also + slightly faster than the original memset. + \param dst Destination pointer. + \param fill Word to fill with. + \param size Fill-length in bytes. + \note The \a dst pointer and \a size need not be + word-aligned. In the case of unaligned fills, \a fill + will be masked off to match the situation. +*/ +void __toncset(void *dst, u32 fill, uint size) +{ + if(size==0 || dst==0) + return; + + uint left= (u32)dst&3; + u32 *dst32= (u32*)(dst-left); + u32 count, mask; + + // Unaligned head. + if(left != 0) + { + // Adjust for very small stint. + if(left+size<4) + { + mask= BIT_MASK(size*8)<<(left*8); + *dst32= (*dst32 &~ mask) | (fill & mask); + return; + } + + mask= BIT_MASK(left*8); + *dst32= (*dst32 & mask) | (fill&~mask); + dst32++; + size -= 4-left; + } + + // Main stint. + count= size/4; + uint tmp= count&3; + count /= 4; + + switch(tmp) { + do { *dst32++ = fill; + case 3: *dst32++ = fill; + case 2: *dst32++ = fill; + case 1: *dst32++ = fill; + case 0: ; } while(count--); + } + + // Tail + size &= 3; + if(size) + { + mask= BIT_MASK(size*8); + *dst32= (*dst32 &~ mask) | (fill & mask); + } +} diff --git a/arm9/source/tonccpy.h b/arm9/source/tonccpy.h new file mode 100644 index 0000000..dd4267d --- /dev/null +++ b/arm9/source/tonccpy.h @@ -0,0 +1,43 @@ +//# Stuff you may not have yet. + +#ifndef TONCCPY_H +#define TONCCPY_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef unsigned int uint; +#define BIT_MASK(len) ( (1<<(len))-1 ) +static inline u32 quad8(u16 x) { x |= x<<8; return x | x<<16; } + + +//# Declarations and inlines. + +void tonccpy(void *dst, const void *src, uint size); + +void __toncset(void *dst, u32 fill, uint size); +static inline void toncset(void *dst, u8 src, uint size); +static inline void toncset16(void *dst, u16 src, uint size); +static inline void toncset32(void *dst, u32 src, uint size); + + +//! VRAM-safe memset, byte version. Size in bytes. +static inline void toncset(void *dst, u8 src, uint size) +{ __toncset(dst, quad8(src), size); } + +//! VRAM-safe memset, halfword version. Size in hwords. +static inline void toncset16(void *dst, u16 src, uint size) +{ __toncset(dst, src|src<<16, size*2); } + +//! VRAM-safe memset, word version. Size in words. +static inline void toncset32(void *dst, u32 src, uint size) +{ __toncset(dst, src, size*4); } + +#ifdef __cplusplus +} +#endif +#endif diff --git a/bootstub/Makefile b/bootstub/Makefile new file mode 100644 index 0000000..14bcc1f --- /dev/null +++ b/bootstub/Makefile @@ -0,0 +1,13 @@ +include $(DEVKITARM)/base_tools + +TARGET := bootstub + +../data/$(TARGET).bin: $(TARGET).elf + $(OBJCOPY) -O binary $< $@ + +$(TARGET).elf: $(TARGET).s Makefile + $(CC) -Wl,-Ttext=0 -x assembler-with-cpp -nostartfiles -nostdlib $(TARGET).s -o $@ + + +clean: + rm -f $(TARGET).elf $(TARGET).bin diff --git a/bootstub/bootstub.s b/bootstub/bootstub.s new file mode 100644 index 0000000..f79e5fb --- /dev/null +++ b/bootstub/bootstub.s @@ -0,0 +1,176 @@ +/*----------------------------------------------------------------- + + Copyright (C) 2010 Dave "WinterMute" Murphy + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +------------------------------------------------------------------*/ + .global _start + +//----------------------------------------------------------------- +_start: +//----------------------------------------------------------------- + .ascii "bootstub" + .word hook7from9 - _start + .word hook9from7 - _start +_loader_size: + .word 0 + + .arch armv4t + .cpu arm7tdmi + +//----------------------------------------------------------------- +hook9from7: +//----------------------------------------------------------------- + ldr r0, arm9bootaddr + adr r1, hook7from9 + str r1, [r0] + + mov r3, #0x04000000 + ldr r0, resetcode + str r0, [r3, #0x188] + add r3, r3, #0x180 + + adr r0, waitcode_start + ldr r1, arm7base + adr r2, waitcode_end +1: ldr r4, [r0],#4 + str r4, [r1],#4 + cmp r2, r0 + bne 1b + + ldr r1, arm7base + bx r1 + +//----------------------------------------------------------------- +waitcode_start: +//----------------------------------------------------------------- + push {lr} + mov r2, #1 + bl waitsync + + mov r0, #0x100 + strh r0, [r3] + + mov r2, #0 + bl waitsync + + mov r0, #0 + strh r0, [r3] + pop {lr} + + bx lr + +waitsync: + ldrh r0, [r3] + and r0, r0, #0x000f + cmp r0, r2 + bne waitsync + bx lr +waitcode_end: + +arm7base: + .word 0x037f8000 +arm7bootaddr: + .word 0x02FFFE34 +arm9bootaddr: + .word 0x02FFFE24 +tcmpudisable: + .word 0x2078 + +resetcode: + .word 0x0c04000c + +//----------------------------------------------------------------- +hook7from9: +//----------------------------------------------------------------- + mov r12, #0x04000000 + strb r12, [r12,#0x208] + + .arch armv5te + .cpu arm946e-s + + ldr r1, tcmpudisable @ disable TCM and protection unit + mcr p15, 0, r1, c1, c0 + + @ Disable cache + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 @ Instruction cache + mcr p15, 0, r0, c7, c6, 0 @ Data cache + mcr p15, 0, r0, c3, c0, 0 @ write buffer + + @ Wait for write buffer to empty + mcr p15, 0, r0, c7, c10, 4 + + add r3, r12, #0x180 @ r3 = 4000180 + + mov r0,#0x80 + strb r0,[r3,#0x242-0x180] + + adr r0, _loader + ldr r2, _loader_size + mov r1, #0x06800000 + add r1, r1, #0x40000 + add r2, r0, r2 +_copyloader: + ldr r4, [r0], #4 + str r4, [r1], #4 + cmp r0, r2 + blt _copyloader + + mov r0,#0x82 + strb r0,[r3,#0x242-0x180] + + ldrh r0,[r3,#0x204-0x180] + orr r0,r0,#(1<<11) | (1<<7) + strh r0,[r3,#0x204-0x180] + + + ldr r0, arm7bootaddr + mov r1, #0x06000000 + str r1, [r0] + + ldr r0, resetcode + str r0, [r12, #0x188] + + mov r2, #1 + bl waitsync + + mov r0, #0x100 + strh r0, [r3] + + mov r2, #0 + bl waitsync + + mov r0, #0 + strh r0, [r3] + +// set up and enter passme loop + + ldr r0,arm9branchaddr + ldr r1,branchinst + str r1,[r0] + str r0,[r0,#0x20] + + bx r0 + +branchinst: + .word 0xE59FF018 + +arm9branchaddr: + .word 0x02fffe04 + + +_loader: diff --git a/gbaframe.bmp b/gbaframe.bmp new file mode 100644 index 0000000..5da8897 Binary files /dev/null and b/gbaframe.bmp differ diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..da9d78b --- /dev/null +++ b/license.txt @@ -0,0 +1,18 @@ + Copyright (C) 2005 - 2013 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + Claudio "sverx" + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US diff --git a/ndsbootloader/Makefile b/ndsbootloader/Makefile new file mode 100644 index 0000000..5237a8c --- /dev/null +++ b/ndsbootloader/Makefile @@ -0,0 +1,123 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) +endif + +-include $(DEVKITARM)/ds_rules + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := load +BUILD ?= build +SOURCES := source source/patches +INCLUDES := build +SPECS := specs + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -g -Wall -Os \ + -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\ + -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) $(EXTRA_CFLAGS) -DARM7 + +ASFLAGS := -g $(ARCH) $(EXTRA_CFLAGS) $(INCLUDE) +LDFLAGS = -nostartfiles -T $(TOPDIR)/load.ld -Wl,-g $(ARCH) -Wl,-Map,$(TARGET).map + +LIBS := + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBNDS) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export TOPDIR := $(CURDIR) +export LOADBIN ?= $(CURDIR)/$(TARGET).bin +export LOADELF := $(CURDIR)/$(TARGET).elf +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) + +export CC := $(PREFIX)gcc +export CXX := $(PREFIX)g++ +export AR := $(PREFIX)ar +export OBJCOPY := $(PREFIX)objcopy + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) + +export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +#--------------------------------------------------------------------------------- +# use CC for linking standard C +#--------------------------------------------------------------------------------- +export LD := $(CC) +#--------------------------------------------------------------------------------- + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) *.elf *.bin + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(LOADBIN) : $(LOADELF) + @$(OBJCOPY) -O binary $< $@ + @echo built ... $(notdir $@) + + +$(LOADELF) : $(OFILES) + @echo linking $(notdir $@) + @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ + +arm9mpu_reset.o: mpu_reset.bin + +mpu_reset.bin: mpu_reset.elf + $(OBJCOPY) -O binary $< $@ + +mpu_reset.elf: $(TOPDIR)/arm9code/mpu_reset.s + $(CC) $(ASFLAGS) -Ttext=0 -x assembler-with-cpp -nostartfiles -nostdlib $< -o $@ + +-include $(DEPENDS) +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/ndsbootloader/arm9code/mpu_reset.s b/ndsbootloader/arm9code/mpu_reset.s new file mode 100644 index 0000000..ff92752 --- /dev/null +++ b/ndsbootloader/arm9code/mpu_reset.s @@ -0,0 +1,110 @@ +/* + Copyright 2006 - 2015 Dave Murphy (WinterMute) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include + + .text + .align 4 + + .arm + + .arch armv5te + .cpu arm946e-s + +@--------------------------------------------------------------------------------- + .global _start + .type _start STT_FUNC +@--------------------------------------------------------------------------------- +_start: +@--------------------------------------------------------------------------------- + @ Switch off MPU + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #PROTECT_ENABLE + mcr p15, 0, r0, c1, c0, 0 + + + adr r12, mpu_initial_data + ldmia r12, {r0-r10} + + mcr p15, 0, r0, c2, c0, 0 + mcr p15, 0, r0, c2, c0, 1 + mcr p15, 0, r1, c3, c0, 0 + mcr p15, 0, r2, c5, c0, 2 + mcr p15, 0, r3, c5, c0, 3 + mcr p15, 0, r4, c6, c0, 0 + mcr p15, 0, r5, c6, c1, 0 + mcr p15, 0, r6, c6, c3, 0 + mcr p15, 0, r7, c6, c4, 0 + mcr p15, 0, r8, c6, c6, 0 + mcr p15, 0, r9, c6, c7, 0 + mcr p15, 0, r10, c9, c1, 0 + + mov r0, #0 + mcr p15, 0, r0, c6, c2, 0 @ PU Protection Unit Data/Unified Region 2 + mcr p15, 0, r0, c6, c5, 0 @ PU Protection Unit Data/Unified Region 5 + + mrc p15, 0, r0, c9, c1, 0 @ DTCM + mov r0, r0, lsr #12 @ base + mov r0, r0, lsl #12 @ size + add r0, r0, #0x4000 @ dtcm top + + sub r0, r0, #4 @ irq vector + mov r1, #0 + str r1, [r0] + sub r0, r0, #4 @ IRQ1 Check Bits + str r1, [r0] + + sub r0, r0, #128 + bic r0, r0, #7 + + msr cpsr_c, #0xd3 @ svc mode + mov sp, r0 + sub r0, r0, #128 + msr cpsr_c, #0xd2 @ irq mode + mov sp, r0 + sub r0, r0, #128 + msr cpsr_c, #0xdf @ system mode + mov sp, r0 + + @ enable cache & tcm + mrc p15, 0, r0, c1, c0, 0 + ldr r1,= ITCM_ENABLE | DTCM_ENABLE | ICACHE_ENABLE | DCACHE_ENABLE + orr r0,r0,r1 + mcr p15, 0, r0, c1, c0, 0 + + ldr r10, =0x2FFFE04 + ldr r0, =0xE59FF018 + str r0, [r10] + add r1, r10, #0x20 + str r10, [r1] + bx r10 + + .pool + +mpu_initial_data: + .word 0x00000042 @ p15,0,c2,c0,0..1,r0 ;PU Cachability Bits for Data/Unified+Instruction Protection Region + .word 0x00000002 @ p15,0,c3,c0,0,r1 ;PU Write-Bufferability Bits for Data Protection Regions + .word 0x15111011 @ p15,0,c5,c0,2,r2 ;PU Extended Access Permission Data/Unified Protection Region + .word 0x05100011 @ p15,0,c5,c0,3,r3 ;PU Extended Access Permission Instruction Protection Region + .word 0x04000033 @ p15,0,c6,c0,0,r4 ;PU Protection Unit Data/Unified Region 0 + .word 0x0200002b @ p15,0,c6,c1,0,r5 ;PU Protection Unit Data/Unified Region 1 4MB + .word 0x08000035 @ p15,0,c6,c3,0,r6 ;PU Protection Unit Data/Unified Region 3 + .word 0x0300001b @ p15,0,c6,c4,0,r7 ;PU Protection Unit Data/Unified Region 4 + .word 0xffff001d @ p15,0,c6,c6,0,r8 ;PU Protection Unit Data/Unified Region 6 + .word 0x02fff017 @ p15,0,c6,c7,0,r9 ;PU Protection Unit Data/Unified Region 7 4KB + .word 0x0300000a @ p15,0,c9,c1,0,r10 ;TCM Data TCM Base and Virtual Size diff --git a/ndsbootloader/load.ld b/ndsbootloader/load.ld new file mode 100644 index 0000000..ced0c3b --- /dev/null +++ b/ndsbootloader/load.ld @@ -0,0 +1,198 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) + +MEMORY { + + vram : ORIGIN = 0x06020000, LENGTH = 64K +} + +__vram_start = ORIGIN(vram); +__vram_top = ORIGIN(vram)+ LENGTH(vram); +__sp_irq = __vram_top - 0x60; +__sp_svc = __sp_irq - 0x100; +__sp_usr = __sp_svc - 0x100; + +__irq_flags = __vram_top - 8; +__irq_vector = __vram_top - 4; + +SECTIONS +{ + .init : + { + __text_start = . ; + KEEP (*(.init)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >vram = 0xff + + .plt : + { + *(.plt) + } >vram = 0xff + + .text : /* ALIGN (4): */ + { + + *(.text*) + *(.stub) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + *(.glue_7) + *(.glue_7t) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >vram = 0xff + + .fini : + { + KEEP (*(.fini)) + } >vram =0xff + + __text_end = . ; + + .rodata : + { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r*) + SORT(CONSTRUCTORS) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >vram = 0xff + + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >vram + __exidx_start = .; + .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >vram + __exidx_end = .; + +/* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { KEEP (*(.preinit_array)) } >vram = 0xff + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { KEEP (*(.init_array)) } >vram = 0xff + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { KEEP (*(.fini_array)) } >vram = 0xff + PROVIDE (__fini_array_end = .); + + .ctors : + { + /* gcc uses crtbegin.o to find the start of the constructors, so + we make sure it is first. Because this is a wildcard, it + doesn't matter if the user does not actually link against + crtbegin.o; the linker won't look for a file to match a + wildcard. The wildcard also means that it doesn't matter which + directory crtbegin.o is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >vram = 0xff + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >vram = 0xff + + .eh_frame : + { + KEEP (*(.eh_frame)) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >vram = 0xff + + .gcc_except_table : + { + *(.gcc_except_table) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >vram = 0xff + .jcr : { KEEP (*(.jcr)) } >vram = 0 + .got : { *(.got.plt) *(.got) } >vram = 0 + + + .vram ALIGN(4) : + { + __vram_start = ABSOLUTE(.) ; + *(.vram) + *vram.*(.text) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + __vram_end = ABSOLUTE(.) ; + } >vram = 0xff + + + .data ALIGN(4) : { + __data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + CONSTRUCTORS + . = ALIGN(4); + __data_end = ABSOLUTE(.) ; + } >vram = 0xff + + + + .bss ALIGN(4) : + { + __bss_start = ABSOLUTE(.); + __bss_start__ = ABSOLUTE(.); + *(.dynbss) + *(.gnu.linkonce.b*) + *(.bss*) + *(COMMON) + . = ALIGN(4); /* REQUIRED. LD is flaky without it. */ + } >vram + + __bss_end = . ; + __bss_end__ = . ; + + _end = . ; + __end__ = . ; + PROVIDE (end = _end); + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + .stack 0x80000 : { _stack = .; *(.stack) } + /* These must appear regardless of . */ +} diff --git a/ndsbootloader/source/arm7clear.s b/ndsbootloader/source/arm7clear.s new file mode 100644 index 0000000..ffa9fcf --- /dev/null +++ b/ndsbootloader/source/arm7clear.s @@ -0,0 +1,70 @@ +/*----------------------------------------------------------------- + + Copyright (C) 2005 Michael "Chishm" Chisholm + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + If you use this code, please give due credit and email me about your + project at chishm@hotmail.com +------------------------------------------------------------------*/ + + .arm + .global arm7clearRAM + .type arm7clearRAM STT_FUNC +arm7clearRAM: + + push {r0-r9} + // clear exclusive IWRAM + // 0380:0000 to 0380:FFFF, total 64KiB + mov r0, #0 + mov r1, #0 + mov r2, #0 + mov r3, #0 + mov r4, #0 + mov r5, #0 + mov r6, #0 + mov r7, #0 + mov r8, #0x03800000 + sub r8, #0x00008000 + mov r9, #0x03800000 + orr r9, r9, #0x10000 +clear_IWRAM_loop: + stmia r8!, {r0, r1, r2, r3, r4, r5, r6, r7} + cmp r8, r9 + blt clear_IWRAM_loop + + // clear most of EWRAM - except after RAM end - 0xc000, which has the bootstub + mov r8, #0x02000000 + + ldr r9,=0x4004008 + ldr r9,[r9] + ands r9,r9,#0x8000 + bne dsi_mode + + mov r9, #0x02400000 + b ds_mode +dsi_mode: + mov r9, #0x03000000 +ds_mode: + sub r9, #0x0000c000 +clear_EWRAM_loop: + stmia r8!, {r0, r1, r2, r3, r4, r5, r6, r7} + cmp r8, r9 + blt clear_EWRAM_loop + + pop {r0-r9} + + bx lr + diff --git a/ndsbootloader/source/arm9clear.arm.c b/ndsbootloader/source/arm9clear.arm.c new file mode 100644 index 0000000..15030a1 --- /dev/null +++ b/ndsbootloader/source/arm9clear.arm.c @@ -0,0 +1,70 @@ +#define ARM9 +#undef ARM7 + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "boot.h" + +/*------------------------------------------------------------------------- +resetMemory2_ARM9 +Clears the ARM9's DMA channels and resets video memory +Written by Darkain. +Modified by Chishm: + * Changed MultiNDS specific stuff +--------------------------------------------------------------------------*/ +void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9 (void) +{ + register int i; + + //clear out ARM9 DMA channels + for (i=0; i<4; i++) { + DMA_CR(i) = 0; + DMA_SRC(i) = 0; + DMA_DEST(i) = 0; + TIMER_CR(i) = 0; + TIMER_DATA(i) = 0; + } + + //set shared ram to ARM7 + WRAM_CR = 0x03; + + // Return to passme loop + *((vu32*)0x02FFFE04) = (u32)0xE59FF018; // ldr pc, 0x02FFFE24 + *((vu32*)0x02FFFE24) = (u32)0x02FFFE04; // Set ARM9 Loop address + + asm volatile( + "\tbx %0\n" + : : "r" (0x02FFFE04) + ); + while(1); +} + +/*------------------------------------------------------------------------- +startBinary_ARM9 +Jumps to the ARM9 NDS binary in sync with the display and ARM7 +Written by Darkain. +Modified by Chishm: + * Removed MultiNDS specific stuff +--------------------------------------------------------------------------*/ +void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9 (void) +{ + REG_IME=0; + REG_EXMEMCNT = 0xE880; + // set ARM9 load address to 0 and wait for it to change again + ARM9_START_FLAG = 0; + while(REG_VCOUNT!=191); + while(REG_VCOUNT==191); + while ( ARM9_START_FLAG != 1 ); + VoidFn arm9code = *(VoidFn*)(0x2FFFE24); + arm9code(); + while(1); +} + diff --git a/ndsbootloader/source/arm9mpu_reset.s b/ndsbootloader/source/arm9mpu_reset.s new file mode 100644 index 0000000..be79fb5 --- /dev/null +++ b/ndsbootloader/source/arm9mpu_reset.s @@ -0,0 +1,6 @@ + .arm + .global mpu_reset, mpu_reset_end + +mpu_reset: + .incbin "mpu_reset.bin" +mpu_reset_end: diff --git a/ndsbootloader/source/bios.s b/ndsbootloader/source/bios.s new file mode 100644 index 0000000..92c2d8e --- /dev/null +++ b/ndsbootloader/source/bios.s @@ -0,0 +1,13 @@ + .text + .align 4 + + .thumb + +@--------------------------------------------------------------------------------- + .global swiDelay + .thumb_func +@--------------------------------------------------------------------------------- +swiDelay: +@--------------------------------------------------------------------------------- + swi 0x03 + bx lr diff --git a/ndsbootloader/source/boot.c b/ndsbootloader/source/boot.c new file mode 100644 index 0000000..e02fec7 --- /dev/null +++ b/ndsbootloader/source/boot.c @@ -0,0 +1,379 @@ +/*----------------------------------------------------------------- + boot.c + + BootLoader + Loads a file into memory and runs it + + All resetMemory and startBinary functions are based + on the MultiNDS loader by Darkain. + Original source available at: + http://cvs.sourceforge.net/viewcvs.py/ndslib/ndslib/examples/loader/boot/main.cpp + +License: + Copyright (C) 2005 Michael "Chishm" Chisholm + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + If you use this code, please give due credit and email me about your + project at chishm@hotmail.com + +Helpful information: + This code runs from VRAM bank C on ARM7 +------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include "fat.h" +#include "dldi_patcher.h" +#include "card.h" +#include "boot.h" +#include "sdmmc.h" + +void arm7clearRAM(); + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Important things +#define TEMP_MEM 0x02FFD000 +#define TWL_HEAD 0x02FFE000 +#define NDS_HEAD 0x02FFFE00 +#define TEMP_ARM9_START_ADDRESS (*(vu32*)0x02FFFFF4) + + +const char* bootName = "BOOT.NDS"; + +extern unsigned long _start; +extern unsigned long storedFileCluster; +extern unsigned long initDisc; +extern unsigned long wantToPatchDLDI; +extern unsigned long argStart; +extern unsigned long argSize; +extern unsigned long dsiSD; +extern unsigned long dsiMode; + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Firmware stuff + +#define FW_READ 0x03 + +void boot_readFirmware (uint32 address, uint8 * buffer, uint32 size) { + uint32 index; + + // Read command + while (REG_SPICNT & SPI_BUSY); + REG_SPICNT = SPI_ENABLE | SPI_CONTINUOUS | SPI_DEVICE_NVRAM; + REG_SPIDATA = FW_READ; + while (REG_SPICNT & SPI_BUSY); + + // Set the address + REG_SPIDATA = (address>>16) & 0xFF; + while (REG_SPICNT & SPI_BUSY); + REG_SPIDATA = (address>>8) & 0xFF; + while (REG_SPICNT & SPI_BUSY); + REG_SPIDATA = (address) & 0xFF; + while (REG_SPICNT & SPI_BUSY); + + for (index = 0; index < size; index++) { + REG_SPIDATA = 0; + while (REG_SPICNT & SPI_BUSY); + buffer[index] = REG_SPIDATA & 0xFF; + } + REG_SPICNT = 0; +} + + +static inline void copyLoop (u32* dest, const u32* src, u32 size) { + size = (size +3) & ~3; + do { + *dest++ = *src++; + } while (size -= 4); +} + +//#define resetCpu() __asm volatile("\tswi 0x000000\n"); + +static char boot_nds[] = "fat:/boot.nds"; +static unsigned long argbuf[4]; +/*------------------------------------------------------------------------- +passArgs_ARM7 +Copies the command line arguments to the end of the ARM9 binary, +then sets a flag in memory for the loaded NDS to use +--------------------------------------------------------------------------*/ +void passArgs_ARM7 (void) { + u32 ARM9_DST = *((u32*)(NDS_HEAD + 0x028)); + u32 ARM9_LEN = *((u32*)(NDS_HEAD + 0x02C)); + u32* argSrc; + u32* argDst; + + if (!argStart || !argSize) { + + char *arg = boot_nds; + argSize = __builtin_strlen(boot_nds); + + if (dsiSD) { + arg++; + arg[0] = 's'; + arg[1] = 'd'; + } + __builtin_memcpy(argbuf,arg,argSize+1); + argSrc = argbuf; + } else { + argSrc = (u32*)(argStart + (int)&_start); + } + + if ( ARM9_DST == 0 && ARM9_LEN == 0) { + ARM9_DST = *((u32*)(NDS_HEAD + 0x038)); + ARM9_LEN = *((u32*)(NDS_HEAD + 0x03C)); + } + + + argDst = (u32*)((ARM9_DST + ARM9_LEN + 3) & ~3); // Word aligned + + if (dsiMode && (*(u8*)(NDS_HEAD + 0x012) & BIT(1))) + { + u32 ARM9i_DST = *((u32*)(TWL_HEAD + 0x1C8)); + u32 ARM9i_LEN = *((u32*)(TWL_HEAD + 0x1CC)); + if (ARM9i_LEN) + { + u32* argDst2 = (u32*)((ARM9i_DST + ARM9i_LEN + 3) & ~3); // Word aligned + if (argDst2 > argDst) + argDst = argDst2; + } + } + + copyLoop(argDst, argSrc, argSize); + + __system_argv->argvMagic = ARGV_MAGIC; + __system_argv->commandLine = (char*)argDst; + __system_argv->length = argSize; +} + + + + +/*------------------------------------------------------------------------- +resetMemory_ARM7 +Clears all of the NDS's RAM that is visible to the ARM7 +Written by Darkain. +Modified by Chishm: + * Added STMIA clear mem loop +--------------------------------------------------------------------------*/ +void resetMemory_ARM7 (void) +{ + int i; + u8 settings1, settings2; + u32 settingsOffset = 0; + + REG_IME = 0; + + for (i=0; i<16; i++) { + SCHANNEL_CR(i) = 0; + SCHANNEL_TIMER(i) = 0; + SCHANNEL_SOURCE(i) = 0; + SCHANNEL_LENGTH(i) = 0; + } + + REG_SOUNDCNT = 0; + + //clear out ARM7 DMA channels and timers + for (i=0; i<4; i++) { + DMA_CR(i) = 0; + DMA_SRC(i) = 0; + DMA_DEST(i) = 0; + TIMER_CR(i) = 0; + TIMER_DATA(i) = 0; + } + + arm7clearRAM(); + + REG_IE = 0; + REG_IF = ~0; + (*(vu32*)(0x04000000-4)) = 0; //IRQ_HANDLER ARM7 version + (*(vu32*)(0x04000000-8)) = ~0; //VBLANK_INTR_WAIT_FLAGS, ARM7 version + REG_POWERCNT = 1; //turn off power to stuff + + // Get settings location + boot_readFirmware((u32)0x00020, (u8*)&settingsOffset, 0x2); + settingsOffset *= 8; + + // Reload DS Firmware settings + boot_readFirmware(settingsOffset + 0x070, &settings1, 0x1); + boot_readFirmware(settingsOffset + 0x170, &settings2, 0x1); + + if ((settings1 & 0x7F) == ((settings2+1) & 0x7F)) { + boot_readFirmware(settingsOffset + 0x000, (u8*)0x02FFFC80, 0x70); + } else { + boot_readFirmware(settingsOffset + 0x100, (u8*)0x02FFFC80, 0x70); + } + + ((vu32*)0x040044f0)[2] = 0x202DDD1D; + ((vu32*)0x040044f0)[3] = 0xE1A00005; + while((*(vu32*)0x04004400) & 0x2000000); + +} + + +void loadBinary_ARM7 (u32 fileCluster) +{ + u32 ndsHeader[0x170>>2]; + + // read NDS header + fileRead ((char*)ndsHeader, fileCluster, 0, 0x170); + // read ARM9 info from NDS header + u32 ARM9_SRC = ndsHeader[0x020>>2]; + char* ARM9_DST = (char*)ndsHeader[0x028>>2]; + u32 ARM9_LEN = ndsHeader[0x02C>>2]; + // read ARM7 info from NDS header + u32 ARM7_SRC = ndsHeader[0x030>>2]; + char* ARM7_DST = (char*)ndsHeader[0x038>>2]; + u32 ARM7_LEN = ndsHeader[0x03C>>2]; + + // Load binaries into memory + fileRead(ARM9_DST, fileCluster, ARM9_SRC, ARM9_LEN); + fileRead(ARM7_DST, fileCluster, ARM7_SRC, ARM7_LEN); + + // first copy the header to its proper location, excluding + // the ARM9 start address, so as not to start it + TEMP_ARM9_START_ADDRESS = ndsHeader[0x024>>2]; // Store for later + ndsHeader[0x024>>2] = 0; + dmaCopyWords(3, (void*)ndsHeader, (void*)NDS_HEAD, 0x170); + + if (dsiMode && (ndsHeader[0x10>>2]&BIT(16+1))) + { + // Read full TWL header + fileRead((char*)TWL_HEAD, fileCluster, 0, 0x1000); + + u32 ARM9i_SRC = *(u32*)(TWL_HEAD+0x1C0); + char* ARM9i_DST = (char*)*(u32*)(TWL_HEAD+0x1C8); + u32 ARM9i_LEN = *(u32*)(TWL_HEAD+0x1CC); + u32 ARM7i_SRC = *(u32*)(TWL_HEAD+0x1D0); + char* ARM7i_DST = (char*)*(u32*)(TWL_HEAD+0x1D8); + u32 ARM7i_LEN = *(u32*)(TWL_HEAD+0x1DC); + + if (ARM9i_LEN) + fileRead(ARM9i_DST, fileCluster, ARM9i_SRC, ARM9i_LEN); + if (ARM7i_LEN) + fileRead(ARM7i_DST, fileCluster, ARM7i_SRC, ARM7i_LEN); + } +} + +/*------------------------------------------------------------------------- +startBinary_ARM7 +Jumps to the ARM7 NDS binary in sync with the display and ARM9 +Written by Darkain. +Modified by Chishm: + * Removed MultiNDS specific stuff +--------------------------------------------------------------------------*/ +void startBinary_ARM7 (void) { + REG_IME=0; + while(REG_VCOUNT!=191); + while(REG_VCOUNT==191); + // copy NDS ARM9 start address into the header, starting ARM9 + *((vu32*)0x02FFFE24) = TEMP_ARM9_START_ADDRESS; + ARM9_START_FLAG = 1; + // Start ARM7 + VoidFn arm7code = *(VoidFn*)(0x2FFFE34); + arm7code(); +} +#ifndef NO_SDMMC +int sdmmc_sd_readsectors(u32 sector_no, u32 numsectors, void *out); +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Main function +bool sdmmc_inserted() { + return true; +} + +bool sdmmc_startup() { + sdmmc_controller_init(true); + return sdmmc_sdcard_init() == 0; +} + +bool sdmmc_readsectors(u32 sector_no, u32 numsectors, void *out) { + return sdmmc_sdcard_readsectors(sector_no, numsectors, out) == 0; +} +#endif +void mpu_reset(); +void mpu_reset_end(); + +int main (void) { +#ifdef NO_DLDI + dsiSD = true; + dsiMode = true; +#endif +#ifndef NO_SDMMC + if (dsiSD && dsiMode) { + _io_dldi.fn_readSectors = sdmmc_readsectors; + _io_dldi.fn_isInserted = sdmmc_inserted; + _io_dldi.fn_startup = sdmmc_startup; + } +#endif + u32 fileCluster = storedFileCluster; + // Init card + if(!FAT_InitFiles(initDisc))return -1; + /* Invalid file cluster specified */ + if ((fileCluster < CLUSTER_FIRST) || (fileCluster >= CLUSTER_EOF))fileCluster = getBootFileCluster(bootName); + if (fileCluster == CLUSTER_FREE)return -1; + + // ARM9 clears its memory part 2 + // copy ARM9 function to RAM, and make the ARM9 jump to it + copyLoop((void*)TEMP_MEM, (void*)resetMemory2_ARM9, resetMemory2_ARM9_size); + (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function + // Wait until the ARM9 has completed its task + while ((*(vu32*)0x02FFFE24) == (u32)TEMP_MEM); + + // ARM9 sets up mpu + // copy ARM9 function to RAM, and make the ARM9 jump to it + copyLoop((void*)TEMP_MEM, (void*)mpu_reset, mpu_reset_end - mpu_reset); + (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function + // Wait until the ARM9 has completed its task + while ((*(vu32*)0x02FFFE24) == (u32)TEMP_MEM); + + // Get ARM7 to clear RAM + resetMemory_ARM7(); + + // ARM9 enters a wait loop + // copy ARM9 function to RAM, and make the ARM9 jump to it + copyLoop((void*)TEMP_MEM, (void*)startBinary_ARM9, startBinary_ARM9_size); + (*(vu32*)0x02FFFE24) = (u32)TEMP_MEM; // Make ARM9 jump to the function + + // Load the NDS file + loadBinary_ARM7(fileCluster); + +#ifndef NO_DLDI + // Patch with DLDI if desired + if (wantToPatchDLDI) { + dldiPatchBinary ((u8*)((u32*)NDS_HEAD)[0x0A], ((u32*)NDS_HEAD)[0x0B]); + } +#endif + +#ifndef NO_SDMMC + if (dsiSD && dsiMode) { + sdmmc_controller_init(true); + *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFDu; + *(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDDu; + *(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 0; + } +#endif + // Pass command line arguments to loaded program + passArgs_ARM7(); + + startBinary_ARM7(); + + return 0; +} + diff --git a/ndsbootloader/source/boot.h b/ndsbootloader/source/boot.h new file mode 100644 index 0000000..ec76384 --- /dev/null +++ b/ndsbootloader/source/boot.h @@ -0,0 +1,10 @@ +#ifndef _BOOT_H_ +#define _BOOT_H_ + +#define resetMemory2_ARM9_size 0x400 +void __attribute__ ((long_call)) __attribute__((naked)) __attribute__((noreturn)) resetMemory2_ARM9(); +#define startBinary_ARM9_size 0x100 +void __attribute__ ((long_call)) __attribute__((noreturn)) __attribute__((naked)) startBinary_ARM9 (); +#define ARM9_START_FLAG (*(vu8*)0x02FFFDFB) + +#endif // _BOOT_H_ diff --git a/ndsbootloader/source/card.h b/ndsbootloader/source/card.h new file mode 100644 index 0000000..fca738e --- /dev/null +++ b/ndsbootloader/source/card.h @@ -0,0 +1,45 @@ +/*----------------------------------------------------------------- + + Copyright (C) 2005 Michael "Chishm" Chisholm + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + If you use this code, please give due credit and email me about your + project at chishm@hotmail.com +------------------------------------------------------------------*/ + +#ifndef CARD_H +#define CARD_H + +#include "disc_io.h" +#include "io_dldi.h" + +static inline bool CARD_StartUp (void) { + return _io_dldi.fn_startup(); +} + +static inline bool CARD_IsInserted (void) { + return _io_dldi.fn_isInserted(); +} + +static inline bool CARD_ReadSector (u32 sector, void *buffer) { + return _io_dldi.fn_readSectors(sector, 1, buffer); +} + +static inline bool CARD_ReadSectors (u32 sector, int count, void *buffer) { + return _io_dldi.fn_readSectors(sector, count, buffer); +} + +#endif // CARD_H diff --git a/ndsbootloader/source/disc_io.h b/ndsbootloader/source/disc_io.h new file mode 100644 index 0000000..f97d353 --- /dev/null +++ b/ndsbootloader/source/disc_io.h @@ -0,0 +1,82 @@ +/* + disc_io.h + Interface template for low level disc functions. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 2006-07-11 - Chishm + * Original release + + 2006-07-16 - Chishm + * Renamed _CF_USE_DMA to _IO_USE_DMA + * Renamed _CF_ALLOW_UNALIGNED to _IO_ALLOW_UNALIGNED +*/ + +#ifndef _DISC_IO_H +#define _DISC_IO_H + +#include +#define BYTES_PER_SECTOR 512 + +//---------------------------------------------------------------------- +// Customisable features + +// Use DMA to read the card, remove this line to use normal reads/writes +// #define _IO_USE_DMA + +// Allow buffers not alligned to 16 bits when reading files. +// Note that this will slow down access speed, so only use if you have to. +// It is also incompatible with DMA +#define _IO_ALLOW_UNALIGNED + +#if defined _IO_USE_DMA && defined _IO_ALLOW_UNALIGNED + #error "You can't use both DMA and unaligned memory" +#endif + +#define FEATURE_MEDIUM_CANREAD 0x00000001 +#define FEATURE_MEDIUM_CANWRITE 0x00000002 +#define FEATURE_SLOT_GBA 0x00000010 +#define FEATURE_SLOT_NDS 0x00000020 + +typedef bool (* FN_MEDIUM_STARTUP)(void) ; +typedef bool (* FN_MEDIUM_ISINSERTED)(void) ; +typedef bool (* FN_MEDIUM_READSECTORS)(u32 sector, u32 numSectors, void* buffer) ; +typedef bool (* FN_MEDIUM_WRITESECTORS)(u32 sector, u32 numSectors, const void* buffer) ; +typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ; +typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ; + +struct IO_INTERFACE_STRUCT { + unsigned long ioType ; + unsigned long features ; + FN_MEDIUM_STARTUP fn_startup ; + FN_MEDIUM_ISINSERTED fn_isInserted ; + FN_MEDIUM_READSECTORS fn_readSectors ; + FN_MEDIUM_WRITESECTORS fn_writeSectors ; + FN_MEDIUM_CLEARSTATUS fn_clearStatus ; + FN_MEDIUM_SHUTDOWN fn_shutdown ; +} ; + +typedef struct IO_INTERFACE_STRUCT IO_INTERFACE ; + +#endif // define _DISC_IO_H diff --git a/ndsbootloader/source/dldi_patcher.c b/ndsbootloader/source/dldi_patcher.c new file mode 100644 index 0000000..06108ed --- /dev/null +++ b/ndsbootloader/source/dldi_patcher.c @@ -0,0 +1,223 @@ +/*----------------------------------------------------------------- + + Copyright (C) 2005 Michael "Chishm" Chisholm + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + If you use this code, please give due credit and email me about your + project at chishm@hotmail.com +------------------------------------------------------------------*/ +#ifndef NO_DLDI +#include +#include +#include "dldi_patcher.h" + +#define FIX_ALL 0x01 +#define FIX_GLUE 0x02 +#define FIX_GOT 0x04 +#define FIX_BSS 0x08 + +enum DldiOffsets { + DO_magicString = 0x00, // "\xED\xA5\x8D\xBF Chishm" + DO_magicToken = 0x00, // 0xBF8DA5ED + DO_magicShortString = 0x04, // " Chishm" + DO_version = 0x0C, + DO_driverSize = 0x0D, + DO_fixSections = 0x0E, + DO_allocatedSpace = 0x0F, + + DO_friendlyName = 0x10, + + DO_text_start = 0x40, // Data start + DO_data_end = 0x44, // Data end + DO_glue_start = 0x48, // Interworking glue start -- Needs address fixing + DO_glue_end = 0x4C, // Interworking glue end + DO_got_start = 0x50, // GOT start -- Needs address fixing + DO_got_end = 0x54, // GOT end + DO_bss_start = 0x58, // bss start -- Needs setting to zero + DO_bss_end = 0x5C, // bss end + + // IO_INTERFACE data + DO_ioType = 0x60, + DO_features = 0x64, + DO_startup = 0x68, + DO_isInserted = 0x6C, + DO_readSectors = 0x70, + DO_writeSectors = 0x74, + DO_clearStatus = 0x78, + DO_shutdown = 0x7C, + DO_code = 0x80 +}; + +static addr_t readAddr (data_t *mem, addr_t offset) { + return ((addr_t*)mem)[offset/sizeof(addr_t)]; +} + +static void writeAddr (data_t *mem, addr_t offset, addr_t value) { + ((addr_t*)mem)[offset/sizeof(addr_t)] = value; +} + + +static addr_t quickFind (const data_t* data, const data_t* search, size_t dataLen, size_t searchLen) { + const int* dataChunk = (const int*) data; + int searchChunk = ((const int*)search)[0]; + addr_t i; + addr_t dataChunkEnd = (addr_t)(dataLen / sizeof(int)); + + for ( i = 0; i < dataChunkEnd; i++) { + if (dataChunk[i] == searchChunk) { + if ((i*sizeof(int) + searchLen) > dataLen) { + return -1; + } + if (memcmp (&data[i*sizeof(int)], search, searchLen) == 0) { + return i*sizeof(int); + } + } + } + + return -1; +} + +// Strings are stored with bit 0x20 flipped (case inverted) to prevent accidental DLDI patching of them +#define DLDI_MAGIC_LEN 12 +#define DLDI_MAGIC_MANGLE_VALUE 0x20 +static const data_t dldiMagicStringMangled[DLDI_MAGIC_LEN] = "\xCD\x85\xAD\x9F\0cHISHM"; // Normal DLDI file + +// Demangle the magic string by XORing every byte with 0x20, except the NULL terminator +static void demangleMagicString(data_t *dest, const data_t *src) { + int i; + + memcpy(dest, src, DLDI_MAGIC_LEN); + for (i = 0; i < DLDI_MAGIC_LEN - 1; ++i) { + dest[i] ^= DLDI_MAGIC_MANGLE_VALUE; + } +} + +#define DEVICE_TYPE_DLDI 0x49444C44 + +extern data_t _dldi_start[]; + +bool dldiPatchBinary (data_t *binData, u32 binSize) { + + addr_t memOffset; // Offset of DLDI after the file is loaded into memory + addr_t patchOffset; // Position of patch destination in the file + addr_t relocationOffset; // Value added to all offsets within the patch to fix it properly + addr_t ddmemOffset; // Original offset used in the DLDI file + addr_t ddmemStart; // Start of range that offsets can be in the DLDI file + addr_t ddmemEnd; // End of range that offsets can be in the DLDI file + addr_t ddmemSize; // Size of range that offsets can be in the DLDI file + addr_t addrIter; + + data_t *pDH; + data_t *pAH; + + data_t dldiMagicString[DLDI_MAGIC_LEN]; + size_t dldiFileSize = 0; + + // Find the DLDI reserved space in the file + demangleMagicString(dldiMagicString, dldiMagicStringMangled); + patchOffset = quickFind (binData, dldiMagicString, binSize, sizeof(dldiMagicString)); + + if (patchOffset < 0) { + // does not have a DLDI section + return false; + } + + pDH = _dldi_start; + pAH = &(binData[patchOffset]); + + if (*((u32*)(pDH + DO_ioType)) == DEVICE_TYPE_DLDI) { + // No DLDI patch + return false; + } + + if (pDH[DO_driverSize] > pAH[DO_allocatedSpace]) { + // Not enough space for patch + return false; + } + + dldiFileSize = 1 << pDH[DO_driverSize]; + + memOffset = readAddr (pAH, DO_text_start); + if (memOffset == 0) { + memOffset = readAddr (pAH, DO_startup) - DO_code; + } + ddmemOffset = readAddr (pDH, DO_text_start); + relocationOffset = memOffset - ddmemOffset; + + ddmemStart = readAddr (pDH, DO_text_start); + ddmemSize = (1 << pDH[DO_driverSize]); + ddmemEnd = ddmemStart + ddmemSize; + + // Remember how much space is actually reserved + pDH[DO_allocatedSpace] = pAH[DO_allocatedSpace]; + // Copy the DLDI patch into the application + memcpy (pAH, pDH, dldiFileSize); + + // Fix the section pointers in the header + writeAddr (pAH, DO_text_start, readAddr (pAH, DO_text_start) + relocationOffset); + writeAddr (pAH, DO_data_end, readAddr (pAH, DO_data_end) + relocationOffset); + writeAddr (pAH, DO_glue_start, readAddr (pAH, DO_glue_start) + relocationOffset); + writeAddr (pAH, DO_glue_end, readAddr (pAH, DO_glue_end) + relocationOffset); + writeAddr (pAH, DO_got_start, readAddr (pAH, DO_got_start) + relocationOffset); + writeAddr (pAH, DO_got_end, readAddr (pAH, DO_got_end) + relocationOffset); + writeAddr (pAH, DO_bss_start, readAddr (pAH, DO_bss_start) + relocationOffset); + writeAddr (pAH, DO_bss_end, readAddr (pAH, DO_bss_end) + relocationOffset); + // Fix the function pointers in the header + writeAddr (pAH, DO_startup, readAddr (pAH, DO_startup) + relocationOffset); + writeAddr (pAH, DO_isInserted, readAddr (pAH, DO_isInserted) + relocationOffset); + writeAddr (pAH, DO_readSectors, readAddr (pAH, DO_readSectors) + relocationOffset); + writeAddr (pAH, DO_writeSectors, readAddr (pAH, DO_writeSectors) + relocationOffset); + writeAddr (pAH, DO_clearStatus, readAddr (pAH, DO_clearStatus) + relocationOffset); + writeAddr (pAH, DO_shutdown, readAddr (pAH, DO_shutdown) + relocationOffset); + + // Put the correct DLDI magic string back into the DLDI header + memcpy (pAH, dldiMagicString, sizeof (dldiMagicString)); + + if (pDH[DO_fixSections] & FIX_ALL) { + // Search through and fix pointers within the data section of the file + for (addrIter = (readAddr(pDH, DO_text_start) - ddmemStart); addrIter < (readAddr(pDH, DO_data_end) - ddmemStart); addrIter++) { + if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) { + writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset); + } + } + } + + if (pDH[DO_fixSections] & FIX_GLUE) { + // Search through and fix pointers within the glue section of the file + for (addrIter = (readAddr(pDH, DO_glue_start) - ddmemStart); addrIter < (readAddr(pDH, DO_glue_end) - ddmemStart); addrIter++) { + if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) { + writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset); + } + } + } + + if (pDH[DO_fixSections] & FIX_GOT) { + // Search through and fix pointers within the Global Offset Table section of the file + for (addrIter = (readAddr(pDH, DO_got_start) - ddmemStart); addrIter < (readAddr(pDH, DO_got_end) - ddmemStart); addrIter++) { + if ((ddmemStart <= readAddr(pAH, addrIter)) && (readAddr(pAH, addrIter) < ddmemEnd)) { + writeAddr (pAH, addrIter, readAddr(pAH, addrIter) + relocationOffset); + } + } + } + + if (pDH[DO_fixSections] & FIX_BSS) { + // Initialise the BSS to 0 + memset (&pAH[readAddr(pDH, DO_bss_start) - ddmemStart] , 0, readAddr(pDH, DO_bss_end) - readAddr(pDH, DO_bss_start)); + } + + return true; +} +#endif \ No newline at end of file diff --git a/ndsbootloader/source/dldi_patcher.h b/ndsbootloader/source/dldi_patcher.h new file mode 100644 index 0000000..19d50ad --- /dev/null +++ b/ndsbootloader/source/dldi_patcher.h @@ -0,0 +1,32 @@ +/*----------------------------------------------------------------- + + Copyright (C) 2005 Michael "Chishm" Chisholm + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + If you use this code, please give due credit and email me about your + project at chishm@hotmail.com +------------------------------------------------------------------*/ + +#ifndef DLDI_PATCHER_H +#define DLDI_PATCHER_H + +#include + +typedef signed int addr_t; +typedef unsigned char data_t; +bool dldiPatchBinary (data_t *binData, u32 binSize); + +#endif // DLDI_PATCHER_H diff --git a/ndsbootloader/source/fat.c b/ndsbootloader/source/fat.c new file mode 100644 index 0000000..e94f25f --- /dev/null +++ b/ndsbootloader/source/fat.c @@ -0,0 +1,592 @@ +/*----------------------------------------------------------------- + fat.c + + NDS MP + GBAMP NDS Firmware Hack Version 2.12 + An NDS aware firmware patch for the GBA Movie Player. + By Michael Chisholm (Chishm) + + Filesystem code based on GBAMP_CF.c by Chishm (me). + +License: + Copyright (C) 2005 Michael "Chishm" Chisholm + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + If you use this code, please give due credit and email me about your + project at chishm@hotmail.com +------------------------------------------------------------------*/ + +#include "fat.h" +#include "card.h" + + +//--------------------------------------------------------------- +// FAT constants + +#define FILE_LAST 0x00 +#define FILE_FREE 0xE5 + +#define ATTRIB_ARCH 0x20 +#define ATTRIB_DIR 0x10 +#define ATTRIB_LFN 0x0F +#define ATTRIB_VOL 0x08 +#define ATTRIB_HID 0x02 +#define ATTRIB_SYS 0x04 +#define ATTRIB_RO 0x01 + +#define FAT16_ROOT_DIR_CLUSTER 0x00 + +// File Constants +#ifndef EOF +#define EOF -1 +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 +#endif + + +//----------------------------------------------------------------- +// FAT constants +#define CLUSTER_EOF_16 0xFFFF + +#define ATTRIB_ARCH 0x20 +#define ATTRIB_DIR 0x10 +#define ATTRIB_LFN 0x0F +#define ATTRIB_VOL 0x08 +#define ATTRIB_HID 0x02 +#define ATTRIB_SYS 0x04 +#define ATTRIB_RO 0x01 + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Data Structures + +#define __PACKED __attribute__ ((__packed__)) + +// BIOS Parameter Block +typedef struct { + + u16 bytesPerSector; + u8 sectorsPerCluster; + u16 reservedSectors; + u8 numFATs; + u16 rootEntries; + u16 numSectorsSmall; + u8 mediaDesc; + u16 sectorsPerFAT; + u16 sectorsPerTrk; + u16 numHeads; + u32 numHiddenSectors; + u32 numSectors; +} __PACKED BIOS_BPB; + +// Boot Sector - must be packed +typedef struct +{ + u8 jmpBoot[3]; + u8 OEMName[8]; + BIOS_BPB bpb; + union // Different types of extended BIOS Parameter Block for FAT16 and FAT32 + { + struct + { + // Ext BIOS Parameter Block for FAT16 + u8 driveNumber; + u8 reserved1; + u8 extBootSig; + u32 volumeID; + u8 volumeLabel[11]; + u8 fileSysType[8]; + // Bootcode + u8 bootCode[448]; + } __PACKED fat16; + struct + { + // FAT32 extended block + u32 sectorsPerFAT32; + u16 extFlags; + u16 fsVer; + u32 rootClus; + u16 fsInfo; + u16 bkBootSec; + u8 reserved[12]; + // Ext BIOS Parameter Block for FAT16 + u8 driveNumber; + u8 reserved1; + u8 extBootSig; + u32 volumeID; + u8 volumeLabel[11]; + u8 fileSysType[8]; + // Bootcode + u8 bootCode[420]; + } __PACKED fat32; + } __PACKED extBlock; + + __PACKED u16 bootSig; + +} __PACKED BOOT_SEC; + +// _Static_assert(sizeof(BOOT_SEC) == 512); + +// Directory entry - must be packed +typedef struct +{ + u8 name[8]; + u8 ext[3]; + u8 attrib; + u8 reserved; + u8 cTime_ms; + u16 cTime; + u16 cDate; + u16 aDate; + u16 startClusterHigh; + u16 mTime; + u16 mDate; + u16 startCluster; + u32 fileSize; +} __PACKED DIR_ENT; + +// File information - no need to pack +typedef struct +{ + u32 firstCluster; + u32 length; + u32 curPos; + u32 curClus; // Current cluster to read from + int curSect; // Current sector within cluster + int curByte; // Current byte within sector + char readBuffer[512]; // Buffer used for unaligned reads + u32 appClus; // Cluster to append to + int appSect; // Sector within cluster for appending + int appByte; // Byte within sector for appending + bool read; // Can read from file + bool write; // Can write to file + bool append;// Can append to file + bool inUse; // This file is open + u32 dirEntSector; // The sector where the directory entry is stored + int dirEntOffset; // The offset within the directory sector +} FAT_FILE; + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// Global Variables + +// _VARS_IN_RAM variables are stored in the largest section of WRAM +// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA + +// Locations on card +int discRootDir; +int discRootDirClus; +int discFAT; +int discSecPerFAT; +int discNumSec; +int discData; +int discBytePerSec; +int discSecPerClus; +int discBytePerClus; + +enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} discFileSystem; + +// Global sector buffer to save on stack space +unsigned char globalBuffer[BYTES_PER_SECTOR]; + + +//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +//FAT routines + +u32 FAT_ClustToSect (u32 cluster) { + return (((cluster-2) * discSecPerClus) + discData); +} + +/*----------------------------------------------------------------- +FAT_NextCluster +Internal function - gets the cluster linked from input cluster +-----------------------------------------------------------------*/ +u32 FAT_NextCluster(u32 cluster) +{ + u32 nextCluster = CLUSTER_FREE; + u32 sector; + int offset; + + + switch (discFileSystem) + { + case FS_UNKNOWN: + nextCluster = CLUSTER_FREE; + break; + + case FS_FAT12: + sector = discFAT + (((cluster * 3) / 2) / BYTES_PER_SECTOR); + offset = ((cluster * 3) / 2) % BYTES_PER_SECTOR; + CARD_ReadSector(sector, globalBuffer); + nextCluster = ((u8*) globalBuffer)[offset]; + offset++; + + if (offset >= BYTES_PER_SECTOR) { + offset = 0; + sector++; + } + + CARD_ReadSector(sector, globalBuffer); + nextCluster |= (((u8*) globalBuffer)[offset]) << 8; + + if (cluster & 0x01) { + nextCluster = nextCluster >> 4; + } else { + nextCluster &= 0x0FFF; + } + + break; + + case FS_FAT16: + sector = discFAT + ((cluster << 1) / BYTES_PER_SECTOR); + offset = cluster % (BYTES_PER_SECTOR >> 1); + + CARD_ReadSector(sector, globalBuffer); + // read the nextCluster value + nextCluster = ((u16*)globalBuffer)[offset]; + + if (nextCluster >= 0xFFF7) + { + nextCluster = CLUSTER_EOF; + } + break; + + case FS_FAT32: + sector = discFAT + ((cluster << 2) / BYTES_PER_SECTOR); + offset = cluster % (BYTES_PER_SECTOR >> 2); + + CARD_ReadSector(sector, globalBuffer); + // read the nextCluster value + nextCluster = (((u32*)globalBuffer)[offset]) & 0x0FFFFFFF; + + if (nextCluster >= 0x0FFFFFF7) + { + nextCluster = CLUSTER_EOF; + } + break; + + default: + nextCluster = CLUSTER_FREE; + break; + } + + return nextCluster; +} + +/*----------------------------------------------------------------- +ucase +Returns the uppercase version of the given char +char IN: a character +char return OUT: uppercase version of character +-----------------------------------------------------------------*/ +char ucase (char character) +{ + if ((character > 0x60) && (character < 0x7B)) + character = character - 0x20; + return (character); +} + +/*----------------------------------------------------------------- +FAT_InitFiles +Reads the FAT information from the CF card. +You need to call this before reading any files. +bool return OUT: true if successful. +-----------------------------------------------------------------*/ +bool FAT_InitFiles (bool initCard) +{ + int i; + int bootSector; + BOOT_SEC* bootSec; + + if (initCard && !CARD_StartUp()) + { + return (false); + } + + // Read first sector of card + if (!CARD_ReadSector (0, globalBuffer)) + { + return false; + } + // Check if there is a FAT string, which indicates this is a boot sector + if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T')) + { + bootSector = 0; + } + // Check for FAT32 + else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T')) + { + bootSector = 0; + } + else // This is an MBR + { + // Find first valid partition from MBR + // First check for an active partition + for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10); + // If it didn't find an active partition, search for any valid partition + if (i == 0x1FE) + for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10); + + // Go to first valid partition + if ( i != 0x1FE) // Make sure it found a partition + { + bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F); + } else { + bootSector = 0; // No partition found, assume this is a MBR free disk + } + } + + // Read in boot sector + bootSec = (BOOT_SEC*) globalBuffer; + CARD_ReadSector (bootSector, bootSec); + + // Store required information about the file system + if (bootSec->bpb.sectorsPerFAT != 0) + { + discSecPerFAT = bootSec->bpb.sectorsPerFAT; + } + else + { + discSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32; + } + + if (bootSec->bpb.numSectorsSmall != 0) + { + discNumSec = bootSec->bpb.numSectorsSmall; + } + else + { + discNumSec = bootSec->bpb.numSectors; + } + + discBytePerSec = BYTES_PER_SECTOR; // Sector size is redefined to be 512 bytes + discSecPerClus = bootSec->bpb.sectorsPerCluster * bootSec->bpb.bytesPerSector / BYTES_PER_SECTOR; + discBytePerClus = discBytePerSec * discSecPerClus; + discFAT = bootSector + bootSec->bpb.reservedSectors; + + discRootDir = discFAT + (bootSec->bpb.numFATs * discSecPerFAT); + discData = discRootDir + ((bootSec->bpb.rootEntries * sizeof(DIR_ENT)) / BYTES_PER_SECTOR); + + if ((discNumSec - discData) / bootSec->bpb.sectorsPerCluster < 4085) + { + discFileSystem = FS_FAT12; + } + else if ((discNumSec - discData) / bootSec->bpb.sectorsPerCluster < 65525) + { + discFileSystem = FS_FAT16; + } + else + { + discFileSystem = FS_FAT32; + } + + if (discFileSystem != FS_FAT32) + { + discRootDirClus = FAT16_ROOT_DIR_CLUSTER; + } + else // Set up for the FAT32 way + { + discRootDirClus = bootSec->extBlock.fat32.rootClus; + // Check if FAT mirroring is enabled + if (!(bootSec->extBlock.fat32.extFlags & 0x80)) + { + // Use the active FAT + discFAT = discFAT + ( discSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F)); + } + } + + return (true); +} + + +/*----------------------------------------------------------------- +getBootFileCluster +-----------------------------------------------------------------*/ +u32 getBootFileCluster (const char* bootName) +{ + DIR_ENT dir; + int firstSector = 0; + bool notFound = false; + bool found = false; +// int maxSectors; + u32 wrkDirCluster = discRootDirClus; + u32 wrkDirSector = 0; + int wrkDirOffset = 0; + int nameOffset; + + dir.startCluster = CLUSTER_FREE; // default to no file found + dir.startClusterHigh = CLUSTER_FREE; + + + // Check if fat has been initialised + if (discBytePerSec == 0) + { + return (CLUSTER_FREE); + } + + char *ptr = (char*)bootName; + while (*ptr != '.') ptr++; + int namelen = ptr - bootName; + +// maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (discData - discRootDir) : discSecPerClus); + // Scan Dir for correct entry + firstSector = discRootDir; + CARD_ReadSector (firstSector + wrkDirSector, globalBuffer); + found = false; + notFound = false; + wrkDirOffset = -1; // Start at entry zero, Compensating for increment + while (!found && !notFound) { + wrkDirOffset++; + if (wrkDirOffset == BYTES_PER_SECTOR / sizeof (DIR_ENT)) + { + wrkDirOffset = 0; + wrkDirSector++; + if ((wrkDirSector == discSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER)) + { + wrkDirSector = 0; + wrkDirCluster = FAT_NextCluster(wrkDirCluster); + if (wrkDirCluster == CLUSTER_EOF) + { + notFound = true; + } + firstSector = FAT_ClustToSect(wrkDirCluster); + } + else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (discData - discRootDir))) + { + notFound = true; // Got to end of root dir + } + CARD_ReadSector (firstSector + wrkDirSector, globalBuffer); + } + dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset]; + found = true; + if ((dir.attrib & ATTRIB_DIR) || (dir.attrib & ATTRIB_VOL)) + { + found = false; + } + if(namelen<8 && dir.name[namelen]!=0x20) found = false; + for (nameOffset = 0; nameOffset < namelen && found; nameOffset++) + { + if (ucase(dir.name[nameOffset]) != bootName[nameOffset]) + found = false; + } + for (nameOffset = 0; nameOffset < 3 && found; nameOffset++) + { + if (ucase(dir.ext[nameOffset]) != bootName[nameOffset+namelen+1]) + found = false; + } + if (dir.name[0] == FILE_LAST) + { + notFound = true; + } + } + + // If no file is found, return CLUSTER_FREE + if (notFound) + { + return CLUSTER_FREE; + } + + return (dir.startCluster | (dir.startClusterHigh << 16)); +} + +/*----------------------------------------------------------------- +fileRead(buffer, cluster, startOffset, length) +-----------------------------------------------------------------*/ +u32 fileRead (char* buffer, u32 cluster, u32 startOffset, u32 length) +{ + int curByte; + int curSect; + + int dataPos = 0; + int chunks; + int beginBytes; + + if (cluster == CLUSTER_FREE || cluster == CLUSTER_EOF) + { + return 0; + } + + // Follow cluster list until desired one is found + for (chunks = startOffset / discBytePerClus; chunks > 0; chunks--) + { + cluster = FAT_NextCluster (cluster); + } + + // Calculate the sector and byte of the current position, + // and store them + curSect = (startOffset % discBytePerClus) / BYTES_PER_SECTOR; + curByte = startOffset % BYTES_PER_SECTOR; + + // Load sector buffer for new position in file + CARD_ReadSector( curSect + FAT_ClustToSect(cluster), globalBuffer); + curSect++; + + // Number of bytes needed to read to align with a sector + beginBytes = (BYTES_PER_SECTOR < length + curByte ? (BYTES_PER_SECTOR - curByte) : length); + + // Read first part from buffer, to align with sector boundary + for (dataPos = 0 ; dataPos < beginBytes; dataPos++) + { + buffer[dataPos] = globalBuffer[curByte++]; + } + + // Read in all the 512 byte chunks of the file directly, saving time + for ( chunks = ((int)length - beginBytes) / BYTES_PER_SECTOR; chunks > 0;) + { + int sectorsToRead; + + // Move to the next cluster if necessary + if (curSect >= discSecPerClus) + { + curSect = 0; + cluster = FAT_NextCluster (cluster); + } + + // Calculate how many sectors to read (read a maximum of discSecPerClus at a time) + sectorsToRead = discSecPerClus - curSect; + if(chunks < sectorsToRead) + sectorsToRead = chunks; + + // Read the sectors + CARD_ReadSectors(curSect + FAT_ClustToSect(cluster), sectorsToRead, buffer + dataPos); + chunks -= sectorsToRead; + curSect += sectorsToRead; + dataPos += BYTES_PER_SECTOR * sectorsToRead; + } + + // Take care of any bytes left over before end of read + if (dataPos < length) + { + + // Update the read buffer + curByte = 0; + if (curSect >= discSecPerClus) + { + curSect = 0; + cluster = FAT_NextCluster (cluster); + } + CARD_ReadSector( curSect + FAT_ClustToSect( cluster), globalBuffer); + + // Read in last partial chunk + for (; dataPos < length; dataPos++) + { + buffer[dataPos] = globalBuffer[curByte]; + curByte++; + } + } + + return dataPos; +} diff --git a/ndsbootloader/source/fat.h b/ndsbootloader/source/fat.h new file mode 100644 index 0000000..e059618 --- /dev/null +++ b/ndsbootloader/source/fat.h @@ -0,0 +1,46 @@ +/*----------------------------------------------------------------- + fat.h + + NDS MP + GBAMP NDS Firmware Hack Version 2.12 + An NDS aware firmware patch for the GBA Movie Player. + By Michael Chisholm (Chishm) + + Filesystem code based on GBAMP_CF.c by Chishm (me). + +License: + Copyright (C) 2005 Michael "Chishm" Chisholm + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + If you use this code, please give due credit and email me about your + project at chishm@hotmail.com +------------------------------------------------------------------*/ + +#ifndef FAT_H +#define FAT_H + +#include + +#define CLUSTER_FREE 0x00000000 +#define CLUSTER_EOF 0x0FFFFFFF +#define CLUSTER_FIRST 0x00000002 + +bool FAT_InitFiles (bool initCard); +u32 getBootFileCluster (const char* bootName); +u32 fileRead (char* buffer, u32 cluster, u32 startOffset, u32 length); +u32 FAT_ClustToSect (u32 cluster); + +#endif // FAT_H diff --git a/ndsbootloader/source/io_dldi.h b/ndsbootloader/source/io_dldi.h new file mode 100644 index 0000000..0af8e39 --- /dev/null +++ b/ndsbootloader/source/io_dldi.h @@ -0,0 +1,44 @@ +/* + io_dldi.h + + Reserved space for post-compilation adding of an extra driver + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 2006-12-22 - Chishm + * Original release +*/ + +#ifndef IO_DLDI_H +#define IO_DLDI_H + +// 'DLDD' +#define DEVICE_TYPE_DLDD 0x49444C44 + +#include "disc_io.h" + +// export interface +extern IO_INTERFACE _io_dldi ; + +#endif // define IO_DLDI_H diff --git a/ndsbootloader/source/io_dldi.s b/ndsbootloader/source/io_dldi.s new file mode 100644 index 0000000..cf17747 --- /dev/null +++ b/ndsbootloader/source/io_dldi.s @@ -0,0 +1,125 @@ +/*----------------------------------------------------------------- + + Copyright (C) 2005 Michael "Chishm" Chisholm + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + If you use this code, please give due credit and email me about your + project at chishm@hotmail.com +------------------------------------------------------------------*/ +@--------------------------------------------------------------------------------- + .align 4 + .arm + .global _dldi_start + .global _io_dldi +@--------------------------------------------------------------------------------- +.equ FEATURE_MEDIUM_CANREAD, 0x00000001 +.equ FEATURE_MEDIUM_CANWRITE, 0x00000002 +.equ FEATURE_SLOT_GBA, 0x00000010 +.equ FEATURE_SLOT_NDS, 0x00000020 + + +_dldi_start: +#ifndef NO_DLDI + +@--------------------------------------------------------------------------------- +@ Driver patch file standard header -- 16 bytes +#ifdef STANDARD_DLDI + .word 0xBF8DA5ED @ Magic number to identify this region +#else + .word 0xBF8DA5EE @ Magic number to identify this region +#endif + .asciz " Chishm" @ Identifying Magic string (8 bytes with null terminator) + .byte 0x01 @ Version number + .byte 0x0e @ 16KiB @ Log [base-2] of the size of this driver in bytes. + .byte 0x00 @ Sections to fix + .byte 0x0e @ 16KiB @ Log [base-2] of the allocated space in bytes. + +@--------------------------------------------------------------------------------- +@ Text identifier - can be anything up to 47 chars + terminating null -- 16 bytes + .align 4 + .asciz "Loader (No interface)" + +@--------------------------------------------------------------------------------- +@ Offsets to important sections within the data -- 32 bytes + .align 6 + .word _dldi_start @ data start + .word _dldi_end @ data end + .word 0x00000000 @ Interworking glue start -- Needs address fixing + .word 0x00000000 @ Interworking glue end + .word 0x00000000 @ GOT start -- Needs address fixing + .word 0x00000000 @ GOT end + .word 0x00000000 @ bss start -- Needs setting to zero + .word 0x00000000 @ bss end +@--------------------------------------------------------------------------------- +@ IO_INTERFACE data -- 32 bytes +_io_dldi: + .ascii "DLDI" @ ioType + .word 0x00000000 @ Features + .word _DLDI_startup @ + .word _DLDI_isInserted @ + .word _DLDI_readSectors @ Function pointers to standard device driver functions + .word _DLDI_writeSectors @ + .word _DLDI_clearStatus @ + .word _DLDI_shutdown @ + + +@--------------------------------------------------------------------------------- + +_DLDI_startup: +_DLDI_isInserted: +_DLDI_readSectors: +_DLDI_writeSectors: +_DLDI_clearStatus: +_DLDI_shutdown: + mov r0, #0x00 @ Return false for every function + bx lr + + + +@--------------------------------------------------------------------------------- + .align + .pool + + .space (_dldi_start + 16384) - . @ Fill to 16KiB + +_dldi_end: + .end +@--------------------------------------------------------------------------------- +#else +@--------------------------------------------------------------------------------- +@ IO_INTERFACE data -- 32 bytes +_io_dldi: + .ascii "DLDI" @ ioType + .word 0x00000000 @ Features + .word _DLDI_startup @ + .word _DLDI_isInserted @ + .word _DLDI_readSectors @ Function pointers to standard device driver functions + .word _DLDI_writeSectors @ + .word _DLDI_clearStatus @ + .word _DLDI_shutdown @ + + _DLDI_startup: +_DLDI_isInserted: +_DLDI_readSectors: +_DLDI_writeSectors: +_DLDI_clearStatus: +_DLDI_shutdown: + mov r0, #0x00 @ Return false for every function + bx lr + + +#endif + diff --git a/ndsbootloader/source/load_crt0.s b/ndsbootloader/source/load_crt0.s new file mode 100644 index 0000000..48a8c82 --- /dev/null +++ b/ndsbootloader/source/load_crt0.s @@ -0,0 +1,147 @@ +/*----------------------------------------------------------------- + + Copyright (C) 2005 Michael "Chishm" Chisholm + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + If you use this code, please give due credit and email me about your + project at chishm@hotmail.com +------------------------------------------------------------------*/ + +@--------------------------------------------------------------------------------- + .section ".init" + .global _start + .global storedFileCluster + .global initDisc + .global wantToPatchDLDI + .global argStart + .global argSize + .global dsiSD + .global dsiMode +@--------------------------------------------------------------------------------- + .align 4 + .arm +@--------------------------------------------------------------------------------- +_start: +@--------------------------------------------------------------------------------- + b startUp + +storedFileCluster: + .word 0x0FFFFFFF @ default BOOT.NDS +initDisc: + .word 0x00000001 @ init the disc by default +wantToPatchDLDI: + .word 0x00000001 @ by default patch the DLDI section of the loaded NDS +@ Used for passing arguments to the loaded app +argStart: + .word _end - _start +argSize: + .word 0x00000000 +dldiOffset: + .word _dldi_start - _start +dsiSD: + .word 0 +dsiMode: + .word 0 + +startUp: + mov r0, #0x04000000 + mov r1, #0 + str r1, [r0,#0x208] @ REG_IME + str r1, [r0,#0x210] @ REG_IE + str r1, [r0,#0x218] @ REG_AUXIE + + mov r0, #0x12 @ Switch to IRQ Mode + msr cpsr, r0 + ldr sp, =__sp_irq @ Set IRQ stack + + mov r0, #0x13 @ Switch to SVC Mode + msr cpsr, r0 + ldr sp, =__sp_svc @ Set SVC stack + + mov r0, #0x1F @ Switch to System Mode + msr cpsr, r0 + ldr sp, =__sp_usr @ Set user stack + + ldr r0, =__bss_start @ Clear BSS section to 0x00 + ldr r1, =__bss_end + sub r1, r1, r0 + bl ClearMem + + mov r0, #0 @ int argc + mov r1, #0 @ char *argv[] + ldr r3, =main + bl _blx_r3_stub @ jump to user code + + @ If the user ever returns, restart + b _start + +@--------------------------------------------------------------------------------- +_blx_r3_stub: +@--------------------------------------------------------------------------------- + bx r3 + +@--------------------------------------------------------------------------------- +@ Clear memory to 0x00 if length != 0 +@ r0 = Start Address +@ r1 = Length +@--------------------------------------------------------------------------------- +ClearMem: +@--------------------------------------------------------------------------------- + mov r2, #3 @ Round down to nearest word boundary + add r1, r1, r2 @ Shouldn't be needed + bics r1, r1, r2 @ Clear 2 LSB (and set Z) + bxeq lr @ Quit if copy size is 0 + + mov r2, #0 +ClrLoop: + stmia r0!, {r2} + subs r1, r1, #4 + bne ClrLoop + bx lr + +@--------------------------------------------------------------------------------- +@ Copy memory if length != 0 +@ r1 = Source Address +@ r2 = Dest Address +@ r4 = Dest Address + Length +@--------------------------------------------------------------------------------- +CopyMemCheck: +@--------------------------------------------------------------------------------- + sub r3, r4, r2 @ Is there any data to copy? +@--------------------------------------------------------------------------------- +@ Copy memory +@ r1 = Source Address +@ r2 = Dest Address +@ r3 = Length +@--------------------------------------------------------------------------------- +CopyMem: +@--------------------------------------------------------------------------------- + mov r0, #3 @ These commands are used in cases where + add r3, r3, r0 @ the length is not a multiple of 4, + bics r3, r3, r0 @ even though it should be. + bxeq lr @ Length is zero, so exit +CIDLoop: + ldmia r1!, {r0} + stmia r2!, {r0} + subs r3, r3, #4 + bne CIDLoop + bx lr + +@--------------------------------------------------------------------------------- + .align + .pool + .end +@--------------------------------------------------------------------------------- diff --git a/ndsbootloader/source/sdmmc.c b/ndsbootloader/source/sdmmc.c new file mode 100644 index 0000000..7967fa4 --- /dev/null +++ b/ndsbootloader/source/sdmmc.c @@ -0,0 +1,242 @@ +#ifndef NO_SDMMC +#include +#include +#include "sdmmc.h" + +static struct mmcdevice deviceSD; + +//--------------------------------------------------------------------------------- +int geterror(struct mmcdevice *ctx) { +//--------------------------------------------------------------------------------- + //if(ctx->error == 0x4) return -1; + //else return 0; + return (ctx->error << 29) >> 31; +} + + +//--------------------------------------------------------------------------------- +void setTarget(struct mmcdevice *ctx) { +//--------------------------------------------------------------------------------- + sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber); + setckl(ctx->clk); + if (ctx->SDOPT == 0) { + sdmmc_mask16(REG_SDOPT, 0, 0x8000); + } else { + sdmmc_mask16(REG_SDOPT, 0x8000, 0); + } + +} + + +//--------------------------------------------------------------------------------- +void sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args) { +//--------------------------------------------------------------------------------- + int i; + bool getSDRESP = (cmd << 15) >> 31; + uint16_t flags = (cmd << 15) >> 31; + const bool readdata = cmd & 0x20000; + const bool writedata = cmd & 0x40000; + + if(readdata || writedata) + { + flags |= TMIO_STAT0_DATAEND; + } + + ctx->error = 0; + while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working? + sdmmc_write16(REG_SDIRMASK0,0); + sdmmc_write16(REG_SDIRMASK1,0); + sdmmc_write16(REG_SDSTATUS0,0); + sdmmc_write16(REG_SDSTATUS1,0); + sdmmc_write16(REG_SDCMDARG0,args &0xFFFF); + sdmmc_write16(REG_SDCMDARG1,args >> 16); + sdmmc_write16(REG_SDCMD,cmd &0xFFFF); + + uint32_t size = ctx->size; + uint16_t *dataPtr = (uint16_t*)ctx->data; + uint32_t *dataPtr32 = (uint32_t*)ctx->data; + + bool useBuf = ( NULL != dataPtr ); + bool useBuf32 = (useBuf && (0 == (3 & ((uint32_t)dataPtr)))); + + uint16_t status0 = 0; + + while(1) { + volatile uint16_t status1 = sdmmc_read16(REG_SDSTATUS1); + volatile uint16_t ctl32 = sdmmc_read16(REG_SDDATACTL32); + if((ctl32 & 0x100)) + { + if(readdata) { + if(useBuf) { + sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0); + if(size > 0x1FF) { + if(useBuf32) { + for(i = 0; i<0x200; i+=4) { + *dataPtr32++ = sdmmc_read32(REG_SDFIFO32); + } + } else { + for(i = 0; i<0x200; i+=2) { + *dataPtr++ = sdmmc_read16(REG_SDFIFO); + } + } + size -= 0x200; + } + } + + sdmmc_mask16(REG_SDDATACTL32, 0x800, 0); + } + } + + if(status1 & TMIO_MASK_GW) { + ctx->error |= 4; + break; + } + + if(!(status1 & TMIO_STAT1_CMD_BUSY)) { + status0 = sdmmc_read16(REG_SDSTATUS0); + if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND) { + ctx->error |= 0x1; + } + if(status0 & TMIO_STAT0_DATAEND) { + ctx->error |= 0x2; + } + + if((status0 & flags) == flags) + break; + } + } + ctx->stat0 = sdmmc_read16(REG_SDSTATUS0); + ctx->stat1 = sdmmc_read16(REG_SDSTATUS1); + sdmmc_write16(REG_SDSTATUS0,0); + sdmmc_write16(REG_SDSTATUS1,0); + + if(getSDRESP != 0) { + ctx->ret[0] = sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16); + ctx->ret[1] = sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16); + ctx->ret[2] = sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16); + ctx->ret[3] = sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16); + } +} + + +//--------------------------------------------------------------------------------- +int sdmmc_cardinserted() { +//--------------------------------------------------------------------------------- + return 1; //sdmmc_cardready; +} + +//--------------------------------------------------------------------------------- +void sdmmc_controller_init(bool force) { +//--------------------------------------------------------------------------------- + deviceSD.isSDHC = 0; + deviceSD.SDOPT = 0; + deviceSD.res = 0; + deviceSD.initarg = 0; + deviceSD.clk = 0x80; + deviceSD.devicenumber = 0; + + *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xF7FFu; + *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xEFFFu; + *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u; + *(vu16*)(SDMMC_BASE + REG_SDDATACTL) = (*(vu16*)(SDMMC_BASE + REG_SDDATACTL) & 0xFFDD) | 2; + *(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFFu; + *(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDFu; + *(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 512; + *(vu16*)(SDMMC_BASE + REG_SDBLKCOUNT32) = 1; + *(vu16*)(SDMMC_BASE + REG_SDRESET) &= 0xFFFEu; + *(vu16*)(SDMMC_BASE + REG_SDRESET) |= 1u; + *(vu16*)(SDMMC_BASE + REG_SDIRMASK0) |= TMIO_MASK_ALL; + *(vu16*)(SDMMC_BASE + REG_SDIRMASK1) |= TMIO_MASK_ALL>>16; + *(vu16*)(SDMMC_BASE + 0x0fc) |= 0xDBu; //SDCTL_RESERVED7 + *(vu16*)(SDMMC_BASE + 0x0fe) |= 0xDBu; //SDCTL_RESERVED8 + *(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu; + *(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x20; + *(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EE; + *(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu; + *(vu16*)(SDMMC_BASE + REG_SDBLKLEN) = 512; + *(vu16*)(SDMMC_BASE + REG_SDSTOP) = 0; + +} + +//--------------------------------------------------------------------------------- +int sdmmc_sdcard_init() { +//--------------------------------------------------------------------------------- + setTarget(&deviceSD); + swiDelay(0xF000); + sdmmc_send_command(&deviceSD,0,0); + sdmmc_send_command(&deviceSD,0x10408,0x1AA); + u32 temp = (deviceSD.error & 0x1) << 0x1E; + + u32 temp2 = 0; + do { + do { + sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10); + sdmmc_send_command(&deviceSD,0x10769,0x00FF8000 | temp); + temp2 = 1; + } while ( !(deviceSD.error & 1) ); + + } while((deviceSD.ret[0] & 0x80000000) == 0); + + if(!((deviceSD.ret[0] >> 30) & 1) || !temp) + temp2 = 0; + + deviceSD.isSDHC = temp2; + + sdmmc_send_command(&deviceSD,0x10602,0); + if (deviceSD.error & 0x4) return -1; + + sdmmc_send_command(&deviceSD,0x10403,0); + if (deviceSD.error & 0x4) return -1; + deviceSD.initarg = deviceSD.ret[0] >> 0x10; + + sdmmc_send_command(&deviceSD,0x10609,deviceSD.initarg << 0x10); + if (deviceSD.error & 0x4) return -1; + + deviceSD.clk = 1; + setckl(1); + + sdmmc_send_command(&deviceSD,0x10507,deviceSD.initarg << 0x10); + if (deviceSD.error & 0x4) return -1; + + sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10); + if (deviceSD.error & 0x4) return -1; + + sdmmc_send_command(&deviceSD,0x1076A,0x0); + if (deviceSD.error & 0x4) return -1; + + sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10); + if (deviceSD.error & 0x4) return -1; + + deviceSD.SDOPT = 1; + sdmmc_send_command(&deviceSD,0x10446,0x2); + if (deviceSD.error & 0x4) return -1; + + sdmmc_send_command(&deviceSD,0x1040D,deviceSD.initarg << 0x10); + if (deviceSD.error & 0x4) return -1; + + sdmmc_send_command(&deviceSD,0x10410,0x200); + if (deviceSD.error & 0x4) return -1; + deviceSD.clk |= 0x200; + + return 0; + +} + +//--------------------------------------------------------------------------------- +int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out) { +//--------------------------------------------------------------------------------- + if (deviceSD.isSDHC == 0) + sector_no <<= 9; + setTarget(&deviceSD); + sdmmc_write16(REG_SDSTOP,0x100); + + sdmmc_write16(REG_SDBLKCOUNT32,numsectors); + sdmmc_write16(REG_SDBLKLEN32,0x200); + + sdmmc_write16(REG_SDBLKCOUNT,numsectors); + deviceSD.data = out; + deviceSD.size = numsectors << 9; + sdmmc_send_command(&deviceSD,0x33C12,sector_no); + return geterror(&deviceSD); +} +#endif \ No newline at end of file diff --git a/ndsbootloader/source/sdmmc.h b/ndsbootloader/source/sdmmc.h new file mode 100644 index 0000000..b48248a --- /dev/null +++ b/ndsbootloader/source/sdmmc.h @@ -0,0 +1,194 @@ +#ifndef __SDMMC_H__ +#define __SDMMC_H__ + +#include + +#define DATA32_SUPPORT + +#define SDMMC_BASE 0x04004800 + + +#define REG_SDCMD 0x00 +#define REG_SDPORTSEL 0x02 +#define REG_SDCMDARG 0x04 +#define REG_SDCMDARG0 0x04 +#define REG_SDCMDARG1 0x06 +#define REG_SDSTOP 0x08 +#define REG_SDRESP 0x0c +#define REG_SDBLKCOUNT 0x0a + +#define REG_SDRESP0 0x0c +#define REG_SDRESP1 0x0e +#define REG_SDRESP2 0x10 +#define REG_SDRESP3 0x12 +#define REG_SDRESP4 0x14 +#define REG_SDRESP5 0x16 +#define REG_SDRESP6 0x18 +#define REG_SDRESP7 0x1a + +#define REG_SDSTATUS0 0x1c +#define REG_SDSTATUS1 0x1e + +#define REG_SDIRMASK0 0x20 +#define REG_SDIRMASK1 0x22 +#define REG_SDCLKCTL 0x24 + +#define REG_SDBLKLEN 0x26 +#define REG_SDOPT 0x28 +#define REG_SDFIFO 0x30 + +#define REG_SDDATACTL 0xd8 +#define REG_SDRESET 0xe0 +#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not? + +#define REG_SDDATACTL32 0x100 +#define REG_SDBLKLEN32 0x104 +#define REG_SDBLKCOUNT32 0x108 +#define REG_SDFIFO32 0x10C + +#define REG_CLK_AND_WAIT_CTL 0x138 +#define REG_RESET_SDIO 0x1e0 +//The below defines are from linux kernel drivers/mmc tmio_mmc.h. +/* Definitions for values the CTRL_STATUS register can take. */ +#define TMIO_STAT0_CMDRESPEND 0x0001 +#define TMIO_STAT0_DATAEND 0x0004 +#define TMIO_STAT0_CARD_REMOVE 0x0008 +#define TMIO_STAT0_CARD_INSERT 0x0010 +#define TMIO_STAT0_SIGSTATE 0x0020 +#define TMIO_STAT0_WRPROTECT 0x0080 +#define TMIO_STAT0_CARD_REMOVE_A 0x0100 +#define TMIO_STAT0_CARD_INSERT_A 0x0200 +#define TMIO_STAT0_SIGSTATE_A 0x0400 + +#define TMIO_STAT1_CMD_IDX_ERR 0x0001 +#define TMIO_STAT1_CRCFAIL 0x0002 +#define TMIO_STAT1_STOPBIT_ERR 0x0004 +#define TMIO_STAT1_DATATIMEOUT 0x0008 +#define TMIO_STAT1_RXOVERFLOW 0x0010 +#define TMIO_STAT1_TXUNDERRUN 0x0020 +#define TMIO_STAT1_CMDTIMEOUT 0x0040 +#define TMIO_STAT1_RXRDY 0x0100 +#define TMIO_STAT1_TXRQ 0x0200 +#define TMIO_STAT1_ILL_FUNC 0x2000 +#define TMIO_STAT1_CMD_BUSY 0x4000 +#define TMIO_STAT1_ILL_ACCESS 0x8000 + +#define SDMC_NORMAL 0x00000000 +#define SDMC_ERR_COMMAND 0x00000001 +#define SDMC_ERR_CRC 0x00000002 +#define SDMC_ERR_END 0x00000004 +#define SDMC_ERR_TIMEOUT 0x00000008 +#define SDMC_ERR_FIFO_OVF 0x00000010 +#define SDMC_ERR_FIFO_UDF 0x00000020 +#define SDMC_ERR_WP 0x00000040 +#define SDMC_ERR_ABORT 0x00000080 +#define SDMC_ERR_FPGA_TIMEOUT 0x00000100 +#define SDMC_ERR_PARAM 0x00000200 +#define SDMC_ERR_R1_STATUS 0x00000800 +#define SDMC_ERR_NUM_WR_SECTORS 0x00001000 +#define SDMC_ERR_RESET 0x00002000 +#define SDMC_ERR_ILA 0x00004000 +#define SDMC_ERR_INFO_DETECT 0x00008000 + +#define SDMC_STAT_ERR_UNKNOWN 0x00080000 +#define SDMC_STAT_ERR_CC 0x00100000 +#define SDMC_STAT_ERR_ECC_FAILED 0x00200000 +#define SDMC_STAT_ERR_CRC 0x00800000 +#define SDMC_STAT_ERR_OTHER 0xf9c70008 + +#define TMIO_MASK_ALL 0x837f031d + +#define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \ + TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR) + +#define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND) +#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND) + +typedef struct mmcdevice { + u8* data; + u32 size; + u32 error; + u16 stat0; + u16 stat1; + u32 ret[4]; + u32 initarg; + u32 isSDHC; + u32 clk; + u32 SDOPT; + u32 devicenumber; + u32 total_size; //size in sectors of the device + u32 res; +} mmcdevice; + +enum { + MMC_DEVICE_SDCARD, + MMC_DEVICE_NAND, +}; + +void sdmmc_controller_init(bool force_init); +void sdmmc_initirq(); +int sdmmc_cardinserted(); + +int sdmmc_sdcard_init(); +int sdmmc_nand_init(); +void sdmmc_get_cid(int devicenumber, u32 *cid); + +static inline void sdmmc_nand_cid( u32 *cid) { + sdmmc_get_cid(MMC_DEVICE_NAND,cid); +} + +static inline void sdmmc_sdcard_cid( u32 *cid) { + sdmmc_get_cid(MMC_DEVICE_SDCARD,cid); +} + +int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out); +int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, void *in); +int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, void *out); +int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, void *in); + +extern u32 sdmmc_cid[]; +extern int sdmmc_curdevice; + +//--------------------------------------------------------------------------------- +static inline u16 sdmmc_read16(u16 reg) { +//--------------------------------------------------------------------------------- + return *(vu16*)(SDMMC_BASE + reg); +} + +//--------------------------------------------------------------------------------- +static inline void sdmmc_write16(u16 reg, u16 val) { +//--------------------------------------------------------------------------------- + *(vu16*)(SDMMC_BASE + reg) = val; +} + +//--------------------------------------------------------------------------------- +static inline u32 sdmmc_read32(u16 reg) { +//--------------------------------------------------------------------------------- + return *(vu32*)(SDMMC_BASE + reg); +} + +//--------------------------------------------------------------------------------- +static inline void sdmmc_write32(u16 reg, u32 val) { +//--------------------------------------------------------------------------------- + *(vu32*)(SDMMC_BASE + reg) = val; +} + +//--------------------------------------------------------------------------------- +static inline void sdmmc_mask16(u16 reg, u16 clear, u16 set) { +//--------------------------------------------------------------------------------- + u16 val = sdmmc_read16(reg); + val &= ~clear; + val |= set; + sdmmc_write16(reg, val); +} + + +//--------------------------------------------------------------------------------- +static inline void setckl(u32 data) { +//--------------------------------------------------------------------------------- + sdmmc_mask16(REG_SDCLKCTL, 0x100, 0); + sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF); + sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100); +} + +#endif