mirror of
https://github.com/ApacheThunder/EZP_Bootstrap.git
synced 2025-06-18 11:15:35 -04:00
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.
This commit is contained in:
commit
06cbab11ed
342
COPYING
Normal file
342
COPYING
Normal file
@ -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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 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.
|
88
Makefile
Normal file
88
Makefile
Normal file
@ -0,0 +1,88 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
|
||||
|
BIN
NitroFS/Nitro.img
Normal file
BIN
NitroFS/Nitro.img
Normal file
Binary file not shown.
94
README.md
Normal file
94
README.md
Normal file
@ -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.
|
||||
```
|
124
arm7/Makefile
Normal file
124
arm7/Makefile
Normal file
@ -0,0 +1,124 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
|
||||
#---------------------------------------------------------------------------------------
|
15
arm7/source/biosCalls.s
Normal file
15
arm7/source/biosCalls.s
Normal file
@ -0,0 +1,15 @@
|
||||
.TEXT
|
||||
.ARM
|
||||
|
||||
@---------------------------------------------------------------------------------------
|
||||
.GLOBAL swiSwitchToGBAModeFixed
|
||||
.func swiSwitchToGBAModeFixed
|
||||
@---------------------------------------------------------------------------------------
|
||||
swiSwitchToGBAModeFixed:
|
||||
mov r2,#0x40
|
||||
swi 0x1f0000
|
||||
|
||||
.endfunc
|
||||
|
||||
.end
|
||||
|
71
arm7/source/main.c
Normal file
71
arm7/source/main.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include <nds.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
143
arm9/Makefile
Normal file
143
arm9/Makefile
Normal file
@ -0,0 +1,143 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
|
||||
#---------------------------------------------------------------------------------------
|
||||
|
25
arm9/gfx/font.grit
Normal file
25
arm9/gfx/font.grit
Normal file
@ -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
|
||||
|
BIN
arm9/gfx/font.png
Normal file
BIN
arm9/gfx/font.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 688 B |
4
arm9/gfx/font6x8.grit
Normal file
4
arm9/gfx/font6x8.grit
Normal file
@ -0,0 +1,4 @@
|
||||
-gB8
|
||||
-gTFFFFFF
|
||||
# use lz77 compression
|
||||
-gzl
|
BIN
arm9/gfx/font6x8.png
Normal file
BIN
arm9/gfx/font6x8.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 758 B |
9
arm9/gfx/hbmenu_banner.grit
Normal file
9
arm9/gfx/hbmenu_banner.grit
Normal file
@ -0,0 +1,9 @@
|
||||
-W3
|
||||
# disable alpha and set opaque bit for all pixels
|
||||
-gT!
|
||||
|
||||
# use lz77 compression
|
||||
-gzl
|
||||
|
||||
# 16 bit bitmap
|
||||
-gB16
|
BIN
arm9/gfx/hbmenu_banner.png
Normal file
BIN
arm9/gfx/hbmenu_banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
5
arm9/gfx/hbmenu_consolebg.grit
Normal file
5
arm9/gfx/hbmenu_consolebg.grit
Normal file
@ -0,0 +1,5 @@
|
||||
# 8 bit bitmap
|
||||
-gB8
|
||||
|
||||
# bitmap format
|
||||
-gb
|
BIN
arm9/gfx/hbmenu_consolebg.png
Normal file
BIN
arm9/gfx/hbmenu_consolebg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
arm9/include/hbNoIcon.bin
Normal file
BIN
arm9/include/hbNoIcon.bin
Normal file
Binary file not shown.
294
arm9/source/args.cpp
Normal file
294
arm9/source/args.cpp
Normal file
@ -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 <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<string>& 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<char> 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<string>& 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<string> argsGetExtensionList() {
|
||||
vector<string> 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;
|
||||
}
|
47
arm9/source/args.h
Normal file
47
arm9/source/args.h
Normal file
@ -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 <string>
|
||||
#include <vector>
|
||||
|
||||
/* 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<std::string>& argarray);
|
||||
|
||||
/* Return a list of all file extensions that can be browsed and opened.
|
||||
*/
|
||||
std::vector<std::string> argsGetExtensionList();
|
||||
|
||||
#endif // ARGS_H
|
5
arm9/source/defaultBanners.s
Normal file
5
arm9/source/defaultBanners.s
Normal file
@ -0,0 +1,5 @@
|
||||
.arm
|
||||
.global hbNoIcon_bin
|
||||
|
||||
hbNoIcon_bin: .incbin "../include/hbNoIcon.bin"
|
||||
|
38
arm9/source/ez5n.c
Normal file
38
arm9/source/ez5n.c
Normal file
@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
//
|
||||
// SPDX-FileContributor: Antonio Niño Díaz, 2023
|
||||
// SPDX-FileContributor: lifehackerhansol, 2024
|
||||
|
||||
#include <nds/ndstypes.h>
|
||||
|
||||
#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; }
|
||||
|
30
arm9/source/ez5n.h
Normal file
30
arm9/source/ez5n.h
Normal file
@ -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
|
248
arm9/source/file_browse.cpp
Normal file
248
arm9/source/file_browse.cpp
Normal file
@ -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 <vector>
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <nds.h>
|
||||
#include <fat.h>
|
||||
|
||||
#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<string> 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<DirEntry>& dirContents, const vector<string> 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<DirEntry>& dirContents) {
|
||||
vector<string> extensionList;
|
||||
getDirectoryContents (dirContents, extensionList);
|
||||
}
|
||||
|
||||
void showDirectoryContents (const vector<DirEntry>& 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<string>& extensionList) {
|
||||
int pressed = 0;
|
||||
int screenOffset = 0;
|
||||
int fileOffset = 0;
|
||||
vector<DirEntry> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
32
arm9/source/file_browse.h
Normal file
32
arm9/source/file_browse.h
Normal file
@ -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 <string>
|
||||
#include <vector>
|
||||
|
||||
std::string browseForFile (const std::vector<std::string>& extensionList);
|
||||
|
||||
|
||||
|
||||
#endif //FILE_BROWSE_H
|
242
arm9/source/iconTitle.cpp
Normal file
242
arm9/source/iconTitle.cpp
Normal file
@ -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 <nds.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#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<TEXT_WIDTH;i++)
|
||||
writecharRS (rownum, i, 0);
|
||||
}
|
||||
|
||||
static inline void clearIcon (void) { dmaFillHalfWords(0, sprite, sizeof(banner.icon)); }
|
||||
|
||||
void iconTitleInit (void) {
|
||||
// initialize video mode
|
||||
videoSetMode(MODE_4_2D);
|
||||
|
||||
// initialize VRAM banks
|
||||
vramSetBankA(VRAM_A_MAIN_BG);
|
||||
vramSetBankB(VRAM_B_MAIN_SPRITE);
|
||||
|
||||
// initialize bg2 as a rotation background and bg3 as a bmp background
|
||||
// http://mtheall.com/vram.html#T2=3&RNT2=96&MB2=3&TB2=0&S2=2&T3=6&MB3=1&S3=1
|
||||
bg2 = bgInit(2, BgType_Rotation, BgSize_R_512x512, 3, 0);
|
||||
bg3 = bgInit(3, BgType_Bmp16, BgSize_B16_256x256, 1, 0);
|
||||
|
||||
// initialize rotate, scale, and scroll
|
||||
bgSetRotateScale(bg3, 0, 1<<8, 1<<8);
|
||||
bgSetScroll(bg3, 0, 0);
|
||||
bgSetRotateScale(bg2, 0, 8*(1<<8)/6, 1<<8);
|
||||
bgSetScroll(bg2, -TITLE_POS_X, -TITLE_POS_Y);
|
||||
|
||||
// clear bg2's map: 512x512 pixels is 64x64 tiles is 4KB
|
||||
dmaFillHalfWords(0, bgGetMapPtr(bg2), 4096);
|
||||
// load compressed font into bg2's tile data
|
||||
decompress(font6x8Tiles, bgGetGfxPtr(bg2), LZ77Vram);
|
||||
|
||||
// load compressed bitmap into bg3
|
||||
decompress(hbmenu_bannerBitmap, bgGetGfxPtr(bg3), LZ77Vram);
|
||||
|
||||
// load font palette
|
||||
dmaCopy(font6x8Pal, BG_PALETTE, font6x8PalLen);
|
||||
|
||||
// apply the bg changes
|
||||
bgUpdate();
|
||||
|
||||
// initialize OAM
|
||||
oamInit(&oamMain, SpriteMapping_1D_128, false);
|
||||
sprite = oamAllocateGfx(&oamMain, SpriteSize_32x32, SpriteColorFormat_16Color);
|
||||
dmaFillHalfWords(0, sprite, sizeof(banner.icon));
|
||||
oamSet(&oamMain, 0, ICON_POS_X, ICON_POS_Y, 0, 0,
|
||||
SpriteSize_32x32, SpriteColorFormat_16Color, sprite,
|
||||
-1, 0, 0, 0, 0, 0);
|
||||
|
||||
// oam can only be updated during vblank
|
||||
swiWaitForVBlank();
|
||||
oamUpdate(&oamMain);
|
||||
|
||||
// Load Default Icon.
|
||||
DC_FlushAll();
|
||||
dmaCopy(hbNoIcon_bin.icon, sprite, sizeof(hbNoIcon_bin.icon));
|
||||
dmaCopy(hbNoIcon_bin.palette, SPRITE_PALETTE, sizeof(hbNoIcon_bin.palette));
|
||||
|
||||
// everything's ready :)
|
||||
writeRow (1,"===>>> 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));
|
||||
}
|
||||
}
|
||||
|
25
arm9/source/iconTitle.h
Normal file
25
arm9/source/iconTitle.h
Normal file
@ -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 <string>
|
||||
|
||||
void iconTitleInit (void);
|
||||
void iconTitleUpdate (int isdir, const std::string& name);
|
90
arm9/source/ioezp.c
Normal file
90
arm9/source/ioezp.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
EZ-Flash Parallel
|
||||
Card IO routines
|
||||
|
||||
Copyright (C) 2023-2024 lifehackerhansol
|
||||
|
||||
SPDX-License-Identifier: Zlib
|
||||
*/
|
||||
|
||||
#include <nds/card.h>
|
||||
#include <nds/ndstypes.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
53
arm9/source/ioezp.h
Normal file
53
arm9/source/ioezp.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
EZ-Flash Parallel
|
||||
Card IO routines
|
||||
|
||||
Copyright (C) 2023-2024 lifehackerhansol
|
||||
|
||||
SPDX-License-Identifier: Zlib
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nds/ndstypes.h>
|
||||
|
||||
#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);
|
80
arm9/source/libtwl_card.c
Normal file
80
arm9/source/libtwl_card.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
This file is a part of libtwl (card.c)
|
||||
|
||||
Copyright (C) 2023 Gericom
|
||||
|
||||
SPDX-License-Identifier: Zlib
|
||||
*/
|
||||
|
||||
#include <nds/ndstypes.h>
|
||||
#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());
|
||||
}
|
132
arm9/source/libtwl_card.h
Normal file
132
arm9/source/libtwl_card.h
Normal file
@ -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
|
248
arm9/source/main.cpp
Normal file
248
arm9/source/main.cpp
Normal file
@ -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 <nds.h>
|
||||
#include <nds/fifocommon.h>
|
||||
#include <nds/fifomessages.h>
|
||||
#include <stdio.h>
|
||||
#include <fat.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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<string> extensionList = argsGetExtensionList();
|
||||
// if (access("fat:/nds", F_OK) == 0)chdir("fat:/nds");
|
||||
while(1) {
|
||||
string filename = browseForFile(extensionList);
|
||||
// Construct a command line
|
||||
vector<string> 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<const char*> 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();
|
||||
}
|
||||
|
370
arm9/source/nds_loader_arm9.c
Normal file
370
arm9/source/nds_loader_arm9.c
Normal file
@ -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 <string.h>
|
||||
#include <nds.h>
|
||||
#include <nds/arm9/dldi.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fat.h>
|
||||
|
||||
#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
|
||||
}*/
|
||||
|
51
arm9/source/nds_loader_arm9.h
Normal file
51
arm9/source/nds_loader_arm9.h
Normal file
@ -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
|
||||
|
75
arm9/source/nrio_detect.c
Normal file
75
arm9/source/nrio_detect.c
Normal file
@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
//
|
||||
// SPDX-FileContributor: Adrian "asie" Siekierka, 2024
|
||||
|
||||
#include <nds.h>
|
||||
#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;
|
||||
}
|
||||
|
31
arm9/source/nrio_detect.h
Normal file
31
arm9/source/nrio_detect.h
Normal file
@ -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 <stdint.h>
|
||||
|
||||
#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_ */
|
||||
|
293
arm9/source/skin.cpp
Normal file
293
arm9/source/skin.cpp
Normal file
@ -0,0 +1,293 @@
|
||||
#include <fat.h>
|
||||
#include <nds.h>
|
||||
#include <sys/dir.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#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<bmw)||(BMPHeader.biHeight<bmh)) {
|
||||
free(bmdata);
|
||||
bmdata=NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 gr=0,gg=0,gb=0;
|
||||
|
||||
#define Gravity(c,cg) { \
|
||||
c+=cg; \
|
||||
cg=c&7; \
|
||||
c=c>>3; \
|
||||
if((c&(~0x1f))!=0) c=(c<0) ? 0x00 : 0x1f; \
|
||||
}
|
||||
|
||||
for(u32 y=0;y<bmh;y++) {
|
||||
u8 *pSrcBM=&BMPHeader.pBitmap[(BMPHeader.biHeight-1-y)*BMPHeader.DataWidth];
|
||||
u16 *pDstBM=&pbm[y*bmw];
|
||||
switch(BMPHeader.biBitCount) {
|
||||
case 8: {
|
||||
u8 *PaletteTable=BMPHeader.pPalette;
|
||||
for(u32 x=0;x<bmw;x++){
|
||||
u8 *pal;
|
||||
u32 r,g,b;
|
||||
|
||||
pal=&PaletteTable[*pSrcBM*4];
|
||||
pSrcBM+=1;
|
||||
|
||||
b=pal[0];
|
||||
g=pal[1];
|
||||
r=pal[2];
|
||||
|
||||
Gravity(b,gb);
|
||||
Gravity(g,gg);
|
||||
Gravity(r,gr);
|
||||
|
||||
pDstBM[x]=RGB15(r,g,b) | BIT(15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 24: {
|
||||
for(u32 x=0;x<bmw;x++) {
|
||||
u32 r,g,b;
|
||||
|
||||
b=pSrcBM[0];
|
||||
g=pSrcBM[1];
|
||||
r=pSrcBM[2];
|
||||
pSrcBM+=3;
|
||||
|
||||
Gravity(b,gb);
|
||||
Gravity(g,gg);
|
||||
Gravity(r,gr);
|
||||
|
||||
pDstBM[x]=RGB15(r,g,b) | BIT(15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef Gravity
|
||||
|
||||
free(bmdata);
|
||||
bmdata=NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ALIGN(4) static u16 *pBuf;
|
||||
|
||||
bool LoadSkin(int mode, const char *Name) {
|
||||
u16 *pDstBuf1, *pDstBuf2;
|
||||
|
||||
pBuf = (u16*)malloc(256*192*2);
|
||||
if (!intLoadBM(Name, pBuf, 256, 192)) { free(pBuf); return false; }
|
||||
|
||||
switch (mode) {
|
||||
case 0: pDstBuf1 = (u16*)0x06020000; break;
|
||||
case 1: pDstBuf1 = (u16*)0x06220000; break;
|
||||
case 2:
|
||||
pDstBuf1 = (u16*)0x06000000;
|
||||
pDstBuf2 = (u16*)0x06020000;
|
||||
break;
|
||||
case 3: {
|
||||
pDstBuf1 = BG_BMP_RAM(0);
|
||||
pDstBuf2 = BG_BMP_RAM(8);
|
||||
} break;
|
||||
default: pDstBuf1 = (u16*)0x06020000; break;
|
||||
}
|
||||
|
||||
for (s32 y = 0; y < 192; y++) {
|
||||
for (s32 x = 0; x < 256; x++) {
|
||||
pDstBuf1[x] = pBuf[x];
|
||||
if ((mode == 2) || (mode == 3))pDstBuf2[x] = pBuf[x];
|
||||
}
|
||||
pDstBuf1 += 256;
|
||||
if ((mode == 2) || (mode == 3))pDstBuf2 += 256;
|
||||
pBuf += 256;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
10
arm9/source/skin.h
Normal file
10
arm9/source/skin.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern bool LoadSkin(int mode, const char *Name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
136
arm9/source/tonccpy.c
Normal file
136
arm9/source/tonccpy.c
Normal file
@ -0,0 +1,136 @@
|
||||
#include "tonccpy.h"
|
||||
//# tonccpy.c
|
||||
|
||||
//! VRAM-safe cpy.
|
||||
/*! This version mimics memcpy in functionality, with
|
||||
the benefit of working for VRAM as well. It is also
|
||||
slightly faster than the original memcpy, but faster
|
||||
implementations can be made.
|
||||
\param dst Destination pointer.
|
||||
\param src Source pointer.
|
||||
\param size Fill-length in bytes.
|
||||
\note The pointers and size need not be word-aligned.
|
||||
*/
|
||||
void tonccpy(void *dst, const void *src, uint size)
|
||||
{
|
||||
if(size==0 || dst==0 || src==0)
|
||||
return;
|
||||
|
||||
uint count;
|
||||
u16 *dst16; // hword destination
|
||||
u8 *src8; // byte source
|
||||
|
||||
// Ideal case: copy by 4x words. Leaves tail for later.
|
||||
if( ((u32)src|(u32)dst)%4==0 && size>=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);
|
||||
}
|
||||
}
|
43
arm9/source/tonccpy.h
Normal file
43
arm9/source/tonccpy.h
Normal file
@ -0,0 +1,43 @@
|
||||
//# Stuff you may not have yet.
|
||||
|
||||
#ifndef TONCCPY_H
|
||||
#define TONCCPY_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <nds/ndstypes.h>
|
||||
|
||||
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
|
13
bootstub/Makefile
Normal file
13
bootstub/Makefile
Normal file
@ -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
|
176
bootstub/bootstub.s
Normal file
176
bootstub/bootstub.s
Normal file
@ -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:
|
BIN
gbaframe.bmp
Normal file
BIN
gbaframe.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 144 KiB |
18
license.txt
Normal file
18
license.txt
Normal file
@ -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
|
123
ndsbootloader/Makefile
Normal file
123
ndsbootloader/Makefile
Normal file
@ -0,0 +1,123 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
|
||||
#---------------------------------------------------------------------------------------
|
110
ndsbootloader/arm9code/mpu_reset.s
Normal file
110
ndsbootloader/arm9code/mpu_reset.s
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <nds/arm9/cache_asm.h>
|
||||
|
||||
.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
|
198
ndsbootloader/load.ld
Normal file
198
ndsbootloader/load.ld
Normal file
@ -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 . */
|
||||
}
|
70
ndsbootloader/source/arm7clear.s
Normal file
70
ndsbootloader/source/arm7clear.s
Normal file
@ -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
|
||||
|
70
ndsbootloader/source/arm9clear.arm.c
Normal file
70
ndsbootloader/source/arm9clear.arm.c
Normal file
@ -0,0 +1,70 @@
|
||||
#define ARM9
|
||||
#undef ARM7
|
||||
|
||||
#include <nds/ndstypes.h>
|
||||
#include <nds/dma.h>
|
||||
#include <nds/system.h>
|
||||
#include <nds/interrupts.h>
|
||||
#include <nds/timers.h>
|
||||
|
||||
#include <nds/memory.h>
|
||||
#include <nds/arm9/video.h>
|
||||
#include <nds/arm9/input.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
6
ndsbootloader/source/arm9mpu_reset.s
Normal file
6
ndsbootloader/source/arm9mpu_reset.s
Normal file
@ -0,0 +1,6 @@
|
||||
.arm
|
||||
.global mpu_reset, mpu_reset_end
|
||||
|
||||
mpu_reset:
|
||||
.incbin "mpu_reset.bin"
|
||||
mpu_reset_end:
|
13
ndsbootloader/source/bios.s
Normal file
13
ndsbootloader/source/bios.s
Normal file
@ -0,0 +1,13 @@
|
||||
.text
|
||||
.align 4
|
||||
|
||||
.thumb
|
||||
|
||||
@---------------------------------------------------------------------------------
|
||||
.global swiDelay
|
||||
.thumb_func
|
||||
@---------------------------------------------------------------------------------
|
||||
swiDelay:
|
||||
@---------------------------------------------------------------------------------
|
||||
swi 0x03
|
||||
bx lr
|
379
ndsbootloader/source/boot.c
Normal file
379
ndsbootloader/source/boot.c
Normal file
@ -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 <nds/ndstypes.h>
|
||||
#include <nds/dma.h>
|
||||
#include <nds/system.h>
|
||||
#include <nds/interrupts.h>
|
||||
#include <nds/timers.h>
|
||||
#include <nds/memory.h>
|
||||
#include <nds/arm7/audio.h>
|
||||
#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;
|
||||
}
|
||||
|
10
ndsbootloader/source/boot.h
Normal file
10
ndsbootloader/source/boot.h
Normal file
@ -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_
|
45
ndsbootloader/source/card.h
Normal file
45
ndsbootloader/source/card.h
Normal file
@ -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
|
82
ndsbootloader/source/disc_io.h
Normal file
82
ndsbootloader/source/disc_io.h
Normal file
@ -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 <nds/ndstypes.h>
|
||||
#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
|
223
ndsbootloader/source/dldi_patcher.c
Normal file
223
ndsbootloader/source/dldi_patcher.c
Normal file
@ -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 <string.h>
|
||||
#include <nds.h>
|
||||
#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
|
32
ndsbootloader/source/dldi_patcher.h
Normal file
32
ndsbootloader/source/dldi_patcher.h
Normal file
@ -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 <nds/ndstypes.h>
|
||||
|
||||
typedef signed int addr_t;
|
||||
typedef unsigned char data_t;
|
||||
bool dldiPatchBinary (data_t *binData, u32 binSize);
|
||||
|
||||
#endif // DLDI_PATCHER_H
|
592
ndsbootloader/source/fat.c
Normal file
592
ndsbootloader/source/fat.c
Normal file
@ -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;
|
||||
}
|
46
ndsbootloader/source/fat.h
Normal file
46
ndsbootloader/source/fat.h
Normal file
@ -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 <nds/ndstypes.h>
|
||||
|
||||
#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
|
44
ndsbootloader/source/io_dldi.h
Normal file
44
ndsbootloader/source/io_dldi.h
Normal file
@ -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
|
125
ndsbootloader/source/io_dldi.s
Normal file
125
ndsbootloader/source/io_dldi.s
Normal file
@ -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
|
||||
|
147
ndsbootloader/source/load_crt0.s
Normal file
147
ndsbootloader/source/load_crt0.s
Normal file
@ -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
|
||||
@---------------------------------------------------------------------------------
|
242
ndsbootloader/source/sdmmc.c
Normal file
242
ndsbootloader/source/sdmmc.c
Normal file
@ -0,0 +1,242 @@
|
||||
#ifndef NO_SDMMC
|
||||
#include <nds/bios.h>
|
||||
#include <stddef.h>
|
||||
#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
|
194
ndsbootloader/source/sdmmc.h
Normal file
194
ndsbootloader/source/sdmmc.h
Normal file
@ -0,0 +1,194 @@
|
||||
#ifndef __SDMMC_H__
|
||||
#define __SDMMC_H__
|
||||
|
||||
#include <nds/ndstypes.h>
|
||||
|
||||
#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
|
Loading…
Reference in New Issue
Block a user