http://bootmii-devel.googlegroups.com/web/ppcskel.zip
authorBernhard Urban <lewurm@gmx.net>
Thu, 27 Aug 2009 15:16:23 +0000 (17:16 +0200)
committerBernhard Urban <lewurm@gmx.net>
Thu, 27 Aug 2009 15:16:23 +0000 (17:16 +0200)
60 files changed:
.deps/console.d [new file with mode: 0644]
.deps/diskio.d [new file with mode: 0644]
.deps/exception.d [new file with mode: 0644]
.deps/fat.d [new file with mode: 0644]
.deps/ff.d [new file with mode: 0644]
.deps/font.d [new file with mode: 0644]
.deps/gecko.d [new file with mode: 0644]
.deps/input.d [new file with mode: 0644]
.deps/ipc.d [new file with mode: 0644]
.deps/main.d [new file with mode: 0644]
.deps/malloc.d [new file with mode: 0644]
.deps/mini_ipc.d [new file with mode: 0644]
.deps/nandfs.d [new file with mode: 0644]
.deps/printf.d [new file with mode: 0644]
.deps/string.d [new file with mode: 0644]
.deps/sync.d [new file with mode: 0644]
.deps/time.d [new file with mode: 0644]
.deps/video_low.d [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
bootmii_ppc.h [new file with mode: 0644]
broadway.mk [new file with mode: 0644]
common.mk [new file with mode: 0644]
console.c [new file with mode: 0644]
console.h [new file with mode: 0644]
crt0.S [new file with mode: 0644]
diskio.c [new file with mode: 0644]
diskio.h [new file with mode: 0644]
exception.c [new file with mode: 0644]
exception_2200.S [new file with mode: 0644]
fat.c [new file with mode: 0644]
fat.h [new file with mode: 0644]
ff.c [new file with mode: 0644]
ff.h [new file with mode: 0644]
font.c [new file with mode: 0644]
gecko.c [new file with mode: 0644]
input.c [new file with mode: 0644]
input.h [new file with mode: 0644]
ipc.c [new file with mode: 0644]
ipc.h [new file with mode: 0644]
main.c [new file with mode: 0644]
malloc.c [new file with mode: 0644]
malloc.h [new file with mode: 0644]
mini.ld [new file with mode: 0644]
mini_ipc.c [new file with mode: 0644]
mini_ipc.h [new file with mode: 0644]
nandfs.c [new file with mode: 0644]
nandfs.h [new file with mode: 0644]
ppcboot.elf [new file with mode: 0755]
ppcboot.elf.map [new file with mode: 0644]
printf.c [new file with mode: 0644]
printf.h [new file with mode: 0644]
realmode.S [new file with mode: 0644]
string.c [new file with mode: 0644]
string.h [new file with mode: 0644]
sync.c [new file with mode: 0644]
time.c [new file with mode: 0644]
types.h [new file with mode: 0644]
video_low.c [new file with mode: 0644]
video_low.h [new file with mode: 0644]

diff --git a/.deps/console.d b/.deps/console.d
new file mode 100644 (file)
index 0000000..ee6e69c
--- /dev/null
@@ -0,0 +1,16 @@
+console.o: console.c bootmii_ppc.h types.h printf.h video_low.h console.h \
+ string.h malloc.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+video_low.h:
+
+console.h:
+
+string.h:
+
+malloc.h:
diff --git a/.deps/diskio.d b/.deps/diskio.d
new file mode 100644 (file)
index 0000000..91a9b0d
--- /dev/null
@@ -0,0 +1,16 @@
+diskio.o: diskio.c bootmii_ppc.h types.h printf.h ipc.h mini_ipc.h \
+ diskio.h string.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+ipc.h:
+
+mini_ipc.h:
+
+diskio.h:
+
+string.h:
diff --git a/.deps/exception.d b/.deps/exception.d
new file mode 100644 (file)
index 0000000..10580b9
--- /dev/null
@@ -0,0 +1,9 @@
+exception.o: exception.c bootmii_ppc.h types.h printf.h string.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+string.h:
diff --git a/.deps/fat.d b/.deps/fat.d
new file mode 100644 (file)
index 0000000..3e0d498
--- /dev/null
@@ -0,0 +1,9 @@
+fat.o: fat.c fat.h ff.h types.h diskio.h
+
+fat.h:
+
+ff.h:
+
+types.h:
+
+diskio.h:
diff --git a/.deps/ff.d b/.deps/ff.d
new file mode 100644 (file)
index 0000000..e1a6e7d
--- /dev/null
@@ -0,0 +1,9 @@
+ff.o: ff.c ff.h types.h diskio.h string.h
+
+ff.h:
+
+types.h:
+
+diskio.h:
+
+string.h:
diff --git a/.deps/font.d b/.deps/font.d
new file mode 100644 (file)
index 0000000..05386da
--- /dev/null
@@ -0,0 +1 @@
+font.o: font.c
diff --git a/.deps/gecko.d b/.deps/gecko.d
new file mode 100644 (file)
index 0000000..a4ffc09
--- /dev/null
@@ -0,0 +1,7 @@
+gecko.o: gecko.c bootmii_ppc.h types.h printf.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
diff --git a/.deps/input.d b/.deps/input.d
new file mode 100644 (file)
index 0000000..106fd9d
--- /dev/null
@@ -0,0 +1,11 @@
+input.o: input.c bootmii_ppc.h types.h printf.h input.h string.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+input.h:
+
+string.h:
diff --git a/.deps/ipc.d b/.deps/ipc.d
new file mode 100644 (file)
index 0000000..7b04dfe
--- /dev/null
@@ -0,0 +1,11 @@
+ipc.o: ipc.c bootmii_ppc.h types.h printf.h ipc.h string.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+ipc.h:
+
+string.h:
diff --git a/.deps/main.d b/.deps/main.d
new file mode 100644 (file)
index 0000000..db1b5bb
--- /dev/null
@@ -0,0 +1,30 @@
+main.o: main.c bootmii_ppc.h types.h printf.h string.h ipc.h mini_ipc.h \
+ nandfs.h fat.h ff.h diskio.h malloc.h video_low.h input.h console.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+string.h:
+
+ipc.h:
+
+mini_ipc.h:
+
+nandfs.h:
+
+fat.h:
+
+ff.h:
+
+diskio.h:
+
+malloc.h:
+
+video_low.h:
+
+input.h:
+
+console.h:
diff --git a/.deps/malloc.d b/.deps/malloc.d
new file mode 100644 (file)
index 0000000..fc65e56
--- /dev/null
@@ -0,0 +1,9 @@
+malloc.o: malloc.c bootmii_ppc.h types.h printf.h string.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+string.h:
diff --git a/.deps/mini_ipc.d b/.deps/mini_ipc.d
new file mode 100644 (file)
index 0000000..eec10fa
--- /dev/null
@@ -0,0 +1,14 @@
+mini_ipc.o: mini_ipc.c bootmii_ppc.h types.h printf.h ipc.h mini_ipc.h \
+ string.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+ipc.h:
+
+mini_ipc.h:
+
+string.h:
diff --git a/.deps/nandfs.d b/.deps/nandfs.d
new file mode 100644 (file)
index 0000000..872043a
--- /dev/null
@@ -0,0 +1,16 @@
+nandfs.o: nandfs.c bootmii_ppc.h types.h printf.h ipc.h mini_ipc.h \
+ nandfs.h string.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+ipc.h:
+
+mini_ipc.h:
+
+nandfs.h:
+
+string.h:
diff --git a/.deps/printf.d b/.deps/printf.d
new file mode 100644 (file)
index 0000000..f233a70
--- /dev/null
@@ -0,0 +1,3 @@
+printf.o: printf.c types.h
+
+types.h:
diff --git a/.deps/string.d b/.deps/string.d
new file mode 100644 (file)
index 0000000..578b675
--- /dev/null
@@ -0,0 +1,5 @@
+string.o: string.c string.h types.h
+
+string.h:
+
+types.h:
diff --git a/.deps/sync.d b/.deps/sync.d
new file mode 100644 (file)
index 0000000..c2a75f6
--- /dev/null
@@ -0,0 +1,7 @@
+sync.o: sync.c bootmii_ppc.h types.h printf.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
diff --git a/.deps/time.d b/.deps/time.d
new file mode 100644 (file)
index 0000000..b78f1ea
--- /dev/null
@@ -0,0 +1,7 @@
+time.o: time.c bootmii_ppc.h types.h printf.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
diff --git a/.deps/video_low.d b/.deps/video_low.d
new file mode 100644 (file)
index 0000000..f1242fd
--- /dev/null
@@ -0,0 +1,12 @@
+video_low.o: video_low.c bootmii_ppc.h types.h printf.h video_low.h \
+ string.h
+
+bootmii_ppc.h:
+
+types.h:
+
+printf.h:
+
+video_low.h:
+
+string.h:
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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) 19yy  <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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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) 19yy 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.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..87ff1fc
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+include broadway.mk
+
+DEFINES = -DLACKS_SYS_TYPES_H -DLACKS_ERRNO_H -DLACKS_STDLIB_H -DLACKS_STRING_H -DLACKS_STRINGS_H -DLACKS_UNISTD_H
+LDSCRIPT = mini.ld
+LIBS = -lgcc
+
+TARGET = ppcboot.elf
+
+OBJS = realmode.o crt0.o main.o string.o sync.o time.o printf.o input.o \
+       exception.o exception_2200.o malloc.o gecko.o video_low.o \
+       ipc.o mini_ipc.o nandfs.o ff.o diskio.o fat.o font.o console.o
+
+include common.mk
+
+upload: $(TARGET)
+       @$(WIIDEV)/bin/bootmii -p $<
+
+.PHONY: upload
+
diff --git a/bootmii_ppc.h b/bootmii_ppc.h
new file mode 100644 (file)
index 0000000..49de6b5
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2008             Segher Boessenkool <segher@kernel.crashing.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#ifndef __PPC_H__
+#define __PPC_H__
+
+#include "types.h"
+#include "printf.h"
+
+#define OK 0
+#define EFAIL 1
+
+#define MEM2_BSS __attribute__ ((section (".bss.mem2")))
+#define MEM2_DATA __attribute__ ((section (".data.mem2")))
+#define MEM2_RODATA __attribute__ ((section (".rodata.mem2")))
+#define ALIGNED(x) __attribute__((aligned(x)))
+
+#define STACK_ALIGN(type, name, cnt, alignment)         \
+       u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + \
+       (((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - \
+       ((sizeof(type)*(cnt))%(alignment))) : 0))]; \
+       type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (( \
+       (u32)(_al__##name))&((alignment)-1))))
+
+// Basic I/O.
+
+static inline u32 read32(u32 addr)
+{
+       u32 x;
+
+       asm volatile("lwz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr));
+
+       return x;
+}
+
+static inline void write32(u32 addr, u32 x)
+{
+       asm("stw %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr));
+}
+
+static inline void mask32(u32 addr, u32 clear, u32 set)
+{
+       write32(addr, (read32(addr)&(~clear)) | set);
+}
+
+static inline u16 read16(u32 addr)
+{
+       u16 x;
+
+       asm volatile("lhz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr));
+
+       return x;
+}
+
+static inline void write16(u32 addr, u16 x)
+{
+       asm("sth %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr));
+}
+
+
+// Address mapping.
+
+static inline u32 virt_to_phys(const void *p)
+{
+       return (u32)p & 0x7fffffff;
+}
+
+static inline void *phys_to_virt(u32 x)
+{
+       return (void *)(x | 0x80000000);
+}
+
+
+// Cache synchronisation.
+
+void sync_before_read(void *p, u32 len);
+void sync_after_write(const void *p, u32 len);
+void sync_before_exec(const void *p, u32 len);
+
+
+// Time.
+
+void udelay(u32 us);
+u64 mftb(void);
+
+
+// Special purpose registers.
+
+#define mtspr(n, x) do { asm("mtspr %1,%0" : : "r"(x), "i"(n)); } while (0)
+#define mfspr(n) ({ \
+       u32 x; asm volatile("mfspr %0,%1" : "=r"(x) : "i"(n)); x; \
+})
+
+
+// Exceptions.
+
+void exception_init(void);
+
+
+// Console.
+
+void gecko_init(void);
+int printf(const char *fmt, ...);
+
+
+// Debug: blink the tray led.
+
+static inline void blink(void)
+{
+       write32(0x0d8000c0, read32(0x0d8000c0) ^ 0x20);
+}
+
+#endif
+
diff --git a/broadway.mk b/broadway.mk
new file mode 100644 (file)
index 0000000..b6100d4
--- /dev/null
@@ -0,0 +1,12 @@
+ifeq ($(strip $(WIIDEV)),)
+$(error "Set WIIDEV in your environment.")
+endif
+
+PREFIX = $(WIIDEV)/bin/powerpc-elf-
+
+CFLAGS = -mcpu=750 -mpaired -m32 -mhard-float -mno-eabi -mno-sdata
+CFLAGS += -ffreestanding -ffunction-sections
+CFLAGS += -Wall -Wextra -Os -pipe
+ASFLAGS =
+LDFLAGS = -mcpu=750 -m32 -n -nostartfiles -nodefaultlibs -Wl,-gc-sections
+
diff --git a/common.mk b/common.mk
new file mode 100644 (file)
index 0000000..cd9fdcb
--- /dev/null
+++ b/common.mk
@@ -0,0 +1,60 @@
+AR = $(PREFIX)ar
+AS = $(PREFIX)as
+CC = $(PREFIX)gcc
+CXX = $(PREFIX)g++
+LD = $(PREFIX)ld
+OBJCOPY = $(PREFIX)objcopy
+RANLIB = $(PREFIX)ranlib
+STRIP = $(PREFIX)strip
+
+BIN2S = $(DEVKITPPC)/bin/bin2s
+
+ifeq ($(NOMAPFILE),)
+LDFLAGS += -Wl,-Map,$(TARGET).map
+endif
+
+ifneq ($(LDSCRIPT),)
+LDFLAGS += -Wl,-T$(LDSCRIPT)
+endif
+
+DEPDIR = .deps
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+       @echo "  LINK      $@"
+       @$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
+
+ifneq ($(LDSCRIPT),)
+$(TARGET): $(LDSCRIPT)
+endif
+
+%.o: %.c
+       @echo "  COMPILE   $<"
+       @mkdir -p $(DEPDIR)
+       @$(CC) $(CFLAGS) $(DEFINES) -Wp,-MMD,$(DEPDIR)/$(*F).d,-MQ,"$@",-MP -c $< -o $@
+
+%.o: %.s
+       @echo "  ASSEMBLE  $<"
+       @$(CC) $(CFLAGS) $(DEFINES) $(ASFLAGS) -c $< -o $@
+
+%.o: %.S
+       @echo "  ASSEMBLE  $<"
+       @$(CC) $(CFLAGS) $(DEFINES) $(ASFLAGS) -c $< -o $@
+
+clean:
+       rm -rf $(DEPDIR)
+       rm -f $(TARGET) $(TARGET).map $(OBJS)
+
+define bin2o
+       @echo "  BIN2S     $(notdir $<)"
+       @$(BIN2S) -a 32 $< | $(AS) -o $(@)
+       @echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h
+       @echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h
+       @echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h
+endef
+
+-include $(DEPDIR)/*
+
+.PHONY: clean
+
diff --git a/console.c b/console.c
new file mode 100644 (file)
index 0000000..508e4b7
--- /dev/null
+++ b/console.c
@@ -0,0 +1,252 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2009             bLAStY <blasty@bootmii.org>
+Copyright (C) 2009             John Kelley <wiidev@kelley.ca>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+
+#include "video_low.h"
+#include "console.h"
+#include "string.h"
+#include "printf.h"
+#include "malloc.h"
+
+#include <stdarg.h>
+
+typedef struct {
+        u32 x, y;
+        u32 width, height;
+        u8 *raw_data;
+        u32 *yuv_data;  
+        u32 active;
+        u8 has_alpha;
+} gfx_rect;
+
+#define CONSOLE_WIDTH 500
+#define CONSOLE_LINES 10
+#define CONSOLE_Y 100
+
+#define CONSOLE_X  58
+#define CONSOLE_CHAR_HEIGHT 16
+#define CONSOLE_ROW_HEIGHT (CONSOLE_CHAR_HEIGHT + 1) 
+
+static u32 *xfb = NULL;
+static int y_add = 0;
+
+u32 *font_yuv[255];
+
+u32 *get_xfb(void) {
+       return xfb;
+}
+
+static void memcpy32(u32 *dst, u32 *src, u32 count) {
+       while(count--) {
+               *dst = *src;
+               sync_after_write((const void *)dst, 4);
+
+               dst++;
+               src++;
+       }
+}
+
+static void memset32(u32 *dst, u32 setval, u32 count) {
+       while(count--) {
+               *dst = setval;
+               sync_after_write((const void *)dst, 4);
+
+               dst++;
+       }
+}
+
+u32 pal_idx(int i, u8 *pal, u8 *gfx) { 
+       u32 pidx = gfx[i];
+       pidx *= 3;
+
+       return (pal[pidx+0] << 16) | (pal[pidx+1] << 8) | (pal[pidx+2]);
+}
+
+int make_yuv(u8 r1, u8 g1, u8 b1, u8 r2, u8 g2, u8 b2) {
+  int y1, cb1, cr1, y2, cb2, cr2, cb, cr;
+  y1 = (299 * r1 + 587 * g1 + 114 * b1) / 1000;
+  cb1 = (-16874 * r1 - 33126 * g1 + 50000 * b1 + 12800000) / 100000;
+  cr1 = (50000 * r1 - 41869 * g1 - 8131 * b1 + 12800000) / 100000;
+  y2 = (299 * r2 + 587 * g2 + 114 * b2) / 1000;
+  cb2 = (-16874 * r2 - 33126 * g2 + 50000 * b2 + 12800000) / 100000;
+  cr2 = (50000 * r2 - 41869 * g2 - 8131 * b2 + 12800000) / 100000;
+  cb = (cb1 + cb2) >> 1;
+  cr = (cr1 + cr2) >> 1;
+  return ((y1 << 24) | (cb << 16) | (y2 << 8) | cr);
+}
+
+void fill_rect(int x, int y, int w, int h, u8 r, u8 g, u8 b) {
+       u32 *fb = xfb;
+       u32 col = make_yuv(r,g,b, r,g,b);
+
+       fb += ((y + y_add) * (640 >> 1));
+       fb += (x >> 1);
+
+       for(y = 0; y < h; y++) {
+               memset32(fb, col, w >> 1);
+               fb += (640 >> 1);
+       }
+}
+
+void gfx_draw_rect(gfx_rect *n) {
+        u32 y;
+        gfx_rect *d_rect;
+        u32 *fb = xfb;
+
+        d_rect = n;
+
+        fb += ((d_rect->y + y_add) * (640 >> 1));
+        fb += (d_rect->x >> 1);
+
+        for(y = 0; y < d_rect->height; y++) {
+                memcpy32(fb, d_rect->yuv_data + ((d_rect->width >> 1) * y), d_rect->width >> 1);
+                fb += (640 >> 1);
+        }
+}
+
+void scroll(void) {
+       unsigned int y;
+       u32 *fb = xfb;
+
+       fb += ((CONSOLE_Y+y_add) * (640 >> 1));
+       fb += (CONSOLE_X >> 1);
+
+       fb += (CONSOLE_ROW_HEIGHT * (640 >> 1));
+
+       for (y = 0; y < CONSOLE_LINES*CONSOLE_ROW_HEIGHT; y++) {
+               memcpy32(fb - (CONSOLE_ROW_HEIGHT * 320), fb, CONSOLE_WIDTH >> 1);
+               fb += 320;
+       }
+       
+       fill_rect(CONSOLE_X, CONSOLE_Y+(CONSOLE_LINES-1)*CONSOLE_ROW_HEIGHT,
+               CONSOLE_WIDTH, CONSOLE_ROW_HEIGHT, 0, 0, 0);
+}
+
+void print_str_noscroll(int x, int y, char *str) {
+       unsigned int i;
+       gfx_rect d_char;
+
+       d_char.x = x;
+
+       for (i = 0; i < strlen(str); i++) {
+               d_char.width  = 8;
+               d_char.height = CONSOLE_CHAR_HEIGHT;
+               d_char.y = y;
+
+               if (str[i] == '\n') {
+                       y += CONSOLE_CHAR_HEIGHT;
+                       d_char.x = x;
+                       continue;
+               }       
+
+               d_char.yuv_data = font_yuv[ (int)str[i] ];
+               gfx_draw_rect(&d_char);
+               d_char.x += 10;
+       }
+}
+
+void print_str(const char *str, size_t len) {
+       unsigned int i;
+       gfx_rect d_char;
+
+       scroll();
+       d_char.width  = 8;
+       d_char.height = CONSOLE_CHAR_HEIGHT;
+       d_char.y = CONSOLE_Y + ((CONSOLE_LINES - 1) * CONSOLE_ROW_HEIGHT);
+
+       for (i = 0; i < len; i++) {
+               d_char.x = CONSOLE_X + (i * 10);
+               d_char.yuv_data = font_yuv[(int) str[i]];
+               gfx_draw_rect(&d_char);
+       }
+}
+
+int gfx_printf(const char *fmt, ...)
+{
+       va_list args;
+       char buffer[40];
+       int i;
+
+       memset(buffer, 0, sizeof buffer);
+       va_start(args, fmt);
+       i = vsnprintf(buffer, sizeof(buffer), fmt, args);
+       va_end(args);
+
+       if (i > 0) {
+               print_str(buffer, i);
+               printf("%s\n", buffer);
+       } else
+               scroll();
+
+       return i;
+}
+
+void font_to_yuv(void) {
+       int i, x, y;
+       u8 lr,lg,lb, rr,rg,rb;
+
+       for (i = 0; i < 255; i++) {
+               font_yuv[i] = (u32*)malloc(8*CONSOLE_CHAR_HEIGHT*2);
+
+               for (y = 0; y < CONSOLE_CHAR_HEIGHT; y++) {
+                       for (x = 0; x < 8; x+=2) {
+                               if (((console_font_8x16[(i*CONSOLE_CHAR_HEIGHT)+y] >> (7-x)) & 0x01) == 1) {
+                                       lr = 255; lg = 255; lb = 255;
+                               } else {
+                                       lr = 0; lg = 0; lb = 0;
+                               }
+
+                               if (((console_font_8x16[(i*CONSOLE_CHAR_HEIGHT)+y] >> (7-(x+1))) & 0x01) == 1) {
+                                       rr = 255; rg = 255; rb = 255;
+                               } else {
+                                       rr = 0; rg = 0; rb = 0;
+                               }
+
+                               font_yuv[i][(y<<2)+(x>>1)] = make_yuv(lr, lg, lb, rr, rg, rb);
+                       }
+               }
+       }
+}
+
+void init_fb(int vmode) {
+       int i;
+       u32 *fb;
+       u32 fill_col = make_yuv(0,0,0, 0,0,0);
+
+       font_to_yuv();
+
+       switch(vmode) {
+       case VIDEO_640X480_NTSCi_YUV16:
+       case VIDEO_640X480_PAL60_YUV16:
+       case VIDEO_640X480_NTSCp_YUV16:
+               y_add = 0;
+               break;
+
+       case VIDEO_640X480_PAL50_YUV16:
+               y_add = 48;
+               break;
+       }
+
+       xfb = memalign(32, 640 * (480 + (y_add*2)) * 2);
+
+       fb  = xfb;
+       for (i = 0; i < (480 + (y_add*2)) * 2 * (640 >> 1); i++) {
+               *fb = fill_col;
+               sync_after_write(fb, 4);
+               fb++;
+       }
+}
+
diff --git a/console.h b/console.h
new file mode 100644 (file)
index 0000000..cc7133e
--- /dev/null
+++ b/console.h
@@ -0,0 +1,24 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2009             bLAStY <blasty@bootmii.org>
+Copyright (C) 2009             John Kelley <wiidev@kelley.ca>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#ifndef __CONSOLE_H__
+#define __CONSOLE_H__
+
+void init_fb(int vmode);
+void print_str(const char *str, size_t len);
+void print_str_noscroll(int x, int y, char *str);
+int console_printf(const char *fmt, ...);
+u32 *get_xfb(void);
+
+extern unsigned char console_font_8x16[];
+
+#endif
+
diff --git a/crt0.S b/crt0.S
new file mode 100644 (file)
index 0000000..dc0fc33
--- /dev/null
+++ b/crt0.S
@@ -0,0 +1,289 @@
+# crt0.s file for the GameCube V1.0 by Costis (costis@gbaemu.com)!
+
+
+       .globl _start
+_start:
+
+       # Clear all GPRs
+       .irp i, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
+       li \i,0
+       .endr
+
+       lis 1,_stack_bot@h ; ori 1,1,_stack_bot@l
+       stwu 0,-64(1)
+
+       lis      2,0x8000
+       stw      1,0x34(2) # write sp
+#      lis      13,_SDA_BASE_@h
+#      ori      13,13,_SDA_BASE_@l # Set the Small Data (Read\Write) base register.    
+
+       bl       InitHardware # Initialize the GameCube Hardware (Floating Point Registers, Caches, etc.) 
+       bl       SystemInit # Initialize more cache aspects, clear a few SPR's, and disable interrupts.
+       
+       # clear BSS
+       lis 3, __bss_start@h
+       ori 3, 3, __bss_start@l
+       li 4, 0
+       lis 5, __bss_end@h
+       ori 5, 5, __bss_end@l
+       sub 5, 5, 3
+       
+       bl memset
+       
+       bl       main # Branch to the user code!
+       b .     # If the main function returns, then just loop endlessly.
+
+InitHardware:
+       mflr     31 # Store the link register in r31
+       
+       bl               PSInit         # Initialize Paired Singles
+       bl       FPRInit        # Initialize the FPR's
+       bl       CacheInit      # Initialize the system caches
+
+       mtlr     31 # Retreive the link register from r31
+       blr
+
+PSInit:
+       mfspr    3, 920 # (HID2)
+       oris     3, 3, 0xA000
+       mtspr    920, 3 # (HID2)
+
+       # Set the Instruction Cache invalidation bit in HID0
+       mfspr    3,1008
+       ori      3,3,0x0800
+       mtspr    1008,3
+
+       sync
+
+       # Clear various Special Purpose Registers
+       li       3,0
+       mtspr    912,3
+       mtspr    913,3
+       mtspr    914,3
+       mtspr    915,3
+       mtspr    916,3
+       mtspr    917,3
+       mtspr    918,3
+       mtspr    919,3
+
+       # Return 
+       blr
+
+FPRInit:
+       # Enable the Floating Point Registers
+       mfmsr    3
+       ori      3,3,0x2000
+       mtmsr    3
+       
+       # Clear all of the FPR's to 0
+       lis              3, zfloat@h
+       ori              3, 3, zfloat@l
+       lfd              0, 0(3)
+       fmr      1,0
+       fmr      2,0
+       fmr      3,0
+       fmr      4,0
+       fmr      5,0
+       fmr      6,0
+       fmr      7,0
+       fmr      8,0
+       fmr      9,0
+       fmr      10,0
+       fmr      11,0
+       fmr      12,0
+       fmr      13,0
+       fmr      14,0
+       fmr      15,0
+       fmr      16,0
+       fmr      17,0
+       fmr      18,0
+       fmr      19,0
+       fmr      20,0
+       fmr      21,0
+       fmr      22,0
+       fmr      23,0
+       fmr      24,0
+       fmr      25,0
+       fmr      26,0
+       fmr      27,0
+       fmr      28,0
+       fmr      29,0
+       fmr      30,0
+       fmr      31,0
+       mtfsf    255,0
+
+       # Return
+       blr
+
+CacheInit:
+       mflr     0
+       stw      0, 4(1)
+       stwu     1, -16(1)
+       stw      31, 12(1)
+       stw      30, 8(1)
+
+       mfspr    3,1008 # (HID0)
+       rlwinm   0, 3, 0, 16, 16
+       cmplwi   0, 0x0000 # Check if the Instruction Cache has been enabled or not.
+       bne      ICEnabled
+
+       # If not, then enable it.
+       isync
+       mfspr    3, 1008
+       ori      3, 3, 0x8000
+       mtspr    1008, 3
+
+ICEnabled:
+       mfspr    3, 1008 # bl       PPCMfhid0
+       rlwinm   0, 3, 0, 17, 17
+       cmplwi   0, 0x0000 # Check if the Data Cache has been enabled or not.
+       bne      DCEnabled
+       
+       # If not, then enable it.
+       sync
+       mfspr    3, 1008
+       ori      3, 3, 0x4000
+       mtspr    1008, 3
+       
+DCEnabled:
+       
+       mfspr    3, 1017 # (L2CR)
+       clrrwi   0, 3, 31 # Clear all of the bits except 31
+       cmplwi   0, 0x0000
+       bne      L2GISkip # Skip the L2 Global Cache Invalidation process if it has already been done befor.
+
+       # Store the current state of the MSR in r30
+       mfmsr    3
+       mr       30,3
+       
+       sync
+       
+       # Enable Instruction and Data Address Translation
+       li       3, 48
+       mtmsr    3
+
+       sync
+       sync
+
+       # Disable the L2 Global Cache.
+       mfspr    3, 1017 # (L2CR
+       clrlwi   3, 3, 1
+       mtspr    1017, 3 # (L2CR)
+       sync
+
+       # Invalidate the L2 Global Cache.
+       bl       L2GlobalInvalidate
+
+       # Restore the previous state of the MSR from r30
+       mr       3, 30
+       mtmsr    3
+
+       # Enable the L2 Global Cache and disable the L2 Data Only bit and the L2 Global Invalidate Bit.
+       mfspr    3, 1017 # (L2CR)
+       oris     0, 3, 0x8000
+       rlwinm   3, 0, 0, 11, 9
+       mtspr    1017, 3 # (L2CR)
+
+
+L2GISkip:
+       # Restore the non-volatile registers to their previous values and return.
+       lwz      0, 20(1)
+       lwz      31, 12(1)
+       lwz      30, 8(1)
+       addi     1, 1, 16
+       mtlr     0
+       blr
+
+L2GlobalInvalidate:
+       mflr     0
+       stw      0, 4(1)
+       stwu     1, -16(1)
+       stw      31, 12(1)
+       sync
+
+       # Disable the L2 Cache.
+       mfspr    3, 1017  # bl       PPCMf1017
+       clrlwi   3, 3, 1
+       mtspr    1017, 3 # bl       PPCMt1017
+
+       sync
+
+       # Initiate the L2 Cache Global Invalidation process.
+       mfspr    3, 1017  # (L2CR)
+       oris     3, 3, 0x0020
+       mtspr    1017, 3 # (L2CR)
+
+       # Wait until the L2 Cache Global Invalidation has been completed.
+L2GICheckComplete:
+       mfspr    3, 1017 # (L2CR)
+       clrlwi   0, 3, 31
+       cmplwi   0, 0x0000
+       bne      L2GICheckComplete
+       
+       # Clear the L2 Data Only bit and the L2 Global Invalidate Bit.
+       mfspr    3, 1017  # (L2CR)
+       rlwinm   3, 3, 0, 11, 9
+       mtspr    1017, 3 # (L2CR)
+
+       # Wait until the L2 Cache Global Invalidation status bit signifies that it is ready.
+L2GDICheckComplete:
+       mfspr    3, 1017  # (L2CR)
+       clrlwi   0, 3, 31
+       cmplwi   0, 0x0000
+       bne      L2GDICheckComplete
+
+       # Restore the non-volatile registers to their previous values and return.
+       lwz      0, 20(1)
+       lwz      31, 12(1)
+       addi     1, 1, 16
+       mtlr     0
+       blr
+
+SystemInit:
+       mflr    0
+       stw     0, 4(1)
+       stwu    1, -0x18(1)
+       stw     31, 0x14(1)
+       stw     30, 0x10(1)
+       stw     29, 0xC(1)
+
+       # Disable interrupts!
+       mfmsr    3
+       rlwinm   4,3,0,17,15
+       rlwinm   4,4,0,26,24
+       mtmsr    4
+
+       # Clear various SPR's
+       li       3,0
+       mtspr    952, 3
+       mtspr    956, 3
+       mtspr    953, 3
+       mtspr    954, 3
+       mtspr    957, 3
+       mtspr    958, 3
+
+       # Disable Speculative Bus Accesses to non-guarded space from both caches.
+       mfspr    3, 1008 # (HID0)
+       ori      3, 3, 0x0200
+       mtspr    1008, 3
+
+       # Set the Non-IEEE mode in the FPSCR
+       mtfsb1   29
+       
+       mfspr    3,920 # (HID2)
+       rlwinm   3, 3, 0, 2, 0
+       mtspr    920,3 # (HID2)         
+
+       # Restore the non-volatile registers to their previous values and return.
+       lwz     0, 0x1C(1)
+       lwz     31, 0x14(1)
+       lwz     30, 0x10(1)
+       lwz     29, 0xC(1)
+       addi    1, 1, 0x18
+       mtlr    0
+       blr
+
+zfloat:
+       .float  0
+       .align  4
+
diff --git a/diskio.c b/diskio.c
new file mode 100644 (file)
index 0000000..7c5a4c7
--- /dev/null
+++ b/diskio.c
@@ -0,0 +1,139 @@
+/*
+       diskio.c -- glue interface to ElmChan FAT FS driver. Part of the
+       BootMii project.
+
+Copyright (C) 2008, 2009       Haxx Enterprises <bushing@gmail.com>
+Copyright (C) 2008, 2009       Sven Peter <svenpeter@gmail.com>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+#include "ipc.h"
+#include "mini_ipc.h"
+#include "diskio.h"
+#include "string.h"
+
+static u8 *buffer[512] __attribute__((aligned(32)));
+
+DSTATUS disk_initialize (BYTE drv)
+{
+       (void) drv;
+
+       int state = sd_get_state();
+
+       switch (state) {
+       case SDMMC_NO_CARD:
+               return STA_NODISK;
+
+       case SDMMC_NEW_CARD:
+               if (sd_mount())
+                       return STA_NOINIT;
+               else
+                       return 0;
+
+       default:
+               return 0;
+       }
+}
+
+DSTATUS disk_status (BYTE drv)
+{
+       (void) drv;
+
+       int state = sd_get_state();
+
+       switch (state) {
+       case SDMMC_NO_CARD:
+               return STA_NODISK;
+
+       case SDMMC_NEW_CARD:
+               return STA_NOINIT;
+
+       default:
+               return 0;
+       }
+}
+
+DRESULT disk_read (BYTE drv, BYTE *buff, DWORD sector, u32 count)
+{
+       u32 i;
+       DRESULT res;
+       (void) drv;
+
+       if (count > 1 && ((u32) buff % 64) == 0) {
+               if (sd_read(sector, count, buff) != 0)
+                       return RES_ERROR;
+               return RES_OK;
+       }
+
+       res = RES_OK;
+       for (i = 0; i < count; i++) {
+               if (sd_read(sector + i, 1, buffer) != 0) {
+                       res = RES_ERROR;
+                       break;
+               }
+
+               memcpy(buff + i * 512, buffer, 512);
+       }
+
+       return res;
+}
+
+#if _READONLY == 0
+DRESULT disk_write (BYTE drv, const BYTE *buff,        DWORD sector, u32 count)
+{
+       u32 i;
+       DRESULT res;
+       (void) drv;
+
+       res = RES_OK;
+       if (count > 1 && ((u32) buff % 64) == 0) {
+               if (sd_write(sector, count, buff) != 0)
+                       return RES_ERROR;
+               return RES_OK;
+       }
+
+       for (i = 0; i < count; i++) {
+               memcpy(buffer, buff + i * 512, 512);
+               if (sd_write(sector + i, 1, buffer) != 0) {
+                       res = RES_ERROR;
+                       break;
+               }
+       }
+
+       return res;
+}
+#endif /* _READONLY */
+
+DRESULT disk_ioctl (BYTE drv, BYTE ctrl, void *buff)
+{
+       (void) drv;
+       u32 *buff_u32 = (u32 *) buff;
+       DRESULT res = RES_OK;
+
+       switch (ctrl) {
+       case CTRL_SYNC:
+               break;
+       case GET_SECTOR_COUNT:
+               *buff_u32 = sd_getsize();
+               break;
+       case GET_SECTOR_SIZE:
+               *buff_u32 = 512;
+               break;
+       case GET_BLOCK_SIZE:
+               *buff_u32 = 512;
+               break;
+       default:
+               res = RES_PARERR;
+               break;
+       }
+
+       return res;
+}
+
+DWORD get_fattime(void)
+{
+       return 0; // TODO
+}
diff --git a/diskio.h b/diskio.h
new file mode 100644 (file)
index 0000000..c58ee82
--- /dev/null
+++ b/diskio.h
@@ -0,0 +1,53 @@
+/*-----------------------------------------------------------------------
+/  Low level disk interface modlue include file  R0.07   (C)ChaN, 2009
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO
+
+#define _READONLY      0       /* 1: Read-only mode */
+#define _USE_IOCTL     1
+
+#include "types.h"
+
+/* Status of Disk Functions */
+typedef BYTE   DSTATUS;
+
+/* Results of Disk Functions */
+typedef enum {
+       RES_OK = 0,             /* 0: Successful */
+       RES_ERROR,              /* 1: R/W Error */
+       RES_WRPRT,              /* 2: Write Protected */
+       RES_NOTRDY,             /* 3: Not Ready */
+       RES_PARERR              /* 4: Invalid Parameter */
+} DRESULT;
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+DSTATUS disk_initialize (BYTE);
+DSTATUS disk_status (BYTE);
+DRESULT disk_read (BYTE, BYTE*, DWORD, u32);
+#if    _READONLY == 0
+DRESULT disk_write (BYTE, const BYTE*, DWORD, u32);
+#endif
+DRESULT disk_ioctl (BYTE, BYTE, void*);
+
+DWORD get_fattime(void);
+
+/* Disk Status Bits (DSTATUS) */
+
+#define STA_NOINIT             0x01    /* Drive not initialized */
+#define STA_NODISK             0x02    /* No medium in the drive */
+#define STA_PROTECT            0x04    /* Write protected */
+
+
+/* Command code for disk_ioctrl() */
+
+/* Generic command */
+#define CTRL_SYNC                      0       /* Mandatory for write functions */
+#define GET_SECTOR_COUNT       1       /* Mandatory for only f_mkfs() */
+#define GET_SECTOR_SIZE                2
+#define GET_BLOCK_SIZE         3       /* Mandatory for only f_mkfs() */
+
+#define _DISKIO
+#endif
diff --git a/exception.c b/exception.c
new file mode 100644 (file)
index 0000000..095de44
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2008             Segher Boessenkool <segher@kernel.crashing.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+
+#include "string.h"
+
+extern char exception_2200_start, exception_2200_end;
+
+void exception_handler(int exception)
+{
+       u32 *x;
+       u32 i;
+
+       printf("\nException %04x occurred!\n", exception);
+
+       x = (u32 *)0x80002000;
+
+       printf("\n R0..R7    R8..R15  R16..R23  R24..R31\n");
+       for (i = 0; i < 8; i++) {
+               printf("%08x  %08x  %08x  %08x\n", x[0], x[8], x[16], x[24]);
+               x++;
+       }
+       x = (u32 *)0x80002080;
+
+       printf("\n CR/XER    LR/CTR  SRR0/SRR1 DAR/DSISR\n");
+       for (i = 0; i < 2; i++) {
+               printf("%08x  %08x  %08x  %08x\n", x[0], x[2], x[4], x[6]);
+               x++;
+       }
+
+       // Hang.
+       for (;;)
+               ;
+}
+
+void exception_init(void)
+{
+       u32 vector;
+       u32 len_2200;
+
+       for (vector = 0x100; vector < 0x2000; vector += 0x10) {
+               u32 *insn = (u32 *)(0x80000000 + vector);
+
+               insn[0] = 0xbc002000;                   // stmw 0,0x2000(0)
+               insn[1] = 0x38600000 | (u32)vector;     // li 3,vector
+               insn[2] = 0x48002202;                   // ba 0x2200
+               insn[3] = 0;
+       }
+       sync_before_exec((void *)0x80000100, 0x1f00);
+
+       len_2200 = &exception_2200_end - &exception_2200_start;
+       memcpy((void *)0x80002200, &exception_2200_start, len_2200);
+       sync_before_exec((void *)0x80002200, len_2200);
+}
+
diff --git a/exception_2200.S b/exception_2200.S
new file mode 100644 (file)
index 0000000..9f10dab
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+       
+Copyright (C) 2008             Segher Boessenkool <segher@kernel.crashing.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+.globl exception_2200_start, exception_2200_end
+
+exception_2200_start:
+       # store all interesting regs
+       mfcr 0 ;  stw 0,0x2080(0)
+       mfxer 0 ; stw 0,0x2084(0)
+       mflr 0 ;  stw 0,0x2088(0)
+       mfctr 0 ; stw 0,0x208c(0)
+       mfsrr0 0 ;  stw 0,0x2090(0)
+       mfsrr1 0 ;  stw 0,0x2094(0)
+       mfdar 0 ;   stw 0,0x2098(0)
+       mfdsisr 0 ; stw 0,0x209c(0)
+
+       # switch on FP, DR, IR
+       mfmsr 0 ; ori 0,0,0x2030 ; mtsrr1 0
+
+       # go to C handler
+       lis 0,exception_handler@h ; ori 0,0,exception_handler@l ; mtsrr0 0
+       rfi
+exception_2200_end:
+
diff --git a/fat.c b/fat.c
new file mode 100644 (file)
index 0000000..1c1db75
--- /dev/null
+++ b/fat.c
@@ -0,0 +1,41 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2009             Andre Heider "dhewg" <dhewg@wiibrew.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "fat.h"
+
+static FATFS fatfs;
+
+u32 fat_mount(void) {
+       DSTATUS stat;
+
+       f_mount(0, NULL);
+
+       stat = disk_initialize(0);
+
+       if (stat & STA_NODISK)
+               return -1;
+
+       if (stat & STA_NOINIT)
+               return -2;
+
+       return f_mount(0, &fatfs);
+}
+
+u32 fat_umount(void) {
+       f_mount(0, NULL);
+
+       return 0;
+}
+
+u32 fat_clust2sect(u32 clust) {
+       return clust2sect(&fatfs, clust);
+}
+
+
diff --git a/fat.h b/fat.h
new file mode 100644 (file)
index 0000000..9df3500
--- /dev/null
+++ b/fat.h
@@ -0,0 +1,22 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2009             Andre Heider "dhewg" <dhewg@wiibrew.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#ifndef __FAT_H__
+#define __FAT_H__
+
+#include "ff.h"
+#include "diskio.h"
+
+u32 fat_mount(void);
+u32 fat_umount(void);
+u32 fat_clust2sect(u32 clust);
+
+#endif
+
diff --git a/ff.c b/ff.c
new file mode 100644 (file)
index 0000000..6cf4158
--- /dev/null
+++ b/ff.c
@@ -0,0 +1,2954 @@
+/*----------------------------------------------------------------------------/
+/  FatFs - FAT file system module  R0.07                     (C)ChaN, 2009
+/-----------------------------------------------------------------------------/
+/ FatFs module is an open source software to implement FAT file system to
+/ small embedded systems. This is a free software and is opened for education,
+/ research and commecial developments under license policy of following trems.
+/
+/  Copyright (C) 2009, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/-----------------------------------------------------------------------------/
+/ Feb 26,'06 R0.00  Prototype.
+/
+/ Apr 29,'06 R0.01  First stable version.
+/
+/ Jun 01,'06 R0.02  Added FAT12 support.
+/                   Removed unbuffered mode.
+/                   Fixed a problem on small (<32M) patition.
+/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
+/
+/ Sep 22,'06 R0.03  Added f_rename().
+/                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
+/ Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast.
+/                   Fixed f_mkdir() creates incorrect directory on FAT32.
+/
+/ Feb 04,'07 R0.04  Supported multiple drive system.
+/                   Changed some interfaces for multiple drive system.
+/                   Changed f_mountdrv() to f_mount().
+/                   Added f_mkfs().
+/ Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive.
+/                   Added a capability of extending file size to f_lseek().
+/                   Added minimization level 3.
+/                   Fixed an endian sensitive code in f_mkfs().
+/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
+/                   Added FSInfo support.
+/                   Fixed DBCS name can result FR_INVALID_NAME.
+/                   Fixed short seek (<= csize) collapses the file object.
+/
+/ Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
+/                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
+/                   Fixed f_mkdir() on FAT32 creates incorrect directory.
+/ Feb 03,'08 R0.05a Added f_truncate() and f_utime().
+/                   Fixed off by one error at FAT sub-type determination.
+/                   Fixed btr in f_read() can be mistruncated.
+/                   Fixed cached sector is not flushed when create and close
+/                   without write.
+/
+/ Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
+/                   Improved performance of f_lseek() on moving to the same
+/                   or following cluster.
+/
+/ Apr 01,'09 R0.07  Merged Tiny-FatFs as a buffer configuration option.
+/                   Added long file name support.
+/                   Added multiple code page support.
+/                   Added re-entrancy for multitask operation.
+/                   Added auto cluster size selection to f_mkfs().
+/                   Added rewind option to f_readdir().
+/                   Changed result code of critical errors.
+/                   Renamed string functions to avoid name collision.
+/---------------------------------------------------------------------------*/
+
+#include "ff.h"                        /* FatFs configurations and declarations */
+#include "diskio.h"            /* Declarations of low level disk I/O functions */
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if _EXCLUDE_LIB
+static
+void MemCpy (void* dst, const void* src, int cnt) {
+       char *d = (char*)dst;
+       const char *s = (const char *)src;
+       while (cnt--) *d++ = *s++;
+}
+
+static
+void MemSet (void* dst, int val, int cnt) {
+       char *d = (char*)dst;
+       while (cnt--) *d++ = val;
+}
+
+static
+int MemCmp (const void* dst, const void* src, int cnt) {
+       const char *d = (const char *)dst, *s = (const char *)src;
+       int r = 0;
+       while (cnt-- && !(r = *d++ - *s++));
+       return r;
+}
+
+static
+char *StrChr (const char* str, int chr) {
+       while (*str && *str != chr) str++;
+       return (*str == chr) ? (char*)str : 0;
+}
+
+#else
+#include "string.h"
+#define MemCpy(x,y,z)  memcpy(x,y,z)
+#define MemCmp(x,y,z)  memcmp(x,y,z)
+#define MemSet(x,y,z)  memset(x,y,z)
+#define StrChr(x,y)            strchr(x,y)
+
+#endif
+
+#ifndef NULL
+#define        NULL    0
+#endif
+
+
+#if _FS_REENTRANT
+#if _USE_LFN == 1
+#error Static LFN work area must not be used in re-entrant configuration.
+#endif
+#define        ENTER_FF(fs)            { if (!lock_fs(fs)) return FR_TIMEOUT; }
+#define        LEAVE_FF(fs, res)       { unlock_fs(fs, res); return res; }
+
+#else
+#define        ENTER_FF(fs)
+#define LEAVE_FF(fs, res)      return res
+
+#endif
+
+
+#define        ABORT(fs, res)          { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Work Area
+
+---------------------------------------------------------------------------*/
+
+static
+FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */
+static
+WORD Fsid;                             /* File system mount ID */
+
+
+#if _USE_LFN == 1      /* LFN with static LFN working buffer */
+static
+WORD LfnBuf[_MAX_LFN + 1];
+#define        NAMEBUF(sp,lp)  BYTE sp[12]; WCHAR *lp = LfnBuf
+#define INITBUF(dj,sp,lp)      dj.fn = sp; dj.lfn = lp
+
+#elif _USE_LFN > 1     /* LFN with dynamic LFN working buffer */
+#define        NAMEBUF(sp,lp)  BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf
+#define INITBUF(dj,sp,lp)      dj.fn = sp; dj.lfn = lp
+
+#else                          /* No LFN */
+#define        NAMEBUF(sp,lp)  BYTE sp[12]
+#define INITBUF(dj,sp,lp)      dj.fn = sp
+
+#endif
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the fs object (Platform dependent)    */
+/*-----------------------------------------------------------------------*/
+#if _FS_REENTRANT
+static
+BOOL lock_fs (
+       FATFS *fs               /* File system object */
+)
+{
+       return (WaitForSingleObject(fs->h_mutex, _TIMEOUT) == WAIT_OBJECT_0) ? TRUE : FALSE;
+}
+
+
+static
+void unlock_fs (
+       FATFS *fs,              /* File system object */
+       FRESULT res             /* Result code to be returned */
+)
+{
+       if (res != FR_NOT_ENABLED &&
+               res != FR_INVALID_DRIVE &&
+               ree != FR_INVALID_OBJECT &&
+               res != FR_TIMEOUT) {
+               ReleaseMutex(fs->h_mutex);
+       }
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change window offset                                                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT move_window (
+       FATFS *fs,              /* File system object */
+       DWORD sector    /* Sector number to make apperance in the fs->win[] */
+)                                      /* Move to zero only writes back dirty window */
+{
+       DWORD wsect;
+
+
+       wsect = fs->winsect;
+       if (wsect != sector) {  /* Changed current window */
+#if !_FS_READONLY
+               if (fs->wflag) {        /* Write back dirty window if needed */
+                       if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK)
+                               return FR_DISK_ERR;
+                       fs->wflag = 0;
+                       if (wsect < (fs->fatbase + fs->sects_fat)) {    /* In FAT area */
+                               BYTE nf;
+                               for (nf = fs->n_fats; nf >= 2; nf--) {  /* Refrect the change to FAT copy */
+                                       wsect += fs->sects_fat;
+                                       disk_write(fs->drive, fs->win, wsect, 1);
+                               }
+                       }
+               }
+#endif
+               if (sector) {
+                       if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK)
+                               return FR_DISK_ERR;
+                       fs->winsect = sector;
+               }
+       }
+
+       return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Clean-up cached data                                                  */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */
+       FATFS *fs       /* File system object */
+)
+{
+       FRESULT res;
+
+
+       res = move_window(fs, 0);
+       if (res == FR_OK) {
+               /* Update FSInfo sector if needed */
+               if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
+                       fs->winsect = 0;
+                       MemSet(fs->win, 0, 512);
+                       ST_WORD(fs->win+BS_55AA, 0xAA55);
+                       ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
+                       ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
+                       ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
+                       ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
+                       disk_write(fs->drive, fs->win, fs->fsi_sector, 1);
+                       fs->fsi_flag = 0;
+               }
+               /* Make sure that no pending write process in the physical drive */
+               if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK)
+                       res = FR_DISK_ERR;
+       }
+
+       return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get a cluster status                                                  */
+/*-----------------------------------------------------------------------*/
+
+static
+DWORD get_cluster (    /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */
+       FATFS *fs,              /* File system object */
+       DWORD clst              /* Cluster# to get the link information */
+)
+{
+       WORD wc, bc;
+       DWORD fsect;
+
+
+       if (clst < 2 || clst >= fs->max_clust)  /* Check cluster address range */
+               return 1;
+
+       fsect = fs->fatbase;
+       switch (fs->fs_type) {
+       case FS_FAT12 :
+               bc = (WORD)clst * 3 / 2;
+               if (move_window(fs, fsect + (bc / SS(fs)))) break;
+               wc = fs->win[bc & (SS(fs) - 1)]; bc++;
+               if (move_window(fs, fsect + (bc / SS(fs)))) break;
+               wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8;
+               return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
+
+       case FS_FAT16 :
+               if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break;
+               return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]);
+
+       case FS_FAT32 :
+               if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break;
+               return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF;
+       }
+
+       return 0xFFFFFFFF;      /* An error occured at the disk I/O layer */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change a cluster status                                               */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT put_cluster (
+       FATFS *fs,              /* File system object */
+       DWORD clst,             /* Cluster# to be changed (must be 2 to fs->max_clust-1) */
+       DWORD val               /* New value to mark the cluster */
+)
+{
+       WORD bc;
+       BYTE *p;
+       DWORD fsect;
+       FRESULT res;
+
+
+       if (clst < 2 || clst >= fs->max_clust) {        /* Check cluster address range */
+               res = FR_INT_ERR;
+
+       } else {
+               fsect = fs->fatbase;
+               switch (fs->fs_type) {
+               case FS_FAT12 :
+                       bc = (WORD)clst * 3 / 2;
+                       res = move_window(fs, fsect + (bc / SS(fs)));
+                       if (res != FR_OK) break;
+                       p = &fs->win[bc & (SS(fs) - 1)];
+                       *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
+                       bc++;
+                       fs->wflag = 1;
+                       res = move_window(fs, fsect + (bc / SS(fs)));
+                       if (res != FR_OK) break;
+                       p = &fs->win[bc & (SS(fs) - 1)];
+                       *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
+                       break;
+
+               case FS_FAT16 :
+                       res = move_window(fs, fsect + (clst / (SS(fs) / 2)));
+                       if (res != FR_OK) break;
+                       ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val);
+                       break;
+
+               case FS_FAT32 :
+                       res = move_window(fs, fsect + (clst / (SS(fs) / 4)));
+                       if (res != FR_OK) break;
+                       ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val);
+                       break;
+
+               default :
+                       res = FR_INT_ERR;
+               }
+               fs->wflag = 1;
+       }
+
+       return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Remove a cluster chain                                                */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT remove_chain (
+       FATFS *fs,                      /* File system object */
+       DWORD clst                      /* Cluster# to remove chain from */
+)
+{
+       FRESULT res;
+       DWORD nxt;
+
+
+       if (clst < 2 || clst >= fs->max_clust) {        /* Check cluster address range */
+               res = FR_INT_ERR;
+
+       } else {
+               res = FR_OK;
+               while (clst < fs->max_clust) {                  /* Not a last link? */
+                       nxt = get_cluster(fs, clst);            /* Get cluster status */
+                       if (nxt == 0) break;                            /* Empty cluster? */
+                       if (nxt == 1) { res = FR_INT_ERR; break; }      /* Internal error? */
+                       if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }    /* Disk error? */
+                       res = put_cluster(fs, clst, 0);         /* Mark the cluster "empty" */
+                       if (res != FR_OK) break;
+                       if (fs->free_clust != 0xFFFFFFFF) {     /* Update FSInfo */
+                               fs->free_clust++;
+                               fs->fsi_flag = 1;
+                       }
+                       clst = nxt;     /* Next cluster */
+               }
+       }
+
+       return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Stretch or create a cluster chain                                     */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+DWORD create_chain (   /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+       FATFS *fs,                      /* File system object */
+       DWORD clst                      /* Cluster# to stretch. 0 means create a new chain. */
+)
+{
+       DWORD cs, ncl, scl, mcl;
+
+
+       mcl = fs->max_clust;
+       if (clst == 0) {                /* Create new chain */
+               scl = fs->last_clust;                   /* Get suggested start point */
+               if (scl == 0 || scl >= mcl) scl = 1;
+       }
+       else {                                  /* Stretch existing chain */
+               cs = get_cluster(fs, clst);             /* Check the cluster status */
+               if (cs < 2) return 1;                   /* It is an invalid cluster */
+               if (cs < mcl) return cs;                /* It is already followed by next cluster */
+               scl = clst;
+       }
+
+       ncl = scl;                              /* Start cluster */
+       for (;;) {
+               ncl++;                                                  /* Next cluster */
+               if (ncl >= mcl) {                               /* Wrap around */
+                       ncl = 2;
+                       if (ncl > scl) return 0;        /* No free custer */
+               }
+               cs = get_cluster(fs, ncl);              /* Get the cluster status */
+               if (cs == 0) break;                             /* Found a free cluster */
+               if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */
+                       return cs;
+               if (ncl == scl) return 0;               /* No free custer */
+       }
+
+       if (put_cluster(fs, ncl, 0x0FFFFFFF))   /* Mark the new cluster "in use" */
+               return 0xFFFFFFFF;
+       if (clst != 0) {                                                /* Link it to previous one if needed */
+               if (put_cluster(fs, clst, ncl))
+                       return 0xFFFFFFFF;
+       }
+
+       fs->last_clust = ncl;                           /* Update FSINFO */
+       if (fs->free_clust != 0xFFFFFFFF) {
+               fs->free_clust--;
+               fs->fsi_flag = 1;
+       }
+
+       return ncl;             /* Return new cluster number */
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get sector# from cluster#                                             */
+/*-----------------------------------------------------------------------*/
+
+/*static*/
+DWORD clust2sect (     /* !=0: sector number, 0: failed - invalid cluster# */
+       FATFS *fs,              /* File system object */
+       DWORD clst              /* Cluster# to be converted */
+)
+{
+       clst -= 2;
+       if (clst >= (fs->max_clust - 2)) return 0;              /* Invalid cluster# */
+       return clst * fs->csize + fs->database;
+}
+
+DWORD f_check_contig(FIL *fp)
+{
+       DWORD nxt;
+       DWORD clust = fp->org_clust;
+       DWORD retval = 1;
+       while (clust >= 2 && clust < fp->fs->max_clust) {
+               nxt = get_cluster(fp->fs, clust);
+               if (nxt == 1) return retval;
+               if (nxt != (clust + 1)) return retval;
+               clust = nxt;
+               retval++;
+       }
+       return retval;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Seek directory index                                                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_seek (
+       DIR *dj,                /* Pointer to directory object */
+       WORD idx                /* Directory index number */
+)
+{
+       DWORD clst;
+       WORD ic;
+
+
+       dj->index = idx;
+       clst = dj->sclust;
+       if (clst == 1 || clst >= dj->fs->max_clust)     /* Check start cluster range */
+               return FR_INT_ERR;
+
+       if (clst == 0) {        /* Static table */
+               if (idx >= dj->fs->n_rootdir)           /* Index is out of range */
+                       return FR_INT_ERR;
+               dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32);
+       }
+       else {                          /* Dynamic table */
+               ic = SS(dj->fs) / 32 * dj->fs->csize;   /* Indexes per cluster */
+               while (idx >= ic) {     /* Follow cluster chain */
+                       clst = get_cluster(dj->fs, clst);                       /* Get next cluster */
+                       if (clst == 0xFFFFFFFF) return FR_DISK_ERR;     /* Disk error */
+                       if (clst < 2 || clst >= dj->fs->max_clust)      /* Reached to end of table or int error */
+                               return FR_INT_ERR;
+                       idx -= ic;
+               }
+               dj->clust = clst;
+               dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32);
+       }
+       dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32;
+
+       return FR_OK;   /* Seek succeeded */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Move directory index next                                             */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_next (     /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */
+       DIR *dj,                /* Pointer to directory object */
+       BOOL streach    /* FALSE: Do not streach table, TRUE: Streach table if needed */
+)
+{
+       DWORD clst;
+       WORD i;
+
+
+       i = dj->index + 1;
+       if (!i || !dj->sect)    /* Report EOT when index has reached 65535 */
+               return FR_NO_FILE;
+
+       if (!(i % (SS(dj->fs) / 32))) { /* Sector changed? */
+               dj->sect++;                                     /* Next sector */
+
+               if (dj->sclust == 0) {  /* Static table */
+                       if (i >= dj->fs->n_rootdir)     /* Report EOT when end of table */
+                               return FR_NO_FILE;
+               }
+               else {                                  /* Dynamic table */
+                       if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) {     /* Cluster changed? */
+                               clst = get_cluster(dj->fs, dj->clust);                  /* Get next cluster */
+                               if (clst <= 1) return FR_INT_ERR;
+                               if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+                               if (clst >= dj->fs->max_clust) {                                /* When it reached end of dinamic table */
+#if !_FS_READONLY
+                                       BYTE c;
+                                       if (!streach) return FR_NO_FILE;                        /* When do not streach, report EOT */
+                                       clst = create_chain(dj->fs, dj->clust);         /* Streach cluster chain */
+                                       if (clst == 0) return FR_DENIED;                        /* No free cluster */
+                                       if (clst == 1) return FR_INT_ERR;
+                                       if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+                                       /* Clean-up streached table */
+                                       if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */
+                                       MemSet(dj->fs->win, 0, SS(fs));                         /* Clear window buffer */
+                                       dj->fs->winsect = clust2sect(dj->fs, clst);     /* Cluster start sector */
+                                       for (c = 0; c < dj->fs->csize; c++) {           /* Fill the new cluster with 0 */
+                                               dj->fs->wflag = 1;
+                                               if (move_window(dj->fs, 0)) return FR_DISK_ERR;
+                                               dj->fs->winsect++;
+                                       }
+                                       dj->fs->winsect -= c;                                           /* Rewind window address */
+#else
+                                       return FR_NO_FILE;                      /* Report EOT */
+#endif
+                               }
+                               dj->clust = clst;                               /* Initialize data for new cluster */
+                               dj->sect = clust2sect(dj->fs, clst);
+                       }
+               }
+       }
+
+       dj->index = i;
+       dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32;
+
+       return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Test/Pick/Fit an LFN segment from/to directory entry                  */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};     /* Offset of LFN chars in the directory entry */
+
+
+static
+BOOL test_lfn (                        /* TRUE:Matched, FALSE:Not matched */
+       WCHAR *lfnbuf,          /* Pointer to the LFN to be compared */
+       BYTE *dir                       /* Pointer to the directory entry containing a part of LFN */
+)
+{
+       int i, s;
+       WCHAR wc1, wc2;
+
+
+       i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13;  /* Offset in the LFN buffer */
+       s = 0;
+       do {
+               if (i >= _MAX_LFN) return FALSE;        /* Out of buffer range? */
+               wc1 = LD_WORD(dir+LfnOfs[s]);           /* Get both characters to compare */
+               wc2 = lfnbuf[i++];
+               if (IsLower(wc1)) wc1 -= 0x20;          /* Compare it (ignore case) */
+               if (IsLower(wc2)) wc2 -= 0x20;
+               if (wc1 != wc2) return FALSE;
+       } while (++s < 13 && wc1);                              /* Repeat until last char or a NUL char is processed */
+
+       return TRUE;                                                    /* The LFN entry matched */
+}
+
+
+
+static
+BOOL pick_lfn (                        /* TRUE:Succeeded, FALSE:Buffer overflow */
+       WCHAR *lfnbuf,          /* Pointer to the Unicode-LFN buffer */
+       BYTE *dir                       /* Pointer to the directory entry */
+)
+{
+       int i, s;
+       WCHAR wchr;
+
+
+       i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13;  /* Offset in the LFN buffer */
+       s = 0;
+       do {
+               wchr = LD_WORD(dir+LfnOfs[s]);          /* Get an LFN char */
+               if (!wchr) break;                                       /* End of LFN? */
+               if (i >= _MAX_LFN) return FALSE;        /* Buffer overflow */
+               lfnbuf[i++] = wchr;                                     /* Store it */
+       } while (++s < 13);                                             /* Repeat until last char is copied */
+       if (dir[LDIR_Ord] & 0x40) lfnbuf[i] = 0;        /* Put terminator if last LFN entry */
+
+       return TRUE;
+}
+
+
+#if !_FS_READONLY
+static
+void fit_lfn (
+       const WCHAR *lfnbuf,    /* Pointer to the LFN buffer */
+       BYTE *dir,                              /* Pointer to the directory entry */
+       BYTE ord,                               /* LFN order (1-20) */
+       BYTE sum                                /* SFN sum */
+)
+{
+       int i, s;
+       WCHAR wchr;
+
+
+       dir[LDIR_Chksum] = sum;                 /* Set check sum */
+       dir[LDIR_Attr] = AM_LFN;                /* Set attribute. LFN entry */
+       dir[LDIR_Type] = 0;
+       ST_WORD(dir+LDIR_FstClusLO, 0);
+
+       i = (ord - 1) * 13;                             /* Offset in the LFN buffer */
+       s = wchr = 0;
+       do {
+               if (wchr != 0xFFFF) wchr = lfnbuf[i++]; /* Get an effective char */
+               ST_WORD(dir+LfnOfs[s], wchr);   /* Put it */
+               if (!wchr) wchr = 0xFFFF;       /* Padding chars following last char */
+       } while (++s < 13);
+       if (wchr == 0xFFFF || !lfnbuf[i]) ord |= 0x40;/* Bottom LFN part is the start of LFN sequence */
+       dir[LDIR_Ord] = ord;                    /* Set the LFN order */
+}
+
+#endif
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create numbered name                                                  */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+void gen_numname (
+       BYTE *dst,                      /* Pointer to genartated SFN */
+       const BYTE *src,        /* Pointer to source SFN to be modified */
+       const WCHAR *lfn,       /* Pointer to LFN */
+       WORD num                        /* Sequense number */
+)
+{
+       char ns[8];
+       int i, j;
+
+
+       MemCpy(dst, src, 11);
+
+       if (num > 5) {  /* On many collisions, generate a hash number instead of sequencial number */
+               do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn);
+       }
+
+       /* itoa */
+       i = 7;
+       do {
+               ns[i--] = (num % 10) + '0';
+               num /= 10;
+       } while (num);
+       ns[i] = '~';
+
+       /* Append the number */
+       for (j = 0; j < i && dst[j] != ' '; j++) {
+               if (IsDBCS1(dst[j])) {
+                       if (j == i - 1) break;
+                       j++;
+               }
+       }
+       do {
+               dst[j++] = (i < 8) ? ns[i++] : ' ';
+       } while (j < 8);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Calculate sum of an SFN                                               */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+BYTE sum_sfn (
+       const BYTE *dir         /* Ptr to directory entry */
+)
+{
+       BYTE sum = 0;
+       int n = 11;
+
+       do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
+       return sum;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Find an object in the directory                                       */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_find (
+       DIR *dj                 /* Pointer to the directory object linked to the file name */
+)
+{
+       FRESULT res;
+       BYTE a, c, stat, ord, sum, *dir;
+
+       ord = sum = 0xFF; stat = *(dj->fn+11);
+       do {
+               res = move_window(dj->fs, dj->sect);
+               if (res != FR_OK) break;
+               dir = dj->dir;                                  /* Ptr to the directory entry of current index */
+               c = dir[DIR_Name];
+               if (c == 0) { res = FR_NO_FILE; break; }        /* Reached to end of table */
+               a = dir[DIR_Attr] & AM_MASK;
+#if _USE_LFN   /* LFN configuration */
+               if (c == 0xE5 || c == '.' || ((a & AM_VOL) && a != AM_LFN)) {   /* An entry without valid data */
+                       ord = 0xFF;
+               } else {
+                       if (a == AM_LFN) {                      /* An LFN entry is found */
+                               if (dj->lfn) {
+                                       if (c & 0x40) {         /* Is it start of LFN sequence? */
+                                               sum = dir[LDIR_Chksum];
+                                               c &= 0xBF; ord = c;             /* LFN start order */
+                                               dj->lfn_idx = dj->index;
+                                       }
+                                       /* Check LFN validity. Compare LFN if it is out of 8.3 format */
+                                       ord = (c == ord && sum == dir[LDIR_Chksum] && (!(stat & 1) || test_lfn(dj->lfn, dir))) ? ord - 1 : 0xFF;
+                               }
+                       } else {                                        /* An SFN entry is found */
+                               if (ord || sum != sum_sfn(dir)) {       /* Did not LFN match? */
+                                       dj->lfn_idx = 0xFFFF;
+                                       ord = 0xFF;
+                               }
+                               if (stat & 1) {                 /* Match LFN if it is out of 8.3 format */
+                                       if (ord == 0) break;
+                               } else {                                /* Match SFN if LFN is in 8.3 format */
+                                       if (!MemCmp(dir, dj->fn, 11)) break;
+                               }
+                       }
+               }
+#else  /* Non LFN configuration */
+               if (c != 0xE5 && c != '.' && !(a & AM_VOL) && !MemCmp(dir, dj->fn, 11)) /* Is it a valid entry? */
+                       break;
+#endif
+               res = dir_next(dj, FALSE);                              /* Next entry */
+       } while (res == FR_OK);
+
+       return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory                                     */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 2
+static
+FRESULT dir_read (
+       DIR *dj                 /* Pointer to the directory object to store read object name */
+)
+{
+       FRESULT res;
+       BYTE a, c, ord, sum, *dir;
+
+
+       ord = sum = 0xFF;
+       res = FR_NO_FILE;
+       while (dj->sect) {
+               res = move_window(dj->fs, dj->sect);
+               if (res != FR_OK) break;
+               dir = dj->dir;                                  /* Ptr to the directory entry of current index */
+               c = dir[DIR_Name];
+               if (c == 0) { res = FR_NO_FILE; break; }        /* Reached to end of table */
+               a = dir[DIR_Attr] & AM_MASK;
+#if _USE_LFN   /* LFN configuration */
+               if (c == 0xE5 || c == '.' || ((a & AM_VOL) && a != AM_LFN)) {   /* An entry without valid data */
+                       ord = 0xFF;
+               } else {
+                       if (a == AM_LFN) {                      /* An LFN entry is found */
+                               if (c & 0x40) {                 /* Is it start of LFN sequence? */
+                                       sum = dir[LDIR_Chksum];
+                                       c &= 0xBF; ord = c;
+                                       dj->lfn_idx = dj->index;
+                               }
+                               /* Check LFN validity and capture it */
+                               ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+                       } else {                                        /* An SFN entry is found */
+                               if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN entry? */
+                                       dj->lfn_idx = 0xFFFF;           /* No LFN. */
+                               break;
+                       }
+               }
+#else  /* Non LFN configuration */
+               if (c != 0xE5 && c != '.' && !(a & AM_VOL))     /* Is it a valid entry? */
+                       break;
+#endif
+               res = dir_next(dj, FALSE);                              /* Next entry */
+               if (res != FR_OK) break;
+       }
+
+       if (res != FR_OK) dj->sect = 0;
+
+       return res;
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
+       DIR *dj                         /* Target directory with object name to be created */
+)
+{
+       FRESULT res;
+       BYTE c, *dir;
+
+#if _USE_LFN   /* LFN configuration */
+       WORD n, ne, is;
+       BYTE sn[12], *fn, sum;
+       WCHAR *lfn;
+
+       fn = dj->fn; lfn = dj->lfn;
+       MemCpy(sn, fn, 12);
+       if (sn[11] & 1) {               /* When LFN is out of 8.3 format, generate a numbered name */
+               fn[11] = 0; dj->lfn = NULL;                     /* Find only SFN */
+               for (n = 1; n < 100; n++) {
+                       gen_numname(fn, sn, lfn, n);    /* Generate a numbered name */
+                       res = dir_seek(dj, 0);
+                       if (res != FR_OK) break;
+                       res = dir_find(dj);                             /* Check if the name collides with existing SFN */
+                       if (res != FR_OK) break;
+               }
+               if (n == 100) return FR_DENIED;         /* Abort if too many collisions */
+               if (res != FR_NO_FILE) return res;      /* Abort if the result is other than 'not collided' */
+               fn[11] = sn[11]; dj->lfn = lfn;
+       }
+       if (sn[11] & 2) {               /* When eliminate LFN, reserve only an SFN entry. */
+               ne = 1;
+       } else {                                /* Otherwise reserve an SFN + LFN entries. */
+               for (ne = 0; lfn[ne]; ne++) ;
+               ne = (ne + 25) / 13;
+       }
+
+       /* Reserve contiguous entries */
+       res = dir_seek(dj, 0);
+       if (res != FR_OK) return res;
+       n = is = 0;
+       do {
+               res = move_window(dj->fs, dj->sect);
+               if (res != FR_OK) break;
+               c = *dj->dir;   /* Check the entry status */
+               if (c == 0xE5 || c == 0) {      /* Is it a blank entry? */
+                       if (n == 0) is = dj->index;     /* First index of the contigulus entry */
+                       if (++n == ne) break;   /* A contiguous entry that requiered count is found */
+               } else {
+                       n = 0;                                  /* Not a blank entry. Restart to search */
+               }
+               res = dir_next(dj, TRUE);       /* Next entry with table streach */
+       } while (res == FR_OK);
+
+       if (res == FR_OK && ne > 1) {   /* Initialize LFN entry if needed */
+               res = dir_seek(dj, is);
+               if (res == FR_OK) {
+                       sum = sum_sfn(dj->fn);  /* Sum of the SFN tied to the LFN */
+                       ne--;
+                       do {                                    /* Store LFN entries in bottom first */
+                               res = move_window(dj->fs, dj->sect);
+                               if (res != FR_OK) break;
+                               fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
+                               dj->fs->wflag = 1;
+                               res = dir_next(dj, FALSE);      /* Next entry */
+                       } while (res == FR_OK && --ne);
+               }
+       }
+
+#else  /* Non LFN configuration */
+       res = dir_seek(dj, 0);
+       if (res == FR_OK) {
+               do {    /* Find a blank entry for the SFN */
+                       res = move_window(dj->fs, dj->sect);
+                       if (res != FR_OK) break;
+                       c = *dj->dir;
+                       if (c == 0xE5 || c == 0) break; /* Is it a blank entry? */
+                       res = dir_next(dj, TRUE);               /* Next entry with table streach */
+               } while (res == FR_OK);
+       }
+#endif
+
+       if (res == FR_OK) {             /* Initialize the SFN entry */
+               res = move_window(dj->fs, dj->sect);
+               if (res == FR_OK) {
+                       dir = dj->dir;
+                       MemSet(dir, 0, 32);                     /* Clean the entry */
+                       MemCpy(dir, dj->fn, 11);        /* Put SFN */
+                       dir[DIR_NTres] = *(dj->fn+11) & 0x18;   /* Put NT flag */
+                       dj->fs->wflag = 1;
+               }
+       }
+
+       return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY && !_FS_MINIMIZE
+static
+FRESULT dir_remove (   /* FR_OK: Successful, FR_DISK_ERR: A disk error */
+       DIR *dj                         /* Directory object pointing the entry to be removed */
+)
+{
+       FRESULT res;
+
+#if _USE_LFN   /* LFN configuration */
+       WORD i;
+
+       i = dj->index;  /* SFN index */
+       res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx));  /* Goto the SFN or top of the LFN entries */
+       if (res == FR_OK) {
+               do {
+                       res = move_window(dj->fs, dj->sect);
+                       if (res != FR_OK) break;
+                       *dj->dir = 0xE5;                                /* Mark the entry "deleted" */
+                       dj->fs->wflag = 1;
+                       if (dj->index >= i) break;              /* When SFN is deleted, all entries of the object is deleted. */
+                       res = dir_next(dj, FALSE);              /* Next entry */
+               } while (res == FR_OK);
+               if (res == FR_NO_FILE) res = FR_INT_ERR;
+       }
+
+#else                  /* Non LFN configuration */
+       res = dir_seek(dj, dj->index);
+       if (res == FR_OK) {
+               res = move_window(dj->fs, dj->sect);
+               if (res == FR_OK) {
+                       *dj->dir = 0xE5;                                /* Mark the entry "deleted" */
+                       dj->fs->wflag = 1;
+               }
+       }
+#endif
+
+       return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a segment and create the object name in directory form           */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT create_name (
+       DIR *dj,                        /* Pointer to the directory object */
+       const char **path       /* Pointer to pointer to the segment in the path string */
+)
+{
+#if _USE_LFN
+       BYTE c, b, cf, *sfn;
+       WCHAR w, *lfn;
+       int i, ni, si, di;
+       const char *p;
+
+       /* Create LFN in Unicode */
+       si = di = 0;
+       p = *path;
+       lfn = dj->lfn;
+       for (;;) {
+               w = (BYTE)p[si++];                              /* Get a character */
+               if (w < ' ' || w == '/' || w == '\\') break;    /* Break on end of segment */
+               if (IsDBCS1(w)) {                               /* If it is DBC 1st byte */
+                       c = p[si++];                            /* Get 2nd byte */
+                       if (!IsDBCS2(c))                        /* Reject invalid DBC */
+                               return FR_INVALID_NAME;
+                       w = (w << 8) + c;
+               } else {
+                       if (StrChr("\"*:<>?|\x7F", w))  /* Reject unallowable chars for LFN */
+                               return FR_INVALID_NAME;
+               }
+               w = ff_convert(w, 1);                   /* Convert OEM to Unicode, store it */
+               if (!w || di >= _MAX_LFN)               /* Reject invalid code or too long name */
+                       return FR_INVALID_NAME;
+               lfn[di++] = w;
+       }
+       *path = &p[si];                                         /* Rerurn pointer to the next segment */
+       cf = (w < ' ') ? 4 : 0;                         /* Set last segment flag if end of path */
+
+       while (di) {                                            /* Strip trailing spaces and dots */
+               w = lfn[di - 1];
+               if (w != ' ' && w != '.') break;
+               di--;
+       }
+       if (!di) return FR_INVALID_NAME;        /* Reject null string */
+
+       lfn[di] = 0;                                            /* LFN is created */
+
+       /* Create SFN in directory form */
+       sfn = dj->fn;
+       MemSet(sfn, ' ', 11);
+       for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;  /* Strip leading spaces and dots */
+       if (si) cf |= 1;
+       while (di && lfn[di - 1] != '.') di--;  /* Find extension (di<=si: no extension) */
+
+       b = i = 0; ni = 8;
+       for (;;) {
+               w = lfn[si++];                                  /* Get an LFN char */
+               if (w == 0) break;                              /* Break when enf of the LFN */
+               if (w == ' ' || (w == '.' && si != di)) {       /* Remove spaces and dots */
+                       cf |= 1; continue;
+               }
+               if (i >= ni || si == di) {              /* Here is extension or end of SFN */
+                       if (ni == 11) {                         /* Extension is longer than 3 bytes */
+                               cf |= 1; break;
+                       }
+                       if (si != di) cf |= 1;          /* File name is longer than 8 bytes */
+                       if (si > di) break;                     /* No extension */
+                       si = di; i = 8; ni = 11;        /* Enter extension section */
+                       b <<= 2; continue;
+               }
+               w = ff_convert(w, 0);                   /* Unicode -> OEM code */
+               if (w >= 0x80) cf |= 0x20;              /* If there is any extended char, force create an LFN */
+               if (w >= 0x100) {                               /* Double byte char */
+                       if (i >= ni - 1) {
+                               cf |= 1; i = ni; continue;
+                       }
+                       sfn[i++] = (BYTE)(w >> 8);
+               } else {                                                /* Single byte char */
+                       if (StrChr("+,;[=]", w)) {      /* Replace unallowable chars for SFN */
+                               w = '_'; cf |= 1;
+                       } else {
+                               if (IsUpper(w)) {               /* Large capital */
+                                       b |= 2;
+                               } else {
+                                       if (IsLower(w)) {       /* Small capital */
+                                               b |= 1; w -= 0x20;
+                                       }
+                               }
+                       }
+               }
+               sfn[i++] = (BYTE)w;
+       }
+       if (sfn[0] == 0xE5) sfn[0] = 0x05;      /* When first char collides with 0xE5, replace it with 0x05 */
+
+       if (ni == 8) b <<= 2;
+       if ((cf & 0x21) == 0) { /* When LFN is in 8.3 format without extended char, NT flags are created */
+               if ((b & 0x03) == 0x01) cf |= 0x10;     /* NT flag (Extension has only small capital) */
+               if ((b & 0x0C) == 0x04) cf |= 0x08;     /* NT flag (Filename has only small capital) */
+               if ((b & 0x0C) != 0x0C && (b & 0x03) != 0x03) cf |= 2;  /* Eliminate LFN when non composite capitals */
+       }
+
+       sfn[11] = cf;           /* SFN is created */
+
+#else
+       BYTE c, d, b, *sfn;
+       int ni, si, i;
+       const char *p;
+
+       /* Create file name in directory form */
+       sfn = dj->fn;
+       MemSet(sfn, ' ', 11);
+       si = i = b = 0; ni = 8;
+       p = *path;
+       for (;;) {
+               c = p[si++];
+               if (c < ' ' || c == '/' || c == '\\') break;    /* Break on end of segment */
+               if (c == '.' || i >= ni) {
+                       if (ni != 8 || c != '.') return FR_INVALID_NAME;
+                       i = 8; ni = 11;
+                       b <<= 2; continue;
+               }
+               if (c >= 0x80) b |= 3;                  /* If there is any extended char, eliminate NT flag */
+               if (IsDBCS1(c)) {                               /* If it is DBC 1st byte */
+                       d = p[si++];                            /* Get 2nd byte */
+                       if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */
+                               return FR_INVALID_NAME;
+                       sfn[i++] = c;
+                       sfn[i++] = d;
+               } else {
+                       if (StrChr(" +,;[=]\"*:<>?|\x7F", c))   /* Reject unallowable chrs for SFN */
+                               return FR_INVALID_NAME;
+                       if (IsUpper(c)) {
+                               b |= 2;
+                       } else {
+                               if (IsLower(c)) {
+                                       b |= 1; c -= 0x20;
+                               }
+                       }
+                       sfn[i++] = c;
+               }
+       }
+       *path = &p[si];                                         /* Rerurn pointer to the next segment */
+       c = (c < ' ') ? 4 : 0;                          /* Set last segment flag if end of path */
+
+       if (!i) return FR_INVALID_NAME;         /* Reject null string */
+       if (sfn[0] == 0xE5) sfn[0] = 0x05;      /* When first char collides with 0xE5, replace it with 0x05 */
+
+       if (ni == 8) b <<= 2;
+       if ((b & 0x03) == 0x01) c |= 0x10;      /* NT flag (Extension has only small capital) */
+       if ((b & 0x0C) == 0x04) c |= 0x08;      /* NT flag (Filename has only small capital) */
+
+       sfn[11] = c;            /* Store NT flag, File name is created */
+#endif
+
+       return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry                             */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+void get_fileinfo (            /* No return code */
+       DIR *dj,                        /* Pointer to the directory object */
+       FILINFO *fno            /* Pointer to store the file information */
+)
+{
+       int i;
+       BYTE c, nt, *dir;
+       char *p;
+
+
+       p = fno->fname;
+       if (dj->sect) {
+               dir = dj->dir;
+               nt = dir[DIR_NTres];            /* NT flag */
+               for (i = 0; i < 8; i++) {       /* Copy file name body */
+                       c = dir[i];
+                       if (c == ' ') break;
+                       if (c == 0x05) c = 0xE5;
+                       if ((nt & 0x08) && IsUpper(c)) c += 0x20;
+                       *p++ = c;
+               }
+               if (dir[8] != ' ') {            /* Copy file name extension */
+                       *p++ = '.';
+                       for (i = 8; i < 11; i++) {
+                               c = dir[i];
+                               if (c == ' ') break;
+                               if ((nt & 0x10) && IsUpper(c)) c += 0x20;
+                               *p++ = c;
+                       }
+               }
+               fno->fattrib = dir[DIR_Attr];                           /* Attribute */
+               fno->fsize = LD_DWORD(dir+DIR_FileSize);        /* Size */
+               fno->fdate = LD_WORD(dir+DIR_WrtDate);          /* Date */
+               fno->ftime = LD_WORD(dir+DIR_WrtTime);          /* Time */
+       }
+       *p = 0;
+
+#if _USE_LFN
+       p = fno->lfname;
+       if (p) {
+               WCHAR wchr, *lfn;
+
+               i = 0;
+               if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
+                       lfn = dj->lfn;
+                       while ((wchr = *lfn++) != 0) {          /* Get an LFN char */
+                               wchr = ff_convert(wchr, 0);             /* Unicode -> OEM code */
+                               if (!wchr) { i = 0; break; }    /* Conversion error, no LFN */
+                               if (_DF1S && wchr >= 0x100)             /* Put 1st byte if it is a DBC */
+                                       p[i++] = (char)(wchr >> 8);
+                               p[i++] = (char)wchr;
+                               if (i >= fno->lfsize) { i = 0; break; } /* Buffer overrun, no LFN */
+                       }
+               }
+               p[i] = 0;       /* Terminator */
+       }
+#endif
+}
+#endif /* _FS_MINIMIZE <= 1 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path                                                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT follow_path (  /* FR_OK(0): successful, !=0: error code */
+       DIR *dj,                        /* Directory object to return last directory and found object */
+       const char *path        /* Full-path string to find a file or directory */
+)
+{
+       FRESULT res;
+       BYTE *dir, stat;
+
+
+       if (*path == '/' || *path == '\\' ) path++;     /* Strip heading separator */
+
+       dj->sclust =                                            /* Set start directory (root dir) */
+               (dj->fs->fs_type == FS_FAT32) ? dj->fs->dirbase : 0;
+
+       if ((BYTE)*path < ' ') {                        /* Null path means the root directory */
+               res = dir_seek(dj, 0);
+               dj->dir = NULL;
+
+       } else {                                                        /* Follow path */
+               for (;;) {
+                       res = dir_seek(dj, 0);                  /* Rewind directory object */
+                       if (res != FR_OK) break;
+                       res = create_name(dj, &path);   /* Get a segment */
+                       if (res != FR_OK) break;
+                       res = dir_find(dj);                             /* Find it */
+                       stat = *(dj->fn+11);
+                       if (res != FR_OK) {                             /* Could not find the object */
+                               if (res == FR_NO_FILE && !(stat & 4))
+                                       res = FR_NO_PATH;
+                               break;
+                       }
+                       if (stat & 4) break;                    /* Last segment match. Function completed. */
+                       dir = dj->dir;                                  /* There is next segment. Follow the sub directory */
+                       if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */
+                               res = FR_NO_PATH; break;
+                       }
+                       dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+               }
+       }
+
+       return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load boot record and check if it is an FAT boot record                */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE check_fs (        /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
+       FATFS *fs,      /* File system object */
+       DWORD sect      /* Sector# (lba) to check if it is an FAT boot record or not */
+)
+{
+       if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK)   /* Load boot record */
+               return 3;
+       if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)                               /* Check record signature (always placed at offset 510 even if the sector size is >512) */
+               return 2;
+
+       if (!MemCmp(&fs->win[BS_FilSysType], "FAT", 3))         /* Check FAT signature */
+               return 0;
+       if (!MemCmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80))
+               return 0;
+
+       return 1;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Make sure that the file system is valid                               */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT auto_mount (   /* FR_OK(0): successful, !=0: any error occured */
+       const char **path,      /* Pointer to pointer to the path name (drive number) */
+       FATFS **rfs,            /* Pointer to pointer to the found file system object */
+       BYTE chk_wp                     /* !=0: Check media write protection for write access */
+)
+{
+       FRESULT res;
+       BYTE drv, fmt, *tbl;
+       DSTATUS stat;
+       DWORD bsect, fsize, tsect, mclst;
+       const char *p = *path;
+       FATFS *fs;
+
+
+       /* Get drive number from the path name */
+       drv = p[0] - '0';                       /* Is there a drive number? */
+       if (drv <= 9 && p[1] == ':') {
+               p += 2;                                 /* Found a drive number, get and strip it */
+               *path = p;                              /* Return pointer to the path name */
+       } else {
+               drv = 0;                                /* No drive number is given, use drive number 0 as default */
+       }
+
+       /* Check if the drive number is valid or not */
+       if (drv >= _DRIVES) return FR_INVALID_DRIVE;    /* Is the drive number valid? */
+       *rfs = fs = FatFs[drv];                                 /* Returen pointer to the corresponding file system object */
+       if (!fs) return FR_NOT_ENABLED;                 /* Is the file system object registered? */
+
+       ENTER_FF(fs);                           /* Lock file system */
+
+       if (fs->fs_type) {                                              /* If the logical drive has been mounted */
+               stat = disk_status(fs->drive);
+               if (!(stat & STA_NOINIT)) {                     /* and physical drive is kept initialized (has not been changed), */
+#if !_FS_READONLY
+                       if (chk_wp && (stat & STA_PROTECT))     /* Check write protection if needed */
+                               return FR_WRITE_PROTECTED;
+#endif
+                       return FR_OK;                                   /* The file system object is valid */
+               }
+       }
+
+       /* The logical drive must be re-mounted. Following code attempts to mount the logical drive */
+
+       fs->fs_type = 0;                                        /* Clear the file system object */
+       fs->drive = LD2PD(drv);                         /* Bind the logical drive and a physical drive */
+       stat = disk_initialize(fs->drive);      /* Initialize low level disk I/O layer */
+       if (stat & STA_NOINIT)                          /* Check if the drive is ready */
+               return FR_NOT_READY;
+#if S_MAX_SIZ > 512                                            /* Get disk sector size if needed */
+       if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > S_MAX_SIZ)
+               return FR_NO_FILESYSTEM;
+#endif
+#if !_FS_READONLY
+       if (chk_wp && (stat & STA_PROTECT))     /* Check write protection if needed */
+               return FR_WRITE_PROTECTED;
+#endif
+       /* Search FAT partition on the drive */
+       fmt = check_fs(fs, bsect = 0);          /* Check sector 0 as an SFD format */
+       if (fmt == 1) {                                         /* Not an FAT boot record, it may be patitioned */
+               /* Check a partition listed in top of the partition table */
+               tbl = &fs->win[MBR_Table + LD2PT(drv) * 16];    /* Partition table */
+               if (tbl[4]) {                                                                   /* Is the partition existing? */
+                       bsect = LD_DWORD(&tbl[8]);                                      /* Partition offset in LBA */
+                       fmt = check_fs(fs, bsect);                                      /* Check the partition */
+               }
+       }
+       if (fmt == 3) return FR_DISK_ERR;
+       if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))   /* No valid FAT patition is found */
+               return FR_NO_FILESYSTEM;
+
+       /* Initialize the file system object */
+       fsize = LD_WORD(fs->win+BPB_FATSz16);                           /* Number of sectors per FAT */
+       if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32);
+       fs->sects_fat = fsize;
+       fs->n_fats = fs->win[BPB_NumFATs];                                      /* Number of FAT copies */
+       fsize *= fs->n_fats;                                                            /* (Number of sectors in FAT area) */
+       fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */
+       fs->csize = fs->win[BPB_SecPerClus];                            /* Number of sectors per cluster */
+       fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);        /* Nmuber of root directory entries */
+       tsect = LD_WORD(fs->win+BPB_TotSec16);                          /* Number of sectors on the file system */
+       if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
+       fs->max_clust = mclst = (tsect                                          /* Last cluster# + 1 */
+               - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32)
+               ) / fs->csize + 2;
+
+       fmt = FS_FAT12;                                                                         /* Determine the FAT sub type */
+       if (mclst >= 0xFF7) fmt = FS_FAT16;                             /* Number of clusters >= 0xFF5 */
+       if (mclst >= 0xFFF7) fmt = FS_FAT32;                    /* Number of clusters >= 0xFFF5 */
+
+       if (fmt == FS_FAT32)
+               fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);   /* Root directory start cluster */
+       else
+               fs->dirbase = fs->fatbase + fsize;                              /* Root directory start sector (lba) */
+       fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32);       /* Data start sector (lba) */
+
+#if !_FS_READONLY
+       /* Initialize allocation information */
+       fs->free_clust = 0xFFFFFFFF;
+       fs->wflag = 0;
+       /* Get fsinfo if needed */
+       if (fmt == FS_FAT32) {
+               fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo);
+               fs->fsi_flag = 0;
+               if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK &&
+                       LD_WORD(fs->win+BS_55AA) == 0xAA55 &&
+                       LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 &&
+                       LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) {
+                       fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
+                       fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
+               }
+       }
+#endif
+       fs->winsect = 0;
+       fs->fs_type = fmt;                      /* FAT syb-type */
+       fs->id = ++Fsid;                        /* File system mount ID */
+       res = FR_OK;
+
+       return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/dir object is valid or not                          */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT validate (     /* FR_OK(0): The object is valid, !=0: Invalid */
+       FATFS *fs,              /* Pointer to the file system object */
+       WORD id                 /* Member id of the target object to be checked */
+)
+{
+       if (!fs || !fs->fs_type || fs->id != id)
+               return FR_INVALID_OBJECT;
+
+       ENTER_FF(fs);           /* Lock file system */
+
+       if (disk_status(fs->drive) & STA_NOINIT)
+               return FR_NOT_READY;
+
+       return FR_OK;
+}
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Public Functions
+
+--------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Locical Drive                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+       BYTE drv,               /* Logical drive number to be mounted/unmounted */
+       FATFS *fs               /* Pointer to new file system object (NULL for unmount)*/
+)
+{
+       FATFS *rfs;
+
+
+       if (drv >= _DRIVES)
+               return FR_INVALID_DRIVE;
+
+       rfs = FatFs[drv];
+
+       if (rfs) {
+#if _FS_REENTRANT                                      /* Discard mutex of the current fs. (Platform dependent) */
+               CloseHandle(rfs->h_mutex);      /* Discard mutex */
+#endif
+               rfs->fs_type = 0;                       /* Clear old fs object */
+       }
+
+       if (fs) {
+               fs->fs_type = 0;        /* Clear new fs object */
+#if _FS_REENTRANT                              /* Create mutex for the new fs. (Platform dependent) */
+               fs->h_mutex = CreateMutex(NULL, FALSE, NULL);
+#endif
+       }
+       FatFs[drv] = fs;                /* Register new fs object */
+
+       return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+       FIL *fp,                        /* Pointer to the blank file object */
+       const char *path,       /* Pointer to the file name */
+       BYTE mode                       /* Access mode and file open mode flags */
+)
+{
+       FRESULT res;
+       DIR dj;
+       NAMEBUF(sfn, lfn);
+       BYTE *dir;
+
+
+       fp->fs = NULL;          /* Clear file object */
+#if !_FS_READONLY
+       mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW);
+       res = auto_mount(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)));
+#else
+       mode &= FA_READ;
+       res = auto_mount(&path, &dj.fs, 0);
+#endif
+       if (res != FR_OK) LEAVE_FF(dj.fs, res);
+       INITBUF(dj, sfn, lfn);
+       res = follow_path(&dj, path);   /* Follow the file path */
+
+#if !_FS_READONLY
+       /* Create or Open a file */
+       if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+               DWORD ps, cl;
+
+               if (res != FR_OK) {             /* No file, create new */
+                       if (res == FR_NO_FILE)
+                               res = dir_register(&dj);
+                       if (res != FR_OK) LEAVE_FF(dj.fs, res);
+                       mode |= FA_CREATE_ALWAYS;
+                       dir = dj.dir;
+               }
+               else {                                  /* Any object is already existing */
+                       if (mode & FA_CREATE_NEW)                       /* Cannot create new */
+                               LEAVE_FF(dj.fs, FR_EXIST);
+                       dir = dj.dir;
+                       if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR)))        /* Cannot overwrite it (R/O or DIR) */
+                               LEAVE_FF(dj.fs, FR_DENIED);
+                       if (mode & FA_CREATE_ALWAYS) {          /* Resize it to zero if needed */
+                               cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);    /* Get start cluster */
+                               ST_WORD(dir+DIR_FstClusHI, 0);  /* cluster = 0 */
+                               ST_WORD(dir+DIR_FstClusLO, 0);
+                               ST_DWORD(dir+DIR_FileSize, 0);  /* size = 0 */
+                               dj.fs->wflag = 1;
+                               ps = dj.fs->winsect;                    /* Remove the cluster chain */
+                               if (cl) {
+                                       res = remove_chain(dj.fs, cl);
+                                       if (res) LEAVE_FF(dj.fs, res);
+                                       dj.fs->last_clust = cl - 1;     /* Reuse the cluster hole */
+                               }
+                               res = move_window(dj.fs, ps);
+                               if (res != FR_OK) LEAVE_FF(dj.fs, res);
+                       }
+               }
+               if (mode & FA_CREATE_ALWAYS) {
+                       dir[DIR_Attr] = 0;                                      /* Reset attribute */
+                       ps = get_fattime();
+                       ST_DWORD(dir+DIR_CrtTime, ps);          /* Created time */
+                       dj.fs->wflag = 1;
+                       mode |= FA__WRITTEN;                            /* Set file changed flag */
+               }
+       }
+       /* Open an existing file */
+       else {
+#endif /* !_FS_READONLY */
+               if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */
+               dir = dj.dir;
+               if (!dir || (dir[DIR_Attr] & AM_DIR))   /* It is a directory */
+                       LEAVE_FF(dj.fs, FR_NO_FILE);
+#if !_FS_READONLY
+               if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
+                       LEAVE_FF(dj.fs, FR_DENIED);
+       }
+       fp->dir_sect = dj.fs->winsect;          /* Pointer to the directory entry */
+       fp->dir_ptr = dj.dir;
+#endif
+       fp->flag = mode;                                        /* File access mode */
+       fp->org_clust =                                         /* File start cluster */
+               ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+       fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */
+       fp->fptr = 0; fp->csect = 255;          /* File pointer */
+       fp->dsect = 0;
+       fp->fs = dj.fs; fp->id = dj.fs->id;     /* Owner file system object of the file */
+
+       LEAVE_FF(dj.fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File                                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+       FIL *fp,                /* Pointer to the file object */
+       void *buff,             /* Pointer to data buffer */
+       UINT btr,               /* Number of bytes to read */
+       UINT *br                /* Pointer to number of bytes read */
+)
+{
+       FRESULT res;
+       DWORD clst, sect, remain;
+       UINT rcnt, cc;
+       BYTE *rbuff = buff;
+
+
+       *br = 0;
+
+       res = validate(fp->fs, fp->id);                                 /* Check validity of the object */
+       if (res != FR_OK) LEAVE_FF(fp->fs, res);
+       if (fp->flag & FA__ERROR)                                               /* Check abort flag */
+               LEAVE_FF(fp->fs, FR_INT_ERR);
+       if (!(fp->flag & FA_READ))                                              /* Check access mode */
+               LEAVE_FF(fp->fs, FR_DENIED);
+       remain = fp->fsize - fp->fptr;
+       if (btr > remain) btr = (UINT)remain;                   /* Truncate btr by remaining bytes */
+
+       for ( ;  btr;                                                                   /* Repeat until all data transferred */
+               rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
+               if ((fp->fptr % SS(fp->fs)) == 0) {                     /* On the sector boundary? */
+                       if (fp->csect >= fp->fs->csize) {               /* On the cluster boundary? */
+                               clst = (fp->fptr == 0) ?                        /* On the top of the file? */
+                                       fp->org_clust : get_cluster(fp->fs, fp->curr_clust);
+                               if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+                               if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                               fp->curr_clust = clst;                          /* Update current cluster */
+                               fp->csect = 0;                                          /* Reset sector offset in the cluster */
+                       }
+                       sect = clust2sect(fp->fs, fp->curr_clust);      /* Get current sector */
+                       if (!sect) ABORT(fp->fs, FR_INT_ERR);
+                       sect += fp->csect;
+                       cc = btr / SS(fp->fs);                                  /* When remaining bytes >= sector size, */
+                       if (cc) {                                                               /* Read maximum contiguous sectors directly */
+                               if (fp->csect + cc > fp->fs->csize)     /* Clip at cluster boundary */
+                                       cc = fp->fs->csize - fp->csect;
+                               if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK)
+                                       ABORT(fp->fs, FR_DISK_ERR);
+                               fp->csect += (BYTE)cc;                          /* Next sector address in the cluster */
+                               rcnt = SS(fp->fs) * cc;                         /* Number of bytes transferred */
+                               continue;
+                       }
+#if !_FS_TINY
+#if !_FS_READONLY
+                       if (fp->flag & FA__DIRTY) {                     /* Write sector I/O buffer if needed */
+                               if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+                                       ABORT(fp->fs, FR_DISK_ERR);
+                               fp->flag &= (BYTE)~FA__DIRTY;
+                       }
+#endif
+                       if (fp->dsect != sect) {                        /* Fill sector buffer with file data */
+                               if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
+                                       ABORT(fp->fs, FR_DISK_ERR);
+                       }
+#endif
+                       fp->dsect = sect;
+                       fp->csect++;                                                    /* Next sector address in the cluster */
+               }
+               rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));    /* Get partial sector data from sector buffer */
+               if (rcnt > btr) rcnt = btr;
+#if _FS_TINY
+               if (move_window(fp->fs, fp->dsect))                     /* Move sector window */
+                       ABORT(fp->fs, FR_DISK_ERR);
+               MemCpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);       /* Pick partial sector */
+#else
+               MemCpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);   /* Pick partial sector */
+#endif
+       }
+
+
+       LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+       FIL *fp,                        /* Pointer to the file object */
+       const void *buff,       /* Pointer to the data to be written */
+       UINT btw,                       /* Number of bytes to write */
+       UINT *bw                        /* Pointer to number of bytes written */
+)
+{
+       FRESULT res;
+       DWORD clst, sect;
+       UINT wcnt, cc;
+       const BYTE *wbuff = buff;
+
+
+       *bw = 0;
+
+       res = validate(fp->fs, fp->id);                                 /* Check validity of the object */
+       if (res != FR_OK) LEAVE_FF(fp->fs, res);
+       if (fp->flag & FA__ERROR)                                               /* Check abort flag */
+               LEAVE_FF(fp->fs, FR_INT_ERR);
+       if (!(fp->flag & FA_WRITE))                                             /* Check access mode */
+               LEAVE_FF(fp->fs, FR_DENIED);
+       if (fp->fsize + btw < fp->fsize) btw = 0;               /* File size cannot reach 4GB */
+
+       for ( ;  btw;                                                                   /* Repeat until all data transferred */
+               wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
+               if ((fp->fptr % SS(fp->fs)) == 0) {                     /* On the sector boundary? */
+                       if (fp->csect >= fp->fs->csize) {               /* On the cluster boundary? */
+                               if (fp->fptr == 0) {                            /* On the top of the file? */
+                                       clst = fp->org_clust;                   /* Follow from the origin */
+                                       if (clst == 0)                                  /* When there is no cluster chain, */
+                                               fp->org_clust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */
+                               } else {                                                        /* Middle or end of the file */
+                                       clst = create_chain(fp->fs, fp->curr_clust);                    /* Follow or streach cluster chain */
+                               }
+                               if (clst == 0) break;                           /* Could not allocate a new cluster (disk full) */
+                               if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+                               if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                               fp->curr_clust = clst;                          /* Update current cluster */
+                               fp->csect = 0;                                          /* Reset sector address in the cluster */
+                       }
+#if _FS_TINY
+                       if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0))     /* Write back data buffer prior to following direct transfer */
+                               ABORT(fp->fs, FR_DISK_ERR);
+#else
+                       if (fp->flag & FA__DIRTY) {             /* Write back data buffer prior to following direct transfer */
+                               if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+                                       ABORT(fp->fs, FR_DISK_ERR);
+                               fp->flag &= (BYTE)~FA__DIRTY;
+                       }
+#endif
+                       sect = clust2sect(fp->fs, fp->curr_clust);      /* Get current sector */
+                       if (!sect) ABORT(fp->fs, FR_INT_ERR);
+                       sect += fp->csect;
+                       cc = btw / SS(fp->fs);                                  /* When remaining bytes >= sector size, */
+                       if (cc) {                                                               /* Write maximum contiguous sectors directly */
+                               if (fp->csect + cc > fp->fs->csize)     /* Clip at cluster boundary */
+                                       cc = fp->fs->csize - fp->csect;
+                               if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK)
+                                       ABORT(fp->fs, FR_DISK_ERR);
+                               fp->csect += (BYTE)cc;                          /* Next sector address in the cluster */
+                               wcnt = SS(fp->fs) * cc;                         /* Number of bytes transferred */
+                               continue;
+                       }
+#if _FS_TINY
+                       if (fp->fptr >= fp->fsize) {                    /* Avoid silly buffer filling at growing edge */
+                               if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
+                               fp->fs->winsect = sect;
+                       }
+#else
+                       if (fp->dsect != sect) {                                /* Fill sector buffer with file data */
+                               if (fp->fptr < fp->fsize &&
+                                       disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
+                                               ABORT(fp->fs, FR_DISK_ERR);
+                       }
+#endif
+                       fp->dsect = sect;
+                       fp->csect++;                                                    /* Next sector address in the cluster */
+               }
+               wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));    /* Put partial sector into file I/O buffer */
+               if (wcnt > btw) wcnt = btw;
+#if _FS_TINY
+               if (move_window(fp->fs, fp->dsect))                     /* Move sector window */
+                       ABORT(fp->fs, FR_DISK_ERR);
+               MemCpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);       /* Fit partial sector */
+               fp->fs->wflag = 1;
+#else
+               MemCpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);   /* Fit partial sector */
+               fp->flag |= FA__DIRTY;
+#endif
+       }
+
+       if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */
+       fp->flag |= FA__WRITTEN;                                                /* Set file changed flag */
+
+       LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File Object                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+       FIL *fp         /* Pointer to the file object */
+)
+{
+       FRESULT res;
+       DWORD tim;
+       BYTE *dir;
+
+
+       res = validate(fp->fs, fp->id);         /* Check validity of the object */
+       if (res == FR_OK) {
+               if (fp->flag & FA__WRITTEN) {   /* Has the file been written? */
+#if !_FS_TINY  /* Write-back dirty buffer */
+                       if (fp->flag & FA__DIRTY) {
+                               if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+                                       LEAVE_FF(fp->fs, FR_DISK_ERR);
+                               fp->flag &= (BYTE)~FA__DIRTY;
+                       }
+#endif
+                       /* Update the directory entry */
+                       res = move_window(fp->fs, fp->dir_sect);
+                       if (res == FR_OK) {
+                               dir = fp->dir_ptr;
+                               dir[DIR_Attr] |= AM_ARC;                                        /* Set archive bit */
+                               ST_DWORD(dir+DIR_FileSize, fp->fsize);          /* Update file size */
+                               ST_WORD(dir+DIR_FstClusLO, fp->org_clust);      /* Update start cluster */
+                               ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16);
+                               tim = get_fattime();                                    /* Updated time */
+                               ST_DWORD(dir+DIR_WrtTime, tim);
+                               fp->flag &= (BYTE)~FA__WRITTEN;
+                               fp->fs->wflag = 1;
+                               res = sync(fp->fs);
+                       }
+               }
+       }
+
+       LEAVE_FF(fp->fs, res);
+}
+
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+       FIL *fp         /* Pointer to the file object to be closed */
+)
+{
+       FRESULT res;
+
+
+#if _FS_READONLY
+       res = validate(fp->fs, fp->id);
+       if (res == FR_OK) fp->fs = NULL;
+       LEAVE_FF(fp->fs, res);
+#else
+       res = f_sync(fp);
+       if (res == FR_OK) fp->fs = NULL;
+       return res;
+#endif
+}
+
+
+
+
+#if _FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File R/W Pointer                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+       FIL *fp,                /* Pointer to the file object */
+       DWORD ofs               /* File pointer from top of file */
+)
+{
+       FRESULT res;
+       DWORD clst, bcs, nsect, ifptr;
+
+
+       res = validate(fp->fs, fp->id);         /* Check validity of the object */
+       if (res != FR_OK) LEAVE_FF(fp->fs, res);
+       if (fp->flag & FA__ERROR)                       /* Check abort flag */
+               LEAVE_FF(fp->fs, FR_INT_ERR);
+       if (ofs > fp->fsize                                     /* In read-only mode, clip offset with the file size */
+#if !_FS_READONLY
+                && !(fp->flag & FA_WRITE)
+#endif
+               ) ofs = fp->fsize;
+
+       ifptr = fp->fptr;
+       fp->fptr = 0; fp->csect = 255;
+       nsect = 0;
+       if (ofs > 0) {
+               bcs = (DWORD)fp->fs->csize * SS(fp->fs);        /* Cluster size (byte) */
+               if (ifptr > 0 &&
+                       (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
+                       fp->fptr = (ifptr - 1) & ~(bcs - 1);    /* start from the current cluster */
+                       ofs -= fp->fptr;
+                       clst = fp->curr_clust;
+               } else {                                                                        /* When seek to back cluster, */
+                       clst = fp->org_clust;                                   /* start from the first cluster */
+#if !_FS_READONLY
+                       if (clst == 0) {                                                /* If no cluster chain, create a new chain */
+                               clst = create_chain(fp->fs, 0);
+                               if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+                               if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                               fp->org_clust = clst;
+                       }
+#endif
+                       fp->curr_clust = clst;
+               }
+               if (clst != 0) {
+                       while (ofs > bcs) {                                             /* Cluster following loop */
+#if !_FS_READONLY
+                               if (fp->flag & FA_WRITE) {                      /* Check if in write mode or not */
+                                       clst = create_chain(fp->fs, clst);      /* Force streached if in write mode */
+                                       if (clst == 0) {                                /* When disk gets full, clip file size */
+                                               ofs = bcs; break;
+                                       }
+                               } else
+#endif
+                                       clst = get_cluster(fp->fs, clst);       /* Follow cluster chain if not in write mode */
+                               if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                               if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR);
+                               fp->curr_clust = clst;
+                               fp->fptr += bcs;
+                               ofs -= bcs;
+                       }
+                       fp->fptr += ofs;
+                       fp->csect = (BYTE)(ofs / SS(fp->fs));   /* Sector offset in the cluster */
+                       if (ofs % SS(fp->fs)) {
+                               nsect = clust2sect(fp->fs, clst);       /* Current sector */
+                               if (!nsect) ABORT(fp->fs, FR_INT_ERR);
+                               nsect += fp->csect;
+                               fp->csect++;
+                       }
+               }
+       }
+       if (nsect && nsect != fp->dsect && fp->fptr % SS(fp->fs)) {
+#if !_FS_TINY
+#if !_FS_READONLY
+               if (fp->flag & FA__DIRTY) {                     /* Write-back dirty buffer if needed */
+                       if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+                               ABORT(fp->fs, FR_DISK_ERR);
+                       fp->flag &= (BYTE)~FA__DIRTY;
+               }
+#endif
+               if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK)
+                       ABORT(fp->fs, FR_DISK_ERR);
+#endif
+               fp->dsect = nsect;
+       }
+#if !_FS_READONLY
+       if (fp->fptr > fp->fsize) {                     /* Set changed flag if the file size is extended */
+               fp->fsize = fp->fptr;
+               fp->flag |= FA__WRITTEN;
+       }
+#endif
+
+       LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+#if _FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directroy Object                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+       DIR *dj,                        /* Pointer to directory object to create */
+       const char *path        /* Pointer to the directory path */
+)
+{
+       FRESULT res;
+       NAMEBUF(sfn, lfn);
+       BYTE *dir;
+
+
+       res = auto_mount(&path, &dj->fs, 0);
+       if (res == FR_OK) {
+               INITBUF((*dj), sfn, lfn);
+               res = follow_path(dj, path);                    /* Follow the path to the directory */
+               if (res == FR_OK) {                                             /* Follow completed */
+                       dir = dj->dir;
+                       if (dir) {                                                      /* It is not the root dir */
+                               if (dir[DIR_Attr] & AM_DIR) {   /* The object is a directory */
+                                       dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+                               } else {                                                /* The object is not a directory */
+                                       res = FR_NO_PATH;
+                               }
+                       } else {                                                        /* It is the root dir */
+                               dj->sclust = (dj->fs->fs_type == FS_FAT32) ? dj->fs->dirbase : 0;
+                       }
+                       if (res == FR_OK) res = dir_seek(dj, 0);
+                       dj->id = dj->fs->id;
+               } else {
+                       if (res == FR_NO_FILE) res = FR_NO_PATH;
+               }
+       }
+
+       LEAVE_FF(dj->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entry in Sequense                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+       DIR *dj,                        /* Pointer to the open directory object */
+       FILINFO *fno            /* Pointer to file information to return */
+)
+{
+       FRESULT res;
+       NAMEBUF(sfn, lfn);
+
+
+       res = validate(dj->fs, dj->id);                 /* Check validity of the object */
+       if (res == FR_OK) {
+               INITBUF((*dj), sfn, lfn);
+               if (!fno) {
+                       res = dir_seek(dj, 0);
+               } else {
+                       res = dir_read(dj);
+                       if (res == FR_NO_FILE) {
+                               dj->sect = 0;
+                               res = FR_OK;
+                       }
+                       if (res == FR_OK) {                             /* A valid entry is found */
+                               get_fileinfo(dj, fno);          /* Get the object information */
+                               res = dir_next(dj, FALSE);      /* Increment index for next */
+                               if (res == FR_NO_FILE) {
+                                       dj->sect = 0;
+                                       res = FR_OK;
+                               }
+                       }
+               }
+       }
+
+       LEAVE_FF(dj->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+       const char *path,       /* Pointer to the file path */
+       FILINFO *fno            /* Pointer to file information to return */
+)
+{
+       FRESULT res;
+       DIR dj;
+       NAMEBUF(sfn, lfn);
+
+
+       res = auto_mount(&path, &dj.fs, 0);
+       if (res == FR_OK) {
+               INITBUF(dj, sfn, lfn);
+               res = follow_path(&dj, path);   /* Follow the file path */
+               if (res == FR_OK) {                             /* Follwo completed */
+                       if (dj.dir)     /* Found an object */
+                               get_fileinfo(&dj, fno);
+                       else            /* It is root dir */
+                               res = FR_INVALID_NAME;
+               }
+       }
+
+       LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Truncate File                                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+       FIL *fp         /* Pointer to the file object */
+)
+{
+       FRESULT res;
+       DWORD ncl;
+
+
+       res = validate(fp->fs, fp->id);         /* Check validity of the object */
+       if (res != FR_OK) LEAVE_FF(fp->fs, res);
+       if (fp->flag & FA__ERROR)                       /* Check abort flag */
+               LEAVE_FF(fp->fs, FR_INT_ERR);
+       if (!(fp->flag & FA_WRITE))                     /* Check access mode */
+               LEAVE_FF(fp->fs, FR_DENIED);
+
+       if (fp->fsize > fp->fptr) {
+               fp->fsize = fp->fptr;   /* Set file size to current R/W point */
+               fp->flag |= FA__WRITTEN;
+               if (fp->fptr == 0) {    /* When set file size to zero, remove entire cluster chain */
+                       res = remove_chain(fp->fs, fp->org_clust);
+                       fp->org_clust = 0;
+               } else {                                /* When truncate a part of the file, remove remaining clusters */
+                       ncl = get_cluster(fp->fs, fp->curr_clust);
+                       res = FR_OK;
+                       if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+                       if (ncl == 1) res = FR_INT_ERR;
+                       if (res == FR_OK && ncl < fp->fs->max_clust) {
+                               res = put_cluster(fp->fs, fp->curr_clust, 0x0FFFFFFF);
+                               if (res == FR_OK) res = remove_chain(fp->fs, ncl);
+                       }
+               }
+       }
+       if (res != FR_OK) fp->flag |= FA__ERROR;
+
+       LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+       const char *path,       /* Pointer to the logical drive number (root dir) */
+       DWORD *nclst,           /* Pointer to the variable to return number of free clusters */
+       FATFS **fatfs           /* Pointer to pointer to corresponding file system object to return */
+)
+{
+       FRESULT res;
+       DWORD n, clst, sect;
+       BYTE fat, f, *p;
+
+
+       /* Get drive number */
+       res = auto_mount(&path, fatfs, 0);
+       if (res != FR_OK) LEAVE_FF(*fatfs, res);
+
+       /* If number of free cluster is valid, return it without cluster scan. */
+       if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) {
+               *nclst = (*fatfs)->free_clust;
+               LEAVE_FF(*fatfs, FR_OK);
+       }
+
+       /* Get number of free clusters */
+       fat = (*fatfs)->fs_type;
+       n = 0;
+       if (fat == FS_FAT12) {
+               clst = 2;
+               do {
+                       if ((WORD)get_cluster(*fatfs, clst) == 0) n++;
+               } while (++clst < (*fatfs)->max_clust);
+       } else {
+               clst = (*fatfs)->max_clust;
+               sect = (*fatfs)->fatbase;
+               f = 0; p = 0;
+               do {
+                       if (!f) {
+                               res = move_window(*fatfs, sect++);
+                               if (res != FR_OK)
+                                       LEAVE_FF(*fatfs, res);
+                               p = (*fatfs)->win;
+                       }
+                       if (fat == FS_FAT16) {
+                               if (LD_WORD(p) == 0) n++;
+                               p += 2; f += 1;
+                       } else {
+                               if (LD_DWORD(p) == 0) n++;
+                               p += 4; f += 2;
+                       }
+               } while (--clst);
+       }
+       (*fatfs)->free_clust = n;
+       if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1;
+       *nclst = n;
+
+       LEAVE_FF(*fatfs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File or Directory                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+       const char *path                /* Pointer to the file or directory path */
+)
+{
+       FRESULT res;
+       DIR dj, sdj;
+       NAMEBUF(sfn, lfn);
+       BYTE *dir;
+       DWORD dclst;
+
+
+       res = auto_mount(&path, &dj.fs, 1);
+       if (res != FR_OK) LEAVE_FF(dj.fs, res);
+
+       INITBUF(dj, sfn, lfn);
+       res = follow_path(&dj, path);                   /* Follow the file path */
+       if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */
+
+       dir = dj.dir;
+       if (!dir)                                                               /* Is it the root directory? */
+               LEAVE_FF(dj.fs, FR_INVALID_NAME);
+       if (dir[DIR_Attr] & AM_RDO)                             /* Is it a R/O object? */
+               LEAVE_FF(dj.fs, FR_DENIED);
+       dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+
+       if (dir[DIR_Attr] & AM_DIR) {                   /* It is a sub-directory */
+               if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR);
+               MemCpy(&sdj, &dj, sizeof(DIR));         /* Check if the sub-dir is empty or not */
+               sdj.sclust = dclst;
+               res = dir_seek(&sdj, 0);
+               if (res != FR_OK) LEAVE_FF(dj.fs, res);
+               res = dir_read(&sdj);
+               if (res == FR_OK) res = FR_DENIED;      /* Not empty sub-dir */
+               if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res);
+       }
+
+       res = dir_remove(&dj);                                  /* Remove directory entry */
+       if (res == FR_OK) {
+               if (dclst)
+                       res = remove_chain(dj.fs, dclst);       /* Remove the cluster chain */
+               if (res == FR_OK) res = sync(dj.fs);
+       }
+
+       LEAVE_FF(dj.fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory                                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+       const char *path                /* Pointer to the directory path */
+)
+{
+       FRESULT res;
+       DIR dj;
+       NAMEBUF(sfn, lfn);
+       BYTE *dir, n;
+       DWORD dsect, dclst, pclst, tim;
+
+
+       res = auto_mount(&path, &dj.fs, 1);
+       if (res != FR_OK) LEAVE_FF(dj.fs, res);
+
+       INITBUF(dj, sfn, lfn);
+       res = follow_path(&dj, path);                   /* Follow the file path */
+       if (res == FR_OK) res = FR_EXIST;               /* Any file or directory is already existing */
+       if (res != FR_NO_FILE)                                  /* Any error occured */
+               LEAVE_FF(dj.fs, res);
+
+       dclst = create_chain(dj.fs, 0);                 /* Allocate a new cluster for new directory table */
+       res = FR_OK;
+       if (dclst == 0) res = FR_DENIED;
+       if (dclst == 1) res = FR_INT_ERR;
+       if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR;
+       if (res == FR_OK)
+               res = move_window(dj.fs, 0);
+       if (res != FR_OK) LEAVE_FF(dj.fs, res);
+       dsect = clust2sect(dj.fs, dclst);
+
+       dir = dj.fs->win;                                               /* Initialize the new directory table */
+       MemSet(dir, 0, SS(dj.fs));
+       MemSet(dir+DIR_Name, ' ', 8+3);         /* Create "." entry */
+       dir[DIR_Name] = '.';
+       dir[DIR_Attr] = AM_DIR;
+       tim = get_fattime();
+       ST_DWORD(dir+DIR_WrtTime, tim);
+       ST_WORD(dir+DIR_FstClusLO, dclst);
+       ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
+       MemCpy(dir+32, dir, 32);                        /* Create ".." entry */
+       dir[33] = '.';
+       pclst = dj.sclust;
+       if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase)
+               pclst = 0;
+       ST_WORD(dir+32+DIR_FstClusLO, pclst);
+       ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16);
+       for (n = 0; n < dj.fs->csize; n++) {    /* Write dot entries and clear left sectors */
+               dj.fs->winsect = dsect++;
+               dj.fs->wflag = 1;
+               res = move_window(dj.fs, 0);
+               if (res) LEAVE_FF(dj.fs, res);
+               MemSet(dir, 0, SS(dj.fs));
+       }
+
+       res = dir_register(&dj);
+       if (res != FR_OK) {
+               remove_chain(dj.fs, dclst);
+       } else {
+               dir = dj.dir;
+               dir[DIR_Attr] = AM_DIR;                                 /* Attribute */
+               ST_DWORD(dir+DIR_WrtTime, tim);                 /* Crated time */
+               ST_WORD(dir+DIR_FstClusLO, dclst);              /* Table start cluster */
+               ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
+               dj.fs->wflag = 1;
+               res = sync(dj.fs);
+       }
+
+       LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change File Attribute                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+       const char *path,       /* Pointer to the file path */
+       BYTE value,                     /* Attribute bits */
+       BYTE mask                       /* Attribute mask to change */
+)
+{
+       FRESULT res;
+       DIR dj;
+       NAMEBUF(sfn, lfn);
+       BYTE *dir;
+
+
+       res = auto_mount(&path, &dj.fs, 1);
+       if (res == FR_OK) {
+               INITBUF(dj, sfn, lfn);
+               res = follow_path(&dj, path);           /* Follow the file path */
+               if (res == FR_OK) {
+                       dir = dj.dir;
+                       if (!dir) {                                             /* Is it a root directory? */
+                               res = FR_INVALID_NAME;
+                       } else {                                                /* File or sub directory */
+                               mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;    /* Valid attribute mask */
+                               dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
+                               dj.fs->wflag = 1;
+                               res = sync(dj.fs);
+                       }
+               }
+       }
+
+       LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+       const char *path,       /* Pointer to the file/directory name */
+       const FILINFO *fno      /* Pointer to the timestamp to be set */
+)
+{
+       FRESULT res;
+       DIR dj;
+       NAMEBUF(sfn, lfn);
+       BYTE *dir;
+
+
+       res = auto_mount(&path, &dj.fs, 1);
+       if (res == FR_OK) {
+               INITBUF(dj, sfn, lfn);
+               res = follow_path(&dj, path);   /* Follow the file path */
+               if (res == FR_OK) {
+                       dir = dj.dir;
+                       if (!dir) {                             /* Root directory */
+                               res = FR_INVALID_NAME;
+                       } else {                                /* File or sub-directory */
+                               ST_WORD(dir+DIR_WrtTime, fno->ftime);
+                               ST_WORD(dir+DIR_WrtDate, fno->fdate);
+                               dj.fs->wflag = 1;
+                               res = sync(dj.fs);
+                       }
+               }
+       }
+
+       LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename File/Directory                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+       const char *path_old,   /* Pointer to the old name */
+       const char *path_new    /* Pointer to the new name */
+)
+{
+       FRESULT res;
+       DIR dj_old, dj_new;
+       NAMEBUF(sfn, lfn);
+       BYTE buf[21], *dir;
+       DWORD dw;
+
+
+       INITBUF(dj_old, sfn, lfn);
+       res = auto_mount(&path_old, &dj_old.fs, 1);
+       if (res == FR_OK) {
+               dj_new.fs = dj_old.fs;
+               res = follow_path(&dj_old, path_old);   /* Check old object */
+       }
+       if (res != FR_OK) LEAVE_FF(dj_old.fs, res);     /* The old object is not found */
+
+       if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE);       /* Is root dir? */
+       MemCpy(buf, dj_old.dir+DIR_Attr, 21);           /* Save the object information */
+
+       MemCpy(&dj_new, &dj_old, sizeof(DIR));
+       res = follow_path(&dj_new, path_new);           /* Check new object */
+       if (res == FR_OK) res = FR_EXIST;                       /* The new object name is already existing */
+       if (res == FR_NO_FILE) {                                        /* Is it a valid path and no name collision? */
+               res = dir_register(&dj_new);                    /* Register the new object */
+               if (res == FR_OK) {
+                       dir = dj_new.dir;                                       /* Copy object information into new entry */
+                       MemCpy(dir+13, buf+2, 19);
+                       dir[DIR_Attr] = buf[0];
+                       dj_old.fs->wflag = 1;
+                       if (dir[DIR_Attr] & AM_DIR) {           /* Update .. entry in the directory if needed */
+                               dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO));
+                               if (!dw) {
+                                       res = FR_INT_ERR;
+                               } else {
+                                       res = move_window(dj_new.fs, dw);
+                                       dir = dj_new.fs->win+32;
+                                       if (res == FR_OK && dir[1] == '.') {
+                                               dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust;
+                                               ST_WORD(dir+DIR_FstClusLO, dw);
+                                               ST_WORD(dir+DIR_FstClusHI, dw >> 16);
+                                               dj_new.fs->wflag = 1;
+                                       }
+                               }
+                       }
+                       if (res == FR_OK) {
+                               res = dir_remove(&dj_old);                      /* Remove old entry */
+                               if (res == FR_OK)
+                                       res = sync(dj_old.fs);
+                       }
+               }
+       }
+
+       LEAVE_FF(dj_old.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _FS_MINIMIZE == 0 */
+#endif /* _FS_MINIMIZE <= 1 */
+#endif /* _FS_MINIMIZE <= 2 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Forward data to the stream directly (Available on only _FS_TINY cfg)  */
+/*-----------------------------------------------------------------------*/
+#if _USE_FORWARD && _FS_TINY
+
+FRESULT f_forward (
+       FIL *fp,                                                /* Pointer to the file object */
+       UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
+       UINT btr,                                               /* Number of bytes to forward */
+       UINT *bf                                                /* Pointer to number of bytes forwarded */
+)
+{
+       FRESULT res;
+       DWORD remain, clst, sect;
+       UINT rcnt;
+
+
+       *bf = 0;
+
+       res = validate(fp->fs, fp->id);                                 /* Check validity of the object */
+       if (res != FR_OK) LEAVE_FF(fp->fs, res);
+       if (fp->flag & FA__ERROR)                                               /* Check error flag */
+               LEAVE_FF(fp->fs, FR_INT_ERR);
+       if (!(fp->flag & FA_READ))                                              /* Check access mode */
+               LEAVE_FF(fp->fs, FR_DENIED);
+
+       remain = fp->fsize - fp->fptr;
+       if (btr > remain) btr = (UINT)remain;                   /* Truncate btr by remaining bytes */
+
+       for ( ;  btr && (*func)(NULL, 0);                               /* Repeat until all data transferred or stream becomes busy */
+               fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
+               if ((fp->fptr % SS(fp->fs)) == 0) {                     /* On the sector boundary? */
+                       if (fp->csect >= fp->fs->csize) {               /* On the cluster boundary? */
+                               clst = (fp->fptr == 0) ?                        /* On the top of the file? */
+                                       fp->org_clust : get_cluster(fp->fs, fp->curr_clust);
+                               if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+                               if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                               fp->curr_clust = clst;                          /* Update current cluster */
+                               fp->csect = 0;                                          /* Reset sector address in the cluster */
+                       }
+                       fp->csect++;                                                    /* Next sector address in the cluster */
+               }
+               sect = clust2sect(fp->fs, fp->curr_clust);      /* Get current data sector */
+               if (!sect) ABORT(fp->fs, FR_INT_ERR);
+               sect += fp->csect - 1;
+               if (move_window(fp->fs, sect))                          /* Move sector window */
+                       ABORT(fp->fs, FR_DISK_ERR);
+               fp->dsect = sect;
+               rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));      /* Forward data from sector window */
+               if (rcnt > btr) rcnt = btr;
+               rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
+               if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
+       }
+
+       LEAVE_FF(fp->fs, FR_OK);
+}
+#endif /* _USE_FORWARD */
+
+
+
+#if _USE_MKFS && !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create File System on the Drive                                       */
+/*-----------------------------------------------------------------------*/
+#define N_ROOTDIR      512                     /* Multiple of 32 and <= 2048 */
+#define N_FATS         1                       /* 1 or 2 */
+#define MAX_SECTOR     131072000UL     /* Maximum partition size */
+#define MIN_SECTOR     2000UL          /* Minimum partition size */
+
+
+FRESULT f_mkfs (
+       BYTE drv,                       /* Logical drive number */
+       BYTE partition,         /* Partitioning rule 0:FDISK, 1:SFD */
+       WORD allocsize          /* Allocation unit size [bytes] */
+)
+{
+       static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000,   0 };
+       static const WORD cstbl[] =  {   32768,   16384,   8192,   4096,   2048, 16384,  8192,  4096, 2048, 1024, 512 };
+       BYTE fmt, m, *tbl;
+       DWORD b_part, b_fat, b_dir, b_data;             /* Area offset (LBA) */
+       DWORD n_part, n_rsv, n_fat, n_dir;              /* Area size */
+       DWORD n_clst, n;
+       WORD as;
+       FATFS *fs;
+       DSTATUS stat;
+
+
+       /* Check validity of the parameters */
+       if (drv >= _DRIVES) return FR_INVALID_DRIVE;
+       if (partition >= 2) return FR_MKFS_ABORTED;
+
+       /* Check mounted drive and clear work area */
+       fs = FatFs[drv];
+       if (!fs) return FR_NOT_ENABLED;
+       fs->fs_type = 0;
+       drv = LD2PD(drv);
+
+       /* Get disk statics */
+       stat = disk_initialize(drv);
+       if (stat & STA_NOINIT) return FR_NOT_READY;
+       if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+       if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR)
+               return FR_MKFS_ABORTED;
+       if (n_part > MAX_SECTOR) n_part = MAX_SECTOR;
+       b_part = (!partition) ? 63 : 0;         /* Boot sector */
+       n_part -= b_part;
+#if MAX_SS == 512
+       if (!allocsize) {                                       /* Auto selection of cluster size */
+               for (n = 0; n_part < sstbl[n]; n++) ;
+               allocsize = cstbl[n];
+       }
+#endif
+       for (as = 512; as <= 32768U && as != allocsize; as <<= 1);
+       if (as != allocsize) return FR_MKFS_ABORTED;
+#if MAX_SS > 512                                               /* Check disk sector size */
+       if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
+               || SS(fs) > S_MAX_SIZ
+               || SS(fs) > allocsize)
+               return FR_MKFS_ABORTED;
+#endif
+       allocsize /= SS(fs);            /* Number of sectors per cluster */
+
+       /* Pre-compute number of clusters and FAT type */
+       n_clst = n_part / allocsize;
+       fmt = FS_FAT12;
+       if (n_clst >= 0xFF5) fmt = FS_FAT16;
+       if (n_clst >= 0xFFF5) fmt = FS_FAT32;
+
+       /* Determine offset and size of FAT structure */
+       switch (fmt) {
+       case FS_FAT12:
+               n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs);
+               n_rsv = 1 + partition;
+               n_dir = N_ROOTDIR * 32 / SS(fs);
+               break;
+       case FS_FAT16:
+               n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs);
+               n_rsv = 1 + partition;
+               n_dir = N_ROOTDIR * 32 / SS(fs);
+               break;
+       default:
+               n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
+               n_rsv = 33 - partition;
+               n_dir = 0;
+       }
+       b_fat = b_part + n_rsv;                 /* FATs start sector */
+       b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */
+       b_data = b_dir + n_dir;                 /* Data start sector */
+
+       /* Align data start sector to erase block boundary (for flash memory media) */
+       if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED;
+       n = (b_data + n - 1) & ~(n - 1);
+       n_fat += (n - b_data) / N_FATS;
+       /* b_dir and b_data are no longer used below */
+
+       /* Determine number of cluster and final check of validity of the FAT type */
+       n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize;
+       if (   (fmt == FS_FAT16 && n_clst < 0xFF5)
+               || (fmt == FS_FAT32 && n_clst < 0xFFF5))
+               return FR_MKFS_ABORTED;
+
+       /* Create partition table if needed */
+       if (!partition) {
+               DWORD n_disk = b_part + n_part;
+
+               tbl = fs->win+MBR_Table;
+               ST_DWORD(tbl, 0x00010180);              /* Partition start in CHS */
+               if (n_disk < 63UL * 255 * 1024) {       /* Partition end in CHS */
+                       n_disk = n_disk / 63 / 255;
+                       tbl[7] = (BYTE)n_disk;
+                       tbl[6] = (BYTE)((n_disk >> 2) | 63);
+               } else {
+                       ST_WORD(&tbl[6], 0xFFFF);
+               }
+               tbl[5] = 254;
+               if (fmt != FS_FAT32)                    /* System ID */
+                       tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06;
+               else
+                       tbl[4] = 0x0c;
+               ST_DWORD(tbl+8, 63);                    /* Partition start in LBA */
+               ST_DWORD(tbl+12, n_part);               /* Partition size in LBA */
+               ST_WORD(tbl+64, 0xAA55);                /* Signature */
+               if (disk_write(drv, fs->win, 0, 1) != RES_OK)
+                       return FR_DISK_ERR;
+       }
+
+       /* Create boot record */
+       tbl = fs->win;                                                          /* Clear buffer */
+       MemSet(tbl, 0, SS(fs));
+       ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB);                     /* Boot code (jmp $, nop) */
+       memcpy(&tbl[BS_OEMName], "bootmii", 8);
+       ST_WORD(tbl+BPB_BytsPerSec, SS(fs));            /* Sector size */
+       tbl[BPB_SecPerClus] = (BYTE)allocsize;          /* Sectors per cluster */
+       ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);                     /* Reserved sectors */
+       tbl[BPB_NumFATs] = N_FATS;                                      /* Number of FATs */
+       ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */
+       if (n_part < 0x10000) {                                         /* Number of total sectors */
+               ST_WORD(tbl+BPB_TotSec16, n_part);
+       } else {
+               ST_DWORD(tbl+BPB_TotSec32, n_part);
+       }
+       tbl[BPB_Media] = 0xF8;                                          /* Media descripter */
+       ST_WORD(tbl+BPB_SecPerTrk, 63);                         /* Number of sectors per track */
+       ST_WORD(tbl+BPB_NumHeads, 255);                         /* Number of heads */
+       ST_DWORD(tbl+BPB_HiddSec, b_part);                      /* Hidden sectors */
+       n = get_fattime();                                                      /* Use current time as a VSN */
+       if (fmt != FS_FAT32) {
+               ST_DWORD(tbl+BS_VolID, n);                              /* Volume serial number */
+               ST_WORD(tbl+BPB_FATSz16, n_fat);                /* Number of secters per FAT */
+               tbl[BS_DrvNum] = 0x80;                                  /* Drive number */
+               tbl[BS_BootSig] = 0x29;                                 /* Extended boot signature */
+               MemCpy(tbl+BS_VolLab, "backupmii  FAT     ", 19);       /* Volume lavel, FAT signature */
+       } else {
+               ST_DWORD(tbl+BS_VolID32, n);                    /* Volume serial number */
+               ST_DWORD(tbl+BPB_FATSz32, n_fat);               /* Number of secters per FAT */
+               ST_DWORD(tbl+BPB_RootClus, 2);                  /* Root directory cluster (2) */
+               ST_WORD(tbl+BPB_FSInfo, 1);                             /* FSInfo record offset (bs+1) */
+               ST_WORD(tbl+BPB_BkBootSec, 6);                  /* Backup boot record offset (bs+6) */
+               tbl[BS_DrvNum32] = 0x80;                                /* Drive number */
+               tbl[BS_BootSig32] = 0x29;                               /* Extended boot signature */
+               MemCpy(tbl+BS_VolLab32, "backupmii  FAT32   ", 19);     /* Volume lavel, FAT signature */
+       }
+       ST_WORD(tbl+BS_55AA, 0xAA55);                           /* Signature */
+       if (disk_write(drv, tbl, b_part+0, 1) != RES_OK)
+               return FR_DISK_ERR;
+       if (fmt == FS_FAT32)
+               disk_write(drv, tbl, b_part+6, 1);
+
+       /* Initialize FAT area */
+       for (m = 0; m < N_FATS; m++) {
+               MemSet(tbl, 0, SS(fs));         /* 1st sector of the FAT  */
+               if (fmt != FS_FAT32) {
+                       n = (fmt == FS_FAT12) ? 0x00FFFFF8 : 0xFFFFFFF8;
+                       ST_DWORD(tbl, n);                               /* Reserve cluster #0-1 (FAT12/16) */
+               } else {
+                       ST_DWORD(tbl+0, 0xFFFFFFF8);    /* Reserve cluster #0-1 (FAT32) */
+                       ST_DWORD(tbl+4, 0xFFFFFFFF);
+                       ST_DWORD(tbl+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */
+               }
+               if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+                       return FR_DISK_ERR;
+               MemSet(tbl, 0, SS(fs));         /* Following FAT entries are filled by zero */
+               for (n = 1; n < n_fat; n++) {
+                       if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+                               return FR_DISK_ERR;
+               }
+       }
+
+       /* Initialize Root directory */
+       m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir);
+       do {
+               if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+                       return FR_DISK_ERR;
+       } while (--m);
+
+       /* Create FSInfo record if needed */
+       if (fmt == FS_FAT32) {
+               ST_WORD(tbl+BS_55AA, 0xAA55);
+               ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
+               ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
+               ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);
+               ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF);
+               disk_write(drv, tbl, b_part+1, 1);
+               disk_write(drv, tbl, b_part+7, 1);
+       }
+
+       return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR;
+}
+
+#endif /* _USE_MKFS && !_FS_READONLY */
+
+
+
+
+#if _USE_STRFUNC
+/*-----------------------------------------------------------------------*/
+/* Get a string from the file                                            */
+/*-----------------------------------------------------------------------*/
+char* f_gets (
+       char* buff,     /* Pointer to the string buffer to read */
+       int len,        /* Size of string buffer */
+       FIL* fil        /* Pointer to the file object */
+)
+{
+       int i = 0;
+       char *p = buff;
+       UINT rc;
+
+
+       while (i < len - 1) {                   /* Read bytes until buffer gets filled */
+               f_read(fil, p, 1, &rc);
+               if (rc != 1) break;                     /* Break when no data to read */
+#if _USE_STRFUNC >= 2
+               if (*p == '\r') continue;       /* Strip '\r' */
+#endif
+               i++;
+               if (*p++ == '\n') break;        /* Break when reached end of line */
+       }
+       *p = 0;
+       return i ? buff : NULL;                 /* When no data read (eof or error), return with error. */
+}
+
+
+
+#if !_FS_READONLY
+#include <stdarg.h>
+/*-----------------------------------------------------------------------*/
+/* Put a character to the file                                           */
+/*-----------------------------------------------------------------------*/
+int f_putc (
+       int chr,        /* A character to be output */
+       FIL* fil        /* Ponter to the file object */
+)
+{
+       UINT bw;
+       char c;
+
+
+#if _USE_STRFUNC >= 2
+       if (chr == '\n') f_putc ('\r', fil);    /* LF -> CRLF conversion */
+#endif
+       if (!fil) {     /* Special value may be used to switch the destination to any other device */
+       /*      put_console(chr);       */
+               return chr;
+       }
+       c = (char)chr;
+       f_write(fil, &c, 1, &bw);       /* Write a byte to the file */
+       return bw ? chr : EOF;          /* Return the result */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a string to the file                                              */
+/*-----------------------------------------------------------------------*/
+int f_puts (
+       const char* str,        /* Pointer to the string to be output */
+       FIL* fil                        /* Pointer to the file object */
+)
+{
+       int n;
+
+
+       for (n = 0; *str; str++, n++) {
+               if (f_putc(*str, fil) == EOF) return EOF;
+       }
+       return n;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a formatted string to the file                                    */
+/*-----------------------------------------------------------------------*/
+int f_printf (
+       FIL* fil,                       /* Pointer to the file object */
+       const char* str,        /* Pointer to the format string */
+       ...                                     /* Optional arguments... */
+)
+{
+       va_list arp;
+       UCHAR c, f, r;
+       ULONG val;
+       char s[16];
+       int i, w, res, cc;
+
+
+       va_start(arp, str);
+
+       for (cc = res = 0; cc != EOF; res += cc) {
+               c = *str++;
+               if (c == 0) break;                      /* End of string */
+               if (c != '%') {                         /* Non escape cahracter */
+                       cc = f_putc(c, fil);
+                       if (cc != EOF) cc = 1;
+                       continue;
+               }
+               w = f = 0;
+               c = *str++;
+               if (c == '0') {                         /* Flag: '0' padding */
+                       f = 1; c = *str++;
+               }
+               while (c >= '0' && c <= '9') {  /* Precision */
+                       w = w * 10 + (c - '0');
+                       c = *str++;
+               }
+               if (c == 'l') {                         /* Prefix: Size is long int */
+                       f |= 2; c = *str++;
+               }
+               if (c == 's') {                         /* Type is string */
+                       cc = f_puts(va_arg(arp, char*), fil);
+                       continue;
+               }
+               if (c == 'c') {                         /* Type is character */
+                       cc = f_putc(va_arg(arp, int), fil);
+                       if (cc != EOF) cc = 1;
+                       continue;
+               }
+               r = 0;
+               if (c == 'd') r = 10;           /* Type is signed decimal */
+               if (c == 'u') r = 10;           /* Type is unsigned decimal */
+               if (c == 'X') r = 16;           /* Type is unsigned hexdecimal */
+               if (r == 0) break;                      /* Unknown type */
+               if (f & 2) {                            /* Get the value */
+                       val = (ULONG)va_arg(arp, long);
+               } else {
+                       val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int);
+               }
+               /* Put numeral string */
+               if (c == 'd') {
+                       if (val & 0x80000000) {
+                               val = 0 - val;
+                               f |= 4;
+                       }
+               }
+               i = sizeof(s) - 1; s[i] = 0;
+               do {
+                       c = (UCHAR)(val % r + '0');
+                       if (c > '9') c += 7;
+                       s[--i] = c;
+                       val /= r;
+               } while (i && val);
+               if (i && (f & 4)) s[--i] = '-';
+               w = sizeof(s) - 1 - w;
+               while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';
+               cc = f_puts(&s[i], fil);
+       }
+
+       va_end(arp);
+       return (cc == EOF) ? cc : res;
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_STRFUNC */
diff --git a/ff.h b/ff.h
new file mode 100644 (file)
index 0000000..ea30078
--- /dev/null
+++ b/ff.h
@@ -0,0 +1,534 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module include file  R0.07        (C)ChaN, 2009
+/----------------------------------------------------------------------------/
+/ FatFs module is an open source project to implement FAT file system to small
+/ embedded systems. It is opened for education, research and development under
+/ license policy of following trems.
+/
+/  Copyright (C) 2009, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is no warranty.
+/ * You can use, modify and/or redistribute it for personal, non-profit or
+/   commercial use without any restriction under your responsibility.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/----------------------------------------------------------------------------*/
+
+#include "types.h"
+
+/*---------------------------------------------------------------------------/
+/ FatFs Configuration Options
+/
+/ CAUTION! Do not forget to make clean the project after any changes to
+/ the configuration options.
+/
+/----------------------------------------------------------------------------*/
+#ifndef _FATFS
+#define _FATFS
+
+#define _WORD_ACCESS   0
+/* The _WORD_ACCESS option defines which access method is used to the word
+/  data in the FAT structure.
+/
+/   0: Byte-by-byte access. Always compatible with all platforms.
+/   1: Word access. Do not choose this unless following condition is met.
+/
+/  When the byte order on the memory is big-endian or address miss-aligned
+/  word access results incorrect behavior, the _WORD_ACCESS must be set to 0.
+/  If it is not the case, the value can also be set to 1 to improve the
+/  performance and code efficiency. */
+
+
+#define _FS_READONLY   0
+/* Setting _FS_READONLY to 1 defines read only configuration. This removes
+/  writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename,
+/  f_truncate and useless f_getfree. */
+
+
+#define _FS_MINIMIZE   0
+/* The _FS_MINIMIZE option defines minimization level to remove some functions.
+/
+/   0: Full function.
+/   1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename
+/      are removed.
+/   2: f_opendir and f_readdir are removed in addition to level 1.
+/   3: f_lseek is removed in addition to level 2. */
+
+
+#define        _FS_TINY        0
+/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system
+/  object instead of the sector buffer in the individual file object for file
+/  data transfer. This reduces memory consumption 512 bytes each file object. */
+
+
+#define _DRIVES                1
+/* Number of volumes (logical drives) to be used. */
+
+
+#define        _USE_STRFUNC    0
+/* To enable string functions, set _USE_STRFUNC to 1 or 2. */
+
+
+#define        _USE_MKFS       1
+/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */
+
+
+#define        _USE_FORWARD    0
+/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */
+
+
+#define        _USE_LFN        0
+#define        _MAX_LFN        255             /* Maximum LFN length to handle (max:255) */
+/* The _USE_LFN option switches the LFN support.
+/
+/   0: Disable LFN.
+/   1: Enable LFN with static working buffer on the bss. Not re-entrant.
+/   2: Enable LFN with dynamic working buffer on the caller's 'stack'.
+/
+/  The working buffer occupies (_MAX_LFN + 1) * 2 bytes. When enable LFN,
+/  a Unicode - OEM code conversion function ff_convert() must be linked. */
+
+
+#define _CODE_PAGE     437
+/* The _CODE_PAGE specifies the OEM code page to be used on the target system.
+/  When it is non LFN configuration, there is no difference between SBCS code
+/  pages. When LFN is enabled, the code page must always be set correctly.
+/   437 - U.S.
+/   720 - Arabic
+/   737 - Greek
+/   775 - Baltic
+/   850 - Multilingual Latin 1
+/   852 - Latin 2
+/   855 - Cyrillic
+/   857 - Turkish
+/   858 - Multilingual Latin 1 + Euro
+/   862 - Hebrew
+/   866 - Russian
+/   874 - Thai
+/   932 - Japanese Shift-JIS       (DBCS)
+/   936 - Simplified Chinese GBK   (DBCS)
+/   949 - Korean                   (DBCS)
+/   950 - Traditional Chinese Big5 (DBCS)
+/   1258 - Vietnam
+*/
+
+
+#define        _MULTI_PARTITION        0
+/* When _MULTI_PARTITION is set to 0, each volume is bound to same physical
+/ drive number and can mount only 1st primaly partition. When it is set to 1,
+/ each volume is tied to the partition listed in Drives[]. */
+
+
+#define _FS_REENTRANT  0
+#define _TIMEOUT               1000
+/* To make the FatFs module re-entrant, set 1 and re-write platform dependent
+/  lock out code that defined arownd _FS_REENTRANT. The _TIMEOUT defines the
+/  time out value in unit of milliseconds on the multi access exclusion. */
+
+
+#define _EXCLUDE_LIB   0
+/* When _EXCLUDE_LIB is set to 1, FatFs module does not use standard library. */
+
+
+
+/* End of configuration options. Do not change followings without care.     */
+/*--------------------------------------------------------------------------*/
+
+
+
+/* Definitions corresponds to multiple sector size (Not tested) */
+
+#define        MAX_SS  512U    /* Do not change */
+#if MAX_SS > 512U
+#define        SS(fs)  ((fs)->s_size)
+#else
+#define        SS(fs)  512U
+#endif
+
+
+
+/* File system object structure */
+
+typedef struct _FATFS {
+       BYTE    fs_type;        /* FAT sub type */
+       BYTE    drive;          /* Physical drive number */
+       BYTE    csize;          /* Number of sectors per cluster */
+       BYTE    n_fats;         /* Number of FAT copies */
+       BYTE    wflag;          /* win[] dirty flag (1:must be written back) */
+       BYTE    pad1;
+       WORD    id;                     /* File system mount ID */
+       WORD    n_rootdir;      /* Number of root directory entries (0 on FAT32) */
+#if _FS_REENTRANT
+       HANDLE  h_mutex;        /* Handle to the mutex (Platform dependent) */
+#endif
+#if MAX_SS > 512U
+       WORD    s_size;         /* Sector size */
+#endif
+#if !_FS_READONLY
+       BYTE    fsi_flag;       /* fsinfo dirty flag (1:must be written back) */
+       BYTE    pad2;
+       DWORD   last_clust;     /* Last allocated cluster */
+       DWORD   free_clust;     /* Number of free clusters */
+       DWORD   fsi_sector;     /* fsinfo sector */
+#endif
+       DWORD   sects_fat;      /* Sectors per fat */
+       DWORD   max_clust;      /* Maximum cluster# + 1. Number of clusters is max_clust - 2 */
+       DWORD   fatbase;        /* FAT start sector */
+       DWORD   dirbase;        /* Root directory start sector (Cluster# on FAT32) */
+       DWORD   database;       /* Data start sector */
+       DWORD   winsect;        /* Current sector appearing in the win[] */
+       BYTE    win[MAX_SS];/* Disk access window for Directory/FAT */
+} FATFS;
+
+
+
+/* Directory object structure */
+
+typedef struct _DIR {
+       WORD    id;                     /* Owner file system mount ID */
+       WORD    index;          /* Current index number */
+       FATFS*  fs;                     /* Pointer to the owner file system object */
+       DWORD   sclust;         /* Table start cluster (0:Static table) */
+       DWORD   clust;          /* Current cluster */
+       DWORD   sect;           /* Current sector */
+       BYTE*   dir;            /* Pointer to the current SFN entry in the win[] */
+       BYTE*   fn;                     /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
+#if _USE_LFN
+       WCHAR*  lfn;            /* Pointer to the LFN working buffer */
+       WORD    lfn_idx;        /* Last matched LFN index (0xFFFF:No LFN) */
+#endif
+} DIR;
+
+
+
+/* File object structure */
+
+typedef struct _FIL {
+       FATFS*  fs;                     /* Pointer to the owner file system object */
+       WORD    id;                     /* Owner file system mount ID */
+       BYTE    flag;           /* File status flags */
+       BYTE    csect;          /* Sector address in the cluster */
+       DWORD   fptr;           /* File R/W pointer */
+       DWORD   fsize;          /* File size */
+       DWORD   org_clust;      /* File start cluster */
+       DWORD   curr_clust;     /* Current cluster */
+       DWORD   dsect;          /* Current data sector */
+#if _FS_READONLY == 0
+       DWORD   dir_sect;       /* Sector containing the directory entry */
+       BYTE*   dir_ptr;        /* Ponter to the directory entry in the window */
+#endif
+#if !_FS_TINY
+       BYTE    buf[MAX_SS];/* File R/W buffer */
+#endif
+} FIL;
+
+
+
+/* File status structure */
+
+typedef struct _FILINFO {
+       DWORD fsize;            /* File size */
+       WORD fdate;                     /* Last modified date */
+       WORD ftime;                     /* Last modified time */
+       BYTE fattrib;           /* Attribute */
+       char fname[13];         /* Short file name (8.3 format) */
+#if _USE_LFN
+       char *lfname;           /* Pointer to the LFN buffer */
+       int lfsize;                     /* Size of LFN buffer [bytes] */
+#endif
+} FILINFO;
+
+
+
+/* DBCS code ranges */
+
+#if _CODE_PAGE == 932  /* CP932 (Japanese Shift-JIS) */
+#define _DF1S  0x81    /* DBC 1st byte range 1 start */
+#define _DF1E  0x9F    /* DBC 1st byte range 1 end */
+#define _DF2S  0xE0    /* DBC 1st byte range 2 start */
+#define _DF2E  0xFC    /* DBC 1st byte range 2 end */
+#define _DS1S  0x40    /* DBC 2nd byte range 1 start */
+#define _DS1E  0x7E    /* DBC 2nd byte range 1 end */
+#define _DS2S  0x80    /* DBC 2nd byte range 2 start */
+#define _DS2E  0xFC    /* DBC 2nd byte range 2 end */
+
+#elif _CODE_PAGE == 936        /* CP936 (Simplified Chinese GBK) */
+#define _DF1S  0x81
+#define _DF1E  0xFE
+#define _DS1S  0x40
+#define _DS1E  0x7E
+#define _DS2S  0x80
+#define _DS2E  0xFE
+
+#elif _CODE_PAGE == 949        /* CP949 (Korean) */
+#define _DF1S  0x81
+#define _DF1E  0xFE
+#define _DS1S  0x41
+#define _DS1E  0x5A
+#define _DS2S  0x61
+#define _DS2E  0x7A
+#define _DS3S  0x81
+#define _DS3E  0xFE
+
+#elif _CODE_PAGE == 950        /* CP950 (Traditional Chinese Big5) */
+#define _DF1S  0x81
+#define _DF1E  0xFE
+#define _DS1S  0x40
+#define _DS1E  0x7E
+#define _DS2S  0xA1
+#define _DS2E  0xFE
+
+#else                                  /* SBCS code pages */
+#define _DF1S  0
+
+#endif
+
+
+
+/* Character code support macros */
+
+#define IsUpper(c)     (((c)>='A')&&((c)<='Z'))
+#define IsLower(c)     (((c)>='a')&&((c)<='z'))
+#define IsDigit(c)     (((c)>='0')&&((c)<='9'))
+
+#if _DF1S      /* DBCS configuration */
+
+#if _DF2S      /* Two 1st byte areas */
+#define IsDBCS1(c)     (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
+#else          /* One 1st byte area */
+#define IsDBCS1(c)     ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
+#endif
+
+#if _DS3S      /* Three 2nd byte areas */
+#define IsDBCS2(c)     (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
+#else          /* Two 2nd byte areas */
+#define IsDBCS2(c)     (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
+#endif
+
+#else          /* SBCS configuration */
+
+#define IsDBCS1(c)     0
+#define IsDBCS2(c)     0
+
+#endif /* _DF1S */
+
+
+
+/* Definitions corresponds to multi partition */
+
+#if _MULTI_PARTITION           /* Multiple partition configuration */
+
+typedef struct _PARTITION {
+       BYTE pd;        /* Physical drive# */
+       BYTE pt;        /* Partition # (0-3) */
+} PARTITION;
+
+extern
+const PARTITION Drives[];                      /* Logical drive# to physical location conversion table */
+#define LD2PD(drv) (Drives[drv].pd)    /* Get physical drive# */
+#define LD2PT(drv) (Drives[drv].pt)    /* Get partition# */
+
+#else                                          /* Single partition configuration */
+
+#define LD2PD(drv) (drv)       /* Physical drive# is equal to the logical drive# */
+#define LD2PT(drv) 0           /* Always mounts the 1st partition */
+
+#endif
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+       FR_OK = 0,                      /* 0 */
+       FR_DISK_ERR,            /* 1 */
+       FR_INT_ERR,                     /* 2 */
+       FR_NOT_READY,           /* 3 */
+       FR_NO_FILE,                     /* 4 */
+       FR_NO_PATH,                     /* 5 */
+       FR_INVALID_NAME,        /* 6 */
+       FR_DENIED,                      /* 7 */
+       FR_EXIST,                       /* 8 */
+       FR_INVALID_OBJECT,      /* 9 */
+       FR_WRITE_PROTECTED,     /* 10 */
+       FR_INVALID_DRIVE,       /* 11 */
+       FR_NOT_ENABLED,         /* 12 */
+       FR_NO_FILESYSTEM,       /* 13 */
+       FR_MKFS_ABORTED,        /* 14 */
+       FR_TIMEOUT                      /* 15 */
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface                           */
+
+FRESULT f_mount (BYTE, FATFS*);                                                /* Mount/Unmount a logical drive */
+FRESULT f_open (FIL*, const char*, BYTE);                      /* Open or create a file */
+FRESULT f_read (FIL*, void*, UINT, UINT*);                     /* Read data from a file */
+FRESULT f_write (FIL*, const void*, UINT, UINT*);      /* Write data to a file */
+FRESULT f_lseek (FIL*, DWORD);                                         /* Move file pointer of a file object */
+FRESULT f_close (FIL*);                                                                /* Close an open file object */
+FRESULT f_opendir (DIR*, const char*);                         /* Open an existing directory */
+FRESULT f_readdir (DIR*, FILINFO*);                                    /* Read a directory item */
+FRESULT f_stat (const char*, FILINFO*);                                /* Get file status */
+FRESULT f_getfree (const char*, DWORD*, FATFS**);      /* Get number of free clusters on the drive */
+FRESULT f_truncate (FIL*);                                                     /* Truncate file */
+FRESULT f_sync (FIL*);                                                         /* Flush cached data of a writing file */
+FRESULT f_unlink (const char*);                                                /* Delete an existing file or directory */
+FRESULT        f_mkdir (const char*);                                          /* Create a new directory */
+FRESULT f_chmod (const char*, BYTE, BYTE);                     /* Change attriburte of the file/dir */
+FRESULT f_utime (const char*, const FILINFO*);         /* Change timestamp of the file/dir */
+FRESULT f_rename (const char*, const char*);           /* Rename/Move a file or directory */
+FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*);      /* Forward data to the stream */
+FRESULT f_mkfs (BYTE, BYTE, WORD);                                     /* Create a file system on the drive */
+
+#if _USE_STRFUNC
+int f_putc (int, FIL*);                                                                /* Put a character to the file */
+int f_puts (const char*, FIL*);                                                /* Put a string to the file */
+int f_printf (FIL*, const char*, ...);                         /* Put a formatted string to the file */
+char* f_gets (char*, int, FIL*);                                       /* Get a string from the file */
+#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)
+#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0)
+#ifndef EOF
+#define EOF -1
+#endif
+#endif
+
+
+/*--------------------------------------------------------------*/
+/* User defined functions                                       */
+
+
+/* Real time clock */
+DWORD get_fattime (void);      /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
+                                                       /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
+
+/* Unicode - OEM code conversion */
+#if _USE_LFN
+WCHAR ff_convert (WCHAR, UINT);
+#endif
+
+DWORD clust2sect(FATFS *fs, DWORD clust);
+DWORD f_check_contig(FIL *fp);
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address                                     */
+
+
+/* File access control and file status flags (FIL.flag) */
+
+#define        FA_READ                         0x01
+#define        FA_OPEN_EXISTING        0x00
+#if _FS_READONLY == 0
+#define        FA_WRITE                        0x02
+#define        FA_CREATE_NEW           0x04
+#define        FA_CREATE_ALWAYS        0x08
+#define        FA_OPEN_ALWAYS          0x10
+#define FA__WRITTEN                    0x20
+#define FA__DIRTY                      0x40
+#endif
+#define FA__ERROR                      0x80
+
+
+/* FAT sub type (FATFS.fs_type) */
+
+#define FS_FAT12       1
+#define FS_FAT16       2
+#define FS_FAT32       3
+
+
+/* File attribute bits for directory entry */
+
+#define        AM_RDO  0x01    /* Read only */
+#define        AM_HID  0x02    /* Hidden */
+#define        AM_SYS  0x04    /* System */
+#define        AM_VOL  0x08    /* Volume label */
+#define AM_LFN 0x0F    /* LFN entry */
+#define AM_DIR 0x10    /* Directory */
+#define AM_ARC 0x20    /* Archive */
+#define AM_MASK        0x3F    /* Mask of defined bits */
+
+
+/* FatFs refers the members in the FAT structures with byte offset instead
+/ of structure member because there are incompatibility of the packing option
+/ between various compilers. */
+
+#define BS_jmpBoot                     0
+#define BS_OEMName                     3
+#define BPB_BytsPerSec         11
+#define BPB_SecPerClus         13
+#define BPB_RsvdSecCnt         14
+#define BPB_NumFATs                    16
+#define BPB_RootEntCnt         17
+#define BPB_TotSec16           19
+#define BPB_Media                      21
+#define BPB_FATSz16                    22
+#define BPB_SecPerTrk          24
+#define BPB_NumHeads           26
+#define BPB_HiddSec                    28
+#define BPB_TotSec32           32
+#define BS_55AA                                510
+
+#define BS_DrvNum                      36
+#define BS_BootSig                     38
+#define BS_VolID                       39
+#define BS_VolLab                      43
+#define BS_FilSysType          54
+
+#define BPB_FATSz32                    36
+#define BPB_ExtFlags           40
+#define BPB_FSVer                      42
+#define BPB_RootClus           44
+#define BPB_FSInfo                     48
+#define BPB_BkBootSec          50
+#define BS_DrvNum32                    64
+#define BS_BootSig32           66
+#define BS_VolID32                     67
+#define BS_VolLab32                    71
+#define BS_FilSysType32                82
+
+#define        FSI_LeadSig                     0
+#define        FSI_StrucSig            484
+#define        FSI_Free_Count          488
+#define        FSI_Nxt_Free            492
+
+#define MBR_Table                      446
+
+#define        DIR_Name                        0
+#define        DIR_Attr                        11
+#define        DIR_NTres                       12
+#define        DIR_CrtTime                     14
+#define        DIR_CrtDate                     16
+#define        DIR_FstClusHI           20
+#define        DIR_WrtTime                     22
+#define        DIR_WrtDate                     24
+#define        DIR_FstClusLO           26
+#define        DIR_FileSize            28
+#define        LDIR_Ord                        0
+#define        LDIR_Attr                       11
+#define        LDIR_Type                       12
+#define        LDIR_Chksum                     13
+#define        LDIR_FstClusLO          26
+
+
+
+/*--------------------------------*/
+/* Multi-byte word access macros  */
+
+#if _WORD_ACCESS == 1  /* Enable word access to the FAT structure */
+#define        LD_WORD(ptr)            (WORD)(*(WORD*)(BYTE*)(ptr))
+#define        LD_DWORD(ptr)           (DWORD)(*(DWORD*)(BYTE*)(ptr))
+#define        ST_WORD(ptr,val)        *(WORD*)(BYTE*)(ptr)=(WORD)(val)
+#define        ST_DWORD(ptr,val)       *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
+#else                                  /* Use byte-by-byte access to the FAT structure */
+#define        LD_WORD(ptr)            (WORD)(((WORD)*(volatile BYTE*)((ptr)+1)<<8)|(WORD)*(volatile BYTE*)(ptr))
+#define        LD_DWORD(ptr)           (DWORD)(((DWORD)*(volatile BYTE*)((ptr)+3)<<24)|((DWORD)*(volatile BYTE*)((ptr)+2)<<16)|((WORD)*(volatile BYTE*)((ptr)+1)<<8)|*(volatile BYTE*)(ptr))
+#define        ST_WORD(ptr,val)        *(volatile BYTE*)(ptr)=(BYTE)(val); *(volatile BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8)
+#define        ST_DWORD(ptr,val)       *(volatile BYTE*)(ptr)=(BYTE)(val); *(volatile BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(volatile BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(volatile BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24)
+#endif
+
+
+#endif /* _FATFS */
diff --git a/font.c b/font.c
new file mode 100644 (file)
index 0000000..bd9414e
--- /dev/null
+++ b/font.c
@@ -0,0 +1,4613 @@
+unsigned char console_font_8x16[] = {
+
+       /* 0 0x00 '^@' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 1 0x01 '^A' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x81, /* 10000001 */
+       0xa5, /* 10100101 */
+       0x81, /* 10000001 */
+       0x81, /* 10000001 */
+       0xbd, /* 10111101 */
+       0x99, /* 10011001 */
+       0x81, /* 10000001 */
+       0x81, /* 10000001 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 2 0x02 '^B' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0xff, /* 11111111 */
+       0xdb, /* 11011011 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xc3, /* 11000011 */
+       0xe7, /* 11100111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 3 0x03 '^C' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x6c, /* 01101100 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0x10, /* 00010000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 4 0x04 '^D' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x7c, /* 01111100 */
+       0xfe, /* 11111110 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0x10, /* 00010000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 5 0x05 '^E' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0xe7, /* 11100111 */
+       0xe7, /* 11100111 */
+       0xe7, /* 11100111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 6 0x06 '^F' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 7 0x07 '^G' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 8 0x08 '^H' */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xe7, /* 11100111 */
+       0xc3, /* 11000011 */
+       0xc3, /* 11000011 */
+       0xe7, /* 11100111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+
+       /* 9 0x09 '^I' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x42, /* 01000010 */
+       0x42, /* 01000010 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 10 0x0a '^J' */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xc3, /* 11000011 */
+       0x99, /* 10011001 */
+       0xbd, /* 10111101 */
+       0xbd, /* 10111101 */
+       0x99, /* 10011001 */
+       0xc3, /* 11000011 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+
+       /* 11 0x0b '^K' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1e, /* 00011110 */
+       0x0e, /* 00001110 */
+       0x1a, /* 00011010 */
+       0x32, /* 00110010 */
+       0x78, /* 01111000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x78, /* 01111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 12 0x0c '^L' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 13 0x0d '^M' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3f, /* 00111111 */
+       0x33, /* 00110011 */
+       0x3f, /* 00111111 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x70, /* 01110000 */
+       0xf0, /* 11110000 */
+       0xe0, /* 11100000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 14 0x0e '^N' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7f, /* 01111111 */
+       0x63, /* 01100011 */
+       0x7f, /* 01111111 */
+       0x63, /* 01100011 */
+       0x63, /* 01100011 */
+       0x63, /* 01100011 */
+       0x63, /* 01100011 */
+       0x67, /* 01100111 */
+       0xe7, /* 11100111 */
+       0xe6, /* 11100110 */
+       0xc0, /* 11000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 15 0x0f '^O' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xdb, /* 11011011 */
+       0x3c, /* 00111100 */
+       0xe7, /* 11100111 */
+       0x3c, /* 00111100 */
+       0xdb, /* 11011011 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 16 0x10 '^P' */
+       0x00, /* 00000000 */
+       0x80, /* 10000000 */
+       0xc0, /* 11000000 */
+       0xe0, /* 11100000 */
+       0xf0, /* 11110000 */
+       0xf8, /* 11111000 */
+       0xfe, /* 11111110 */
+       0xf8, /* 11111000 */
+       0xf0, /* 11110000 */
+       0xe0, /* 11100000 */
+       0xc0, /* 11000000 */
+       0x80, /* 10000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 17 0x11 '^Q' */
+       0x00, /* 00000000 */
+       0x02, /* 00000010 */
+       0x06, /* 00000110 */
+       0x0e, /* 00001110 */
+       0x1e, /* 00011110 */
+       0x3e, /* 00111110 */
+       0xfe, /* 11111110 */
+       0x3e, /* 00111110 */
+       0x1e, /* 00011110 */
+       0x0e, /* 00001110 */
+       0x06, /* 00000110 */
+       0x02, /* 00000010 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 18 0x12 '^R' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 19 0x13 '^S' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 20 0x14 '^T' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7f, /* 01111111 */
+       0xdb, /* 11011011 */
+       0xdb, /* 11011011 */
+       0xdb, /* 11011011 */
+       0x7b, /* 01111011 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 21 0x15 '^U' */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0x60, /* 01100000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x0c, /* 00001100 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 22 0x16 '^V' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 23 0x17 '^W' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 24 0x18 '^X' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 25 0x19 '^Y' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 26 0x1a '^Z' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0xfe, /* 11111110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 27 0x1b '^[' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xfe, /* 11111110 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 28 0x1c '^\' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 29 0x1d '^]' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x28, /* 00101000 */
+       0x6c, /* 01101100 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0x28, /* 00101000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 30 0x1e '^^' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x38, /* 00111000 */
+       0x7c, /* 01111100 */
+       0x7c, /* 01111100 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 31 0x1f '^_' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0x7c, /* 01111100 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0x38, /* 00111000 */
+       0x10, /* 00010000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 32 0x20 ' ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 33 0x21 '!' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 34 0x22 '"' */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x24, /* 00100100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 35 0x23 '#' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 36 0x24 '$' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc2, /* 11000010 */
+       0xc0, /* 11000000 */
+       0x7c, /* 01111100 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x86, /* 10000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 37 0x25 '%' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc2, /* 11000010 */
+       0xc6, /* 11000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xc6, /* 11000110 */
+       0x86, /* 10000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 38 0x26 '&' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 39 0x27 ''' */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 40 0x28 '(' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 41 0x29 ')' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 42 0x2a '*' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0xff, /* 11111111 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 43 0x2b '+' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 44 0x2c ',' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 45 0x2d '-' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 46 0x2e '.' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 47 0x2f '/' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x02, /* 00000010 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xc0, /* 11000000 */
+       0x80, /* 10000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 48 0x30 '0' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 49 0x31 '1' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x38, /* 00111000 */
+       0x78, /* 01111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 50 0x32 '2' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 51 0x33 '3' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x3c, /* 00111100 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 52 0x34 '4' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x0c, /* 00001100 */
+       0x1c, /* 00011100 */
+       0x3c, /* 00111100 */
+       0x6c, /* 01101100 */
+       0xcc, /* 11001100 */
+       0xfe, /* 11111110 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x1e, /* 00011110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 53 0x35 '5' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xfc, /* 11111100 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 54 0x36 '6' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x60, /* 01100000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xfc, /* 11111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 55 0x37 '7' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 56 0x38 '8' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 57 0x39 '9' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7e, /* 01111110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x78, /* 01111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 58 0x3a ':' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 59 0x3b ';' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 60 0x3c '<' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x06, /* 00000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 61 0x3d '=' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 62 0x3e '>' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 63 0x3f '?' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 64 0x40 '@' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xde, /* 11011110 */
+       0xde, /* 11011110 */
+       0xde, /* 11011110 */
+       0xdc, /* 11011100 */
+       0xc0, /* 11000000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 65 0x41 'A' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 66 0x42 'B' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfc, /* 11111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0xfc, /* 11111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 67 0x43 'C' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0xc2, /* 11000010 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc2, /* 11000010 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 68 0x44 'D' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xf8, /* 11111000 */
+       0x6c, /* 01101100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x6c, /* 01101100 */
+       0xf8, /* 11111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 69 0x45 'E' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x66, /* 01100110 */
+       0x62, /* 01100010 */
+       0x68, /* 01101000 */
+       0x78, /* 01111000 */
+       0x68, /* 01101000 */
+       0x60, /* 01100000 */
+       0x62, /* 01100010 */
+       0x66, /* 01100110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 70 0x46 'F' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x66, /* 01100110 */
+       0x62, /* 01100010 */
+       0x68, /* 01101000 */
+       0x78, /* 01111000 */
+       0x68, /* 01101000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 71 0x47 'G' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0xc2, /* 11000010 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xde, /* 11011110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x66, /* 01100110 */
+       0x3a, /* 00111010 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 72 0x48 'H' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 73 0x49 'I' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 74 0x4a 'J' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1e, /* 00011110 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x78, /* 01111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 75 0x4b 'K' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xe6, /* 11100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x6c, /* 01101100 */
+       0x78, /* 01111000 */
+       0x78, /* 01111000 */
+       0x6c, /* 01101100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0xe6, /* 11100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 76 0x4c 'L' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xf0, /* 11110000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x62, /* 01100010 */
+       0x66, /* 01100110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 77 0x4d 'M' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xee, /* 11101110 */
+       0xfe, /* 11111110 */
+       0xfe, /* 11111110 */
+       0xd6, /* 11010110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 78 0x4e 'N' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xe6, /* 11100110 */
+       0xf6, /* 11110110 */
+       0xfe, /* 11111110 */
+       0xde, /* 11011110 */
+       0xce, /* 11001110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 79 0x4f 'O' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 80 0x50 'P' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfc, /* 11111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 81 0x51 'Q' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xd6, /* 11010110 */
+       0xde, /* 11011110 */
+       0x7c, /* 01111100 */
+       0x0c, /* 00001100 */
+       0x0e, /* 00001110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 82 0x52 'R' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfc, /* 11111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x6c, /* 01101100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0xe6, /* 11100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 83 0x53 'S' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x60, /* 01100000 */
+       0x38, /* 00111000 */
+       0x0c, /* 00001100 */
+       0x06, /* 00000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 84 0x54 'T' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x5a, /* 01011010 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 85 0x55 'U' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 86 0x56 'V' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x10, /* 00010000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 87 0x57 'W' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xfe, /* 11111110 */
+       0xee, /* 11101110 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 88 0x58 'X' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x7c, /* 01111100 */
+       0x38, /* 00111000 */
+       0x38, /* 00111000 */
+       0x7c, /* 01111100 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 89 0x59 'Y' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 90 0x5a 'Z' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0x86, /* 10000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xc2, /* 11000010 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 91 0x5b '[' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 92 0x5c '\' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x80, /* 10000000 */
+       0xc0, /* 11000000 */
+       0xe0, /* 11100000 */
+       0x70, /* 01110000 */
+       0x38, /* 00111000 */
+       0x1c, /* 00011100 */
+       0x0e, /* 00001110 */
+       0x06, /* 00000110 */
+       0x02, /* 00000010 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 93 0x5d ']' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 94 0x5e '^' */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 95 0x5f '_' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 96 0x60 '`' */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 97 0x61 'a' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 98 0x62 'b' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xe0, /* 11100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x78, /* 01111000 */
+       0x6c, /* 01101100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 99 0x63 'c' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 100 0x64 'd' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1c, /* 00011100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x3c, /* 00111100 */
+       0x6c, /* 01101100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 101 0x65 'e' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 102 0x66 'f' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1c, /* 00011100 */
+       0x36, /* 00110110 */
+       0x32, /* 00110010 */
+       0x30, /* 00110000 */
+       0x78, /* 01111000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x78, /* 01111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 103 0x67 'g' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x7c, /* 01111100 */
+       0x0c, /* 00001100 */
+       0xcc, /* 11001100 */
+       0x78, /* 01111000 */
+       0x00, /* 00000000 */
+
+       /* 104 0x68 'h' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xe0, /* 11100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x6c, /* 01101100 */
+       0x76, /* 01110110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0xe6, /* 11100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 105 0x69 'i' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 106 0x6a 'j' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x00, /* 00000000 */
+       0x0e, /* 00001110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+
+       /* 107 0x6b 'k' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xe0, /* 11100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x66, /* 01100110 */
+       0x6c, /* 01101100 */
+       0x78, /* 01111000 */
+       0x78, /* 01111000 */
+       0x6c, /* 01101100 */
+       0x66, /* 01100110 */
+       0xe6, /* 11100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 108 0x6c 'l' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 109 0x6d 'm' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xec, /* 11101100 */
+       0xfe, /* 11111110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 110 0x6e 'n' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xdc, /* 11011100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 111 0x6f 'o' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 112 0x70 'p' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xdc, /* 11011100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+       0x00, /* 00000000 */
+
+       /* 113 0x71 'q' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x7c, /* 01111100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x1e, /* 00011110 */
+       0x00, /* 00000000 */
+
+       /* 114 0x72 'r' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xdc, /* 11011100 */
+       0x76, /* 01110110 */
+       0x66, /* 01100110 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 115 0x73 's' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0x60, /* 01100000 */
+       0x38, /* 00111000 */
+       0x0c, /* 00001100 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 116 0x74 't' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0xfc, /* 11111100 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x36, /* 00110110 */
+       0x1c, /* 00011100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 117 0x75 'u' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 118 0x76 'v' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 119 0x77 'w' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xd6, /* 11010110 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 120 0x78 'x' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x38, /* 00111000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 121 0x79 'y' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7e, /* 01111110 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0xf8, /* 11111000 */
+       0x00, /* 00000000 */
+
+       /* 122 0x7a 'z' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xcc, /* 11001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 123 0x7b '{' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x0e, /* 00001110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x70, /* 01110000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x0e, /* 00001110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 124 0x7c '|' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 125 0x7d '}' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x70, /* 01110000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x0e, /* 00001110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x70, /* 01110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 126 0x7e '~' */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 127 0x7f '\7f' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 128 0x80 '\80' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0xc2, /* 11000010 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc2, /* 11000010 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x70, /* 01110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 129 0x81 '\81' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 130 0x82 '\82' */
+       0x00, /* 00000000 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 131 0x83 '\83' */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 132 0x84 '\84' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 133 0x85 '\85' */
+       0x00, /* 00000000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 134 0x86 '\86' */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 135 0x87 '\87' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x18, /* 00011000 */
+       0x70, /* 01110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 136 0x88 '\88' */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 137 0x89 '\89' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 138 0x8a '\8a' */
+       0x00, /* 00000000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 139 0x8b '\8b' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 140 0x8c '\8c' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 141 0x8d '\8d' */
+       0x00, /* 00000000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 142 0x8e '\8e' */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 143 0x8f '\8f' */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 144 0x90 '\90' */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x66, /* 01100110 */
+       0x62, /* 01100010 */
+       0x68, /* 01101000 */
+       0x78, /* 01111000 */
+       0x68, /* 01101000 */
+       0x62, /* 01100010 */
+       0x66, /* 01100110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 145 0x91 '\91' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xec, /* 11101100 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x7e, /* 01111110 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0x6e, /* 01101110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 146 0x92 '\92' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3e, /* 00111110 */
+       0x6c, /* 01101100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xfe, /* 11111110 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xce, /* 11001110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 147 0x93 '\93' */
+       0x00, /* 00000000 */
+       0x10, /* 00010000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 148 0x94 '\94' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 149 0x95 '\95' */
+       0x00, /* 00000000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 150 0x96 '\96' */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x78, /* 01111000 */
+       0xcc, /* 11001100 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 151 0x97 '\97' */
+       0x00, /* 00000000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 152 0x98 '\98' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7e, /* 01111110 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x78, /* 01111000 */
+       0x00, /* 00000000 */
+
+       /* 153 0x99 '\99' */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 154 0x9a '\9a' */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 155 0x9b '\9b' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 156 0x9c '\9c' */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x64, /* 01100100 */
+       0x60, /* 01100000 */
+       0xf0, /* 11110000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0xe6, /* 11100110 */
+       0xfc, /* 11111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 157 0x9d '\9d' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 158 0x9e '\9e' */
+       0x00, /* 00000000 */
+       0xf8, /* 11111000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xf8, /* 11111000 */
+       0xc4, /* 11000100 */
+       0xcc, /* 11001100 */
+       0xde, /* 11011110 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 159 0x9f '\9f' */
+       0x00, /* 00000000 */
+       0x0e, /* 00001110 */
+       0x1b, /* 00011011 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xd8, /* 11011000 */
+       0x70, /* 01110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 160 0xa0 ' ' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x00, /* 00000000 */
+       0x78, /* 01111000 */
+       0x0c, /* 00001100 */
+       0x7c, /* 01111100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 161 0xa1 '¡' */
+       0x00, /* 00000000 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 162 0xa2 '¢' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 163 0xa3 '£' */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x00, /* 00000000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 164 0xa4 '¤' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0xdc, /* 11011100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 165 0xa5 '¥' */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0xc6, /* 11000110 */
+       0xe6, /* 11100110 */
+       0xf6, /* 11110110 */
+       0xfe, /* 11111110 */
+       0xde, /* 11011110 */
+       0xce, /* 11001110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 166 0xa6 '¦' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x3e, /* 00111110 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 167 0xa7 '§' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 168 0xa8 '¨' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xc0, /* 11000000 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x7c, /* 01111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 169 0xa9 '©' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 170 0xaa 'ª' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 171 0xab '«' */
+       0x00, /* 00000000 */
+       0x60, /* 01100000 */
+       0xe0, /* 11100000 */
+       0x62, /* 01100010 */
+       0x66, /* 01100110 */
+       0x6c, /* 01101100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xdc, /* 11011100 */
+       0x86, /* 10000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x3e, /* 00111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 172 0xac '¬' */
+       0x00, /* 00000000 */
+       0x60, /* 01100000 */
+       0xe0, /* 11100000 */
+       0x62, /* 01100010 */
+       0x66, /* 01100110 */
+       0x6c, /* 01101100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x66, /* 01100110 */
+       0xce, /* 11001110 */
+       0x9a, /* 10011010 */
+       0x3f, /* 00111111 */
+       0x06, /* 00000110 */
+       0x06, /* 00000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 173 0xad '­' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 174 0xae '®' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x36, /* 00110110 */
+       0x6c, /* 01101100 */
+       0xd8, /* 11011000 */
+       0x6c, /* 01101100 */
+       0x36, /* 00110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 175 0xaf '¯' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xd8, /* 11011000 */
+       0x6c, /* 01101100 */
+       0x36, /* 00110110 */
+       0x6c, /* 01101100 */
+       0xd8, /* 11011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 176 0xb0 '°' */
+       0x11, /* 00010001 */
+       0x44, /* 01000100 */
+       0x11, /* 00010001 */
+       0x44, /* 01000100 */
+       0x11, /* 00010001 */
+       0x44, /* 01000100 */
+       0x11, /* 00010001 */
+       0x44, /* 01000100 */
+       0x11, /* 00010001 */
+       0x44, /* 01000100 */
+       0x11, /* 00010001 */
+       0x44, /* 01000100 */
+       0x11, /* 00010001 */
+       0x44, /* 01000100 */
+       0x11, /* 00010001 */
+       0x44, /* 01000100 */
+
+       /* 177 0xb1 '±' */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+       0x55, /* 01010101 */
+       0xaa, /* 10101010 */
+
+       /* 178 0xb2 '²' */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+       0xdd, /* 11011101 */
+       0x77, /* 01110111 */
+
+       /* 179 0xb3 '³' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 180 0xb4 '´' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 181 0xb5 'µ' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 182 0xb6 '¶' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf6, /* 11110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 183 0xb7 '·' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 184 0xb8 '¸' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 185 0xb9 '¹' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf6, /* 11110110 */
+       0x06, /* 00000110 */
+       0xf6, /* 11110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 186 0xba 'º' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 187 0xbb '»' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x06, /* 00000110 */
+       0xf6, /* 11110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 188 0xbc '¼' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf6, /* 11110110 */
+       0x06, /* 00000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 189 0xbd '½' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 190 0xbe '¾' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 191 0xbf '¿' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xf8, /* 11111000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 192 0xc0 'À' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 193 0xc1 'Á' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 194 0xc2 'Â' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 195 0xc3 'Ã' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 196 0xc4 'Ä' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 197 0xc5 'Å' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 198 0xc6 'Æ' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 199 0xc7 'Ç' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x37, /* 00110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 200 0xc8 'È' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x37, /* 00110111 */
+       0x30, /* 00110000 */
+       0x3f, /* 00111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 201 0xc9 'É' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3f, /* 00111111 */
+       0x30, /* 00110000 */
+       0x37, /* 00110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 202 0xca 'Ê' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf7, /* 11110111 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 203 0xcb 'Ë' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0xf7, /* 11110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 204 0xcc 'Ì' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x37, /* 00110111 */
+       0x30, /* 00110000 */
+       0x37, /* 00110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 205 0xcd 'Í' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 206 0xce 'Î' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xf7, /* 11110111 */
+       0x00, /* 00000000 */
+       0xf7, /* 11110111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 207 0xcf 'Ï' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 208 0xd0 'Ð' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 209 0xd1 'Ñ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 210 0xd2 'Ò' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 211 0xd3 'Ó' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x3f, /* 00111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 212 0xd4 'Ô' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 213 0xd5 'Õ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 214 0xd6 'Ö' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x3f, /* 00111111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 215 0xd7 '×' */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0xff, /* 11111111 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+
+       /* 216 0xd8 'Ø' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0xff, /* 11111111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 217 0xd9 'Ù' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xf8, /* 11111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 218 0xda 'Ú' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1f, /* 00011111 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 219 0xdb 'Û' */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+
+       /* 220 0xdc 'Ü' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+
+       /* 221 0xdd 'Ý' */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+       0xf0, /* 11110000 */
+
+       /* 222 0xde 'Þ' */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+       0x0f, /* 00001111 */
+
+       /* 223 0xdf 'ß' */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0xff, /* 11111111 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 224 0xe0 'à' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0xdc, /* 11011100 */
+       0x76, /* 01110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 225 0xe1 'á' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x78, /* 01111000 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xcc, /* 11001100 */
+       0xd8, /* 11011000 */
+       0xcc, /* 11001100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xcc, /* 11001100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 226 0xe2 'â' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0xc0, /* 11000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 227 0xe3 'ã' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 228 0xe4 'ä' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 229 0xe5 'å' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0x70, /* 01110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 230 0xe6 'æ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x7c, /* 01111100 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0xc0, /* 11000000 */
+       0x00, /* 00000000 */
+
+       /* 231 0xe7 'ç' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 232 0xe8 'è' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 233 0xe9 'é' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xfe, /* 11111110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 234 0xea 'ê' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0xee, /* 11101110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 235 0xeb 'ë' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1e, /* 00011110 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x3e, /* 00111110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x66, /* 01100110 */
+       0x3c, /* 00111100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 236 0xec 'ì' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0xdb, /* 11011011 */
+       0xdb, /* 11011011 */
+       0xdb, /* 11011011 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 237 0xed 'í' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x03, /* 00000011 */
+       0x06, /* 00000110 */
+       0x7e, /* 01111110 */
+       0xdb, /* 11011011 */
+       0xdb, /* 11011011 */
+       0xf3, /* 11110011 */
+       0x7e, /* 01111110 */
+       0x60, /* 01100000 */
+       0xc0, /* 11000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 238 0xee 'î' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x1c, /* 00011100 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x7c, /* 01111100 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x1c, /* 00011100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 239 0xef 'ï' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7c, /* 01111100 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0xc6, /* 11000110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 240 0xf0 'ð' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0xfe, /* 11111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 241 0xf1 'ñ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x7e, /* 01111110 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 242 0xf2 'ò' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x06, /* 00000110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 243 0xf3 'ó' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x30, /* 00110000 */
+       0x60, /* 01100000 */
+       0x30, /* 00110000 */
+       0x18, /* 00011000 */
+       0x0c, /* 00001100 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 244 0xf4 'ô' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x0e, /* 00001110 */
+       0x1b, /* 00011011 */
+       0x1b, /* 00011011 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+
+       /* 245 0xf5 'õ' */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0xd8, /* 11011000 */
+       0x70, /* 01110000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 246 0xf6 'ö' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 247 0xf7 '÷' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0x76, /* 01110110 */
+       0xdc, /* 11011100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 248 0xf8 'ø' */
+       0x00, /* 00000000 */
+       0x38, /* 00111000 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x38, /* 00111000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 249 0xf9 'ù' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 250 0xfa 'ú' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x18, /* 00011000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 251 0xfb 'û' */
+       0x00, /* 00000000 */
+       0x0f, /* 00001111 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0x0c, /* 00001100 */
+       0xec, /* 11101100 */
+       0x6c, /* 01101100 */
+       0x6c, /* 01101100 */
+       0x3c, /* 00111100 */
+       0x1c, /* 00011100 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 252 0xfc 'ü' */
+       0x00, /* 00000000 */
+       0x6c, /* 01101100 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x36, /* 00110110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 253 0xfd 'ý' */
+       0x00, /* 00000000 */
+       0x3c, /* 00111100 */
+       0x66, /* 01100110 */
+       0x0c, /* 00001100 */
+       0x18, /* 00011000 */
+       0x32, /* 00110010 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 254 0xfe 'þ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x7e, /* 01111110 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+       /* 255 0xff 'ÿ' */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+       0x00, /* 00000000 */
+
+};
+
+
diff --git a/gecko.c b/gecko.c
new file mode 100644 (file)
index 0000000..49f57cf
--- /dev/null
+++ b/gecko.c
@@ -0,0 +1,218 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (c) 2008             Nuke - <wiinuke@gmail.com>
+Copyright (C) 2008, 2009       Hector Martin "marcan" <marcan@marcansoft.com>
+Copyright (C) 2009             Andre Heider "dhewg" <dhewg@wiibrew.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+#include "printf.h"
+
+#define                EXI_REG_BASE                    0xd806800
+#define                EXI0_REG_BASE                   (EXI_REG_BASE+0x000)
+#define                EXI1_REG_BASE                   (EXI_REG_BASE+0x014)
+#define                EXI2_REG_BASE                   (EXI_REG_BASE+0x028)
+
+#define                EXI0_CSR                                (EXI0_REG_BASE+0x000)
+#define                EXI0_MAR                                (EXI0_REG_BASE+0x004)
+#define                EXI0_LENGTH                             (EXI0_REG_BASE+0x008)
+#define                EXI0_CR                                 (EXI0_REG_BASE+0x00c)
+#define                EXI0_DATA                               (EXI0_REG_BASE+0x010)
+
+#define                EXI1_CSR                                (EXI1_REG_BASE+0x000)
+#define                EXI1_MAR                                (EXI1_REG_BASE+0x004)
+#define                EXI1_LENGTH                             (EXI1_REG_BASE+0x008)
+#define                EXI1_CR                                 (EXI1_REG_BASE+0x00c)
+#define                EXI1_DATA                               (EXI1_REG_BASE+0x010)
+
+#define                EXI2_CSR                                (EXI2_REG_BASE+0x000)
+#define                EXI2_MAR                                (EXI2_REG_BASE+0x004)
+#define                EXI2_LENGTH                             (EXI2_REG_BASE+0x008)
+#define                EXI2_CR                                 (EXI2_REG_BASE+0x00c)
+#define                EXI2_DATA                               (EXI2_REG_BASE+0x010)
+
+static int gecko_console_enabled = 0;
+
+static u32 _gecko_command(u32 command) {
+       u32 i;
+       // Memory Card Port B (Channel 1, Device 0, Frequency 3 (32Mhz Clock))
+       write32(EXI1_CSR, 0xd0);
+       write32(EXI1_DATA, command);
+       write32(EXI1_CR, 0x19);
+       i = 1000;
+       while ((read32(EXI1_CR) & 1) && (i--));
+       i = read32(EXI1_DATA);
+       write32(EXI1_CSR, 0);
+       return i;
+}
+
+static u32 _gecko_getid(void)
+{
+       u32 i;
+       // Memory Card Port B (Channel 1, Device 0, Frequency 3 (32Mhz Clock))
+       write32(EXI1_CSR, 0xd0);
+       write32(EXI1_DATA, 0);
+       write32(EXI1_CR, 0x19);
+       while (read32(EXI1_CR) & 1);
+       write32(EXI1_CR, 0x39);
+       while (read32(EXI1_CR) & 1);
+       i = read32(EXI1_DATA);
+       write32(EXI1_CSR, 0);
+       return i;
+}
+
+static u32 _gecko_sendbyte(char sendbyte) {
+       u32 i = 0;
+       i = _gecko_command(0xB0000000 | (sendbyte<<20));
+       if (i&0x04000000)
+               return 1; // Return 1 if byte was sent
+       return 0;
+}
+
+#if 0
+static u32 _gecko_recvbyte(char *recvbyte) {
+       u32 i = 0;
+       *recvbyte = 0;
+       i = _gecko_command(0xA0000000);
+       if (i&0x08000000) {
+               // Return 1 if byte was received
+               *recvbyte = (i>>16)&0xff;
+               return 1;
+       }
+       return 0;
+}
+
+static u32 _gecko_checksend(void) {
+       u32 i = 0;
+       i = _gecko_command(0xC0000000);
+       if (i&0x04000000)
+               return 1; // Return 1 if safe to send
+       return 0;
+}
+
+static u32 _gecko_checkrecv(void) {
+       u32 i = 0;
+       i = _gecko_command(0xD0000000);
+       if (i&0x04000000)
+               return 1; // Return 1 if safe to recv
+       return 0;
+}
+
+static void gecko_flush(void) {
+       char tmp;
+       while(_gecko_recvbyte(&tmp));
+}
+#endif
+
+static int gecko_isalive(void) {
+       u32 i;
+
+       i = _gecko_getid();
+       if (i != 0x00000000)
+               return 0;
+
+       i = _gecko_command(0x90000000);
+       if ((i & 0xFFFF0000) != 0x04700000)
+               return 0;
+
+       return 1;
+}
+
+#if 0
+static int gecko_recvbuffer(void *buffer, u32 size) {
+       u32 left = size;
+       char *ptr = (char*)buffer;
+
+       while(left>0) {
+               if(!_gecko_recvbyte(ptr))
+                       break;
+               ptr++;
+               left--;
+       }
+       return (size - left);
+}
+#endif
+
+static int gecko_sendbuffer(const void *buffer, u32 size) {
+       u32 left = size;
+       char *ptr = (char*)buffer;
+
+       while(left>0) {
+               if(!_gecko_sendbyte(*ptr))
+                       break;
+               ptr++;
+               left--;
+       }
+       return (size - left);
+}
+
+#if 0
+static int gecko_recvbuffer_safe(void *buffer, u32 size) {
+       u32 left = size;
+       char *ptr = (char*)buffer;
+       
+       while(left>0) {
+               if(_gecko_checkrecv()) {
+                       if(!_gecko_recvbyte(ptr))
+                               break;
+                       ptr++;
+                       left--;
+               }
+       }
+       return (size - left);
+}
+
+static int gecko_sendbuffer_safe(const void *buffer, u32 size) {
+       u32 left = size;
+       char *ptr = (char*)buffer;
+       
+       while(left>0) {
+               if(_gecko_checksend()) {
+                       if(!_gecko_sendbyte(*ptr))
+                               break;
+                       ptr++;
+                       left--;
+               }
+       }
+       return (size - left);
+}
+#endif
+
+void gecko_init(void)
+{
+       // unlock EXI
+       write32(0x0d00643c, 0);
+
+       write32(EXI0_CSR, 0);
+       write32(EXI1_CSR, 0);
+       write32(EXI2_CSR, 0);
+       write32(EXI0_CSR, 0x2000);
+       write32(EXI0_CSR, 3<<10);
+       write32(EXI1_CSR, 3<<10);
+
+       if (!gecko_isalive())
+               return;
+
+       gecko_console_enabled = 1;
+}
+
+int printf(const char *fmt, ...) {
+       if (!gecko_console_enabled)
+               return 0;
+
+       va_list args;
+       char buffer[1024];
+       int i;
+
+       va_start(args, fmt);
+       i = vsprintf(buffer, fmt, args);
+       va_end(args);
+
+       return gecko_sendbuffer(buffer, i);
+}
+
diff --git a/input.c b/input.c
new file mode 100644 (file)
index 0000000..d4d9e7c
--- /dev/null
+++ b/input.c
@@ -0,0 +1,142 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+       Input-handling functions for BootMii. Inspired by GC_PAD.c from GCLIB.
+
+Copyright (C) 2008, 2009       Haxx Enterprises <bushing@gmail.com>
+Copyright (C) 2009             Andre Heider "dhewg" <dhewg@wiibrew.org>
+Copyright (C) 2009             John Kelley <wiidev@kelley.ca>
+Copyright (C) 2009             bLAStY <blasty@bootmii.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+#include "input.h"
+#include "string.h"
+
+#define PADREG(x) (0xCD006400 + (x)*4)
+
+static GC_Pad _pad;
+
+static void gcpad_init(void) { 
+       write32(PADREG(0), 0x00400300); // read pad on channel 1
+       write32(PADREG(3), 0x00400300);
+       write32(PADREG(6), 0x00400300);
+       write32(PADREG(8), 0x00400300);
+       write32(PADREG(12), 0x000701f0); // enable poll chan 1, X = 7, Y = 1
+       write32(PADREG(14), 0x80000000); // transfer all buffer
+}
+
+static void gpio_init(void) {
+       // setup power and eject button hollywood IRQ for PPC
+       mask32(0x0d8000fc, 0, 0x41); // set GPIO owner to PPC
+       mask32(0x0d80003c, 1<<10, 0);
+       mask32(0x0d800034, 0, 1<<10);
+       mask32(0x0d8000d4, 0, 0x41);
+       mask32(0x0d8000cc, 0, 0x41);
+}
+
+void input_init(void) {
+       memset(&_pad, 0, sizeof(GC_Pad));
+
+       gpio_init();
+       gcpad_init();
+
+       // Check for any pending GPIO irq's, which should be ACK'd so we don't get ghost presses later.
+       // Try over and over again, until we are out of them.
+       while (read32(0x0d800030) & (1<<10)) {
+               if (read32(0x0d8000f0) & 1) {
+                       while(read32(0x0d8000c8) & 1);
+                       write32(0x0d8000d0, 1);
+               } else if (read32(0x0d8000f0) & 0x40) {
+                       write32(0x0d8000d0, 0x40);
+               }
+
+               write32(0x0d800030, 1<<10);
+       }
+
+       // No IRQ's left to be ACK'd, continue our business.
+}
+
+u16 pad_read(GC_Pad *pad, int chan) {
+       u32 pdata = read32(PADREG(3 * chan + 1));
+       u32 pdata2 = read32(PADREG(3 * chan + 2));
+
+       u16 btns = pdata >> 16;
+
+       if (pad) {
+               u16 prev = pad->btns_held;
+
+               pad->btns_held = btns;
+               pad->btns_up = prev & ~btns;
+               pad->btns_down = btns & (btns ^ prev);
+
+               pad->x = 128 + ((pdata >> 8) & 0xff);
+               pad->y = 128 - (pdata & 0xff);
+
+               pad->cx = 128 + (pdata2 >> 24);
+               pad->cy = 128 - ((pdata2 >> 16) & 0xff);
+               pad->l = (pdata2 >> 8) & 0xff;
+               pad->r = pdata2 & 0xff;
+
+               return pad->btns_down;
+       }
+
+       return btns;
+}
+
+// TODO: Hackity hack, prevent ghost presses
+static u8 reset_delay = 5;
+
+
+u16 gpio_read(void) {
+       u16 res = 0;
+       u32 irq_flag = 0;
+
+       if (!((read32(0x0C003000) >> 16) & 1) && reset_delay == 0) {
+               res |= GPIO_RESET;
+               reset_delay = 5;
+       }
+
+       if (reset_delay > 0)
+               reset_delay--;
+
+       if (read32(0x0d800030) & (1<<10)) {
+               irq_flag = read32(0x0d8000f0);
+
+               if (irq_flag & 1) {
+                       res |= GPIO_POWER;
+
+                       while(read32(0x0d8000c8) & 1);
+                       write32(0x0d8000d0, 1);
+               } else if (irq_flag & 0x40) {
+                       res |= GPIO_EJECT;
+
+                       while(read32(0x0d8000c8) & 0x40);
+                       write32(0x0d8000d0, 0x40);
+               }
+
+               write32(0x0d800030, 1<<10); // ack GPIO irq
+       }
+
+       return res;
+}
+
+u16 input_read(void) {
+       return pad_read(&_pad, 0) | gpio_read();
+}
+
+u16 input_wait(void) {
+       u16 res;
+
+       do {
+               udelay(20000);
+               res = input_read();
+       } while (!(res & PAD_ANY));
+       
+       return res;
+}
+
diff --git a/input.h b/input.h
new file mode 100644 (file)
index 0000000..d41789c
--- /dev/null
+++ b/input.h
@@ -0,0 +1,52 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2009             Andre Heider "dhewg" <dhewg@wiibrew.org>
+Copyright (C) 2009             John Kelley <wiidev@kelley.ca>
+Copyright (C) 2009             bLAStY <blasty@bootmii.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#ifndef __INPUT_H__
+#define __INPUT_H__
+
+#define PAD_LEFT  0x0001
+#define PAD_RIGHT 0x0002
+#define PAD_DOWN  0x0004
+#define PAD_UP    0x0008
+#define PAD_Z     0x0010
+#define PAD_RS    0x0020
+#define PAD_LS    0x0040
+//    unused      0x0080
+#define PAD_A     0x0100
+#define PAD_B     0x0200
+#define PAD_X     0x0400
+#define PAD_Y     0x0800
+#define PAD_START 0x1000
+#define PAD_ANY   0x1F7F
+
+#define GPIO_POWER     PAD_RIGHT
+#define GPIO_RESET     PAD_A
+#define GPIO_EJECT     PAD_START
+
+typedef struct {
+        u16 btns_held;
+        u16 btns_up;
+        u16 btns_down;
+               s8 x, y, cx, cy;
+               u8 l, r;
+} GC_Pad;
+
+void input_init(void);
+
+u16 pad_read(GC_Pad *pad, int chan);
+u16 gpio_read(void);
+
+u16 input_read(void);
+u16 input_wait(void);
+
+#endif
+
diff --git a/ipc.c b/ipc.c
new file mode 100644 (file)
index 0000000..91e3dee
--- /dev/null
+++ b/ipc.c
@@ -0,0 +1,234 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2008, 2009       Hector Martin "marcan" <marcan@marcansoft.com>
+Copyright (C) 2009             Andre Heider "dhewg" <dhewg@wiibrew.org>
+Copyright (C) 2009             John Kelley <wiidev@kelley.ca>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+#include "ipc.h"
+#include "string.h"
+#include <stdarg.h>
+
+static volatile ipc_request *in_queue;
+static volatile ipc_request *out_queue;
+
+static int in_size;
+static int out_size;
+
+static int initialized = 0;
+
+static u16 out_head;
+static u16 in_tail;
+
+typedef const struct {
+       char magic[3];
+       char version;
+       void *mem2_boundary;
+       volatile ipc_request *ipc_in;
+       u32 ipc_in_size;
+       volatile ipc_request *ipc_out;
+       u32 ipc_out_size;
+} ipc_infohdr;
+
+static ipc_infohdr *infohdr;
+
+static u32 cur_tag;
+
+#define                HW_REG_BASE                     0xd000000
+
+#define                HW_IPC_PPCMSG           (HW_REG_BASE + 0x000) //PPC to ARM
+#define                HW_IPC_PPCCTRL          (HW_REG_BASE + 0x004)
+#define                HW_IPC_ARMMSG           (HW_REG_BASE + 0x008) //ARM to PPC
+
+#define                IPC_CTRL_SEND           0x01
+// Set by peer to acknowledge a message. Write one to clear.
+#define                IPC_CTRL_SENT           0x02
+// Set by peer to send a message. Write one to clear.
+#define                IPC_CTRL_RECV           0x04
+// Write one acknowledge a message. Cleared when peer writes one to IPC_CTRL_SENT.
+#define                IPC_CTRL_RECVD          0x08
+// Enable interrupt when a message is received
+#define                IPC_CTRL_INT_RECV       0x10
+// Enable interrupt when a sent message is acknowledged
+#define                IPC_CTRL_INT_SENT       0x20
+
+static inline u16 peek_outtail(void)
+{
+       return read32(HW_IPC_ARMMSG) & 0xFFFF;
+}
+static inline u16 peek_inhead(void)
+{
+       return read32(HW_IPC_ARMMSG) >> 16;
+}
+
+static inline void poke_intail(u16 num)
+{
+       mask32(HW_IPC_PPCMSG, 0xFFFF, num);
+}
+static inline void poke_outhead(u16 num)
+{
+       mask32(HW_IPC_PPCMSG, 0xFFFF0000, num<<16);
+}
+
+int ipc_initialize(void)
+{
+
+       infohdr = (ipc_infohdr*)(read32(0x13fffffc)|0x80000000);
+       sync_before_read((void*)infohdr, sizeof(ipc_infohdr));
+
+       printf("IPC: infoheader at %p %08x\n", infohdr);
+
+       if(memcmp(infohdr->magic, "IPC", 3)) {
+               printf("IPC: bad magic on info structure\n",infohdr);
+               return -1;
+       }
+       if(infohdr->version != 1) {
+               printf("IPC: unknown IPC version %d\n",infohdr->version);
+               return -1;
+       }
+
+       in_queue = (void*)(((u32)infohdr->ipc_in)|0x80000000);
+       out_queue = (void*)(((u32)infohdr->ipc_out)|0x80000000);
+
+       in_size = infohdr->ipc_in_size;
+       out_size = infohdr->ipc_out_size;
+
+       in_tail = read32(HW_IPC_PPCMSG) & 0xffff;
+       out_head = read32(HW_IPC_PPCMSG) >> 16;
+
+       printf("IPC: initial in tail: %d, out head: %d\n", in_tail, out_head);
+
+       cur_tag = 1;
+
+       initialized = 1;
+       return 0;
+}
+
+void ipc_shutdown(void)
+{
+       if(!initialized)
+               return;
+       ipc_flush();
+       initialized = 0;
+}
+
+void ipc_vpost(u32 code, u32 tag, u32 num_args, va_list ap)
+{
+       int arg = 0;
+       int n = 0;
+
+       if(!initialized) {
+               printf("IPC: not inited\n");
+               return;
+       }
+
+       if(peek_inhead() == ((in_tail + 1)&(in_size-1))) {
+               printf("IPC: in queue full, spinning\n");
+               while(peek_inhead() == ((in_tail + 1)&(in_size-1))) {
+                       udelay(10);
+                       if(n++ > 20000) {
+                               printf("IPC: ARM might be stuck, still waiting for inhead %d != %d\n",
+                                       peek_inhead(), ((in_tail + 1)&(in_size-1)));
+                               n = 0;
+                       }
+               }
+       }
+       in_queue[in_tail].code = code;
+       in_queue[in_tail].tag = tag;
+       while(num_args--) {
+               in_queue[in_tail].args[arg++] = va_arg(ap, u32);
+       }
+       sync_after_write((void*)&in_queue[in_tail], 32);
+       in_tail = (in_tail+1)&(in_size-1);
+       poke_intail(in_tail);
+       write32(HW_IPC_PPCCTRL, IPC_CTRL_SEND);
+}
+
+void ipc_post(u32 code, u32 tag, u32 num_args, ...)
+{
+       va_list ap;
+
+       if(num_args)
+               va_start(ap, num_args);
+
+       ipc_vpost(code, tag, num_args, ap);
+
+       if(num_args)
+               va_end(ap);
+}
+
+void ipc_flush(void)
+{
+       int n = 0;
+       if(!initialized) {
+               printf("IPC: not inited\n");
+               return;
+       }
+       while(peek_inhead() != in_tail) {
+               udelay(10);
+               if(n++ > 20000) {
+                       printf("IPC: ARM might be stuck, still waiting for inhead %d == intail %d\n",
+                               peek_inhead(), in_tail);
+                       n = 0;
+               }
+       }
+}
+
+// last IPC message received, copied because we need to make space in the queue
+ipc_request req_recv;
+
+// since we're not using IRQs, we don't use the reception bell at all at the moment
+ipc_request *ipc_receive(void)
+{
+       while(peek_outtail() == out_head);
+       sync_before_read((void*)&out_queue[out_head], 32);
+       req_recv = out_queue[out_head];
+       out_head = (out_head+1)&(out_size-1);
+       poke_outhead(out_head);
+
+       return &req_recv;
+}
+
+void ipc_process_unhandled(volatile ipc_request *rep)
+{
+       printf("IPC: Unhandled message: %08x %08x [%08x %08x %08x %08x %08x %08x]\n",
+               rep->code, rep->tag, rep->args[0], rep->args[1], rep->args[2], rep->args[3], 
+               rep->args[4], rep->args[5]);
+}
+
+ipc_request *ipc_receive_tagged(u32 code, u32 tag)
+{
+       ipc_request *rep;
+       rep = ipc_receive();
+       while(rep->code != code || rep->tag != tag) {
+               ipc_process_unhandled(rep);
+               rep = ipc_receive();
+       }
+       return rep;
+}
+
+ipc_request *ipc_exchange(u32 code, u32 num_args, ...)
+{
+       va_list ap;
+       ipc_request *rep;
+
+       if(num_args)
+               va_start(ap, num_args);
+
+       ipc_vpost(code, cur_tag, num_args, ap);
+
+       if(num_args)
+               va_end(ap);
+
+       rep = ipc_receive_tagged(code, cur_tag);
+
+       cur_tag++;
+       return rep;
+}
+
diff --git a/ipc.h b/ipc.h
new file mode 100644 (file)
index 0000000..9864a8d
--- /dev/null
+++ b/ipc.h
@@ -0,0 +1,183 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2008, 2009       Haxx Enterprises <bushing@gmail.com>
+Copyright (C) 2008, 2009       Hector Martin "marcan" <marcan@marcansoft.com>
+Copyright (C) 2009             Andre Heider "dhewg" <dhewg@wiibrew.org>
+Copyright (C) 2009             John Kelley <wiidev@kelley.ca>
+Copyright (C) 2008, 2009       Sven Peter <svenpeter@gmail.com>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#ifndef __IPC_H__
+#define __IPC_H__
+
+/* TODO: It would be nice to somehow link this header file with mini/ipc.h.
+   Until then, if you do make any changes here, you MUST make them here
+   (or vice-versa).  See warnings in mini/ipc.h. --bushing */
+
+#define IPC_SYS_PING   0x01000000
+#define IPC_SYS_SLWPING        0x00000000
+#define IPC_SYS_JUMP   0x00000001
+#define IPC_SYS_GETVERS        0x00000002
+#define IPC_SYS_GETGITS 0x00000003
+
+#define IPC_SYS_WRITE32        0x01000100
+#define IPC_SYS_WRITE16        0x01000101
+#define IPC_SYS_WRITE8 0x01000102
+#define IPC_SYS_READ32 0x01000103
+#define IPC_SYS_READ16 0x01000104
+#define IPC_SYS_READ8  0x01000105
+#define IPC_SYS_SET32  0x01000106
+#define IPC_SYS_SET16  0x01000107
+#define IPC_SYS_SET8   0x01000108
+#define IPC_SYS_CLEAR32        0x01000109
+#define IPC_SYS_CLEAR16        0x0100010a
+#define IPC_SYS_CLEAR8 0x0100010b
+#define IPC_SYS_MASK32 0x0100010c
+#define IPC_SYS_MASK16 0x0100010d
+#define IPC_SYS_MASK8  0x0100010e
+
+#define        IPC_NAND_RESET  0x00010000
+#define IPC_NAND_GETID 0x00010001
+#define IPC_NAND_READ  0x00010002
+#define IPC_NAND_WRITE 0x00010003
+#define IPC_NAND_ERASE 0x00010004
+#define IPC_NAND_STATUS        0x00010005
+//#define IPC_NAND_USER0 0x00018000
+//#define IPC_NAND_USER1 0x00018001
+// etc.
+
+#define IPC_SDMMC_ACK  0x00070000
+#define IPC_SDMMC_READ 0x00070001
+#define IPC_SDMMC_WRITE 0x00070002
+#define IPC_SDMMC_STATE 0x00070003
+#define IPC_SDMMC_SIZE 0x00070004
+
+#define IPC_SDHC_DISCOVER 0x00020000
+
+#define IPC_KEYS_GETOTP        0x00030000
+#define IPC_KEYS_GETEEP        0x00030001
+
+#define IPC_AES_RESET  0x00040000
+#define IPC_AES_SETIV  0x00040001
+#define        IPC_AES_SETKEY  0x00040002
+#define        IPC_AES_DECRYPT 0x00040003
+
+#define IPC_BOOT2_RUN  0x00050000
+#define IPC_BOOT2_TMD  0x00050001
+
+#define IPC_PPC_BOOT   0x00060000
+
+typedef struct {
+       union {
+               struct {
+                       u8 flags;
+                       u8 device;
+                       u16 req;
+               };
+               u32 code;
+       };
+       u32 tag;
+       u32 args[6];
+} ipc_request;
+
+extern void *mem2_boundary;
+
+int ipc_initialize(void);
+void ipc_shutdown(void);
+
+void ipc_post(u32 code, u32 tag, u32 num_args, ...);
+
+void ipc_flush(void);
+
+ipc_request *ipc_receive(void);
+ipc_request *ipc_receive_tagged(u32 code, u32 tag);
+
+ipc_request *ipc_exchange(u32 code, u32 num_args, ...);
+
+static inline void ipc_sys_write32(u32 addr, u32 x)
+{
+       ipc_post(IPC_SYS_WRITE32, 0, 2, addr, x);
+}
+static inline void ipc_sys_write16(u32 addr, u16 x)
+{
+       ipc_post(IPC_SYS_WRITE16, 0, 2, addr, x);
+}
+static inline void ipc_sys_write8(u32 addr, u8 x)
+{
+       ipc_post(IPC_SYS_WRITE8, 0, 2, addr, x);
+}
+
+static inline u32 ipc_sys_read32(u32 addr)
+{
+       return ipc_exchange(IPC_SYS_READ32, 1, addr)->args[0];
+}
+static inline u16 ipc_sys_read16(u32 addr)
+{
+       return ipc_exchange(IPC_SYS_READ16, 1, addr)->args[0];
+}
+static inline u8 ipc_sys_read8(u32 addr)
+{
+       return ipc_exchange(IPC_SYS_READ8, 1, addr)->args[0];
+}
+
+static inline void ipc_sys_set32(u32 addr, u32 set)
+{
+       ipc_post(IPC_SYS_SET32, 0, 2, addr, set);
+}
+static inline void ipc_sys_set16(u32 addr, u16 set)
+{
+       ipc_post(IPC_SYS_SET16, 0, 2, addr, set);
+}
+static inline void ipc_sys_set8(u32 addr, u8 set)
+{
+       ipc_post(IPC_SYS_SET8, 0, 2, addr, set);
+}
+
+static inline void ipc_sys_clear32(u32 addr, u32 clear)
+{
+       ipc_post(IPC_SYS_CLEAR32, 0, 2, addr, clear);
+}
+static inline void ipc_sys_clear16(u32 addr, u16 clear)
+{
+       ipc_post(IPC_SYS_CLEAR16, 0, 2, addr, clear);
+}
+static inline void ipc_sys_clear8(u32 addr, u8 clear)
+{
+       ipc_post(IPC_SYS_CLEAR8, 0, 2, addr, clear);
+}
+
+static inline void ipc_sys_mask32(u32 addr, u32 clear, u32 set)
+{
+       ipc_post(IPC_SYS_MASK32, 0, 3, addr, clear, set);
+}
+static inline void ipc_sys_mask16(u32 addr, u16 clear, u32 set)
+{
+       ipc_post(IPC_SYS_MASK16, 0, 3, addr, clear, set);
+}
+static inline void ipc_sys_mask8(u32 addr, u8 clear, u32 set)
+{
+       ipc_post(IPC_SYS_MASK8, 0, 3, addr, clear, set);
+}
+
+static inline void ipc_ping(void)
+{
+       ipc_exchange(IPC_SYS_PING, 0);
+}
+
+static inline void ipc_slowping(void)
+{
+       ipc_exchange(IPC_SYS_SLWPING, 0);
+}
+
+static inline u32 ipc_getvers(void)
+{
+       return ipc_exchange(IPC_SYS_GETVERS, 0)->args[0];
+}
+
+#endif
+
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..acd9717
--- /dev/null
+++ b/main.c
@@ -0,0 +1,120 @@
+/*
+        BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+        Requires mini.
+
+Copyright (C) 2008, 2009        Haxx Enterprises <bushing@gmail.com>
+Copyright (C) 2009              Andre Heider "dhewg" <dhewg@wiibrew.org>
+Copyright (C) 2008, 2009        Hector Martin "marcan" <marcan@marcansoft.com>
+Copyright (C) 2008, 2009        Sven Peter <svenpeter@gmail.com>
+Copyright (C) 2009              John Kelley <wiidev@kelley.ca>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+#include "string.h"
+#include "ipc.h"
+#include "mini_ipc.h"
+#include "nandfs.h"
+#include "fat.h"
+#include "malloc.h"
+#include "diskio.h"
+#include "printf.h"
+#include "video_low.h"
+#include "input.h"
+#include "console.h"
+
+#define MINIMUM_MINI_VERSION 0x00010001
+
+otp_t otp;
+seeprom_t seeprom;
+
+static void dsp_reset(void)
+{
+       write16(0x0c00500a, read16(0x0c00500a) & ~0x01f8);
+       write16(0x0c00500a, read16(0x0c00500a) | 0x0010);
+       write16(0x0c005036, 0);
+}
+
+static char ascii(char s) {
+  if(s < 0x20) return '.';
+  if(s > 0x7E) return '.';
+  return s;
+}
+
+void hexdump(void *d, int len) {
+  u8 *data;
+  int i, off;
+  data = (u8*)d;
+  for (off=0; off<len; off += 16) {
+    printf("%08x  ",off);
+    for(i=0; i<16; i++)
+      if((i+off)>=len) printf("   ");
+      else printf("%02x ",data[off+i]);
+
+    printf(" ");
+    for(i=0; i<16; i++)
+      if((i+off)>=len) printf(" ");
+      else printf("%c",ascii(data[off+i]));
+    printf("\n");
+  }
+}
+       
+void testOTP(void)
+{
+       printf("reading OTP...\n");
+       getotp(&otp);
+       printf("read OTP!\n");
+       printf("OTP:\n");
+       hexdump(&otp, sizeof(otp));
+
+       printf("reading SEEPROM...\n");
+       getseeprom(&seeprom);
+       printf("read SEEPROM!\n");
+       printf("SEEPROM:\n");
+       hexdump(&seeprom, sizeof(seeprom));
+}
+
+int main(void)
+{
+       int vmode = -1;
+       exception_init();
+       dsp_reset();
+
+       // clear interrupt mask
+       write32(0x0c003004, 0);
+
+       ipc_initialize();
+       ipc_slowping();
+
+       gecko_init();
+    input_init();
+       init_fb(vmode);
+
+       VIDEO_Init(vmode);
+       VIDEO_SetFrameBuffer(get_xfb());
+       VISetupEncoder();
+
+       u32 version = ipc_getvers();
+       u16 mini_version_major = version >> 16 & 0xFFFF;
+       u16 mini_version_minor = version & 0xFFFF;
+       printf("Mini version: %d.%0d\n", mini_version_major, mini_version_minor);
+
+       if (version < MINIMUM_MINI_VERSION) {
+               printf("Sorry, this version of MINI (armboot.bin)\n"
+                       "is too old, please update to at least %d.%0d.\n", 
+                       (MINIMUM_MINI_VERSION >> 16), (MINIMUM_MINI_VERSION & 0xFFFF));
+               for (;;) 
+                       ; // better ideas welcome!
+       }
+
+    print_str_noscroll(112, 112, "ohai, world!\n");
+
+       testOTP();
+
+       printf("bye, world!\n");
+
+       return 0;
+}
+
diff --git a/malloc.c b/malloc.c
new file mode 100644 (file)
index 0000000..ca9b0d6
--- /dev/null
+++ b/malloc.c
@@ -0,0 +1,5085 @@
+/*
+  This is a version (aka dlmalloc) of malloc/free/realloc written by
+  Doug Lea and released to the public domain, as explained at
+  http://creativecommons.org/licenses/publicdomain.  Send questions,
+  comments, complaints, performance data, etc to dl@cs.oswego.edu
+
+* Version 2.8.3 Thu Sep 22 11:16:15 2005  Doug Lea  (dl at gee)
+
+   Note: There may be an updated version of this malloc obtainable at
+           ftp://gee.cs.oswego.edu/pub/misc/malloc.c
+         Check before installing!
+
+* Quickstart
+
+  This library is all in one file to simplify the most common usage:
+  ftp it, compile it (-O3), and link it into another program. All of
+  the compile-time options default to reasonable values for use on
+  most platforms.  You might later want to step through various
+  compile-time and dynamic tuning options.
+
+  For convenience, an include file for code using this malloc is at:
+     ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.3.h
+  You don't really need this .h file unless you call functions not
+  defined in your system include files.  The .h file contains only the
+  excerpts from this file needed for using this malloc on ANSI C/C++
+  systems, so long as you haven't changed compile-time options about
+  naming and tuning parameters.  If you do, then you can create your
+  own malloc.h that does include all settings by cutting at the point
+  indicated below. Note that you may already by default be using a C
+  library containing a malloc that is based on some version of this
+  malloc (for example in linux). You might still want to use the one
+  in this file to customize settings or to avoid overheads associated
+  with library versions.
+
+* Vital statistics:
+
+  Supported pointer/size_t representation:       4 or 8 bytes
+       size_t MUST be an unsigned type of the same width as
+       pointers. (If you are using an ancient system that declares
+       size_t as a signed type, or need it to be a different width
+       than pointers, you can use a previous release of this malloc
+       (e.g. 2.7.2) supporting these.)
+
+  Alignment:                                     8 bytes (default)
+       This suffices for nearly all current machines and C compilers.
+       However, you can define MALLOC_ALIGNMENT to be wider than this
+       if necessary (up to 128bytes), at the expense of using more space.
+
+  Minimum overhead per allocated chunk:   4 or  8 bytes (if 4byte sizes)
+                                          8 or 16 bytes (if 8byte sizes)
+       Each malloced chunk has a hidden word of overhead holding size
+       and status information, and additional cross-check word
+       if FOOTERS is defined.
+
+  Minimum allocated size: 4-byte ptrs:  16 bytes    (including overhead)
+                          8-byte ptrs:  32 bytes    (including overhead)
+
+       Even a request for zero bytes (i.e., malloc(0)) returns a
+       pointer to something of the minimum allocatable size.
+       The maximum overhead wastage (i.e., number of extra bytes
+       allocated than were requested in malloc) is less than or equal
+       to the minimum size, except for requests >= mmap_threshold that
+       are serviced via mmap(), where the worst case wastage is about
+       32 bytes plus the remainder from a system page (the minimal
+       mmap unit); typically 4096 or 8192 bytes.
+
+  Security: static-safe; optionally more or less
+       The "security" of malloc refers to the ability of malicious
+       code to accentuate the effects of errors (for example, freeing
+       space that is not currently malloc'ed or overwriting past the
+       ends of chunks) in code that calls malloc.  This malloc
+       guarantees not to modify any memory locations below the base of
+       heap, i.e., static variables, even in the presence of usage
+       errors.  The routines additionally detect most improper frees
+       and reallocs.  All this holds as long as the static bookkeeping
+       for malloc itself is not corrupted by some other means.  This
+       is only one aspect of security -- these checks do not, and
+       cannot, detect all possible programming errors.
+
+       If FOOTERS is defined nonzero, then each allocated chunk
+       carries an additional check word to verify that it was malloced
+       from its space.  These check words are the same within each
+       execution of a program using malloc, but differ across
+       executions, so externally crafted fake chunks cannot be
+       freed. This improves security by rejecting frees/reallocs that
+       could corrupt heap memory, in addition to the checks preventing
+       writes to statics that are always on.  This may further improve
+       security at the expense of time and space overhead.  (Note that
+       FOOTERS may also be worth using with MSPACES.)
+
+       By default detected errors cause the program to abort (calling
+       "abort()"). You can override this to instead proceed past
+       errors by defining PROCEED_ON_ERROR.  In this case, a bad free
+       has no effect, and a malloc that encounters a bad address
+       caused by user overwrites will ignore the bad address by
+       dropping pointers and indices to all known memory. This may
+       be appropriate for programs that should continue if at all
+       possible in the face of programming errors, although they may
+       run out of memory because dropped memory is never reclaimed.
+
+       If you don't like either of these options, you can define
+       CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything
+       else. And if if you are sure that your program using malloc has
+       no errors or vulnerabilities, you can define INSECURE to 1,
+       which might (or might not) provide a small performance improvement.
+
+  Thread-safety: NOT thread-safe unless USE_LOCKS defined
+       When USE_LOCKS is defined, each public call to malloc, free,
+       etc is surrounded with either a pthread mutex or a win32
+       spinlock (depending on WIN32). This is not especially fast, and
+       can be a major bottleneck.  It is designed only to provide
+       minimal protection in concurrent environments, and to provide a
+       basis for extensions.  If you are using malloc in a concurrent
+       program, consider instead using ptmalloc, which is derived from
+       a version of this malloc. (See http://www.malloc.de).
+
+  System requirements: Any combination of MORECORE and/or MMAP/MUNMAP
+       This malloc can use unix sbrk or any emulation (invoked using
+       the CALL_MORECORE macro) and/or mmap/munmap or any emulation
+       (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system
+       memory.  On most unix systems, it tends to work best if both
+       MORECORE and MMAP are enabled.  On Win32, it uses emulations
+       based on VirtualAlloc. It also uses common C library functions
+       like memset.
+
+  Compliance: I believe it is compliant with the Single Unix Specification
+       (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably
+       others as well.
+
+* Overview of algorithms
+
+  This is not the fastest, most space-conserving, most portable, or
+  most tunable malloc ever written. However it is among the fastest
+  while also being among the most space-conserving, portable and
+  tunable.  Consistent balance across these factors results in a good
+  general-purpose allocator for malloc-intensive programs.
+
+  In most ways, this malloc is a best-fit allocator. Generally, it
+  chooses the best-fitting existing chunk for a request, with ties
+  broken in approximately least-recently-used order. (This strategy
+  normally maintains low fragmentation.) However, for requests less
+  than 256bytes, it deviates from best-fit when there is not an
+  exactly fitting available chunk by preferring to use space adjacent
+  to that used for the previous small request, as well as by breaking
+  ties in approximately most-recently-used order. (These enhance
+  locality of series of small allocations.)  And for very large requests
+  (>= 256Kb by default), it relies on system memory mapping
+  facilities, if supported.  (This helps avoid carrying around and
+  possibly fragmenting memory used only for large chunks.)
+
+  All operations (except malloc_stats and mallinfo) have execution
+  times that are bounded by a constant factor of the number of bits in
+  a size_t, not counting any clearing in calloc or copying in realloc,
+  or actions surrounding MORECORE and MMAP that have times
+  proportional to the number of non-contiguous regions returned by
+  system allocation routines, which is often just 1.
+
+  The implementation is not very modular and seriously overuses
+  macros. Perhaps someday all C compilers will do as good a job
+  inlining modular code as can now be done by brute-force expansion,
+  but now, enough of them seem not to.
+
+  Some compilers issue a lot of warnings about code that is
+  dead/unreachable only on some platforms, and also about intentional
+  uses of negation on unsigned types. All known cases of each can be
+  ignored.
+
+  For a longer but out of date high-level description, see
+     http://gee.cs.oswego.edu/dl/html/malloc.html
+
+* MSPACES
+  If MSPACES is defined, then in addition to malloc, free, etc.,
+  this file also defines mspace_malloc, mspace_free, etc. These
+  are versions of malloc routines that take an "mspace" argument
+  obtained using create_mspace, to control all internal bookkeeping.
+  If ONLY_MSPACES is defined, only these versions are compiled.
+  So if you would like to use this allocator for only some allocations,
+  and your system malloc for others, you can compile with
+  ONLY_MSPACES and then do something like...
+    static mspace mymspace = create_mspace(0,0); // for example
+    #define mymalloc(bytes)  mspace_malloc(mymspace, bytes)
+
+  (Note: If you only need one instance of an mspace, you can instead
+  use "USE_DL_PREFIX" to relabel the global malloc.)
+
+  You can similarly create thread-local allocators by storing
+  mspaces as thread-locals. For example:
+    static __thread mspace tlms = 0;
+    void*  tlmalloc(size_t bytes) {
+      if (tlms == 0) tlms = create_mspace(0, 0);
+      return mspace_malloc(tlms, bytes);
+    }
+    void  tlfree(void* mem) { mspace_free(tlms, mem); }
+
+  Unless FOOTERS is defined, each mspace is completely independent.
+  You cannot allocate from one and free to another (although
+  conformance is only weakly checked, so usage errors are not always
+  caught). If FOOTERS is defined, then each chunk carries around a tag
+  indicating its originating mspace, and frees are directed to their
+  originating spaces.
+
+ -------------------------  Compile-time options ---------------------------
+
+Be careful in setting #define values for numerical constants of type
+size_t. On some systems, literal values are not automatically extended
+to size_t precision unless they are explicitly casted.
+
+WIN32                    default: defined if _WIN32 defined
+  Defining WIN32 sets up defaults for MS environment and compilers.
+  Otherwise defaults are for unix.
+
+MALLOC_ALIGNMENT         default: (size_t)8
+  Controls the minimum alignment for malloc'ed chunks.  It must be a
+  power of two and at least 8, even on machines for which smaller
+  alignments would suffice. It may be defined as larger than this
+  though. Note however that code and data structures are optimized for
+  the case of 8-byte alignment.
+
+MSPACES                  default: 0 (false)
+  If true, compile in support for independent allocation spaces.
+  This is only supported if HAVE_MMAP is true.
+
+ONLY_MSPACES             default: 0 (false)
+  If true, only compile in mspace versions, not regular versions.
+
+USE_LOCKS                default: 0 (false)
+  Causes each call to each public routine to be surrounded with
+  pthread or WIN32 mutex lock/unlock. (If set true, this can be
+  overridden on a per-mspace basis for mspace versions.)
+
+FOOTERS                  default: 0
+  If true, provide extra checking and dispatching by placing
+  information in the footers of allocated chunks. This adds
+  space and time overhead.
+
+INSECURE                 default: 0
+  If true, omit checks for usage errors and heap space overwrites.
+
+USE_DL_PREFIX            default: NOT defined
+  Causes compiler to prefix all public routines with the string 'dl'.
+  This can be useful when you only want to use this malloc in one part
+  of a program, using your regular system malloc elsewhere.
+
+ABORT                    default: defined as abort()
+  Defines how to abort on failed checks.  On most systems, a failed
+  check cannot die with an "assert" or even print an informative
+  message, because the underlying print routines in turn call malloc,
+  which will fail again.  Generally, the best policy is to simply call
+  abort(). It's not very useful to do more than this because many
+  errors due to overwriting will show up as address faults (null, odd
+  addresses etc) rather than malloc-triggered checks, so will also
+  abort.  Also, most compilers know that abort() does not return, so
+  can better optimize code conditionally calling it.
+
+PROCEED_ON_ERROR           default: defined as 0 (false)
+  Controls whether detected bad addresses cause them to bypassed
+  rather than aborting. If set, detected bad arguments to free and
+  realloc are ignored. And all bookkeeping information is zeroed out
+  upon a detected overwrite of freed heap space, thus losing the
+  ability to ever return it from malloc again, but enabling the
+  application to proceed. If PROCEED_ON_ERROR is defined, the
+  static variable malloc_corruption_error_count is compiled in
+  and can be examined to see if errors have occurred. This option
+  generates slower code than the default abort policy.
+
+DEBUG                    default: NOT defined
+  The DEBUG setting is mainly intended for people trying to modify
+  this code or diagnose problems when porting to new platforms.
+  However, it may also be able to better isolate user errors than just
+  using runtime checks.  The assertions in the check routines spell
+  out in more detail the assumptions and invariants underlying the
+  algorithms.  The checking is fairly extensive, and will slow down
+  execution noticeably. Calling malloc_stats or mallinfo with DEBUG
+  set will attempt to check every non-mmapped allocated and free chunk
+  in the course of computing the summaries.
+
+ABORT_ON_ASSERT_FAILURE   default: defined as 1 (true)
+  Debugging assertion failures can be nearly impossible if your
+  version of the assert macro causes malloc to be called, which will
+  lead to a cascade of further failures, blowing the runtime stack.
+  ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(),
+  which will usually make debugging easier.
+
+MALLOC_FAILURE_ACTION     default: sets errno to ENOMEM, or no-op on win32
+  The action to take before "return 0" when malloc fails to be able to
+  return memory because there is none available.
+
+HAVE_MORECORE             default: 1 (true) unless win32 or ONLY_MSPACES
+  True if this system supports sbrk or an emulation of it.
+
+MORECORE                  default: sbrk
+  The name of the sbrk-style system routine to call to obtain more
+  memory.  See below for guidance on writing custom MORECORE
+  functions. The type of the argument to sbrk/MORECORE varies across
+  systems.  It cannot be size_t, because it supports negative
+  arguments, so it is normally the signed type of the same width as
+  size_t (sometimes declared as "intptr_t").  It doesn't much matter
+  though. Internally, we only call it with arguments less than half
+  the max value of a size_t, which should work across all reasonable
+  possibilities, although sometimes generating compiler warnings.  See
+  near the end of this file for guidelines for creating a custom
+  version of MORECORE.
+
+MORECORE_CONTIGUOUS       default: 1 (true)
+  If true, take advantage of fact that consecutive calls to MORECORE
+  with positive arguments always return contiguous increasing
+  addresses.  This is true of unix sbrk. It does not hurt too much to
+  set it true anyway, since malloc copes with non-contiguities.
+  Setting it false when definitely non-contiguous saves time
+  and possibly wasted space it would take to discover this though.
+
+MORECORE_CANNOT_TRIM      default: NOT defined
+  True if MORECORE cannot release space back to the system when given
+  negative arguments. This is generally necessary only if you are
+  using a hand-crafted MORECORE function that cannot handle negative
+  arguments.
+
+HAVE_MMAP                 default: 1 (true)
+  True if this system supports mmap or an emulation of it.  If so, and
+  HAVE_MORECORE is not true, MMAP is used for all system
+  allocation. If set and HAVE_MORECORE is true as well, MMAP is
+  primarily used to directly allocate very large blocks. It is also
+  used as a backup strategy in cases where MORECORE fails to provide
+  space from system. Note: A single call to MUNMAP is assumed to be
+  able to unmap memory that may have be allocated using multiple calls
+  to MMAP, so long as they are adjacent.
+
+HAVE_MREMAP               default: 1 on linux, else 0
+  If true realloc() uses mremap() to re-allocate large blocks and
+  extend or shrink allocation spaces.
+
+MMAP_CLEARS               default: 1 on unix
+  True if mmap clears memory so calloc doesn't need to. This is true
+  for standard unix mmap using /dev/zero.
+
+USE_BUILTIN_FFS            default: 0 (i.e., not used)
+  Causes malloc to use the builtin ffs() function to compute indices.
+  Some compilers may recognize and intrinsify ffs to be faster than the
+  supplied C version. Also, the case of x86 using gcc is special-cased
+  to an asm instruction, so is already as fast as it can be, and so
+  this setting has no effect. (On most x86s, the asm version is only
+  slightly faster than the C version.)
+
+malloc_getpagesize         default: derive from system includes, or 4096.
+  The system page size. To the extent possible, this malloc manages
+  memory from the system in page-size units.  This may be (and
+  usually is) a function rather than a constant. This is ignored
+  if WIN32, where page size is determined using getSystemInfo during
+  initialization.
+
+USE_DEV_RANDOM             default: 0 (i.e., not used)
+  Causes malloc to use /dev/random to initialize secure magic seed for
+  stamping footers. Otherwise, the current time is used.
+
+NO_MALLINFO                default: 0
+  If defined, don't compile "mallinfo". This can be a simple way
+  of dealing with mismatches between system declarations and
+  those in this file.
+
+MALLINFO_FIELD_TYPE        default: size_t
+  The type of the fields in the mallinfo struct. This was originally
+  defined as "int" in SVID etc, but is more usefully defined as
+  size_t. The value is used only if  HAVE_USR_INCLUDE_MALLOC_H is not set
+
+REALLOC_ZERO_BYTES_FREES    default: not defined
+  This should be set if a call to realloc with zero bytes should 
+  be the same as a call to free. Some people think it should. Otherwise, 
+  since this malloc returns a unique pointer for malloc(0), so does 
+  realloc(p, 0).
+
+LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H
+LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H,  LACKS_ERRNO_H
+LACKS_STDLIB_H                default: NOT defined unless on WIN32
+  Define these if your system does not have these header files.
+  You might need to manually insert some of the declarations they provide.
+
+DEFAULT_GRANULARITY        default: page size if MORECORE_CONTIGUOUS,
+                                system_info.dwAllocationGranularity in WIN32,
+                                otherwise 64K.
+      Also settable using mallopt(M_GRANULARITY, x)
+  The unit for allocating and deallocating memory from the system.  On
+  most systems with contiguous MORECORE, there is no reason to
+  make this more than a page. However, systems with MMAP tend to
+  either require or encourage larger granularities.  You can increase
+  this value to prevent system allocation functions to be called so
+  often, especially if they are slow.  The value must be at least one
+  page and must be a power of two.  Setting to 0 causes initialization
+  to either page size or win32 region size.  (Note: In previous
+  versions of malloc, the equivalent of this option was called
+  "TOP_PAD")
+
+DEFAULT_TRIM_THRESHOLD    default: 2MB
+      Also settable using mallopt(M_TRIM_THRESHOLD, x)
+  The maximum amount of unused top-most memory to keep before
+  releasing via malloc_trim in free().  Automatic trimming is mainly
+  useful in long-lived programs using contiguous MORECORE.  Because
+  trimming via sbrk can be slow on some systems, and can sometimes be
+  wasteful (in cases where programs immediately afterward allocate
+  more large chunks) the value should be high enough so that your
+  overall system performance would improve by releasing this much
+  memory.  As a rough guide, you might set to a value close to the
+  average size of a process (program) running on your system.
+  Releasing this much memory would allow such a process to run in
+  memory.  Generally, it is worth tuning trim thresholds when a
+  program undergoes phases where several large chunks are allocated
+  and released in ways that can reuse each other's storage, perhaps
+  mixed with phases where there are no such chunks at all. The trim
+  value must be greater than page size to have any useful effect.  To
+  disable trimming completely, you can set to MAX_SIZE_T. Note that the trick
+  some people use of mallocing a huge space and then freeing it at
+  program startup, in an attempt to reserve system memory, doesn't
+  have the intended effect under automatic trimming, since that memory
+  will immediately be returned to the system.
+
+DEFAULT_MMAP_THRESHOLD       default: 256K
+      Also settable using mallopt(M_MMAP_THRESHOLD, x)
+  The request size threshold for using MMAP to directly service a
+  request. Requests of at least this size that cannot be allocated
+  using already-existing space will be serviced via mmap.  (If enough
+  normal freed space already exists it is used instead.)  Using mmap
+  segregates relatively large chunks of memory so that they can be
+  individually obtained and released from the host system. A request
+  serviced through mmap is never reused by any other request (at least
+  not directly; the system may just so happen to remap successive
+  requests to the same locations).  Segregating space in this way has
+  the benefits that: Mmapped space can always be individually released
+  back to the system, which helps keep the system level memory demands
+  of a long-lived program low.  Also, mapped memory doesn't become
+  `locked' between other chunks, as can happen with normally allocated
+  chunks, which means that even trimming via malloc_trim would not
+  release them.  However, it has the disadvantage that the space
+  cannot be reclaimed, consolidated, and then used to service later
+  requests, as happens with normal chunks.  The advantages of mmap
+  nearly always outweigh disadvantages for "large" chunks, but the
+  value of "large" may vary across systems.  The default is an
+  empirically derived value that works well in most systems. You can
+  disable mmap by setting to MAX_SIZE_T.
+
+*/
+
+// Wii / bootmii environment settings
+#define HAVE_MMAP 0
+#define malloc_getpagesize 8
+#define ABORT {printf("malloc fail! file=%s line=%d\n", __FILE__, __LINE__); while (1);}
+#define MALLOC_FAILURE_ACTION /**/
+#define DEBUG 1
+
+#include "bootmii_ppc.h"
+#include "string.h"
+
+extern unsigned int _sbrk_start, _sbrk_end;
+
+void *sbrk(int incr) {
+       static unsigned int limit = 0;
+//     printf ("sbrk(%d) limit=%x\n", incr, limit);
+       if (limit == 0) limit = (unsigned int) &_sbrk_start;
+       if (incr < 0) return 0;
+       if ((limit + incr) > (unsigned int) &_sbrk_end) return 0;
+       void *retval = (void*)limit;
+       limit += incr;
+//     printf("Returning %p\n", retval);
+       return retval;
+}
+
+#ifndef WIN32
+#ifdef _WIN32
+#define WIN32 1
+#endif  /* _WIN32 */
+#endif  /* WIN32 */
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#define HAVE_MMAP 1
+#define HAVE_MORECORE 0
+#define LACKS_UNISTD_H
+#define LACKS_SYS_PARAM_H
+#define LACKS_SYS_MMAN_H
+#define LACKS_STRING_H
+#define LACKS_STRINGS_H
+#define LACKS_SYS_TYPES_H
+#define LACKS_ERRNO_H
+#define MALLOC_FAILURE_ACTION
+#define MMAP_CLEARS 0 /* WINCE and some others apparently don't clear */
+#endif  /* WIN32 */
+
+#if defined(DARWIN) || defined(_DARWIN)
+/* Mac OSX docs advise not to use sbrk; it seems better to use mmap */
+#ifndef HAVE_MORECORE
+#define HAVE_MORECORE 0
+#define HAVE_MMAP 1
+#endif  /* HAVE_MORECORE */
+#endif  /* DARWIN */
+
+#ifndef LACKS_SYS_TYPES_H
+#include <sys/types.h>  /* For size_t */
+#endif  /* LACKS_SYS_TYPES_H */
+
+/* The maximum possible size_t value has all bits set */
+#define MAX_SIZE_T           (~(size_t)0)
+
+#ifndef ONLY_MSPACES
+#define ONLY_MSPACES 0
+#endif  /* ONLY_MSPACES */
+#ifndef MSPACES
+#if ONLY_MSPACES
+#define MSPACES 1
+#else   /* ONLY_MSPACES */
+#define MSPACES 0
+#endif  /* ONLY_MSPACES */
+#endif  /* MSPACES */
+#ifndef MALLOC_ALIGNMENT
+#define MALLOC_ALIGNMENT ((size_t)8U)
+#endif  /* MALLOC_ALIGNMENT */
+#ifndef FOOTERS
+#define FOOTERS 0
+#endif  /* FOOTERS */
+#ifndef ABORT
+#define ABORT  abort()
+#endif  /* ABORT */
+#ifndef ABORT_ON_ASSERT_FAILURE
+#define ABORT_ON_ASSERT_FAILURE 1
+#endif  /* ABORT_ON_ASSERT_FAILURE */
+#ifndef PROCEED_ON_ERROR
+#define PROCEED_ON_ERROR 0
+#endif  /* PROCEED_ON_ERROR */
+#ifndef USE_LOCKS
+#define USE_LOCKS 0
+#endif  /* USE_LOCKS */
+#ifndef INSECURE
+#define INSECURE 0
+#endif  /* INSECURE */
+#ifndef HAVE_MMAP
+#define HAVE_MMAP 1
+#endif  /* HAVE_MMAP */
+#ifndef MMAP_CLEARS
+#define MMAP_CLEARS 1
+#endif  /* MMAP_CLEARS */
+#ifndef HAVE_MREMAP
+#ifdef linux
+#define HAVE_MREMAP 1
+#else   /* linux */
+#define HAVE_MREMAP 0
+#endif  /* linux */
+#endif  /* HAVE_MREMAP */
+#ifndef MALLOC_FAILURE_ACTION
+#define MALLOC_FAILURE_ACTION  errno = ENOMEM;
+#endif  /* MALLOC_FAILURE_ACTION */
+#ifndef HAVE_MORECORE
+#if ONLY_MSPACES
+#define HAVE_MORECORE 0
+#else   /* ONLY_MSPACES */
+#define HAVE_MORECORE 1
+#endif  /* ONLY_MSPACES */
+#endif  /* HAVE_MORECORE */
+#if !HAVE_MORECORE
+#define MORECORE_CONTIGUOUS 0
+#else   /* !HAVE_MORECORE */
+#ifndef MORECORE
+#define MORECORE sbrk
+#endif  /* MORECORE */
+#ifndef MORECORE_CONTIGUOUS
+#define MORECORE_CONTIGUOUS 1
+#endif  /* MORECORE_CONTIGUOUS */
+#endif  /* HAVE_MORECORE */
+#ifndef DEFAULT_GRANULARITY
+#if MORECORE_CONTIGUOUS
+#define DEFAULT_GRANULARITY (0)  /* 0 means to compute in init_mparams */
+#else   /* MORECORE_CONTIGUOUS */
+#define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U)
+#endif  /* MORECORE_CONTIGUOUS */
+#endif  /* DEFAULT_GRANULARITY */
+#ifndef DEFAULT_TRIM_THRESHOLD
+#ifndef MORECORE_CANNOT_TRIM
+#define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U)
+#else   /* MORECORE_CANNOT_TRIM */
+#define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T
+#endif  /* MORECORE_CANNOT_TRIM */
+#endif  /* DEFAULT_TRIM_THRESHOLD */
+#ifndef DEFAULT_MMAP_THRESHOLD
+#if HAVE_MMAP
+#define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U)
+#else   /* HAVE_MMAP */
+#define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T
+#endif  /* HAVE_MMAP */
+#endif  /* DEFAULT_MMAP_THRESHOLD */
+#ifndef USE_BUILTIN_FFS
+#define USE_BUILTIN_FFS 0
+#endif  /* USE_BUILTIN_FFS */
+#ifndef USE_DEV_RANDOM
+#define USE_DEV_RANDOM 0
+#endif  /* USE_DEV_RANDOM */
+#ifndef NO_MALLINFO
+#define NO_MALLINFO 0
+#endif  /* NO_MALLINFO */
+#ifndef MALLINFO_FIELD_TYPE
+#define MALLINFO_FIELD_TYPE size_t
+#endif  /* MALLINFO_FIELD_TYPE */
+
+/*
+  mallopt tuning options.  SVID/XPG defines four standard parameter
+  numbers for mallopt, normally defined in malloc.h.  None of these
+  are used in this malloc, so setting them has no effect. But this
+  malloc does support the following options.
+*/
+
+#define M_TRIM_THRESHOLD     (-1)
+#define M_GRANULARITY        (-2)
+#define M_MMAP_THRESHOLD     (-3)
+
+/* ------------------------ Mallinfo declarations ------------------------ */
+
+#if !NO_MALLINFO
+/*
+  This version of malloc supports the standard SVID/XPG mallinfo
+  routine that returns a struct containing usage properties and
+  statistics. It should work on any system that has a
+  /usr/include/malloc.h defining struct mallinfo.  The main
+  declaration needed is the mallinfo struct that is returned (by-copy)
+  by mallinfo().  The malloinfo struct contains a bunch of fields that
+  are not even meaningful in this version of malloc.  These fields are
+  are instead filled by mallinfo() with other numbers that might be of
+  interest.
+
+  HAVE_USR_INCLUDE_MALLOC_H should be set if you have a
+  /usr/include/malloc.h file that includes a declaration of struct
+  mallinfo.  If so, it is included; else a compliant version is
+  declared below.  These must be precisely the same for mallinfo() to
+  work.  The original SVID version of this struct, defined on most
+  systems with mallinfo, declares all fields as ints. But some others
+  define as unsigned long. If your system defines the fields using a
+  type of different width than listed here, you MUST #include your
+  system version and #define HAVE_USR_INCLUDE_MALLOC_H.
+*/
+
+/* #define HAVE_USR_INCLUDE_MALLOC_H */
+
+#ifdef HAVE_USR_INCLUDE_MALLOC_H
+#include "/usr/include/malloc.h"
+#else /* HAVE_USR_INCLUDE_MALLOC_H */
+
+struct mallinfo {
+  MALLINFO_FIELD_TYPE arena;    /* non-mmapped space allocated from system */
+  MALLINFO_FIELD_TYPE ordblks;  /* number of free chunks */
+  MALLINFO_FIELD_TYPE smblks;   /* always 0 */
+  MALLINFO_FIELD_TYPE hblks;    /* always 0 */
+  MALLINFO_FIELD_TYPE hblkhd;   /* space in mmapped regions */
+  MALLINFO_FIELD_TYPE usmblks;  /* maximum total allocated space */
+  MALLINFO_FIELD_TYPE fsmblks;  /* always 0 */
+  MALLINFO_FIELD_TYPE uordblks; /* total allocated space */
+  MALLINFO_FIELD_TYPE fordblks; /* total free space */
+  MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */
+};
+
+#endif /* HAVE_USR_INCLUDE_MALLOC_H */
+#endif /* NO_MALLINFO */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#if !ONLY_MSPACES
+
+/* ------------------- Declarations of public routines ------------------- */
+
+#ifndef USE_DL_PREFIX
+#define dlcalloc               calloc
+#define dlfree                 free
+#define dlmalloc               malloc
+#define dlmemalign             memalign
+#define dlrealloc              realloc
+#define dlvalloc               valloc
+#define dlpvalloc              pvalloc
+#define dlmallinfo             mallinfo
+#define dlmallopt              mallopt
+#define dlmalloc_trim          malloc_trim
+#define dlmalloc_stats         malloc_stats
+#define dlmalloc_usable_size   malloc_usable_size
+#define dlmalloc_footprint     malloc_footprint
+#define dlmalloc_max_footprint malloc_max_footprint
+#define dlindependent_calloc   independent_calloc
+#define dlindependent_comalloc independent_comalloc
+#endif /* USE_DL_PREFIX */
+
+
+/*
+  malloc(size_t n)
+  Returns a pointer to a newly allocated chunk of at least n bytes, or
+  null if no space is available, in which case errno is set to ENOMEM
+  on ANSI C systems.
+
+  If n is zero, malloc returns a minimum-sized chunk. (The minimum
+  size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
+  systems.)  Note that size_t is an unsigned type, so calls with
+  arguments that would be negative if signed are interpreted as
+  requests for huge amounts of space, which will often fail. The
+  maximum supported value of n differs across systems, but is in all
+  cases less than the maximum representable value of a size_t.
+*/
+void* dlmalloc(size_t);
+
+/*
+  free(void* p)
+  Releases the chunk of memory pointed to by p, that had been previously
+  allocated using malloc or a related routine such as realloc.
+  It has no effect if p is null. If p was not malloced or already
+  freed, free(p) will by default cause the current program to abort.
+*/
+void  dlfree(void*);
+
+/*
+  calloc(size_t n_elements, size_t element_size);
+  Returns a pointer to n_elements * element_size bytes, with all locations
+  set to zero.
+*/
+void* dlcalloc(size_t, size_t);
+
+/*
+  realloc(void* p, size_t n)
+  Returns a pointer to a chunk of size n that contains the same data
+  as does chunk p up to the minimum of (n, p's size) bytes, or null
+  if no space is available.
+
+  The returned pointer may or may not be the same as p. The algorithm
+  prefers extending p in most cases when possible, otherwise it
+  employs the equivalent of a malloc-copy-free sequence.
+
+  If p is null, realloc is equivalent to malloc.
+
+  If space is not available, realloc returns null, errno is set (if on
+  ANSI) and p is NOT freed.
+
+  if n is for fewer bytes than already held by p, the newly unused
+  space is lopped off and freed if possible.  realloc with a size
+  argument of zero (re)allocates a minimum-sized chunk.
+
+  The old unix realloc convention of allowing the last-free'd chunk
+  to be used as an argument to realloc is not supported.
+*/
+
+void* dlrealloc(void*, size_t);
+
+/*
+  memalign(size_t alignment, size_t n);
+  Returns a pointer to a newly allocated chunk of n bytes, aligned
+  in accord with the alignment argument.
+
+  The alignment argument should be a power of two. If the argument is
+  not a power of two, the nearest greater power is used.
+  8-byte alignment is guaranteed by normal malloc calls, so don't
+  bother calling memalign with an argument of 8 or less.
+
+  Overreliance on memalign is a sure way to fragment space.
+*/
+void* dlmemalign(size_t, size_t);
+
+/*
+  valloc(size_t n);
+  Equivalent to memalign(pagesize, n), where pagesize is the page
+  size of the system. If the pagesize is unknown, 4096 is used.
+*/
+void* dlvalloc(size_t);
+
+/*
+  mallopt(int parameter_number, int parameter_value)
+  Sets tunable parameters The format is to provide a
+  (parameter-number, parameter-value) pair.  mallopt then sets the
+  corresponding parameter to the argument value if it can (i.e., so
+  long as the value is meaningful), and returns 1 if successful else
+  0.  SVID/XPG/ANSI defines four standard param numbers for mallopt,
+  normally defined in malloc.h.  None of these are use in this malloc,
+  so setting them has no effect. But this malloc also supports other
+  options in mallopt. See below for details.  Briefly, supported
+  parameters are as follows (listed defaults are for "typical"
+  configurations).
+
+  Symbol            param #  default    allowed param values
+  M_TRIM_THRESHOLD     -1   2*1024*1024   any   (MAX_SIZE_T disables)
+  M_GRANULARITY        -2     page size   any power of 2 >= page size
+  M_MMAP_THRESHOLD     -3      256*1024   any   (or 0 if no MMAP support)
+*/
+int dlmallopt(int, int);
+
+/*
+  malloc_footprint();
+  Returns the number of bytes obtained from the system.  The total
+  number of bytes allocated by malloc, realloc etc., is less than this
+  value. Unlike mallinfo, this function returns only a precomputed
+  result, so can be called frequently to monitor memory consumption.
+  Even if locks are otherwise defined, this function does not use them,
+  so results might not be up to date.
+*/
+size_t dlmalloc_footprint(void);
+
+/*
+  malloc_max_footprint();
+  Returns the maximum number of bytes obtained from the system. This
+  value will be greater than current footprint if deallocated space
+  has been reclaimed by the system. The peak number of bytes allocated
+  by malloc, realloc etc., is less than this value. Unlike mallinfo,
+  this function returns only a precomputed result, so can be called
+  frequently to monitor memory consumption.  Even if locks are
+  otherwise defined, this function does not use them, so results might
+  not be up to date.
+*/
+size_t dlmalloc_max_footprint(void);
+
+#if !NO_MALLINFO
+/*
+  mallinfo()
+  Returns (by copy) a struct containing various summary statistics:
+
+  arena:     current total non-mmapped bytes allocated from system
+  ordblks:   the number of free chunks
+  smblks:    always zero.
+  hblks:     current number of mmapped regions
+  hblkhd:    total bytes held in mmapped regions
+  usmblks:   the maximum total allocated space. This will be greater
+                than current total if trimming has occurred.
+  fsmblks:   always zero
+  uordblks:  current total allocated space (normal or mmapped)
+  fordblks:  total free space
+  keepcost:  the maximum number of bytes that could ideally be released
+               back to system via malloc_trim. ("ideally" means that
+               it ignores page restrictions etc.)
+
+  Because these fields are ints, but internal bookkeeping may
+  be kept as longs, the reported values may wrap around zero and
+  thus be inaccurate.
+*/
+struct mallinfo dlmallinfo(void);
+#endif /* NO_MALLINFO */
+
+/*
+  independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);
+
+  independent_calloc is similar to calloc, but instead of returning a
+  single cleared space, it returns an array of pointers to n_elements
+  independent elements that can hold contents of size elem_size, each
+  of which starts out cleared, and can be independently freed,
+  realloc'ed etc. The elements are guaranteed to be adjacently
+  allocated (this is not guaranteed to occur with multiple callocs or
+  mallocs), which may also improve cache locality in some
+  applications.
+
+  The "chunks" argument is optional (i.e., may be null, which is
+  probably the most typical usage). If it is null, the returned array
+  is itself dynamically allocated and should also be freed when it is
+  no longer needed. Otherwise, the chunks array must be of at least
+  n_elements in length. It is filled in with the pointers to the
+  chunks.
+
+  In either case, independent_calloc returns this pointer array, or
+  null if the allocation failed.  If n_elements is zero and "chunks"
+  is null, it returns a chunk representing an array with zero elements
+  (which should be freed if not wanted).
+
+  Each element must be individually freed when it is no longer
+  needed. If you'd like to instead be able to free all at once, you
+  should instead use regular calloc and assign pointers into this
+  space to represent elements.  (In this case though, you cannot
+  independently free elements.)
+
+  independent_calloc simplifies and speeds up implementations of many
+  kinds of pools.  It may also be useful when constructing large data
+  structures that initially have a fixed number of fixed-sized nodes,
+  but the number is not known at compile time, and some of the nodes
+  may later need to be freed. For example:
+
+  struct Node { int item; struct Node* next; };
+
+  struct Node* build_list() {
+    struct Node** pool;
+    int n = read_number_of_nodes_needed();
+    if (n <= 0) return 0;
+    pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
+    if (pool == 0) die();
+    // organize into a linked list...
+    struct Node* first = pool[0];
+    for (i = 0; i < n-1; ++i)
+      pool[i]->next = pool[i+1];
+    free(pool);     // Can now free the array (or not, if it is needed later)
+    return first;
+  }
+*/
+void** dlindependent_calloc(size_t, size_t, void**);
+
+/*
+  independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
+
+  independent_comalloc allocates, all at once, a set of n_elements
+  chunks with sizes indicated in the "sizes" array.    It returns
+  an array of pointers to these elements, each of which can be
+  independently freed, realloc'ed etc. The elements are guaranteed to
+  be adjacently allocated (this is not guaranteed to occur with
+  multiple callocs or mallocs), which may also improve cache locality
+  in some applications.
+
+  The "chunks" argument is optional (i.e., may be null). If it is null
+  the returned array is itself dynamically allocated and should also
+  be freed when it is no longer needed. Otherwise, the chunks array
+  must be of at least n_elements in length. It is filled in with the
+  pointers to the chunks.
+
+  In either case, independent_comalloc returns this pointer array, or
+  null if the allocation failed.  If n_elements is zero and chunks is
+  null, it returns a chunk representing an array with zero elements
+  (which should be freed if not wanted).
+
+  Each element must be individually freed when it is no longer
+  needed. If you'd like to instead be able to free all at once, you
+  should instead use a single regular malloc, and assign pointers at
+  particular offsets in the aggregate space. (In this case though, you
+  cannot independently free elements.)
+
+  independent_comallac differs from independent_calloc in that each
+  element may have a different size, and also that it does not
+  automatically clear elements.
+
+  independent_comalloc can be used to speed up allocation in cases
+  where several structs or objects must always be allocated at the
+  same time.  For example:
+
+  struct Head { ... }
+  struct Foot { ... }
+
+  void send_message(char* msg) {
+    int msglen = strlen(msg);
+    size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
+    void* chunks[3];
+    if (independent_comalloc(3, sizes, chunks) == 0)
+      die();
+    struct Head* head = (struct Head*)(chunks[0]);
+    char*        body = (char*)(chunks[1]);
+    struct Foot* foot = (struct Foot*)(chunks[2]);
+    // ...
+  }
+
+  In general though, independent_comalloc is worth using only for
+  larger values of n_elements. For small values, you probably won't
+  detect enough difference from series of malloc calls to bother.
+
+  Overuse of independent_comalloc can increase overall memory usage,
+  since it cannot reuse existing noncontiguous small chunks that
+  might be available for some of the elements.
+*/
+void** dlindependent_comalloc(size_t, size_t*, void**);
+
+
+/*
+  pvalloc(size_t n);
+  Equivalent to valloc(minimum-page-that-holds(n)), that is,
+  round up n to nearest pagesize.
+ */
+void*  dlpvalloc(size_t);
+
+/*
+  malloc_trim(size_t pad);
+
+  If possible, gives memory back to the system (via negative arguments
+  to sbrk) if there is unused memory at the `high' end of the malloc
+  pool or in unused MMAP segments. You can call this after freeing
+  large blocks of memory to potentially reduce the system-level memory
+  requirements of a program. However, it cannot guarantee to reduce
+  memory. Under some allocation patterns, some large free blocks of
+  memory will be locked between two used chunks, so they cannot be
+  given back to the system.
+
+  The `pad' argument to malloc_trim represents the amount of free
+  trailing space to leave untrimmed. If this argument is zero, only
+  the minimum amount of memory to maintain internal data structures
+  will be left. Non-zero arguments can be supplied to maintain enough
+  trailing space to service future expected allocations without having
+  to re-obtain memory from the system.
+
+  Malloc_trim returns 1 if it actually released any memory, else 0.
+*/
+int  dlmalloc_trim(size_t);
+
+/*
+  malloc_usable_size(void* p);
+
+  Returns the number of bytes you can actually use in
+  an allocated chunk, which may be more than you requested (although
+  often not) due to alignment and minimum size constraints.
+  You can use this many bytes without worrying about
+  overwriting other allocated objects. This is not a particularly great
+  programming practice. malloc_usable_size can be more useful in
+  debugging and assertions, for example:
+
+  p = malloc(n);
+  assert(malloc_usable_size(p) >= 256);
+*/
+size_t dlmalloc_usable_size(void*);
+
+/*
+  malloc_stats();
+  Prints on stderr the amount of space obtained from the system (both
+  via sbrk and mmap), the maximum amount (which may be more than
+  current if malloc_trim and/or munmap got called), and the current
+  number of bytes allocated via malloc (or realloc, etc) but not yet
+  freed. Note that this is the number of bytes allocated, not the
+  number requested. It will be larger than the number requested
+  because of alignment and bookkeeping overhead. Because it includes
+  alignment wastage as being in use, this figure may be greater than
+  zero even when no user-level chunks are allocated.
+
+  The reported current and maximum system memory can be inaccurate if
+  a program makes other calls to system memory allocation functions
+  (normally sbrk) outside of malloc.
+
+  malloc_stats prints only the most commonly interesting statistics.
+  More information can be obtained by calling mallinfo.
+*/
+void  dlmalloc_stats(void);
+
+#endif /* ONLY_MSPACES */
+
+#if MSPACES
+
+/*
+  mspace is an opaque type representing an independent
+  region of space that supports mspace_malloc, etc.
+*/
+typedef void* mspace;
+
+/*
+  create_mspace creates and returns a new independent space with the
+  given initial capacity, or, if 0, the default granularity size.  It
+  returns null if there is no system memory available to create the
+  space.  If argument locked is non-zero, the space uses a separate
+  lock to control access. The capacity of the space will grow
+  dynamically as needed to service mspace_malloc requests.  You can
+  control the sizes of incremental increases of this space by
+  compiling with a different DEFAULT_GRANULARITY or dynamically
+  setting with mallopt(M_GRANULARITY, value).
+*/
+mspace create_mspace(size_t capacity, int locked);
+
+/*
+  destroy_mspace destroys the given space, and attempts to return all
+  of its memory back to the system, returning the total number of
+  bytes freed. After destruction, the results of access to all memory
+  used by the space become undefined.
+*/
+size_t destroy_mspace(mspace msp);
+
+/*
+  create_mspace_with_base uses the memory supplied as the initial base
+  of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
+  space is used for bookkeeping, so the capacity must be at least this
+  large. (Otherwise 0 is returned.) When this initial space is
+  exhausted, additional memory will be obtained from the system.
+  Destroying this space will deallocate all additionally allocated
+  space (if possible) but not the initial base.
+*/
+mspace create_mspace_with_base(void* base, size_t capacity, int locked);
+
+/*
+  mspace_malloc behaves as malloc, but operates within
+  the given space.
+*/
+void* mspace_malloc(mspace msp, size_t bytes);
+
+/*
+  mspace_free behaves as free, but operates within
+  the given space.
+
+  If compiled with FOOTERS==1, mspace_free is not actually needed.
+  free may be called instead of mspace_free because freed chunks from
+  any space are handled by their originating spaces.
+*/
+void mspace_free(mspace msp, void* mem);
+
+/*
+  mspace_realloc behaves as realloc, but operates within
+  the given space.
+
+  If compiled with FOOTERS==1, mspace_realloc is not actually
+  needed.  realloc may be called instead of mspace_realloc because
+  realloced chunks from any space are handled by their originating
+  spaces.
+*/
+void* mspace_realloc(mspace msp, void* mem, size_t newsize);
+
+/*
+  mspace_calloc behaves as calloc, but operates within
+  the given space.
+*/
+void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
+
+/*
+  mspace_memalign behaves as memalign, but operates within
+  the given space.
+*/
+void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
+
+/*
+  mspace_independent_calloc behaves as independent_calloc, but
+  operates within the given space.
+*/
+void** mspace_independent_calloc(mspace msp, size_t n_elements,
+                                 size_t elem_size, void* chunks[]);
+
+/*
+  mspace_independent_comalloc behaves as independent_comalloc, but
+  operates within the given space.
+*/
+void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+                                   size_t sizes[], void* chunks[]);
+
+/*
+  mspace_footprint() returns the number of bytes obtained from the
+  system for this space.
+*/
+size_t mspace_footprint(mspace msp);
+
+/*
+  mspace_max_footprint() returns the peak number of bytes obtained from the
+  system for this space.
+*/
+size_t mspace_max_footprint(mspace msp);
+
+
+#if !NO_MALLINFO
+/*
+  mspace_mallinfo behaves as mallinfo, but reports properties of
+  the given space.
+*/
+struct mallinfo mspace_mallinfo(mspace msp);
+#endif /* NO_MALLINFO */
+
+/*
+  mspace_malloc_stats behaves as malloc_stats, but reports
+  properties of the given space.
+*/
+void mspace_malloc_stats(mspace msp);
+
+/*
+  mspace_trim behaves as malloc_trim, but
+  operates within the given space.
+*/
+int mspace_trim(mspace msp, size_t pad);
+
+/*
+  An alias for mallopt.
+*/
+int mspace_mallopt(int, int);
+
+#endif /* MSPACES */
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif /* __cplusplus */
+
+/*
+  ========================================================================
+  To make a fully customizable malloc.h header file, cut everything
+  above this line, put into file malloc.h, edit to suit, and #include it
+  on the next line, as well as in programs that use this malloc.
+  ========================================================================
+*/
+
+/* #include "malloc.h" */
+
+/*------------------------------ internal #includes ---------------------- */
+
+#ifdef WIN32
+#pragma warning( disable : 4146 ) /* no "unsigned" warnings */
+#endif /* WIN32 */
+
+//#include <stdio.h>       /* for printing in malloc_stats */
+
+#ifndef LACKS_ERRNO_H
+#include <errno.h>       /* for MALLOC_FAILURE_ACTION */
+#endif /* LACKS_ERRNO_H */
+#if FOOTERS
+#include <time.h>        /* for magic initialization */
+#endif /* FOOTERS */
+#ifndef LACKS_STDLIB_H
+#include <stdlib.h>      /* for abort() */
+#endif /* LACKS_STDLIB_H */
+#ifdef DEBUG
+#if ABORT_ON_ASSERT_FAILURE
+#define assert(x) if(!(x)) ABORT
+#else /* ABORT_ON_ASSERT_FAILURE */
+//#include <assert.h>
+#define assert(x) if(!(x)) {printf("Assert failed: \"" #x "\"\n"; while(1);)}
+#endif /* ABORT_ON_ASSERT_FAILURE */
+#else  /* DEBUG */
+#define assert(x)
+#endif /* DEBUG */
+#ifndef LACKS_STRING_H
+#include <string.h>      /* for memset etc */
+#endif  /* LACKS_STRING_H */
+#if USE_BUILTIN_FFS
+#ifndef LACKS_STRINGS_H
+#include <strings.h>     /* for ffs */
+#endif /* LACKS_STRINGS_H */
+#endif /* USE_BUILTIN_FFS */
+#if HAVE_MMAP
+#ifndef LACKS_SYS_MMAN_H
+#include <sys/mman.h>    /* for mmap */
+#endif /* LACKS_SYS_MMAN_H */
+#ifndef LACKS_FCNTL_H
+#include <fcntl.h>
+#endif /* LACKS_FCNTL_H */
+#endif /* HAVE_MMAP */
+#if HAVE_MORECORE
+#ifndef LACKS_UNISTD_H
+#include <unistd.h>     /* for sbrk */
+#else /* LACKS_UNISTD_H */
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+extern void*     sbrk(int);
+#endif /* FreeBSD etc */
+#endif /* LACKS_UNISTD_H */
+#endif /* HAVE_MMAP */
+
+#ifndef WIN32
+#ifndef malloc_getpagesize
+#  ifdef _SC_PAGESIZE         /* some SVR4 systems omit an underscore */
+#    ifndef _SC_PAGE_SIZE
+#      define _SC_PAGE_SIZE _SC_PAGESIZE
+#    endif
+#  endif
+#  ifdef _SC_PAGE_SIZE
+#    define malloc_getpagesize sysconf(_SC_PAGE_SIZE)
+#  else
+#    if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE)
+       extern size_t getpagesize();
+#      define malloc_getpagesize getpagesize()
+#    else
+#      ifdef WIN32 /* use supplied emulation of getpagesize */
+#        define malloc_getpagesize getpagesize()
+#      else
+#        ifndef LACKS_SYS_PARAM_H
+#          include <sys/param.h>
+#        endif
+#        ifdef EXEC_PAGESIZE
+#          define malloc_getpagesize EXEC_PAGESIZE
+#        else
+#          ifdef NBPG
+#            ifndef CLSIZE
+#              define malloc_getpagesize NBPG
+#            else
+#              define malloc_getpagesize (NBPG * CLSIZE)
+#            endif
+#          else
+#            ifdef NBPC
+#              define malloc_getpagesize NBPC
+#            else
+#              ifdef PAGESIZE
+#                define malloc_getpagesize PAGESIZE
+#              else /* just guess */
+#                define malloc_getpagesize ((size_t)4096U)
+#              endif
+#            endif
+#          endif
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+#endif
+
+/* ------------------- size_t and alignment properties -------------------- */
+
+/* The byte and bit size of a size_t */
+#define SIZE_T_SIZE         (sizeof(size_t))
+#define SIZE_T_BITSIZE      (sizeof(size_t) << 3)
+
+/* Some constants coerced to size_t */
+/* Annoying but necessary to avoid errors on some plaftorms */
+#define SIZE_T_ZERO         ((size_t)0)
+#define SIZE_T_ONE          ((size_t)1)
+#define SIZE_T_TWO          ((size_t)2)
+#define TWO_SIZE_T_SIZES    (SIZE_T_SIZE<<1)
+#define FOUR_SIZE_T_SIZES   (SIZE_T_SIZE<<2)
+#define SIX_SIZE_T_SIZES    (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES)
+#define HALF_MAX_SIZE_T     (MAX_SIZE_T / 2U)
+
+/* The bit mask value corresponding to MALLOC_ALIGNMENT */
+#define CHUNK_ALIGN_MASK    (MALLOC_ALIGNMENT - SIZE_T_ONE)
+
+/* True if address a has acceptable alignment */
+#define is_aligned(A)       (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0)
+
+/* the number of bytes to offset an address to align it */
+#define align_offset(A)\
+ ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\
+  ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK))
+
+/* -------------------------- MMAP preliminaries ------------------------- */
+
+/*
+   If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and
+   checks to fail so compiler optimizer can delete code rather than
+   using so many "#if"s.
+*/
+
+
+/* MORECORE and MMAP must return MFAIL on failure */
+#define MFAIL                ((void*)(MAX_SIZE_T))
+#define CMFAIL               ((char*)(MFAIL)) /* defined for convenience */
+
+#if !HAVE_MMAP
+#define IS_MMAPPED_BIT       (SIZE_T_ZERO)
+#define USE_MMAP_BIT         (SIZE_T_ZERO)
+#define CALL_MMAP(s)         MFAIL
+#define CALL_MUNMAP(a, s)    (-1)
+#define DIRECT_MMAP(s)       MFAIL
+
+#else /* HAVE_MMAP */
+#define IS_MMAPPED_BIT       (SIZE_T_ONE)
+#define USE_MMAP_BIT         (SIZE_T_ONE)
+
+#ifndef WIN32
+#define CALL_MUNMAP(a, s)    munmap((a), (s))
+#define MMAP_PROT            (PROT_READ|PROT_WRITE)
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+#define MAP_ANONYMOUS        MAP_ANON
+#endif /* MAP_ANON */
+#ifdef MAP_ANONYMOUS
+#define MMAP_FLAGS           (MAP_PRIVATE|MAP_ANONYMOUS)
+#define CALL_MMAP(s)         mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0)
+#else /* MAP_ANONYMOUS */
+/*
+   Nearly all versions of mmap support MAP_ANONYMOUS, so the following
+   is unlikely to be needed, but is supplied just in case.
+*/
+#define MMAP_FLAGS           (MAP_PRIVATE)
+static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
+#define CALL_MMAP(s) ((dev_zero_fd < 0) ? \
+           (dev_zero_fd = open("/dev/zero", O_RDWR), \
+            mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \
+            mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0))
+#endif /* MAP_ANONYMOUS */
+
+#define DIRECT_MMAP(s)       CALL_MMAP(s)
+#else /* WIN32 */
+
+/* Win32 MMAP via VirtualAlloc */
+static void* win32mmap(size_t size) {
+  void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
+  return (ptr != 0)? ptr: MFAIL;
+}
+
+/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
+static void* win32direct_mmap(size_t size) {
+  void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
+                           PAGE_READWRITE);
+  return (ptr != 0)? ptr: MFAIL;
+}
+
+/* This function supports releasing coalesed segments */
+static int win32munmap(void* ptr, size_t size) {
+  MEMORY_BASIC_INFORMATION minfo;
+  char* cptr = ptr;
+  while (size) {
+    if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0)
+      return -1;
+    if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr ||
+        minfo.State != MEM_COMMIT || minfo.RegionSize > size)
+      return -1;
+    if (VirtualFree(cptr, 0, MEM_RELEASE) == 0)
+      return -1;
+    cptr += minfo.RegionSize;
+    size -= minfo.RegionSize;
+  }
+  return 0;
+}
+
+#define CALL_MMAP(s)         win32mmap(s)
+#define CALL_MUNMAP(a, s)    win32munmap((a), (s))
+#define DIRECT_MMAP(s)       win32direct_mmap(s)
+#endif /* WIN32 */
+#endif /* HAVE_MMAP */
+
+#if HAVE_MMAP && HAVE_MREMAP
+#define CALL_MREMAP(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv))
+#else  /* HAVE_MMAP && HAVE_MREMAP */
+#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL
+#endif /* HAVE_MMAP && HAVE_MREMAP */
+
+#if HAVE_MORECORE
+#define CALL_MORECORE(S)     MORECORE(S)
+#else  /* HAVE_MORECORE */
+#define CALL_MORECORE(S)     MFAIL
+#endif /* HAVE_MORECORE */
+
+/* mstate bit set if continguous morecore disabled or failed */
+#define USE_NONCONTIGUOUS_BIT (4U)
+
+/* segment bit set in create_mspace_with_base */
+#define EXTERN_BIT            (8U)
+
+
+/* --------------------------- Lock preliminaries ------------------------ */
+
+#if USE_LOCKS
+
+/*
+  When locks are defined, there are up to two global locks:
+
+  * If HAVE_MORECORE, morecore_mutex protects sequences of calls to
+    MORECORE.  In many cases sys_alloc requires two calls, that should
+    not be interleaved with calls by other threads.  This does not
+    protect against direct calls to MORECORE by other threads not
+    using this lock, so there is still code to cope the best we can on
+    interference.
+
+  * magic_init_mutex ensures that mparams.magic and other
+    unique mparams values are initialized only once.
+*/
+
+#ifndef WIN32
+/* By default use posix locks */
+#include <pthread.h>
+#define MLOCK_T pthread_mutex_t
+#define INITIAL_LOCK(l)      pthread_mutex_init(l, NULL)
+#define ACQUIRE_LOCK(l)      pthread_mutex_lock(l)
+#define RELEASE_LOCK(l)      pthread_mutex_unlock(l)
+
+#if HAVE_MORECORE
+static MLOCK_T morecore_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif /* HAVE_MORECORE */
+
+static MLOCK_T magic_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#else /* WIN32 */
+/*
+   Because lock-protected regions have bounded times, and there
+   are no recursive lock calls, we can use simple spinlocks.
+*/
+
+#define MLOCK_T long
+static int win32_acquire_lock (MLOCK_T *sl) {
+  for (;;) {
+#ifdef InterlockedCompareExchangePointer
+    if (!InterlockedCompareExchange(sl, 1, 0))
+      return 0;
+#else  /* Use older void* version */
+    if (!InterlockedCompareExchange((void**)sl, (void*)1, (void*)0))
+      return 0;
+#endif /* InterlockedCompareExchangePointer */
+    Sleep (0);
+  }
+}
+
+static void win32_release_lock (MLOCK_T *sl) {
+  InterlockedExchange (sl, 0);
+}
+
+#define INITIAL_LOCK(l)      *(l)=0
+#define ACQUIRE_LOCK(l)      win32_acquire_lock(l)
+#define RELEASE_LOCK(l)      win32_release_lock(l)
+#if HAVE_MORECORE
+static MLOCK_T morecore_mutex;
+#endif /* HAVE_MORECORE */
+static MLOCK_T magic_init_mutex;
+#endif /* WIN32 */
+
+#define USE_LOCK_BIT               (2U)
+#else  /* USE_LOCKS */
+#define USE_LOCK_BIT               (0U)
+#define INITIAL_LOCK(l)
+#endif /* USE_LOCKS */
+
+#if USE_LOCKS && HAVE_MORECORE
+#define ACQUIRE_MORECORE_LOCK()    ACQUIRE_LOCK(&morecore_mutex);
+#define RELEASE_MORECORE_LOCK()    RELEASE_LOCK(&morecore_mutex);
+#else /* USE_LOCKS && HAVE_MORECORE */
+#define ACQUIRE_MORECORE_LOCK()
+#define RELEASE_MORECORE_LOCK()
+#endif /* USE_LOCKS && HAVE_MORECORE */
+
+#if USE_LOCKS
+#define ACQUIRE_MAGIC_INIT_LOCK()  ACQUIRE_LOCK(&magic_init_mutex);
+#define RELEASE_MAGIC_INIT_LOCK()  RELEASE_LOCK(&magic_init_mutex);
+#else  /* USE_LOCKS */
+#define ACQUIRE_MAGIC_INIT_LOCK()
+#define RELEASE_MAGIC_INIT_LOCK()
+#endif /* USE_LOCKS */
+
+
+/* -----------------------  Chunk representations ------------------------ */
+
+/*
+  (The following includes lightly edited explanations by Colin Plumb.)
+
+  The malloc_chunk declaration below is misleading (but accurate and
+  necessary).  It declares a "view" into memory allowing access to
+  necessary fields at known offsets from a given base.
+
+  Chunks of memory are maintained using a `boundary tag' method as
+  originally described by Knuth.  (See the paper by Paul Wilson
+  ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such
+  techniques.)  Sizes of free chunks are stored both in the front of
+  each chunk and at the end.  This makes consolidating fragmented
+  chunks into bigger chunks fast.  The head fields also hold bits
+  representing whether chunks are free or in use.
+
+  Here are some pictures to make it clearer.  They are "exploded" to
+  show that the state of a chunk can be thought of as extending from
+  the high 31 bits of the head field of its header through the
+  prev_foot and PINUSE_BIT bit of the following chunk header.
+
+  A chunk that's in use looks like:
+
+   chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+           | Size of previous chunk (if P = 1)                             |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
+         | Size of this chunk                                         1| +-+
+   mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |                                                               |
+         +-                                                             -+
+         |                                                               |
+         +-                                                             -+
+         |                                                               :
+         +-      size - sizeof(size_t) available payload bytes          -+
+         :                                                               |
+ chunk-> +-                                                             -+
+         |                                                               |
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1|
+       | Size of next chunk (may or may not be in use)               | +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+    And if it's free, it looks like this:
+
+   chunk-> +-                                                             -+
+           | User payload (must be in use, or we would have merged!)       |
+           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P|
+         | Size of this chunk                                         0| +-+
+   mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         | Next pointer                                                  |
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         | Prev pointer                                                  |
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |                                                               :
+         +-      size - sizeof(struct chunk) unused bytes               -+
+         :                                                               |
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         | Size of this chunk                                            |
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0|
+       | Size of next chunk (must be in use, or we would have merged)| +-+
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |                                                               :
+       +- User payload                                                -+
+       :                                                               |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+                                                                     |0|
+                                                                     +-+
+  Note that since we always merge adjacent free chunks, the chunks
+  adjacent to a free chunk must be in use.
+
+  Given a pointer to a chunk (which can be derived trivially from the
+  payload pointer) we can, in O(1) time, find out whether the adjacent
+  chunks are free, and if so, unlink them from the lists that they
+  are on and merge them with the current chunk.
+
+  Chunks always begin on even word boundaries, so the mem portion
+  (which is returned to the user) is also on an even word boundary, and
+  thus at least double-word aligned.
+
+  The P (PINUSE_BIT) bit, stored in the unused low-order bit of the
+  chunk size (which is always a multiple of two words), is an in-use
+  bit for the *previous* chunk.  If that bit is *clear*, then the
+  word before the current chunk size contains the previous chunk
+  size, and can be used to find the front of the previous chunk.
+  The very first chunk allocated always has this bit set, preventing
+  access to non-existent (or non-owned) memory. If pinuse is set for
+  any given chunk, then you CANNOT determine the size of the
+  previous chunk, and might even get a memory addressing fault when
+  trying to do so.
+
+  The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of
+  the chunk size redundantly records whether the current chunk is
+  inuse. This redundancy enables usage checks within free and realloc,
+  and reduces indirection when freeing and consolidating chunks.
+
+  Each freshly allocated chunk must have both cinuse and pinuse set.
+  That is, each allocated chunk borders either a previously allocated
+  and still in-use chunk, or the base of its memory arena. This is
+  ensured by making all allocations from the the `lowest' part of any
+  found chunk.  Further, no free chunk physically borders another one,
+  so each free chunk is known to be preceded and followed by either
+  inuse chunks or the ends of memory.
+
+  Note that the `foot' of the current chunk is actually represented
+  as the prev_foot of the NEXT chunk. This makes it easier to
+  deal with alignments etc but can be very confusing when trying
+  to extend or adapt this code.
+
+  The exceptions to all this are
+
+     1. The special chunk `top' is the top-most available chunk (i.e.,
+        the one bordering the end of available memory). It is treated
+        specially.  Top is never included in any bin, is used only if
+        no other chunk is available, and is released back to the
+        system if it is very large (see M_TRIM_THRESHOLD).  In effect,
+        the top chunk is treated as larger (and thus less well
+        fitting) than any other available chunk.  The top chunk
+        doesn't update its trailing size field since there is no next
+        contiguous chunk that would have to index off it. However,
+        space is still allocated for it (TOP_FOOT_SIZE) to enable
+        separation or merging when space is extended.
+
+     3. Chunks allocated via mmap, which have the lowest-order bit
+        (IS_MMAPPED_BIT) set in their prev_foot fields, and do not set
+        PINUSE_BIT in their head fields.  Because they are allocated
+        one-by-one, each must carry its own prev_foot field, which is
+        also used to hold the offset this chunk has within its mmapped
+        region, which is needed to preserve alignment. Each mmapped
+        chunk is trailed by the first two fields of a fake next-chunk
+        for sake of usage checks.
+
+*/
+
+struct malloc_chunk {
+  size_t               prev_foot;  /* Size of previous chunk (if free).  */
+  size_t               head;       /* Size and inuse bits. */
+  struct malloc_chunk* fd;         /* double links -- used only if free. */
+  struct malloc_chunk* bk;
+};
+
+typedef struct malloc_chunk  mchunk;
+typedef struct malloc_chunk* mchunkptr;
+typedef struct malloc_chunk* sbinptr;  /* The type of bins of chunks */
+typedef unsigned int bindex_t;         /* Described below */
+typedef unsigned int binmap_t;         /* Described below */
+typedef unsigned int flag_t;           /* The type of various bit flag sets */
+
+/* ------------------- Chunks sizes and alignments ----------------------- */
+
+#define MCHUNK_SIZE         (sizeof(mchunk))
+
+#if FOOTERS
+#define CHUNK_OVERHEAD      (TWO_SIZE_T_SIZES)
+#else /* FOOTERS */
+#define CHUNK_OVERHEAD      (SIZE_T_SIZE)
+#endif /* FOOTERS */
+
+/* MMapped chunks need a second word of overhead ... */
+#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES)
+/* ... and additional padding for fake next-chunk at foot */
+#define MMAP_FOOT_PAD       (FOUR_SIZE_T_SIZES)
+
+/* The smallest size we can malloc is an aligned minimal chunk */
+#define MIN_CHUNK_SIZE\
+  ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
+
+/* conversion from malloc headers to user pointers, and back */
+#define chunk2mem(p)        ((void*)((char*)(p)       + TWO_SIZE_T_SIZES))
+#define mem2chunk(mem)      ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES))
+/* chunk associated with aligned address A */
+#define align_as_chunk(A)   (mchunkptr)((A) + align_offset(chunk2mem(A)))
+
+/* Bounds on request (not chunk) sizes. */
+#define MAX_REQUEST         ((-MIN_CHUNK_SIZE) << 2)
+#define MIN_REQUEST         (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE)
+
+/* pad request bytes into a usable size */
+#define pad_request(req) \
+   (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK)
+
+/* pad request, checking for minimum (but not maximum) */
+#define request2size(req) \
+  (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req))
+
+
+/* ------------------ Operations on head and foot fields ----------------- */
+
+/*
+  The head field of a chunk is or'ed with PINUSE_BIT when previous
+  adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in
+  use. If the chunk was obtained with mmap, the prev_foot field has
+  IS_MMAPPED_BIT set, otherwise holding the offset of the base of the
+  mmapped region to the base of the chunk.
+*/
+
+#define PINUSE_BIT          (SIZE_T_ONE)
+#define CINUSE_BIT          (SIZE_T_TWO)
+#define INUSE_BITS          (PINUSE_BIT|CINUSE_BIT)
+
+/* Head value for fenceposts */
+#define FENCEPOST_HEAD      (INUSE_BITS|SIZE_T_SIZE)
+
+/* extraction of fields from head words */
+#define cinuse(p)           ((p)->head & CINUSE_BIT)
+#define pinuse(p)           ((p)->head & PINUSE_BIT)
+#define chunksize(p)        ((p)->head & ~(INUSE_BITS))
+
+#define clear_pinuse(p)     ((p)->head &= ~PINUSE_BIT)
+#define clear_cinuse(p)     ((p)->head &= ~CINUSE_BIT)
+
+/* Treat space at ptr +/- offset as a chunk */
+#define chunk_plus_offset(p, s)  ((mchunkptr)(((char*)(p)) + (s)))
+#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s)))
+
+/* Ptr to next or previous physical malloc_chunk. */
+#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~INUSE_BITS)))
+#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) ))
+
+/* extract next chunk's pinuse bit */
+#define next_pinuse(p)  ((next_chunk(p)->head) & PINUSE_BIT)
+
+/* Get/set size at footer */
+#define get_foot(p, s)  (((mchunkptr)((char*)(p) + (s)))->prev_foot)
+#define set_foot(p, s)  (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s))
+
+/* Set size, pinuse bit, and foot */
+#define set_size_and_pinuse_of_free_chunk(p, s)\
+  ((p)->head = (s|PINUSE_BIT), set_foot(p, s))
+
+/* Set size, pinuse bit, foot, and clear next pinuse */
+#define set_free_with_pinuse(p, s, n)\
+  (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s))
+
+#define is_mmapped(p)\
+  (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_MMAPPED_BIT))
+
+/* Get the internal overhead associated with chunk p */
+#define overhead_for(p)\
+ (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD)
+
+/* Return true if malloced space is not necessarily cleared */
+#if MMAP_CLEARS
+#define calloc_must_clear(p) (!is_mmapped(p))
+#else /* MMAP_CLEARS */
+#define calloc_must_clear(p) (1)
+#endif /* MMAP_CLEARS */
+
+/* ---------------------- Overlaid data structures ----------------------- */
+
+/*
+  When chunks are not in use, they are treated as nodes of either
+  lists or trees.
+
+  "Small"  chunks are stored in circular doubly-linked lists, and look
+  like this:
+
+    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Size of previous chunk                            |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    `head:' |             Size of chunk, in bytes                         |P|
+      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Forward pointer to next chunk in list             |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Back pointer to previous chunk in list            |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Unused space (may be 0 bytes long)                .
+            .                                                               .
+            .                                                               |
+nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    `foot:' |             Size of chunk, in bytes                           |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+  Larger chunks are kept in a form of bitwise digital trees (aka
+  tries) keyed on chunksizes.  Because malloc_tree_chunks are only for
+  free chunks greater than 256 bytes, their size doesn't impose any
+  constraints on user chunk sizes.  Each node looks like:
+
+    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Size of previous chunk                            |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    `head:' |             Size of chunk, in bytes                         |P|
+      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Forward pointer to next chunk of same size        |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Back pointer to previous chunk of same size       |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Pointer to left child (child[0])                  |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Pointer to right child (child[1])                 |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Pointer to parent                                 |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             bin index of this chunk                           |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+            |             Unused space                                      .
+            .                                                               |
+nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    `foot:' |             Size of chunk, in bytes                           |
+            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+  Each tree holding treenodes is a tree of unique chunk sizes.  Chunks
+  of the same size are arranged in a circularly-linked list, with only
+  the oldest chunk (the next to be used, in our FIFO ordering)
+  actually in the tree.  (Tree members are distinguished by a non-null
+  parent pointer.)  If a chunk with the same size an an existing node
+  is inserted, it is linked off the existing node using pointers that
+  work in the same way as fd/bk pointers of small chunks.
+
+  Each tree contains a power of 2 sized range of chunk sizes (the
+  smallest is 0x100 <= x < 0x180), which is is divided in half at each
+  tree level, with the chunks in the smaller half of the range (0x100
+  <= x < 0x140 for the top nose) in the left subtree and the larger
+  half (0x140 <= x < 0x180) in the right subtree.  This is, of course,
+  done by inspecting individual bits.
+
+  Using these rules, each node's left subtree contains all smaller
+  sizes than its right subtree.  However, the node at the root of each
+  subtree has no particular ordering relationship to either.  (The
+  dividing line between the subtree sizes is based on trie relation.)
+  If we remove the last chunk of a given size from the interior of the
+  tree, we need to replace it with a leaf node.  The tree ordering
+  rules permit a node to be replaced by any leaf below it.
+
+  The smallest chunk in a tree (a common operation in a best-fit
+  allocator) can be found by walking a path to the leftmost leaf in
+  the tree.  Unlike a usual binary tree, where we follow left child
+  pointers until we reach a null, here we follow the right child
+  pointer any time the left one is null, until we reach a leaf with
+  both child pointers null. The smallest chunk in the tree will be
+  somewhere along that path.
+
+  The worst case number of steps to add, find, or remove a node is
+  bounded by the number of bits differentiating chunks within
+  bins. Under current bin calculations, this ranges from 6 up to 21
+  (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case
+  is of course much better.
+*/
+
+struct malloc_tree_chunk {
+  /* The first four fields must be compatible with malloc_chunk */
+  size_t                    prev_foot;
+  size_t                    head;
+  struct malloc_tree_chunk* fd;
+  struct malloc_tree_chunk* bk;
+
+  struct malloc_tree_chunk* child[2];
+  struct malloc_tree_chunk* parent;
+  bindex_t                  index;
+};
+
+typedef struct malloc_tree_chunk  tchunk;
+typedef struct malloc_tree_chunk* tchunkptr;
+typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */
+
+/* A little helper macro for trees */
+#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1])
+
+/* ----------------------------- Segments -------------------------------- */
+
+/*
+  Each malloc space may include non-contiguous segments, held in a
+  list headed by an embedded malloc_segment record representing the
+  top-most space. Segments also include flags holding properties of
+  the space. Large chunks that are directly allocated by mmap are not
+  included in this list. They are instead independently created and
+  destroyed without otherwise keeping track of them.
+
+  Segment management mainly comes into play for spaces allocated by
+  MMAP.  Any call to MMAP might or might not return memory that is
+  adjacent to an existing segment.  MORECORE normally contiguously
+  extends the current space, so this space is almost always adjacent,
+  which is simpler and faster to deal with. (This is why MORECORE is
+  used preferentially to MMAP when both are available -- see
+  sys_alloc.)  When allocating using MMAP, we don't use any of the
+  hinting mechanisms (inconsistently) supported in various
+  implementations of unix mmap, or distinguish reserving from
+  committing memory. Instead, we just ask for space, and exploit
+  contiguity when we get it.  It is probably possible to do
+  better than this on some systems, but no general scheme seems
+  to be significantly better.
+
+  Management entails a simpler variant of the consolidation scheme
+  used for chunks to reduce fragmentation -- new adjacent memory is
+  normally prepended or appended to an existing segment. However,
+  there are limitations compared to chunk consolidation that mostly
+  reflect the fact that segment processing is relatively infrequent
+  (occurring only when getting memory from system) and that we
+  don't expect to have huge numbers of segments:
+
+  * Segments are not indexed, so traversal requires linear scans.  (It
+    would be possible to index these, but is not worth the extra
+    overhead and complexity for most programs on most platforms.)
+  * New segments are only appended to old ones when holding top-most
+    memory; if they cannot be prepended to others, they are held in
+    different segments.
+
+  Except for the top-most segment of an mstate, each segment record
+  is kept at the tail of its segment. Segments are added by pushing
+  segment records onto the list headed by &mstate.seg for the
+  containing mstate.
+
+  Segment flags control allocation/merge/deallocation policies:
+  * If EXTERN_BIT set, then we did not allocate this segment,
+    and so should not try to deallocate or merge with others.
+    (This currently holds only for the initial segment passed
+    into create_mspace_with_base.)
+  * If IS_MMAPPED_BIT set, the segment may be merged with
+    other surrounding mmapped segments and trimmed/de-allocated
+    using munmap.
+  * If neither bit is set, then the segment was obtained using
+    MORECORE so can be merged with surrounding MORECORE'd segments
+    and deallocated/trimmed using MORECORE with negative arguments.
+*/
+
+struct malloc_segment {
+  char*        base;             /* base address */
+  size_t       size;             /* allocated size */
+  struct malloc_segment* next;   /* ptr to next segment */
+  flag_t       sflags;           /* mmap and extern flag */
+};
+
+#define is_mmapped_segment(S)  ((S)->sflags & IS_MMAPPED_BIT)
+#define is_extern_segment(S)   ((S)->sflags & EXTERN_BIT)
+
+typedef struct malloc_segment  msegment;
+typedef struct malloc_segment* msegmentptr;
+
+/* ---------------------------- malloc_state ----------------------------- */
+
+/*
+   A malloc_state holds all of the bookkeeping for a space.
+   The main fields are:
+
+  Top
+    The topmost chunk of the currently active segment. Its size is
+    cached in topsize.  The actual size of topmost space is
+    topsize+TOP_FOOT_SIZE, which includes space reserved for adding
+    fenceposts and segment records if necessary when getting more
+    space from the system.  The size at which to autotrim top is
+    cached from mparams in trim_check, except that it is disabled if
+    an autotrim fails.
+
+  Designated victim (dv)
+    This is the preferred chunk for servicing small requests that
+    don't have exact fits.  It is normally the chunk split off most
+    recently to service another small request.  Its size is cached in
+    dvsize. The link fields of this chunk are not maintained since it
+    is not kept in a bin.
+
+  SmallBins
+    An array of bin headers for free chunks.  These bins hold chunks
+    with sizes less than MIN_LARGE_SIZE bytes. Each bin contains
+    chunks of all the same size, spaced 8 bytes apart.  To simplify
+    use in double-linked lists, each bin header acts as a malloc_chunk
+    pointing to the real first node, if it exists (else pointing to
+    itself).  This avoids special-casing for headers.  But to avoid
+    waste, we allocate only the fd/bk pointers of bins, and then use
+    repositioning tricks to treat these as the fields of a chunk.
+
+  TreeBins
+    Treebins are pointers to the roots of trees holding a range of
+    sizes. There are 2 equally spaced treebins for each power of two
+    from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything
+    larger.
+
+  Bin maps
+    There is one bit map for small bins ("smallmap") and one for
+    treebins ("treemap).  Each bin sets its bit when non-empty, and
+    clears the bit when empty.  Bit operations are then used to avoid
+    bin-by-bin searching -- nearly all "search" is done without ever
+    looking at bins that won't be selected.  The bit maps
+    conservatively use 32 bits per map word, even if on 64bit system.
+    For a good description of some of the bit-based techniques used
+    here, see Henry S. Warren Jr's book "Hacker's Delight" (and
+    supplement at http://hackersdelight.org/). Many of these are
+    intended to reduce the branchiness of paths through malloc etc, as
+    well as to reduce the number of memory locations read or written.
+
+  Segments
+    A list of segments headed by an embedded malloc_segment record
+    representing the initial space.
+
+  Address check support
+    The least_addr field is the least address ever obtained from
+    MORECORE or MMAP. Attempted frees and reallocs of any address less
+    than this are trapped (unless INSECURE is defined).
+
+  Magic tag
+    A cross-check field that should always hold same value as mparams.magic.
+
+  Flags
+    Bits recording whether to use MMAP, locks, or contiguous MORECORE
+
+  Statistics
+    Each space keeps track of current and maximum system memory
+    obtained via MORECORE or MMAP.
+
+  Locking
+    If USE_LOCKS is defined, the "mutex" lock is acquired and released
+    around every public call using this mspace.
+*/
+
+/* Bin types, widths and sizes */
+#define NSMALLBINS        (32U)
+#define NTREEBINS         (32U)
+#define SMALLBIN_SHIFT    (3U)
+#define SMALLBIN_WIDTH    (SIZE_T_ONE << SMALLBIN_SHIFT)
+#define TREEBIN_SHIFT     (8U)
+#define MIN_LARGE_SIZE    (SIZE_T_ONE << TREEBIN_SHIFT)
+#define MAX_SMALL_SIZE    (MIN_LARGE_SIZE - SIZE_T_ONE)
+#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD)
+
+struct malloc_state {
+  binmap_t   smallmap;
+  binmap_t   treemap;
+  size_t     dvsize;
+  size_t     topsize;
+  char*      least_addr;
+  mchunkptr  dv;
+  mchunkptr  top;
+  size_t     trim_check;
+  size_t     magic;
+  mchunkptr  smallbins[(NSMALLBINS+1)*2];
+  tbinptr    treebins[NTREEBINS];
+  size_t     footprint;
+  size_t     max_footprint;
+  flag_t     mflags;
+#if USE_LOCKS
+  MLOCK_T    mutex;     /* locate lock among fields that rarely change */
+#endif /* USE_LOCKS */
+  msegment   seg;
+};
+
+typedef struct malloc_state*    mstate;
+
+/* ------------- Global malloc_state and malloc_params ------------------- */
+
+/*
+  malloc_params holds global properties, including those that can be
+  dynamically set using mallopt. There is a single instance, mparams,
+  initialized in init_mparams.
+*/
+
+struct malloc_params {
+  size_t magic;
+  size_t page_size;
+  size_t granularity;
+  size_t mmap_threshold;
+  size_t trim_threshold;
+  flag_t default_mflags;
+};
+
+static struct malloc_params mparams;
+
+/* The global malloc_state used for all non-"mspace" calls */
+static struct malloc_state _gm_;
+#define gm                 (&_gm_)
+#define is_global(M)       ((M) == &_gm_)
+#define is_initialized(M)  ((M)->top != 0)
+
+/* -------------------------- system alloc setup ------------------------- */
+
+/* Operations on mflags */
+
+#define use_lock(M)           ((M)->mflags &   USE_LOCK_BIT)
+#define enable_lock(M)        ((M)->mflags |=  USE_LOCK_BIT)
+#define disable_lock(M)       ((M)->mflags &= ~USE_LOCK_BIT)
+
+#define use_mmap(M)           ((M)->mflags &   USE_MMAP_BIT)
+#define enable_mmap(M)        ((M)->mflags |=  USE_MMAP_BIT)
+#define disable_mmap(M)       ((M)->mflags &= ~USE_MMAP_BIT)
+
+#define use_noncontiguous(M)  ((M)->mflags &   USE_NONCONTIGUOUS_BIT)
+#define disable_contiguous(M) ((M)->mflags |=  USE_NONCONTIGUOUS_BIT)
+
+#define set_lock(M,L)\
+ ((M)->mflags = (L)?\
+  ((M)->mflags | USE_LOCK_BIT) :\
+  ((M)->mflags & ~USE_LOCK_BIT))
+
+/* page-align a size */
+#define page_align(S)\
+ (((S) + (mparams.page_size)) & ~(mparams.page_size - SIZE_T_ONE))
+
+/* granularity-align a size */
+#define granularity_align(S)\
+  (((S) + (mparams.granularity)) & ~(mparams.granularity - SIZE_T_ONE))
+
+#define is_page_aligned(S)\
+   (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0)
+#define is_granularity_aligned(S)\
+   (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0)
+
+/*  True if segment S holds address A */
+#define segment_holds(S, A)\
+  ((char*)(A) >= S->base && (char*)(A) < S->base + S->size)
+
+/* Return segment holding given address */
+static msegmentptr segment_holding(mstate m, char* addr) {
+  msegmentptr sp = &m->seg;
+  for (;;) {
+    if (addr >= sp->base && addr < sp->base + sp->size)
+      return sp;
+    if ((sp = sp->next) == 0)
+      return 0;
+  }
+}
+
+/* Return true if segment contains a segment link */
+static int has_segment_link(mstate m, msegmentptr ss) {
+  msegmentptr sp = &m->seg;
+  for (;;) {
+    if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size)
+      return 1;
+    if ((sp = sp->next) == 0)
+      return 0;
+  }
+}
+
+#ifndef MORECORE_CANNOT_TRIM
+#define should_trim(M,s)  ((s) > (M)->trim_check)
+#else  /* MORECORE_CANNOT_TRIM */
+#define should_trim(M,s)  (0)
+#endif /* MORECORE_CANNOT_TRIM */
+
+/*
+  TOP_FOOT_SIZE is padding at the end of a segment, including space
+  that may be needed to place segment records and fenceposts when new
+  noncontiguous segments are added.
+*/
+#define TOP_FOOT_SIZE\
+  (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE)
+
+
+/* -------------------------------  Hooks -------------------------------- */
+
+/*
+  PREACTION should be defined to return 0 on success, and nonzero on
+  failure. If you are not using locking, you can redefine these to do
+  anything you like.
+*/
+
+#if USE_LOCKS
+
+/* Ensure locks are initialized */
+#define GLOBALLY_INITIALIZE() (mparams.page_size == 0 && init_mparams())
+
+#define PREACTION(M)  ((GLOBALLY_INITIALIZE() || use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0)
+#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); }
+#else /* USE_LOCKS */
+
+#ifndef PREACTION
+#define PREACTION(M) (0)
+#endif  /* PREACTION */
+
+#ifndef POSTACTION
+#define POSTACTION(M)
+#endif  /* POSTACTION */
+
+#endif /* USE_LOCKS */
+
+/*
+  CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses.
+  USAGE_ERROR_ACTION is triggered on detected bad frees and
+  reallocs. The argument p is an address that might have triggered the
+  fault. It is ignored by the two predefined actions, but might be
+  useful in custom actions that try to help diagnose errors.
+*/
+
+#if PROCEED_ON_ERROR
+
+/* A count of the number of corruption errors causing resets */
+int malloc_corruption_error_count;
+
+/* default corruption action */
+static void reset_on_error(mstate m);
+
+#define CORRUPTION_ERROR_ACTION(m)  reset_on_error(m)
+#define USAGE_ERROR_ACTION(m, p)
+
+#else /* PROCEED_ON_ERROR */
+
+#ifndef CORRUPTION_ERROR_ACTION
+#define CORRUPTION_ERROR_ACTION(m) ABORT
+#endif /* CORRUPTION_ERROR_ACTION */
+
+#ifndef USAGE_ERROR_ACTION
+#define USAGE_ERROR_ACTION(m,p) ABORT
+#endif /* USAGE_ERROR_ACTION */
+
+#endif /* PROCEED_ON_ERROR */
+
+/* -------------------------- Debugging setup ---------------------------- */
+
+#if ! DEBUG
+
+#define check_free_chunk(M,P)
+#define check_inuse_chunk(M,P)
+#define check_malloced_chunk(M,P,N)
+#define check_mmapped_chunk(M,P)
+#define check_malloc_state(M)
+#define check_top_chunk(M,P)
+
+#else /* DEBUG */
+#define check_free_chunk(M,P)       do_check_free_chunk(M,P)
+#define check_inuse_chunk(M,P)      do_check_inuse_chunk(M,P)
+#define check_top_chunk(M,P)        do_check_top_chunk(M,P)
+#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N)
+#define check_mmapped_chunk(M,P)    do_check_mmapped_chunk(M,P)
+#define check_malloc_state(M)       do_check_malloc_state(M)
+
+static void   do_check_any_chunk(mstate m, mchunkptr p);
+static void   do_check_top_chunk(mstate m, mchunkptr p);
+static void   do_check_mmapped_chunk(mstate m, mchunkptr p);
+static void   do_check_inuse_chunk(mstate m, mchunkptr p);
+static void   do_check_free_chunk(mstate m, mchunkptr p);
+static void   do_check_malloced_chunk(mstate m, void* mem, size_t s);
+static void   do_check_tree(mstate m, tchunkptr t);
+static void   do_check_treebin(mstate m, bindex_t i);
+static void   do_check_smallbin(mstate m, bindex_t i);
+static void   do_check_malloc_state(mstate m);
+static int    bin_find(mstate m, mchunkptr x);
+static size_t traverse_and_check(mstate m);
+#endif /* DEBUG */
+
+/* ---------------------------- Indexing Bins ---------------------------- */
+
+#define is_small(s)         (((s) >> SMALLBIN_SHIFT) < NSMALLBINS)
+#define small_index(s)      ((s)  >> SMALLBIN_SHIFT)
+#define small_index2size(i) ((i)  << SMALLBIN_SHIFT)
+#define MIN_SMALL_INDEX     (small_index(MIN_CHUNK_SIZE))
+
+/* addressing by index. See above about smallbin repositioning */
+#define smallbin_at(M, i)   ((sbinptr)((char*)&((M)->smallbins[(i)<<1])))
+#define treebin_at(M,i)     (&((M)->treebins[i]))
+
+/* assign tree index for size S to variable I */
+#if defined(__GNUC__) && defined(i386)
+#define compute_tree_index(S, I)\
+{\
+  size_t X = S >> TREEBIN_SHIFT;\
+  if (X == 0)\
+    I = 0;\
+  else if (X > 0xFFFF)\
+    I = NTREEBINS-1;\
+  else {\
+    unsigned int K;\
+    __asm__("bsrl %1,%0\n\t" : "=r" (K) : "rm"  (X));\
+    I =  (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\
+  }\
+}
+#else /* GNUC */
+#define compute_tree_index(S, I)\
+{\
+  size_t X = S >> TREEBIN_SHIFT;\
+  if (X == 0)\
+    I = 0;\
+  else if (X > 0xFFFF)\
+    I = NTREEBINS-1;\
+  else {\
+    unsigned int Y = (unsigned int)X;\
+    unsigned int N = ((Y - 0x100) >> 16) & 8;\
+    unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\
+    N += K;\
+    N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\
+    K = 14 - N + ((Y <<= K) >> 15);\
+    I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\
+  }\
+}
+#endif /* GNUC */
+
+/* Bit representing maximum resolved size in a treebin at i */
+#define bit_for_tree_index(i) \
+   (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2)
+
+/* Shift placing maximum resolved bit in a treebin at i as sign bit */
+#define leftshift_for_tree_index(i) \
+   ((i == NTREEBINS-1)? 0 : \
+    ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2)))
+
+/* The size of the smallest chunk held in bin with index i */
+#define minsize_for_tree_index(i) \
+   ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) |  \
+   (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1)))
+
+
+/* ------------------------ Operations on bin maps ----------------------- */
+
+/* bit corresponding to given index */
+#define idx2bit(i)              ((binmap_t)(1) << (i))
+
+/* Mark/Clear bits with given index */
+#define mark_smallmap(M,i)      ((M)->smallmap |=  idx2bit(i))
+#define clear_smallmap(M,i)     ((M)->smallmap &= ~idx2bit(i))
+#define smallmap_is_marked(M,i) ((M)->smallmap &   idx2bit(i))
+
+#define mark_treemap(M,i)       ((M)->treemap  |=  idx2bit(i))
+#define clear_treemap(M,i)      ((M)->treemap  &= ~idx2bit(i))
+#define treemap_is_marked(M,i)  ((M)->treemap  &   idx2bit(i))
+
+/* index corresponding to given bit */
+
+#if defined(__GNUC__) && defined(i386)
+#define compute_bit2idx(X, I)\
+{\
+  unsigned int J;\
+  __asm__("bsfl %1,%0\n\t" : "=r" (J) : "rm" (X));\
+  I = (bindex_t)J;\
+}
+
+#else /* GNUC */
+#if  USE_BUILTIN_FFS
+#define compute_bit2idx(X, I) I = ffs(X)-1
+
+#else /* USE_BUILTIN_FFS */
+#define compute_bit2idx(X, I)\
+{\
+  unsigned int Y = X - 1;\
+  unsigned int K = Y >> (16-4) & 16;\
+  unsigned int N = K;        Y >>= K;\
+  N += K = Y >> (8-3) &  8;  Y >>= K;\
+  N += K = Y >> (4-2) &  4;  Y >>= K;\
+  N += K = Y >> (2-1) &  2;  Y >>= K;\
+  N += K = Y >> (1-0) &  1;  Y >>= K;\
+  I = (bindex_t)(N + Y);\
+}
+#endif /* USE_BUILTIN_FFS */
+#endif /* GNUC */
+
+/* isolate the least set bit of a bitmap */
+#define least_bit(x)         ((x) & -(x))
+
+/* mask with all bits to left of least bit of x on */
+#define left_bits(x)         ((x<<1) | -(x<<1))
+
+/* mask with all bits to left of or equal to least bit of x on */
+#define same_or_left_bits(x) ((x) | -(x))
+
+
+/* ----------------------- Runtime Check Support ------------------------- */
+
+/*
+  For security, the main invariant is that malloc/free/etc never
+  writes to a static address other than malloc_state, unless static
+  malloc_state itself has been corrupted, which cannot occur via
+  malloc (because of these checks). In essence this means that we
+  believe all pointers, sizes, maps etc held in malloc_state, but
+  check all of those linked or offsetted from other embedded data
+  structures.  These checks are interspersed with main code in a way
+  that tends to minimize their run-time cost.
+
+  When FOOTERS is defined, in addition to range checking, we also
+  verify footer fields of inuse chunks, which can be used guarantee
+  that the mstate controlling malloc/free is intact.  This is a
+  streamlined version of the approach described by William Robertson
+  et al in "Run-time Detection of Heap-based Overflows" LISA'03
+  http://www.usenix.org/events/lisa03/tech/robertson.html The footer
+  of an inuse chunk holds the xor of its mstate and a random seed,
+  that is checked upon calls to free() and realloc().  This is
+  (probablistically) unguessable from outside the program, but can be
+  computed by any code successfully malloc'ing any chunk, so does not
+  itself provide protection against code that has already broken
+  security through some other means.  Unlike Robertson et al, we
+  always dynamically check addresses of all offset chunks (previous,
+  next, etc). This turns out to be cheaper than relying on hashes.
+*/
+
+#if !INSECURE
+/* Check if address a is at least as high as any from MORECORE or MMAP */
+#define ok_address(M, a) ((char*)(a) >= (M)->least_addr)
+/* Check if address of next chunk n is higher than base chunk p */
+#define ok_next(p, n)    ((char*)(p) < (char*)(n))
+/* Check if p has its cinuse bit on */
+#define ok_cinuse(p)     cinuse(p)
+/* Check if p has its pinuse bit on */
+#define ok_pinuse(p)     pinuse(p)
+
+#else /* !INSECURE */
+#define ok_address(M, a) (1)
+#define ok_next(b, n)    (1)
+#define ok_cinuse(p)     (1)
+#define ok_pinuse(p)     (1)
+#endif /* !INSECURE */
+
+#if (FOOTERS && !INSECURE)
+/* Check if (alleged) mstate m has expected magic field */
+#define ok_magic(M)      ((M)->magic == mparams.magic)
+#else  /* (FOOTERS && !INSECURE) */
+#define ok_magic(M)      (1)
+#endif /* (FOOTERS && !INSECURE) */
+
+
+/* In gcc, use __builtin_expect to minimize impact of checks */
+#if !INSECURE
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define RTCHECK(e)  __builtin_expect(e, 1)
+#else /* GNUC */
+#define RTCHECK(e)  (e)
+#endif /* GNUC */
+#else /* !INSECURE */
+#define RTCHECK(e)  (1)
+#endif /* !INSECURE */
+
+/* macros to set up inuse chunks with or without footers */
+
+#if !FOOTERS
+
+#define mark_inuse_foot(M,p,s)
+
+/* Set cinuse bit and pinuse bit of next chunk */
+#define set_inuse(M,p,s)\
+  ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
+  ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
+
+/* Set cinuse and pinuse of this chunk and pinuse of next chunk */
+#define set_inuse_and_pinuse(M,p,s)\
+  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+  ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT)
+
+/* Set size, cinuse and pinuse bit of this chunk */
+#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
+  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT))
+
+#else /* FOOTERS */
+
+/* Set foot of inuse chunk to be xor of mstate and seed */
+#define mark_inuse_foot(M,p,s)\
+  (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic))
+
+#define get_mstate_for(p)\
+  ((mstate)(((mchunkptr)((char*)(p) +\
+    (chunksize(p))))->prev_foot ^ mparams.magic))
+
+#define set_inuse(M,p,s)\
+  ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\
+  (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \
+  mark_inuse_foot(M,p,s))
+
+#define set_inuse_and_pinuse(M,p,s)\
+  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+  (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\
+ mark_inuse_foot(M,p,s))
+
+#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\
+  ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\
+  mark_inuse_foot(M, p, s))
+
+#endif /* !FOOTERS */
+
+/* ---------------------------- setting mparams -------------------------- */
+
+/* Initialize mparams */
+static int init_mparams(void) {
+  if (mparams.page_size == 0) {
+    size_t s;
+
+    mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD;
+    mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD;
+#if MORECORE_CONTIGUOUS
+    mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT;
+#else  /* MORECORE_CONTIGUOUS */
+    mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT;
+#endif /* MORECORE_CONTIGUOUS */
+
+#if (FOOTERS && !INSECURE)
+    {
+#if USE_DEV_RANDOM
+      int fd;
+      unsigned char buf[sizeof(size_t)];
+      /* Try to use /dev/urandom, else fall back on using time */
+      if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 &&
+          read(fd, buf, sizeof(buf)) == sizeof(buf)) {
+        s = *((size_t *) buf);
+        close(fd);
+      }
+      else
+#endif /* USE_DEV_RANDOM */
+        s = (size_t)(time(0) ^ (size_t)0x55555555U);
+
+      s |= (size_t)8U;    /* ensure nonzero */
+      s &= ~(size_t)7U;   /* improve chances of fault for bad values */
+
+    }
+#else /* (FOOTERS && !INSECURE) */
+    s = (size_t)0x58585858U;
+#endif /* (FOOTERS && !INSECURE) */
+    ACQUIRE_MAGIC_INIT_LOCK();
+    if (mparams.magic == 0) {
+      mparams.magic = s;
+      /* Set up lock for main malloc area */
+      INITIAL_LOCK(&gm->mutex);
+      gm->mflags = mparams.default_mflags;
+    }
+    RELEASE_MAGIC_INIT_LOCK();
+
+#ifndef WIN32
+    mparams.page_size = malloc_getpagesize;
+    mparams.granularity = ((DEFAULT_GRANULARITY != 0)?
+                           DEFAULT_GRANULARITY : mparams.page_size);
+#else /* WIN32 */
+    {
+      SYSTEM_INFO system_info;
+      GetSystemInfo(&system_info);
+      mparams.page_size = system_info.dwPageSize;
+      mparams.granularity = system_info.dwAllocationGranularity;
+    }
+#endif /* WIN32 */
+
+    /* Sanity-check configuration:
+       size_t must be unsigned and as wide as pointer type.
+       ints must be at least 4 bytes.
+       alignment must be at least 8.
+       Alignment, min chunk size, and page size must all be powers of 2.
+    */
+    if ((sizeof(size_t) != sizeof(char*)) ||
+        (MAX_SIZE_T < MIN_CHUNK_SIZE)  ||
+        (sizeof(int) < 4)  ||
+        (MALLOC_ALIGNMENT < (size_t)8U) ||
+        ((MALLOC_ALIGNMENT    & (MALLOC_ALIGNMENT-SIZE_T_ONE))    != 0) ||
+        ((MCHUNK_SIZE         & (MCHUNK_SIZE-SIZE_T_ONE))         != 0) ||
+        ((mparams.granularity & (mparams.granularity-SIZE_T_ONE)) != 0) ||
+        ((mparams.page_size   & (mparams.page_size-SIZE_T_ONE))   != 0))
+      ABORT;
+  }
+  return 0;
+}
+
+/* support for mallopt */
+static int change_mparam(int param_number, int value) {
+  size_t val = (size_t)value;
+  init_mparams();
+  switch(param_number) {
+  case M_TRIM_THRESHOLD:
+    mparams.trim_threshold = val;
+    return 1;
+  case M_GRANULARITY:
+    if (val >= mparams.page_size && ((val & (val-1)) == 0)) {
+      mparams.granularity = val;
+      return 1;
+    }
+    else
+      return 0;
+  case M_MMAP_THRESHOLD:
+    mparams.mmap_threshold = val;
+    return 1;
+  default:
+    return 0;
+  }
+}
+
+#if DEBUG
+/* ------------------------- Debugging Support --------------------------- */
+
+/* Check properties of any chunk, whether free, inuse, mmapped etc  */
+static void do_check_any_chunk(mstate m, mchunkptr p) {
+  assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+  assert(ok_address(m, p));
+}
+
+/* Check properties of top chunk */
+static void do_check_top_chunk(mstate m, mchunkptr p) {
+  msegmentptr sp = segment_holding(m, (char*)p);
+  size_t  sz = chunksize(p);
+  assert(sp != 0);
+  assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+  assert(ok_address(m, p));
+  assert(sz == m->topsize);
+  assert(sz > 0);
+  assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE);
+  assert(pinuse(p));
+  assert(!next_pinuse(p));
+}
+
+/* Check properties of (inuse) mmapped chunks */
+static void do_check_mmapped_chunk(mstate m, mchunkptr p) {
+  size_t  sz = chunksize(p);
+  size_t len = (sz + (p->prev_foot & ~IS_MMAPPED_BIT) + MMAP_FOOT_PAD);
+  assert(is_mmapped(p));
+  assert(use_mmap(m));
+  assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
+  assert(ok_address(m, p));
+  assert(!is_small(sz));
+  assert((len & (mparams.page_size-SIZE_T_ONE)) == 0);
+  assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD);
+  assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0);
+}
+
+/* Check properties of inuse chunks */
+static void do_check_inuse_chunk(mstate m, mchunkptr p) {
+  do_check_any_chunk(m, p);
+  assert(cinuse(p));
+  assert(next_pinuse(p));
+  /* If not pinuse and not mmapped, previous chunk has OK offset */
+  assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p);
+  if (is_mmapped(p))
+    do_check_mmapped_chunk(m, p);
+}
+
+/* Check properties of free chunks */
+static void do_check_free_chunk(mstate m, mchunkptr p) {
+  size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT);
+  mchunkptr next = chunk_plus_offset(p, sz);
+  do_check_any_chunk(m, p);
+  assert(!cinuse(p));
+  assert(!next_pinuse(p));
+  assert (!is_mmapped(p));
+  if (p != m->dv && p != m->top) {
+    if (sz >= MIN_CHUNK_SIZE) {
+      assert((sz & CHUNK_ALIGN_MASK) == 0);
+      assert(is_aligned(chunk2mem(p)));
+      assert(next->prev_foot == sz);
+      assert(pinuse(p));
+      assert (next == m->top || cinuse(next));
+      assert(p->fd->bk == p);
+      assert(p->bk->fd == p);
+    }
+    else  /* markers are always of size SIZE_T_SIZE */
+      assert(sz == SIZE_T_SIZE);
+  }
+}
+
+/* Check properties of malloced chunks at the point they are malloced */
+static void do_check_malloced_chunk(mstate m, void* mem, size_t s) {
+  if (mem != 0) {
+    mchunkptr p = mem2chunk(mem);
+    size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT);
+    do_check_inuse_chunk(m, p);
+    assert((sz & CHUNK_ALIGN_MASK) == 0);
+    assert(sz >= MIN_CHUNK_SIZE);
+    assert(sz >= s);
+    /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */
+    assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE));
+  }
+}
+
+/* Check a tree and its subtrees.  */
+static void do_check_tree(mstate m, tchunkptr t) {
+  tchunkptr head = 0;
+  tchunkptr u = t;
+  bindex_t tindex = t->index;
+  size_t tsize = chunksize(t);
+  bindex_t idx;
+  compute_tree_index(tsize, idx);
+  assert(tindex == idx);
+  assert(tsize >= MIN_LARGE_SIZE);
+  assert(tsize >= minsize_for_tree_index(idx));
+  assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1))));
+
+  do { /* traverse through chain of same-sized nodes */
+    do_check_any_chunk(m, ((mchunkptr)u));
+    assert(u->index == tindex);
+    assert(chunksize(u) == tsize);
+    assert(!cinuse(u));
+    assert(!next_pinuse(u));
+    assert(u->fd->bk == u);
+    assert(u->bk->fd == u);
+    if (u->parent == 0) {
+      assert(u->child[0] == 0);
+      assert(u->child[1] == 0);
+    }
+    else {
+      assert(head == 0); /* only one node on chain has parent */
+      head = u;
+      assert(u->parent != u);
+      assert (u->parent->child[0] == u ||
+              u->parent->child[1] == u ||
+              *((tbinptr*)(u->parent)) == u);
+      if (u->child[0] != 0) {
+        assert(u->child[0]->parent == u);
+        assert(u->child[0] != u);
+        do_check_tree(m, u->child[0]);
+      }
+      if (u->child[1] != 0) {
+        assert(u->child[1]->parent == u);
+        assert(u->child[1] != u);
+        do_check_tree(m, u->child[1]);
+      }
+      if (u->child[0] != 0 && u->child[1] != 0) {
+        assert(chunksize(u->child[0]) < chunksize(u->child[1]));
+      }
+    }
+    u = u->fd;
+  } while (u != t);
+  assert(head != 0);
+}
+
+/*  Check all the chunks in a treebin.  */
+static void do_check_treebin(mstate m, bindex_t i) {
+  tbinptr* tb = treebin_at(m, i);
+  tchunkptr t = *tb;
+  int empty = (m->treemap & (1U << i)) == 0;
+  if (t == 0)
+    assert(empty);
+  if (!empty)
+    do_check_tree(m, t);
+}
+
+/*  Check all the chunks in a smallbin.  */
+static void do_check_smallbin(mstate m, bindex_t i) {
+  sbinptr b = smallbin_at(m, i);
+  mchunkptr p = b->bk;
+  unsigned int empty = (m->smallmap & (1U << i)) == 0;
+  if (p == b)
+    assert(empty);
+  if (!empty) {
+    for (; p != b; p = p->bk) {
+      size_t size = chunksize(p);
+      mchunkptr q;
+      /* each chunk claims to be free */
+      do_check_free_chunk(m, p);
+      /* chunk belongs in bin */
+      assert(small_index(size) == i);
+      assert(p->bk == b || chunksize(p->bk) == chunksize(p));
+      /* chunk is followed by an inuse chunk */
+      q = next_chunk(p);
+      if (q->head != FENCEPOST_HEAD)
+        do_check_inuse_chunk(m, q);
+    }
+  }
+}
+
+/* Find x in a bin. Used in other check functions. */
+static int bin_find(mstate m, mchunkptr x) {
+  size_t size = chunksize(x);
+  if (is_small(size)) {
+    bindex_t sidx = small_index(size);
+    sbinptr b = smallbin_at(m, sidx);
+    if (smallmap_is_marked(m, sidx)) {
+      mchunkptr p = b;
+      do {
+        if (p == x)
+          return 1;
+      } while ((p = p->fd) != b);
+    }
+  }
+  else {
+    bindex_t tidx;
+    compute_tree_index(size, tidx);
+    if (treemap_is_marked(m, tidx)) {
+      tchunkptr t = *treebin_at(m, tidx);
+      size_t sizebits = size << leftshift_for_tree_index(tidx);
+      while (t != 0 && chunksize(t) != size) {
+        t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
+        sizebits <<= 1;
+      }
+      if (t != 0) {
+        tchunkptr u = t;
+        do {
+          if (u == (tchunkptr)x)
+            return 1;
+        } while ((u = u->fd) != t);
+      }
+    }
+  }
+  return 0;
+}
+
+/* Traverse each chunk and check it; return total */
+static size_t traverse_and_check(mstate m) {
+  size_t sum = 0;
+  if (is_initialized(m)) {
+    msegmentptr s = &m->seg;
+    sum += m->topsize + TOP_FOOT_SIZE;
+    while (s != 0) {
+      mchunkptr q = align_as_chunk(s->base);
+      mchunkptr lastq = 0;
+      assert(pinuse(q));
+      while (segment_holds(s, q) &&
+             q != m->top && q->head != FENCEPOST_HEAD) {
+        sum += chunksize(q);
+        if (cinuse(q)) {
+          assert(!bin_find(m, q));
+          do_check_inuse_chunk(m, q);
+        }
+        else {
+          assert(q == m->dv || bin_find(m, q));
+          assert(lastq == 0 || cinuse(lastq)); /* Not 2 consecutive free */
+          do_check_free_chunk(m, q);
+        }
+        lastq = q;
+        q = next_chunk(q);
+      }
+      s = s->next;
+    }
+  }
+  return sum;
+}
+
+/* Check all properties of malloc_state. */
+static void do_check_malloc_state(mstate m) {
+  bindex_t i;
+  size_t total;
+  /* check bins */
+  for (i = 0; i < NSMALLBINS; ++i)
+    do_check_smallbin(m, i);
+  for (i = 0; i < NTREEBINS; ++i)
+    do_check_treebin(m, i);
+
+  if (m->dvsize != 0) { /* check dv chunk */
+    do_check_any_chunk(m, m->dv);
+    assert(m->dvsize == chunksize(m->dv));
+    assert(m->dvsize >= MIN_CHUNK_SIZE);
+    assert(bin_find(m, m->dv) == 0);
+  }
+
+  if (m->top != 0) {   /* check top chunk */
+    do_check_top_chunk(m, m->top);
+    assert(m->topsize == chunksize(m->top));
+    assert(m->topsize > 0);
+    assert(bin_find(m, m->top) == 0);
+  }
+
+  total = traverse_and_check(m);
+  assert(total <= m->footprint);
+  assert(m->footprint <= m->max_footprint);
+}
+#endif /* DEBUG */
+
+/* ----------------------------- statistics ------------------------------ */
+
+#if !NO_MALLINFO
+static struct mallinfo internal_mallinfo(mstate m) {
+  struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+  if (!PREACTION(m)) {
+    check_malloc_state(m);
+    if (is_initialized(m)) {
+      size_t nfree = SIZE_T_ONE; /* top always free */
+      size_t mfree = m->topsize + TOP_FOOT_SIZE;
+      size_t sum = mfree;
+      msegmentptr s = &m->seg;
+      while (s != 0) {
+        mchunkptr q = align_as_chunk(s->base);
+        while (segment_holds(s, q) &&
+               q != m->top && q->head != FENCEPOST_HEAD) {
+          size_t sz = chunksize(q);
+          sum += sz;
+          if (!cinuse(q)) {
+            mfree += sz;
+            ++nfree;
+          }
+          q = next_chunk(q);
+        }
+        s = s->next;
+      }
+
+      nm.arena    = sum;
+      nm.ordblks  = nfree;
+      nm.hblkhd   = m->footprint - sum;
+      nm.usmblks  = m->max_footprint;
+      nm.uordblks = m->footprint - mfree;
+      nm.fordblks = mfree;
+      nm.keepcost = m->topsize;
+    }
+
+    POSTACTION(m);
+  }
+  return nm;
+}
+#endif /* !NO_MALLINFO */
+
+static void internal_malloc_stats(mstate m) {
+  if (!PREACTION(m)) {
+    size_t maxfp = 0;
+    size_t fp = 0;
+    size_t used = 0;
+    check_malloc_state(m);
+    if (is_initialized(m)) {
+      msegmentptr s = &m->seg;
+      maxfp = m->max_footprint;
+      fp = m->footprint;
+      used = fp - (m->topsize + TOP_FOOT_SIZE);
+
+      while (s != 0) {
+        mchunkptr q = align_as_chunk(s->base);
+        while (segment_holds(s, q) &&
+               q != m->top && q->head != FENCEPOST_HEAD) {
+          if (!cinuse(q))
+            used -= chunksize(q);
+          q = next_chunk(q);
+        }
+        s = s->next;
+      }
+    }
+
+    printf("max system bytes = %10lu\n", (unsigned long)(maxfp));
+    printf("system bytes     = %10lu\n", (unsigned long)(fp));
+    printf("in use bytes     = %10lu\n", (unsigned long)(used));
+
+    POSTACTION(m);
+  }
+}
+
+/* ----------------------- Operations on smallbins ----------------------- */
+
+/*
+  Various forms of linking and unlinking are defined as macros.  Even
+  the ones for trees, which are very long but have very short typical
+  paths.  This is ugly but reduces reliance on inlining support of
+  compilers.
+*/
+
+/* Link a free chunk into a smallbin  */
+#define insert_small_chunk(M, P, S) {\
+  bindex_t I  = small_index(S);\
+  mchunkptr B = smallbin_at(M, I);\
+  mchunkptr F = B;\
+  assert(S >= MIN_CHUNK_SIZE);\
+  if (!smallmap_is_marked(M, I))\
+    mark_smallmap(M, I);\
+  else if (RTCHECK(ok_address(M, B->fd)))\
+    F = B->fd;\
+  else {\
+    CORRUPTION_ERROR_ACTION(M);\
+  }\
+  B->fd = P;\
+  F->bk = P;\
+  P->fd = F;\
+  P->bk = B;\
+}
+
+/* Unlink a chunk from a smallbin  */
+#define unlink_small_chunk(M, P, S) {\
+  mchunkptr F = P->fd;\
+  mchunkptr B = P->bk;\
+  bindex_t I = small_index(S);\
+  assert(P != B);\
+  assert(P != F);\
+  assert(chunksize(P) == small_index2size(I));\
+  if (F == B)\
+    clear_smallmap(M, I);\
+  else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\
+                   (B == smallbin_at(M,I) || ok_address(M, B)))) {\
+    F->bk = B;\
+    B->fd = F;\
+  }\
+  else {\
+    CORRUPTION_ERROR_ACTION(M);\
+  }\
+}
+
+/* Unlink the first chunk from a smallbin */
+#define unlink_first_small_chunk(M, B, P, I) {\
+  mchunkptr F = P->fd;\
+  assert(P != B);\
+  assert(P != F);\
+  assert(chunksize(P) == small_index2size(I));\
+  if (B == F)\
+    clear_smallmap(M, I);\
+  else if (RTCHECK(ok_address(M, F))) {\
+    B->fd = F;\
+    F->bk = B;\
+  }\
+  else {\
+    CORRUPTION_ERROR_ACTION(M);\
+  }\
+}
+
+/* Replace dv node, binning the old one */
+/* Used only when dvsize known to be small */
+#define replace_dv(M, P, S) {\
+  size_t DVS = M->dvsize;\
+  if (DVS != 0) {\
+    mchunkptr DV = M->dv;\
+    assert(is_small(DVS));\
+    insert_small_chunk(M, DV, DVS);\
+  }\
+  M->dvsize = S;\
+  M->dv = P;\
+}
+
+/* ------------------------- Operations on trees ------------------------- */
+
+/* Insert chunk into tree */
+#define insert_large_chunk(M, X, S) {\
+  tbinptr* H;\
+  bindex_t I;\
+  compute_tree_index(S, I);\
+  H = treebin_at(M, I);\
+  X->index = I;\
+  X->child[0] = X->child[1] = 0;\
+  if (!treemap_is_marked(M, I)) {\
+    mark_treemap(M, I);\
+    *H = X;\
+    X->parent = (tchunkptr)H;\
+    X->fd = X->bk = X;\
+  }\
+  else {\
+    tchunkptr T = *H;\
+    size_t K = S << leftshift_for_tree_index(I);\
+    for (;;) {\
+      if (chunksize(T) != S) {\
+        tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\
+        K <<= 1;\
+        if (*C != 0)\
+          T = *C;\
+        else if (RTCHECK(ok_address(M, C))) {\
+          *C = X;\
+          X->parent = T;\
+          X->fd = X->bk = X;\
+          break;\
+        }\
+        else {\
+          CORRUPTION_ERROR_ACTION(M);\
+          break;\
+        }\
+      }\
+      else {\
+        tchunkptr F = T->fd;\
+        if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\
+          T->fd = F->bk = X;\
+          X->fd = F;\
+          X->bk = T;\
+          X->parent = 0;\
+          break;\
+        }\
+        else {\
+          CORRUPTION_ERROR_ACTION(M);\
+          break;\
+        }\
+      }\
+    }\
+  }\
+}
+
+/*
+  Unlink steps:
+
+  1. If x is a chained node, unlink it from its same-sized fd/bk links
+     and choose its bk node as its replacement.
+  2. If x was the last node of its size, but not a leaf node, it must
+     be replaced with a leaf node (not merely one with an open left or
+     right), to make sure that lefts and rights of descendents
+     correspond properly to bit masks.  We use the rightmost descendent
+     of x.  We could use any other leaf, but this is easy to locate and
+     tends to counteract removal of leftmosts elsewhere, and so keeps
+     paths shorter than minimally guaranteed.  This doesn't loop much
+     because on average a node in a tree is near the bottom.
+  3. If x is the base of a chain (i.e., has parent links) relink
+     x's parent and children to x's replacement (or null if none).
+*/
+
+#define unlink_large_chunk(M, X) {\
+  tchunkptr XP = X->parent;\
+  tchunkptr R;\
+  if (X->bk != X) {\
+    tchunkptr F = X->fd;\
+    R = X->bk;\
+    if (RTCHECK(ok_address(M, F))) {\
+      F->bk = R;\
+      R->fd = F;\
+    }\
+    else {\
+      CORRUPTION_ERROR_ACTION(M);\
+    }\
+  }\
+  else {\
+    tchunkptr* RP;\
+    if (((R = *(RP = &(X->child[1]))) != 0) ||\
+        ((R = *(RP = &(X->child[0]))) != 0)) {\
+      tchunkptr* CP;\
+      while ((*(CP = &(R->child[1])) != 0) ||\
+             (*(CP = &(R->child[0])) != 0)) {\
+        R = *(RP = CP);\
+      }\
+      if (RTCHECK(ok_address(M, RP)))\
+        *RP = 0;\
+      else {\
+        CORRUPTION_ERROR_ACTION(M);\
+      }\
+    }\
+  }\
+  if (XP != 0) {\
+    tbinptr* H = treebin_at(M, X->index);\
+    if (X == *H) {\
+      if ((*H = R) == 0) \
+        clear_treemap(M, X->index);\
+    }\
+    else if (RTCHECK(ok_address(M, XP))) {\
+      if (XP->child[0] == X) \
+        XP->child[0] = R;\
+      else \
+        XP->child[1] = R;\
+    }\
+    else\
+      CORRUPTION_ERROR_ACTION(M);\
+    if (R != 0) {\
+      if (RTCHECK(ok_address(M, R))) {\
+        tchunkptr C0, C1;\
+        R->parent = XP;\
+        if ((C0 = X->child[0]) != 0) {\
+          if (RTCHECK(ok_address(M, C0))) {\
+            R->child[0] = C0;\
+            C0->parent = R;\
+          }\
+          else\
+            CORRUPTION_ERROR_ACTION(M);\
+        }\
+        if ((C1 = X->child[1]) != 0) {\
+          if (RTCHECK(ok_address(M, C1))) {\
+            R->child[1] = C1;\
+            C1->parent = R;\
+          }\
+          else\
+            CORRUPTION_ERROR_ACTION(M);\
+        }\
+      }\
+      else\
+        CORRUPTION_ERROR_ACTION(M);\
+    }\
+  }\
+}
+
+/* Relays to large vs small bin operations */
+
+#define insert_chunk(M, P, S)\
+  if (is_small(S)) insert_small_chunk(M, P, S)\
+  else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); }
+
+#define unlink_chunk(M, P, S)\
+  if (is_small(S)) unlink_small_chunk(M, P, S)\
+  else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); }
+
+
+/* Relays to internal calls to malloc/free from realloc, memalign etc */
+
+#if ONLY_MSPACES
+#define internal_malloc(m, b) mspace_malloc(m, b)
+#define internal_free(m, mem) mspace_free(m,mem);
+#else /* ONLY_MSPACES */
+#if MSPACES
+#define internal_malloc(m, b)\
+   (m == gm)? dlmalloc(b) : mspace_malloc(m, b)
+#define internal_free(m, mem)\
+   if (m == gm) dlfree(mem); else mspace_free(m,mem);
+#else /* MSPACES */
+#define internal_malloc(m, b) dlmalloc(b)
+#define internal_free(m, mem) dlfree(mem)
+#endif /* MSPACES */
+#endif /* ONLY_MSPACES */
+
+/* -----------------------  Direct-mmapping chunks ----------------------- */
+
+/*
+  Directly mmapped chunks are set up with an offset to the start of
+  the mmapped region stored in the prev_foot field of the chunk. This
+  allows reconstruction of the required argument to MUNMAP when freed,
+  and also allows adjustment of the returned chunk to meet alignment
+  requirements (especially in memalign).  There is also enough space
+  allocated to hold a fake next chunk of size SIZE_T_SIZE to maintain
+  the PINUSE bit so frees can be checked.
+*/
+
+/* Malloc using mmap */
+static void* mmap_alloc(mstate m, size_t nb) {
+  size_t mmsize = granularity_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+  if (mmsize > nb) {     /* Check for wrap around 0 */
+    char* mm = (char*)(DIRECT_MMAP(mmsize));
+    if (mm != CMFAIL) {
+      size_t offset = align_offset(chunk2mem(mm));
+      size_t psize = mmsize - offset - MMAP_FOOT_PAD;
+      mchunkptr p = (mchunkptr)(mm + offset);
+      p->prev_foot = offset | IS_MMAPPED_BIT;
+      (p)->head = (psize|CINUSE_BIT);
+      mark_inuse_foot(m, p, psize);
+      chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD;
+      chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0;
+
+      if (mm < m->least_addr)
+        m->least_addr = mm;
+      if ((m->footprint += mmsize) > m->max_footprint)
+        m->max_footprint = m->footprint;
+      assert(is_aligned(chunk2mem(p)));
+      check_mmapped_chunk(m, p);
+      return chunk2mem(p);
+    }
+  }
+  return 0;
+}
+
+/* Realloc using mmap */
+static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) {
+  size_t oldsize = chunksize(oldp);
+  if (is_small(nb)) /* Can't shrink mmap regions below small size */
+    return 0;
+  /* Keep old chunk if big enough but not too big */
+  if (oldsize >= nb + SIZE_T_SIZE &&
+      (oldsize - nb) <= (mparams.granularity << 1))
+    return oldp;
+  else {
+    size_t offset = oldp->prev_foot & ~IS_MMAPPED_BIT;
+    size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD;
+    size_t newmmsize = granularity_align(nb + SIX_SIZE_T_SIZES +
+                                         CHUNK_ALIGN_MASK);
+    char* cp = (char*)CALL_MREMAP((char*)oldp - offset,
+                                  oldmmsize, newmmsize, 1);
+    if (cp != CMFAIL) {
+      mchunkptr newp = (mchunkptr)(cp + offset);
+      size_t psize = newmmsize - offset - MMAP_FOOT_PAD;
+      newp->head = (psize|CINUSE_BIT);
+      mark_inuse_foot(m, newp, psize);
+      chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD;
+      chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0;
+
+      if (cp < m->least_addr)
+        m->least_addr = cp;
+      if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint)
+        m->max_footprint = m->footprint;
+      check_mmapped_chunk(m, newp);
+      return newp;
+    }
+  }
+  return 0;
+}
+
+/* -------------------------- mspace management -------------------------- */
+
+/* Initialize top chunk and its size */
+static void init_top(mstate m, mchunkptr p, size_t psize) {
+  /* Ensure alignment */
+  size_t offset = align_offset(chunk2mem(p));
+  p = (mchunkptr)((char*)p + offset);
+  psize -= offset;
+
+  m->top = p;
+  m->topsize = psize;
+  p->head = psize | PINUSE_BIT;
+  /* set size of fake trailing chunk holding overhead space only once */
+  chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE;
+  m->trim_check = mparams.trim_threshold; /* reset on each update */
+}
+
+/* Initialize bins for a new mstate that is otherwise zeroed out */
+static void init_bins(mstate m) {
+  /* Establish circular links for smallbins */
+  bindex_t i;
+  for (i = 0; i < NSMALLBINS; ++i) {
+    sbinptr bin = smallbin_at(m,i);
+    bin->fd = bin->bk = bin;
+  }
+}
+
+#if PROCEED_ON_ERROR
+
+/* default corruption action */
+static void reset_on_error(mstate m) {
+  int i;
+  ++malloc_corruption_error_count;
+  /* Reinitialize fields to forget about all memory */
+  m->smallbins = m->treebins = 0;
+  m->dvsize = m->topsize = 0;
+  m->seg.base = 0;
+  m->seg.size = 0;
+  m->seg.next = 0;
+  m->top = m->dv = 0;
+  for (i = 0; i < NTREEBINS; ++i)
+    *treebin_at(m, i) = 0;
+  init_bins(m);
+}
+#endif /* PROCEED_ON_ERROR */
+
+/* Allocate chunk and prepend remainder with chunk in successor base. */
+static void* prepend_alloc(mstate m, char* newbase, char* oldbase,
+                           size_t nb) {
+  mchunkptr p = align_as_chunk(newbase);
+  mchunkptr oldfirst = align_as_chunk(oldbase);
+  size_t psize = (char*)oldfirst - (char*)p;
+  mchunkptr q = chunk_plus_offset(p, nb);
+  size_t qsize = psize - nb;
+  set_size_and_pinuse_of_inuse_chunk(m, p, nb);
+
+  assert((char*)oldfirst > (char*)q);
+  assert(pinuse(oldfirst));
+  assert(qsize >= MIN_CHUNK_SIZE);
+
+  /* consolidate remainder with first chunk of old base */
+  if (oldfirst == m->top) {
+    size_t tsize = m->topsize += qsize;
+    m->top = q;
+    q->head = tsize | PINUSE_BIT;
+    check_top_chunk(m, q);
+  }
+  else if (oldfirst == m->dv) {
+    size_t dsize = m->dvsize += qsize;
+    m->dv = q;
+    set_size_and_pinuse_of_free_chunk(q, dsize);
+  }
+  else {
+    if (!cinuse(oldfirst)) {
+      size_t nsize = chunksize(oldfirst);
+      unlink_chunk(m, oldfirst, nsize);
+      oldfirst = chunk_plus_offset(oldfirst, nsize);
+      qsize += nsize;
+    }
+    set_free_with_pinuse(q, qsize, oldfirst);
+    insert_chunk(m, q, qsize);
+    check_free_chunk(m, q);
+  }
+
+  check_malloced_chunk(m, chunk2mem(p), nb);
+  return chunk2mem(p);
+}
+
+
+/* Add a segment to hold a new noncontiguous region */
+static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) {
+  /* Determine locations and sizes of segment, fenceposts, old top */
+  char* old_top = (char*)m->top;
+  msegmentptr oldsp = segment_holding(m, old_top);
+  char* old_end = oldsp->base + oldsp->size;
+  size_t ssize = pad_request(sizeof(struct malloc_segment));
+  char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK);
+  size_t offset = align_offset(chunk2mem(rawsp));
+  char* asp = rawsp + offset;
+  char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp;
+  mchunkptr sp = (mchunkptr)csp;
+  msegmentptr ss = (msegmentptr)(chunk2mem(sp));
+  mchunkptr tnext = chunk_plus_offset(sp, ssize);
+  mchunkptr p = tnext;
+  int nfences = 0;
+
+  /* reset top to new space */
+  init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
+
+  /* Set up segment record */
+  assert(is_aligned(ss));
+  set_size_and_pinuse_of_inuse_chunk(m, sp, ssize);
+  *ss = m->seg; /* Push current record */
+  m->seg.base = tbase;
+  m->seg.size = tsize;
+  m->seg.sflags = mmapped;
+  m->seg.next = ss;
+
+  /* Insert trailing fenceposts */
+  for (;;) {
+    mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE);
+    p->head = FENCEPOST_HEAD;
+    ++nfences;
+    if ((char*)(&(nextp->head)) < old_end)
+      p = nextp;
+    else
+      break;
+  }
+  assert(nfences >= 2);
+
+  /* Insert the rest of old top into a bin as an ordinary free chunk */
+  if (csp != old_top) {
+    mchunkptr q = (mchunkptr)old_top;
+    size_t psize = csp - old_top;
+    mchunkptr tn = chunk_plus_offset(q, psize);
+    set_free_with_pinuse(q, psize, tn);
+    insert_chunk(m, q, psize);
+  }
+
+  check_top_chunk(m, m->top);
+}
+
+/* -------------------------- System allocation -------------------------- */
+
+/* Get memory from system using MORECORE or MMAP */
+static void* sys_alloc(mstate m, size_t nb) {
+  char* tbase = CMFAIL;
+  size_t tsize = 0;
+  flag_t mmap_flag = 0;
+
+  init_mparams();
+
+  /* Directly map large chunks */
+  if (use_mmap(m) && nb >= mparams.mmap_threshold) {
+    void* mem = mmap_alloc(m, nb);
+    if (mem != 0)
+      return mem;
+  }
+
+  /*
+    Try getting memory in any of three ways (in most-preferred to
+    least-preferred order):
+    1. A call to MORECORE that can normally contiguously extend memory.
+       (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or
+       or main space is mmapped or a previous contiguous call failed)
+    2. A call to MMAP new space (disabled if not HAVE_MMAP).
+       Note that under the default settings, if MORECORE is unable to
+       fulfill a request, and HAVE_MMAP is true, then mmap is
+       used as a noncontiguous system allocator. This is a useful backup
+       strategy for systems with holes in address spaces -- in this case
+       sbrk cannot contiguously expand the heap, but mmap may be able to
+       find space.
+    3. A call to MORECORE that cannot usually contiguously extend memory.
+       (disabled if not HAVE_MORECORE)
+  */
+
+  if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) {
+    char* br = CMFAIL;
+    msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top);
+    size_t asize = 0;
+    ACQUIRE_MORECORE_LOCK();
+
+    if (ss == 0) {  /* First time through or recovery */
+      char* base = (char*)CALL_MORECORE(0);
+      if (base != CMFAIL) {
+        asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE);
+        /* Adjust to end on a page boundary */
+        if (!is_page_aligned(base))
+          asize += (page_align((size_t)base) - (size_t)base);
+        /* Can't call MORECORE if size is negative when treated as signed */
+        if (asize < HALF_MAX_SIZE_T &&
+            (br = (char*)(CALL_MORECORE(asize))) == base) {
+          tbase = base;
+          tsize = asize;
+        }
+      }
+    }
+    else {
+      /* Subtract out existing available top space from MORECORE request. */
+      asize = granularity_align(nb - m->topsize + TOP_FOOT_SIZE + SIZE_T_ONE);
+      /* Use mem here only if it did continuously extend old space */
+      if (asize < HALF_MAX_SIZE_T &&
+          (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) {
+        tbase = br;
+        tsize = asize;
+      }
+    }
+
+    if (tbase == CMFAIL) {    /* Cope with partial failure */
+      if (br != CMFAIL) {    /* Try to use/extend the space we did get */
+        if (asize < HALF_MAX_SIZE_T &&
+            asize < nb + TOP_FOOT_SIZE + SIZE_T_ONE) {
+          size_t esize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE - asize);
+          if (esize < HALF_MAX_SIZE_T) {
+            char* end = (char*)CALL_MORECORE(esize);
+            if (end != CMFAIL)
+              asize += esize;
+            else {            /* Can't use; try to release */
+              CALL_MORECORE(-asize);
+              br = CMFAIL;
+            }
+          }
+        }
+      }
+      if (br != CMFAIL) {    /* Use the space we did get */
+        tbase = br;
+        tsize = asize;
+      }
+      else
+        disable_contiguous(m); /* Don't try contiguous path in the future */
+    }
+
+    RELEASE_MORECORE_LOCK();
+  }
+
+  if (HAVE_MMAP && tbase == CMFAIL) {  /* Try MMAP */
+    size_t req = nb + TOP_FOOT_SIZE + SIZE_T_ONE;
+    size_t rsize = granularity_align(req);
+    if (rsize > nb) { /* Fail if wraps around zero */
+      char* mp = (char*)(CALL_MMAP(rsize));
+      if (mp != CMFAIL) {
+        tbase = mp;
+        tsize = rsize;
+        mmap_flag = IS_MMAPPED_BIT;
+      }
+    }
+  }
+
+  if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */
+    size_t asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE);
+    if (asize < HALF_MAX_SIZE_T) {
+      char* br = CMFAIL;
+      char* end = CMFAIL;
+      ACQUIRE_MORECORE_LOCK();
+      br = (char*)(CALL_MORECORE(asize));
+      end = (char*)(CALL_MORECORE(0));
+      RELEASE_MORECORE_LOCK();
+      if (br != CMFAIL && end != CMFAIL && br < end) {
+        size_t ssize = end - br;
+        if (ssize > nb + TOP_FOOT_SIZE) {
+          tbase = br;
+          tsize = ssize;
+        }
+      }
+    }
+  }
+
+  if (tbase != CMFAIL) {
+
+    if ((m->footprint += tsize) > m->max_footprint)
+      m->max_footprint = m->footprint;
+
+    if (!is_initialized(m)) { /* first-time initialization */
+      m->seg.base = m->least_addr = tbase;
+      m->seg.size = tsize;
+      m->seg.sflags = mmap_flag;
+      m->magic = mparams.magic;
+      init_bins(m);
+      if (is_global(m)) 
+        init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE);
+      else {
+        /* Offset top by embedded malloc_state */
+        mchunkptr mn = next_chunk(mem2chunk(m));
+        init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE);
+      }
+    }
+
+    else {
+      /* Try to merge with an existing segment */
+      msegmentptr sp = &m->seg;
+      while (sp != 0 && tbase != sp->base + sp->size)
+        sp = sp->next;
+      if (sp != 0 &&
+          !is_extern_segment(sp) &&
+          (sp->sflags & IS_MMAPPED_BIT) == mmap_flag &&
+          segment_holds(sp, m->top)) { /* append */
+        sp->size += tsize;
+        init_top(m, m->top, m->topsize + tsize);
+      }
+      else {
+        if (tbase < m->least_addr)
+          m->least_addr = tbase;
+        sp = &m->seg;
+        while (sp != 0 && sp->base != tbase + tsize)
+          sp = sp->next;
+        if (sp != 0 &&
+            !is_extern_segment(sp) &&
+            (sp->sflags & IS_MMAPPED_BIT) == mmap_flag) {
+          char* oldbase = sp->base;
+          sp->base = tbase;
+          sp->size += tsize;
+          return prepend_alloc(m, tbase, oldbase, nb);
+        }
+        else
+          add_segment(m, tbase, tsize, mmap_flag);
+      }
+    }
+
+    if (nb < m->topsize) { /* Allocate from new or extended top space */
+      size_t rsize = m->topsize -= nb;
+      mchunkptr p = m->top;
+      mchunkptr r = m->top = chunk_plus_offset(p, nb);
+      r->head = rsize | PINUSE_BIT;
+      set_size_and_pinuse_of_inuse_chunk(m, p, nb);
+      check_top_chunk(m, m->top);
+      check_malloced_chunk(m, chunk2mem(p), nb);
+      return chunk2mem(p);
+    }
+  }
+
+  MALLOC_FAILURE_ACTION;
+  return 0;
+}
+
+/* -----------------------  system deallocation -------------------------- */
+
+/* Unmap and unlink any mmapped segments that don't contain used chunks */
+static size_t release_unused_segments(mstate m) {
+  size_t released = 0;
+  msegmentptr pred = &m->seg;
+  msegmentptr sp = pred->next;
+  while (sp != 0) {
+    char* base = sp->base;
+    size_t size = sp->size;
+    msegmentptr next = sp->next;
+    if (is_mmapped_segment(sp) && !is_extern_segment(sp)) {
+      mchunkptr p = align_as_chunk(base);
+      size_t psize = chunksize(p);
+      /* Can unmap if first chunk holds entire segment and not pinned */
+      if (!cinuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) {
+        tchunkptr tp = (tchunkptr)p;
+        assert(segment_holds(sp, (char*)sp));
+        if (p == m->dv) {
+          m->dv = 0;
+          m->dvsize = 0;
+        }
+        else {
+          unlink_large_chunk(m, tp);
+        }
+        if (CALL_MUNMAP(base, size) == 0) {
+          released += size;
+          m->footprint -= size;
+          /* unlink obsoleted record */
+          sp = pred;
+          sp->next = next;
+        }
+        else { /* back out if cannot unmap */
+          insert_large_chunk(m, tp, psize);
+        }
+      }
+    }
+    pred = sp;
+    sp = next;
+  }
+  return released;
+}
+
+static int sys_trim(mstate m, size_t pad) {
+  size_t released = 0;
+  if (pad < MAX_REQUEST && is_initialized(m)) {
+    pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */
+
+    if (m->topsize > pad) {
+      /* Shrink top space in granularity-size units, keeping at least one */
+      size_t unit = mparams.granularity;
+      size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit -
+                      SIZE_T_ONE) * unit;
+      msegmentptr sp = segment_holding(m, (char*)m->top);
+
+      if (!is_extern_segment(sp)) {
+        if (is_mmapped_segment(sp)) {
+          if (HAVE_MMAP &&
+              sp->size >= extra &&
+              !has_segment_link(m, sp)) { /* can't shrink if pinned */
+            /* Prefer mremap, fall back to munmap */
+            if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) ||
+                (CALL_MUNMAP(sp->base + newsize, extra) == 0)) {
+              released = extra;
+            }
+          }
+        }
+        else if (HAVE_MORECORE) {
+          if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */
+            extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit;
+          ACQUIRE_MORECORE_LOCK();
+          {
+            /* Make sure end of memory is where we last set it. */
+            char* old_br = (char*)(CALL_MORECORE(0));
+            if (old_br == sp->base + sp->size) {
+              char* rel_br = (char*)(CALL_MORECORE(-extra));
+              char* new_br = (char*)(CALL_MORECORE(0));
+              if (rel_br != CMFAIL && new_br < old_br)
+                released = old_br - new_br;
+            }
+          }
+          RELEASE_MORECORE_LOCK();
+        }
+      }
+
+      if (released != 0) {
+        sp->size -= released;
+        m->footprint -= released;
+        init_top(m, m->top, m->topsize - released);
+        check_top_chunk(m, m->top);
+      }
+    }
+
+    /* Unmap any unused mmapped segments */
+    if (HAVE_MMAP) 
+      released += release_unused_segments(m);
+
+    /* On failure, disable autotrim to avoid repeated failed future calls */
+    if (released == 0)
+      m->trim_check = MAX_SIZE_T;
+  }
+
+  return (released != 0)? 1 : 0;
+}
+
+/* ---------------------------- malloc support --------------------------- */
+
+/* allocate a large request from the best fitting chunk in a treebin */
+static void* tmalloc_large(mstate m, size_t nb) {
+  tchunkptr v = 0;
+  size_t rsize = -nb; /* Unsigned negation */
+  tchunkptr t;
+  bindex_t idx;
+  compute_tree_index(nb, idx);
+
+  if ((t = *treebin_at(m, idx)) != 0) {
+    /* Traverse tree for this bin looking for node with size == nb */
+    size_t sizebits = nb << leftshift_for_tree_index(idx);
+    tchunkptr rst = 0;  /* The deepest untaken right subtree */
+    for (;;) {
+      tchunkptr rt;
+      size_t trem = chunksize(t) - nb;
+      if (trem < rsize) {
+        v = t;
+        if ((rsize = trem) == 0)
+          break;
+      }
+      rt = t->child[1];
+      t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1];
+      if (rt != 0 && rt != t)
+        rst = rt;
+      if (t == 0) {
+        t = rst; /* set t to least subtree holding sizes > nb */
+        break;
+      }
+      sizebits <<= 1;
+    }
+  }
+
+  if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */
+    binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap;
+    if (leftbits != 0) {
+      bindex_t i;
+      binmap_t leastbit = least_bit(leftbits);
+      compute_bit2idx(leastbit, i);
+      t = *treebin_at(m, i);
+    }
+  }
+
+  while (t != 0) { /* find smallest of tree or subtree */
+    size_t trem = chunksize(t) - nb;
+    if (trem < rsize) {
+      rsize = trem;
+      v = t;
+    }
+    t = leftmost_child(t);
+  }
+
+  /*  If dv is a better fit, return 0 so malloc will use it */
+  if (v != 0 && rsize < (size_t)(m->dvsize - nb)) {
+    if (RTCHECK(ok_address(m, v))) { /* split */
+      mchunkptr r = chunk_plus_offset(v, nb);
+      assert(chunksize(v) == rsize + nb);
+      if (RTCHECK(ok_next(v, r))) {
+        unlink_large_chunk(m, v);
+        if (rsize < MIN_CHUNK_SIZE)
+          set_inuse_and_pinuse(m, v, (rsize + nb));
+        else {
+          set_size_and_pinuse_of_inuse_chunk(m, v, nb);
+          set_size_and_pinuse_of_free_chunk(r, rsize);
+          insert_chunk(m, r, rsize);
+        }
+        return chunk2mem(v);
+      }
+    }
+    CORRUPTION_ERROR_ACTION(m);
+  }
+  return 0;
+}
+
+/* allocate a small request from the best fitting chunk in a treebin */
+static void* tmalloc_small(mstate m, size_t nb) {
+  tchunkptr t, v;
+  size_t rsize;
+  bindex_t i;
+  binmap_t leastbit = least_bit(m->treemap);
+  compute_bit2idx(leastbit, i);
+
+  v = t = *treebin_at(m, i);
+  rsize = chunksize(t) - nb;
+
+  while ((t = leftmost_child(t)) != 0) {
+    size_t trem = chunksize(t) - nb;
+    if (trem < rsize) {
+      rsize = trem;
+      v = t;
+    }
+  }
+
+  if (RTCHECK(ok_address(m, v))) {
+    mchunkptr r = chunk_plus_offset(v, nb);
+    assert(chunksize(v) == rsize + nb);
+    if (RTCHECK(ok_next(v, r))) {
+      unlink_large_chunk(m, v);
+      if (rsize < MIN_CHUNK_SIZE)
+        set_inuse_and_pinuse(m, v, (rsize + nb));
+      else {
+        set_size_and_pinuse_of_inuse_chunk(m, v, nb);
+        set_size_and_pinuse_of_free_chunk(r, rsize);
+        replace_dv(m, r, rsize);
+      }
+      return chunk2mem(v);
+    }
+  }
+
+  CORRUPTION_ERROR_ACTION(m);
+  return 0;
+}
+
+/* --------------------------- realloc support --------------------------- */
+
+static void* internal_realloc(mstate m, void* oldmem, size_t bytes) {
+  if (bytes >= MAX_REQUEST) {
+    MALLOC_FAILURE_ACTION;
+    return 0;
+  }
+  if (!PREACTION(m)) {
+    mchunkptr oldp = mem2chunk(oldmem);
+    size_t oldsize = chunksize(oldp);
+    mchunkptr next = chunk_plus_offset(oldp, oldsize);
+    mchunkptr newp = 0;
+    void* extra = 0;
+
+    /* Try to either shrink or extend into top. Else malloc-copy-free */
+
+    if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) &&
+                ok_next(oldp, next) && ok_pinuse(next))) {
+      size_t nb = request2size(bytes);
+      if (is_mmapped(oldp))
+        newp = mmap_resize(m, oldp, nb);
+      else if (oldsize >= nb) { /* already big enough */
+        size_t rsize = oldsize - nb;
+        newp = oldp;
+        if (rsize >= MIN_CHUNK_SIZE) {
+          mchunkptr remainder = chunk_plus_offset(newp, nb);
+          set_inuse(m, newp, nb);
+          set_inuse(m, remainder, rsize);
+          extra = chunk2mem(remainder);
+        }
+      }
+      else if (next == m->top && oldsize + m->topsize > nb) {
+        /* Expand into top */
+        size_t newsize = oldsize + m->topsize;
+        size_t newtopsize = newsize - nb;
+        mchunkptr newtop = chunk_plus_offset(oldp, nb);
+        set_inuse(m, oldp, nb);
+        newtop->head = newtopsize |PINUSE_BIT;
+        m->top = newtop;
+        m->topsize = newtopsize;
+        newp = oldp;
+      }
+    }
+    else {
+      USAGE_ERROR_ACTION(m, oldmem);
+      POSTACTION(m);
+      return 0;
+    }
+
+    POSTACTION(m);
+
+    if (newp != 0) {
+      if (extra != 0) {
+        internal_free(m, extra);
+      }
+      check_inuse_chunk(m, newp);
+      return chunk2mem(newp);
+    }
+    else {
+      void* newmem = internal_malloc(m, bytes);
+      if (newmem != 0) {
+        size_t oc = oldsize - overhead_for(oldp);
+        memcpy(newmem, oldmem, (oc < bytes)? oc : bytes);
+        internal_free(m, oldmem);
+      }
+      return newmem;
+    }
+  }
+  return 0;
+}
+
+/* --------------------------- memalign support -------------------------- */
+
+static void* internal_memalign(mstate m, size_t alignment, size_t bytes) {
+  if (alignment <= MALLOC_ALIGNMENT)    /* Can just use malloc */
+    return internal_malloc(m, bytes);
+  if (alignment <  MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */
+    alignment = MIN_CHUNK_SIZE;
+  if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */
+    size_t a = MALLOC_ALIGNMENT << 1;
+    while (a < alignment) a <<= 1;
+    alignment = a;
+  }
+  
+  if (bytes >= MAX_REQUEST - alignment) {
+    if (m != 0)  { /* Test isn't needed but avoids compiler warning */
+      MALLOC_FAILURE_ACTION;
+    }
+  }
+  else {
+    size_t nb = request2size(bytes);
+    size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD;
+    char* mem = (char*)internal_malloc(m, req);
+    if (mem != 0) {
+      void* leader = 0;
+      void* trailer = 0;
+      mchunkptr p = mem2chunk(mem);
+
+      if (PREACTION(m)) return 0;
+      if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */
+        /*
+          Find an aligned spot inside chunk.  Since we need to give
+          back leading space in a chunk of at least MIN_CHUNK_SIZE, if
+          the first calculation places us at a spot with less than
+          MIN_CHUNK_SIZE leader, we can move to the next aligned spot.
+          We've allocated enough total room so that this is always
+          possible.
+        */
+        char* br = (char*)mem2chunk((size_t)(((size_t)(mem +
+                                                       alignment -
+                                                       SIZE_T_ONE)) &
+                                             -alignment));
+        char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)?
+          br : br+alignment;
+        mchunkptr newp = (mchunkptr)pos;
+        size_t leadsize = pos - (char*)(p);
+        size_t newsize = chunksize(p) - leadsize;
+
+        if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */
+          newp->prev_foot = p->prev_foot + leadsize;
+          newp->head = (newsize|CINUSE_BIT);
+        }
+        else { /* Otherwise, give back leader, use the rest */
+          set_inuse(m, newp, newsize);
+          set_inuse(m, p, leadsize);
+          leader = chunk2mem(p);
+        }
+        p = newp;
+      }
+
+      /* Give back spare room at the end */
+      if (!is_mmapped(p)) {
+        size_t size = chunksize(p);
+        if (size > nb + MIN_CHUNK_SIZE) {
+          size_t remainder_size = size - nb;
+          mchunkptr remainder = chunk_plus_offset(p, nb);
+          set_inuse(m, p, nb);
+          set_inuse(m, remainder, remainder_size);
+          trailer = chunk2mem(remainder);
+        }
+      }
+
+      assert (chunksize(p) >= nb);
+      assert((((size_t)(chunk2mem(p))) % alignment) == 0);
+      check_inuse_chunk(m, p);
+      POSTACTION(m);
+      if (leader != 0) {
+        internal_free(m, leader);
+      }
+      if (trailer != 0) {
+        internal_free(m, trailer);
+      }
+      return chunk2mem(p);
+    }
+  }
+  return 0;
+}
+
+/* ------------------------ comalloc/coalloc support --------------------- */
+
+static void** ialloc(mstate m,
+                     size_t n_elements,
+                     size_t* sizes,
+                     int opts,
+                     void* chunks[]) {
+  /*
+    This provides common support for independent_X routines, handling
+    all of the combinations that can result.
+
+    The opts arg has:
+    bit 0 set if all elements are same size (using sizes[0])
+    bit 1 set if elements should be zeroed
+  */
+
+  size_t    element_size;   /* chunksize of each element, if all same */
+  size_t    contents_size;  /* total size of elements */
+  size_t    array_size;     /* request size of pointer array */
+  void*     mem;            /* malloced aggregate space */
+  mchunkptr p;              /* corresponding chunk */
+  size_t    remainder_size; /* remaining bytes while splitting */
+  void**    marray;         /* either "chunks" or malloced ptr array */
+  mchunkptr array_chunk;    /* chunk for malloced ptr array */
+  flag_t    was_enabled;    /* to disable mmap */
+  size_t    size;
+  size_t    i;
+
+  /* compute array length, if needed */
+  if (chunks != 0) {
+    if (n_elements == 0)
+      return chunks; /* nothing to do */
+    marray = chunks;
+    array_size = 0;
+  }
+  else {
+    /* if empty req, must still return chunk representing empty array */
+    if (n_elements == 0)
+      return (void**)internal_malloc(m, 0);
+    marray = 0;
+    array_size = request2size(n_elements * (sizeof(void*)));
+  }
+
+  /* compute total element size */
+  if (opts & 0x1) { /* all-same-size */
+    element_size = request2size(*sizes);
+    contents_size = n_elements * element_size;
+  }
+  else { /* add up all the sizes */
+    element_size = 0;
+    contents_size = 0;
+    for (i = 0; i != n_elements; ++i)
+      contents_size += request2size(sizes[i]);
+  }
+
+  size = contents_size + array_size;
+
+  /*
+     Allocate the aggregate chunk.  First disable direct-mmapping so
+     malloc won't use it, since we would not be able to later
+     free/realloc space internal to a segregated mmap region.
+  */
+  was_enabled = use_mmap(m);
+  disable_mmap(m);
+  mem = internal_malloc(m, size - CHUNK_OVERHEAD);
+  if (was_enabled)
+    enable_mmap(m);
+  if (mem == 0)
+    return 0;
+
+  if (PREACTION(m)) return 0;
+  p = mem2chunk(mem);
+  remainder_size = chunksize(p);
+
+  assert(!is_mmapped(p));
+
+  if (opts & 0x2) {       /* optionally clear the elements */
+    memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size);
+  }
+
+  /* If not provided, allocate the pointer array as final part of chunk */
+  if (marray == 0) {
+    size_t  array_chunk_size;
+    array_chunk = chunk_plus_offset(p, contents_size);
+    array_chunk_size = remainder_size - contents_size;
+    marray = (void**) (chunk2mem(array_chunk));
+    set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size);
+    remainder_size = contents_size;
+  }
+
+  /* split out elements */
+  for (i = 0; ; ++i) {
+    marray[i] = chunk2mem(p);
+    if (i != n_elements-1) {
+      if (element_size != 0)
+        size = element_size;
+      else
+        size = request2size(sizes[i]);
+      remainder_size -= size;
+      set_size_and_pinuse_of_inuse_chunk(m, p, size);
+      p = chunk_plus_offset(p, size);
+    }
+    else { /* the final element absorbs any overallocation slop */
+      set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size);
+      break;
+    }
+  }
+
+#if DEBUG
+  if (marray != chunks) {
+    /* final element must have exactly exhausted chunk */
+    if (element_size != 0) {
+      assert(remainder_size == element_size);
+    }
+    else {
+      assert(remainder_size == request2size(sizes[i]));
+    }
+    check_inuse_chunk(m, mem2chunk(marray));
+  }
+  for (i = 0; i != n_elements; ++i)
+    check_inuse_chunk(m, mem2chunk(marray[i]));
+
+#endif /* DEBUG */
+
+  POSTACTION(m);
+  return marray;
+}
+
+
+/* -------------------------- public routines ---------------------------- */
+
+#if !ONLY_MSPACES
+
+void* dlmalloc(size_t bytes) {
+  /*
+     Basic algorithm:
+     If a small request (< 256 bytes minus per-chunk overhead):
+       1. If one exists, use a remainderless chunk in associated smallbin.
+          (Remainderless means that there are too few excess bytes to
+          represent as a chunk.)
+       2. If it is big enough, use the dv chunk, which is normally the
+          chunk adjacent to the one used for the most recent small request.
+       3. If one exists, split the smallest available chunk in a bin,
+          saving remainder in dv.
+       4. If it is big enough, use the top chunk.
+       5. If available, get memory from system and use it
+     Otherwise, for a large request:
+       1. Find the smallest available binned chunk that fits, and use it
+          if it is better fitting than dv chunk, splitting if necessary.
+       2. If better fitting than any binned chunk, use the dv chunk.
+       3. If it is big enough, use the top chunk.
+       4. If request size >= mmap threshold, try to directly mmap this chunk.
+       5. If available, get memory from system and use it
+
+     The ugly goto's here ensure that postaction occurs along all paths.
+  */
+
+  if (!PREACTION(gm)) {
+    void* mem;
+    size_t nb;
+    if (bytes <= MAX_SMALL_REQUEST) {
+      bindex_t idx;
+      binmap_t smallbits;
+      nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
+      idx = small_index(nb);
+      smallbits = gm->smallmap >> idx;
+
+      if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
+        mchunkptr b, p;
+        idx += ~smallbits & 1;       /* Uses next bin if idx empty */
+        b = smallbin_at(gm, idx);
+        p = b->fd;
+        assert(chunksize(p) == small_index2size(idx));
+        unlink_first_small_chunk(gm, b, p, idx);
+        set_inuse_and_pinuse(gm, p, small_index2size(idx));
+        mem = chunk2mem(p);
+        check_malloced_chunk(gm, mem, nb);
+        goto postaction;
+      }
+
+      else if (nb > gm->dvsize) {
+        if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
+          mchunkptr b, p, r;
+          size_t rsize;
+          bindex_t i;
+          binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
+          binmap_t leastbit = least_bit(leftbits);
+          compute_bit2idx(leastbit, i);
+          b = smallbin_at(gm, i);
+          p = b->fd;
+          assert(chunksize(p) == small_index2size(i));
+          unlink_first_small_chunk(gm, b, p, i);
+          rsize = small_index2size(i) - nb;
+          /* Fit here cannot be remainderless if 4byte sizes */
+          if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
+            set_inuse_and_pinuse(gm, p, small_index2size(i));
+          else {
+            set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+            r = chunk_plus_offset(p, nb);
+            set_size_and_pinuse_of_free_chunk(r, rsize);
+            replace_dv(gm, r, rsize);
+          }
+          mem = chunk2mem(p);
+          check_malloced_chunk(gm, mem, nb);
+          goto postaction;
+        }
+
+        else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) {
+          check_malloced_chunk(gm, mem, nb);
+          goto postaction;
+        }
+      }
+    }
+    else if (bytes >= MAX_REQUEST)
+      nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
+    else {
+      nb = pad_request(bytes);
+      if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) {
+        check_malloced_chunk(gm, mem, nb);
+        goto postaction;
+      }
+    }
+
+    if (nb <= gm->dvsize) {
+      size_t rsize = gm->dvsize - nb;
+      mchunkptr p = gm->dv;
+      if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
+        mchunkptr r = gm->dv = chunk_plus_offset(p, nb);
+        gm->dvsize = rsize;
+        set_size_and_pinuse_of_free_chunk(r, rsize);
+        set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+      }
+      else { /* exhaust dv */
+        size_t dvs = gm->dvsize;
+        gm->dvsize = 0;
+        gm->dv = 0;
+        set_inuse_and_pinuse(gm, p, dvs);
+      }
+      mem = chunk2mem(p);
+      check_malloced_chunk(gm, mem, nb);
+      goto postaction;
+    }
+
+    else if (nb < gm->topsize) { /* Split top */
+      size_t rsize = gm->topsize -= nb;
+      mchunkptr p = gm->top;
+      mchunkptr r = gm->top = chunk_plus_offset(p, nb);
+      r->head = rsize | PINUSE_BIT;
+      set_size_and_pinuse_of_inuse_chunk(gm, p, nb);
+      mem = chunk2mem(p);
+      check_top_chunk(gm, gm->top);
+      check_malloced_chunk(gm, mem, nb);
+      goto postaction;
+    }
+
+    mem = sys_alloc(gm, nb);
+
+  postaction:
+    POSTACTION(gm);
+    return mem;
+  }
+
+  return 0;
+}
+
+void dlfree(void* mem) {
+  /*
+     Consolidate freed chunks with preceeding or succeeding bordering
+     free chunks, if they exist, and then place in a bin.  Intermixed
+     with special cases for top, dv, mmapped chunks, and usage errors.
+  */
+
+  if (mem != 0) {
+    mchunkptr p  = mem2chunk(mem);
+#if FOOTERS
+    mstate fm = get_mstate_for(p);
+    if (!ok_magic(fm)) {
+      USAGE_ERROR_ACTION(fm, p);
+      return;
+    }
+#else /* FOOTERS */
+#define fm gm
+#endif /* FOOTERS */
+    if (!PREACTION(fm)) {
+      check_inuse_chunk(fm, p);
+      if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) {
+        size_t psize = chunksize(p);
+        mchunkptr next = chunk_plus_offset(p, psize);
+        if (!pinuse(p)) {
+          size_t prevsize = p->prev_foot;
+          if ((prevsize & IS_MMAPPED_BIT) != 0) {
+            prevsize &= ~IS_MMAPPED_BIT;
+            psize += prevsize + MMAP_FOOT_PAD;
+            if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+              fm->footprint -= psize;
+            goto postaction;
+          }
+          else {
+            mchunkptr prev = chunk_minus_offset(p, prevsize);
+            psize += prevsize;
+            p = prev;
+            if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
+              if (p != fm->dv) {
+                unlink_chunk(fm, p, prevsize);
+              }
+              else if ((next->head & INUSE_BITS) == INUSE_BITS) {
+                fm->dvsize = psize;
+                set_free_with_pinuse(p, psize, next);
+                goto postaction;
+              }
+            }
+            else
+              goto erroraction;
+          }
+        }
+
+        if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
+          if (!cinuse(next)) {  /* consolidate forward */
+            if (next == fm->top) {
+              size_t tsize = fm->topsize += psize;
+              fm->top = p;
+              p->head = tsize | PINUSE_BIT;
+              if (p == fm->dv) {
+                fm->dv = 0;
+                fm->dvsize = 0;
+              }
+              if (should_trim(fm, tsize))
+                sys_trim(fm, 0);
+              goto postaction;
+            }
+            else if (next == fm->dv) {
+              size_t dsize = fm->dvsize += psize;
+              fm->dv = p;
+              set_size_and_pinuse_of_free_chunk(p, dsize);
+              goto postaction;
+            }
+            else {
+              size_t nsize = chunksize(next);
+              psize += nsize;
+              unlink_chunk(fm, next, nsize);
+              set_size_and_pinuse_of_free_chunk(p, psize);
+              if (p == fm->dv) {
+                fm->dvsize = psize;
+                goto postaction;
+              }
+            }
+          }
+          else
+            set_free_with_pinuse(p, psize, next);
+          insert_chunk(fm, p, psize);
+          check_free_chunk(fm, p);
+          goto postaction;
+        }
+      }
+    erroraction:
+      USAGE_ERROR_ACTION(fm, p);
+    postaction:
+      POSTACTION(fm);
+    }
+  }
+#if !FOOTERS
+#undef fm
+#endif /* FOOTERS */
+}
+
+void* dlcalloc(size_t n_elements, size_t elem_size) {
+  void* mem;
+  size_t req = 0;
+  if (n_elements != 0) {
+    req = n_elements * elem_size;
+    if (((n_elements | elem_size) & ~(size_t)0xffff) &&
+        (req / n_elements != elem_size))
+      req = MAX_SIZE_T; /* force downstream failure on overflow */
+  }
+  mem = dlmalloc(req);
+  if (mem != 0 && calloc_must_clear(mem2chunk(mem)))
+    memset(mem, 0, req);
+  return mem;
+}
+
+void* dlrealloc(void* oldmem, size_t bytes) {
+  if (oldmem == 0)
+    return dlmalloc(bytes);
+#ifdef REALLOC_ZERO_BYTES_FREES
+  if (bytes == 0) {
+    dlfree(oldmem);
+    return 0;
+  }
+#endif /* REALLOC_ZERO_BYTES_FREES */
+  else {
+#if ! FOOTERS
+    mstate m = gm;
+#else /* FOOTERS */
+    mstate m = get_mstate_for(mem2chunk(oldmem));
+    if (!ok_magic(m)) {
+      USAGE_ERROR_ACTION(m, oldmem);
+      return 0;
+    }
+#endif /* FOOTERS */
+    return internal_realloc(m, oldmem, bytes);
+  }
+}
+
+void* dlmemalign(size_t alignment, size_t bytes) {
+  return internal_memalign(gm, alignment, bytes);
+}
+
+void** dlindependent_calloc(size_t n_elements, size_t elem_size,
+                                 void* chunks[]) {
+  size_t sz = elem_size; /* serves as 1-element array */
+  return ialloc(gm, n_elements, &sz, 3, chunks);
+}
+
+void** dlindependent_comalloc(size_t n_elements, size_t sizes[],
+                                   void* chunks[]) {
+  return ialloc(gm, n_elements, sizes, 0, chunks);
+}
+
+void* dlvalloc(size_t bytes) {
+  size_t pagesz;
+  init_mparams();
+  pagesz = mparams.page_size;
+  return dlmemalign(pagesz, bytes);
+}
+
+void* dlpvalloc(size_t bytes) {
+  size_t pagesz;
+  init_mparams();
+  pagesz = mparams.page_size;
+  return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE));
+}
+
+int dlmalloc_trim(size_t pad) {
+  int result = 0;
+  if (!PREACTION(gm)) {
+    result = sys_trim(gm, pad);
+    POSTACTION(gm);
+  }
+  return result;
+}
+
+size_t dlmalloc_footprint(void) {
+  return gm->footprint;
+}
+
+size_t dlmalloc_max_footprint(void) {
+  return gm->max_footprint;
+}
+
+#if !NO_MALLINFO
+struct mallinfo dlmallinfo(void) {
+  return internal_mallinfo(gm);
+}
+#endif /* NO_MALLINFO */
+
+void dlmalloc_stats() {
+  internal_malloc_stats(gm);
+}
+
+size_t dlmalloc_usable_size(void* mem) {
+  if (mem != 0) {
+    mchunkptr p = mem2chunk(mem);
+    if (cinuse(p))
+      return chunksize(p) - overhead_for(p);
+  }
+  return 0;
+}
+
+int dlmallopt(int param_number, int value) {
+  return change_mparam(param_number, value);
+}
+
+#endif /* !ONLY_MSPACES */
+
+/* ----------------------------- user mspaces ---------------------------- */
+
+#if MSPACES
+
+static mstate init_user_mstate(char* tbase, size_t tsize) {
+  size_t msize = pad_request(sizeof(struct malloc_state));
+  mchunkptr mn;
+  mchunkptr msp = align_as_chunk(tbase);
+  mstate m = (mstate)(chunk2mem(msp));
+  memset(m, 0, msize);
+  INITIAL_LOCK(&m->mutex);
+  msp->head = (msize|PINUSE_BIT|CINUSE_BIT);
+  m->seg.base = m->least_addr = tbase;
+  m->seg.size = m->footprint = m->max_footprint = tsize;
+  m->magic = mparams.magic;
+  m->mflags = mparams.default_mflags;
+  disable_contiguous(m);
+  init_bins(m);
+  mn = next_chunk(mem2chunk(m));
+  init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE);
+  check_top_chunk(m, m->top);
+  return m;
+}
+
+mspace create_mspace(size_t capacity, int locked) {
+  mstate m = 0;
+  size_t msize = pad_request(sizeof(struct malloc_state));
+  init_mparams(); /* Ensure pagesize etc initialized */
+
+  if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
+    size_t rs = ((capacity == 0)? mparams.granularity :
+                 (capacity + TOP_FOOT_SIZE + msize));
+    size_t tsize = granularity_align(rs);
+    char* tbase = (char*)(CALL_MMAP(tsize));
+    if (tbase != CMFAIL) {
+      m = init_user_mstate(tbase, tsize);
+      m->seg.sflags = IS_MMAPPED_BIT;
+      set_lock(m, locked);
+    }
+  }
+  return (mspace)m;
+}
+
+mspace create_mspace_with_base(void* base, size_t capacity, int locked) {
+  mstate m = 0;
+  size_t msize = pad_request(sizeof(struct malloc_state));
+  init_mparams(); /* Ensure pagesize etc initialized */
+
+  if (capacity > msize + TOP_FOOT_SIZE &&
+      capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) {
+    m = init_user_mstate((char*)base, capacity);
+    m->seg.sflags = EXTERN_BIT;
+    set_lock(m, locked);
+  }
+  return (mspace)m;
+}
+
+size_t destroy_mspace(mspace msp) {
+  size_t freed = 0;
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    msegmentptr sp = &ms->seg;
+    while (sp != 0) {
+      char* base = sp->base;
+      size_t size = sp->size;
+      flag_t flag = sp->sflags;
+      sp = sp->next;
+      if ((flag & IS_MMAPPED_BIT) && !(flag & EXTERN_BIT) &&
+          CALL_MUNMAP(base, size) == 0)
+        freed += size;
+    }
+  }
+  else {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+  return freed;
+}
+
+/*
+  mspace versions of routines are near-clones of the global
+  versions. This is not so nice but better than the alternatives.
+*/
+
+
+void* mspace_malloc(mspace msp, size_t bytes) {
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  if (!PREACTION(ms)) {
+    void* mem;
+    size_t nb;
+    if (bytes <= MAX_SMALL_REQUEST) {
+      bindex_t idx;
+      binmap_t smallbits;
+      nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes);
+      idx = small_index(nb);
+      smallbits = ms->smallmap >> idx;
+
+      if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */
+        mchunkptr b, p;
+        idx += ~smallbits & 1;       /* Uses next bin if idx empty */
+        b = smallbin_at(ms, idx);
+        p = b->fd;
+        assert(chunksize(p) == small_index2size(idx));
+        unlink_first_small_chunk(ms, b, p, idx);
+        set_inuse_and_pinuse(ms, p, small_index2size(idx));
+        mem = chunk2mem(p);
+        check_malloced_chunk(ms, mem, nb);
+        goto postaction;
+      }
+
+      else if (nb > ms->dvsize) {
+        if (smallbits != 0) { /* Use chunk in next nonempty smallbin */
+          mchunkptr b, p, r;
+          size_t rsize;
+          bindex_t i;
+          binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx));
+          binmap_t leastbit = least_bit(leftbits);
+          compute_bit2idx(leastbit, i);
+          b = smallbin_at(ms, i);
+          p = b->fd;
+          assert(chunksize(p) == small_index2size(i));
+          unlink_first_small_chunk(ms, b, p, i);
+          rsize = small_index2size(i) - nb;
+          /* Fit here cannot be remainderless if 4byte sizes */
+          if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE)
+            set_inuse_and_pinuse(ms, p, small_index2size(i));
+          else {
+            set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+            r = chunk_plus_offset(p, nb);
+            set_size_and_pinuse_of_free_chunk(r, rsize);
+            replace_dv(ms, r, rsize);
+          }
+          mem = chunk2mem(p);
+          check_malloced_chunk(ms, mem, nb);
+          goto postaction;
+        }
+
+        else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) {
+          check_malloced_chunk(ms, mem, nb);
+          goto postaction;
+        }
+      }
+    }
+    else if (bytes >= MAX_REQUEST)
+      nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */
+    else {
+      nb = pad_request(bytes);
+      if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) {
+        check_malloced_chunk(ms, mem, nb);
+        goto postaction;
+      }
+    }
+
+    if (nb <= ms->dvsize) {
+      size_t rsize = ms->dvsize - nb;
+      mchunkptr p = ms->dv;
+      if (rsize >= MIN_CHUNK_SIZE) { /* split dv */
+        mchunkptr r = ms->dv = chunk_plus_offset(p, nb);
+        ms->dvsize = rsize;
+        set_size_and_pinuse_of_free_chunk(r, rsize);
+        set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+      }
+      else { /* exhaust dv */
+        size_t dvs = ms->dvsize;
+        ms->dvsize = 0;
+        ms->dv = 0;
+        set_inuse_and_pinuse(ms, p, dvs);
+      }
+      mem = chunk2mem(p);
+      check_malloced_chunk(ms, mem, nb);
+      goto postaction;
+    }
+
+    else if (nb < ms->topsize) { /* Split top */
+      size_t rsize = ms->topsize -= nb;
+      mchunkptr p = ms->top;
+      mchunkptr r = ms->top = chunk_plus_offset(p, nb);
+      r->head = rsize | PINUSE_BIT;
+      set_size_and_pinuse_of_inuse_chunk(ms, p, nb);
+      mem = chunk2mem(p);
+      check_top_chunk(ms, ms->top);
+      check_malloced_chunk(ms, mem, nb);
+      goto postaction;
+    }
+
+    mem = sys_alloc(ms, nb);
+
+  postaction:
+    POSTACTION(ms);
+    return mem;
+  }
+
+  return 0;
+}
+
+void mspace_free(mspace msp, void* mem) {
+  if (mem != 0) {
+    mchunkptr p  = mem2chunk(mem);
+#if FOOTERS
+    mstate fm = get_mstate_for(p);
+#else /* FOOTERS */
+    mstate fm = (mstate)msp;
+#endif /* FOOTERS */
+    if (!ok_magic(fm)) {
+      USAGE_ERROR_ACTION(fm, p);
+      return;
+    }
+    if (!PREACTION(fm)) {
+      check_inuse_chunk(fm, p);
+      if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) {
+        size_t psize = chunksize(p);
+        mchunkptr next = chunk_plus_offset(p, psize);
+        if (!pinuse(p)) {
+          size_t prevsize = p->prev_foot;
+          if ((prevsize & IS_MMAPPED_BIT) != 0) {
+            prevsize &= ~IS_MMAPPED_BIT;
+            psize += prevsize + MMAP_FOOT_PAD;
+            if (CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+              fm->footprint -= psize;
+            goto postaction;
+          }
+          else {
+            mchunkptr prev = chunk_minus_offset(p, prevsize);
+            psize += prevsize;
+            p = prev;
+            if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */
+              if (p != fm->dv) {
+                unlink_chunk(fm, p, prevsize);
+              }
+              else if ((next->head & INUSE_BITS) == INUSE_BITS) {
+                fm->dvsize = psize;
+                set_free_with_pinuse(p, psize, next);
+                goto postaction;
+              }
+            }
+            else
+              goto erroraction;
+          }
+        }
+
+        if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) {
+          if (!cinuse(next)) {  /* consolidate forward */
+            if (next == fm->top) {
+              size_t tsize = fm->topsize += psize;
+              fm->top = p;
+              p->head = tsize | PINUSE_BIT;
+              if (p == fm->dv) {
+                fm->dv = 0;
+                fm->dvsize = 0;
+              }
+              if (should_trim(fm, tsize))
+                sys_trim(fm, 0);
+              goto postaction;
+            }
+            else if (next == fm->dv) {
+              size_t dsize = fm->dvsize += psize;
+              fm->dv = p;
+              set_size_and_pinuse_of_free_chunk(p, dsize);
+              goto postaction;
+            }
+            else {
+              size_t nsize = chunksize(next);
+              psize += nsize;
+              unlink_chunk(fm, next, nsize);
+              set_size_and_pinuse_of_free_chunk(p, psize);
+              if (p == fm->dv) {
+                fm->dvsize = psize;
+                goto postaction;
+              }
+            }
+          }
+          else
+            set_free_with_pinuse(p, psize, next);
+          insert_chunk(fm, p, psize);
+          check_free_chunk(fm, p);
+          goto postaction;
+        }
+      }
+    erroraction:
+      USAGE_ERROR_ACTION(fm, p);
+    postaction:
+      POSTACTION(fm);
+    }
+  }
+}
+
+void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) {
+  void* mem;
+  size_t req = 0;
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  if (n_elements != 0) {
+    req = n_elements * elem_size;
+    if (((n_elements | elem_size) & ~(size_t)0xffff) &&
+        (req / n_elements != elem_size))
+      req = MAX_SIZE_T; /* force downstream failure on overflow */
+  }
+  mem = internal_malloc(ms, req);
+  if (mem != 0 && calloc_must_clear(mem2chunk(mem)))
+    memset(mem, 0, req);
+  return mem;
+}
+
+void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) {
+  if (oldmem == 0)
+    return mspace_malloc(msp, bytes);
+#ifdef REALLOC_ZERO_BYTES_FREES
+  if (bytes == 0) {
+    mspace_free(msp, oldmem);
+    return 0;
+  }
+#endif /* REALLOC_ZERO_BYTES_FREES */
+  else {
+#if FOOTERS
+    mchunkptr p  = mem2chunk(oldmem);
+    mstate ms = get_mstate_for(p);
+#else /* FOOTERS */
+    mstate ms = (mstate)msp;
+#endif /* FOOTERS */
+    if (!ok_magic(ms)) {
+      USAGE_ERROR_ACTION(ms,ms);
+      return 0;
+    }
+    return internal_realloc(ms, oldmem, bytes);
+  }
+}
+
+void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) {
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  return internal_memalign(ms, alignment, bytes);
+}
+
+void** mspace_independent_calloc(mspace msp, size_t n_elements,
+                                 size_t elem_size, void* chunks[]) {
+  size_t sz = elem_size; /* serves as 1-element array */
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  return ialloc(ms, n_elements, &sz, 3, chunks);
+}
+
+void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+                                   size_t sizes[], void* chunks[]) {
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+    return 0;
+  }
+  return ialloc(ms, n_elements, sizes, 0, chunks);
+}
+
+int mspace_trim(mspace msp, size_t pad) {
+  int result = 0;
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    if (!PREACTION(ms)) {
+      result = sys_trim(ms, pad);
+      POSTACTION(ms);
+    }
+  }
+  else {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+  return result;
+}
+
+void mspace_malloc_stats(mspace msp) {
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    internal_malloc_stats(ms);
+  }
+  else {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+}
+
+size_t mspace_footprint(mspace msp) {
+  size_t result;
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    result = ms->footprint;
+  }
+  USAGE_ERROR_ACTION(ms,ms);
+  return result;
+}
+
+
+size_t mspace_max_footprint(mspace msp) {
+  size_t result;
+  mstate ms = (mstate)msp;
+  if (ok_magic(ms)) {
+    result = ms->max_footprint;
+  }
+  USAGE_ERROR_ACTION(ms,ms);
+  return result;
+}
+
+
+#if !NO_MALLINFO
+struct mallinfo mspace_mallinfo(mspace msp) {
+  mstate ms = (mstate)msp;
+  if (!ok_magic(ms)) {
+    USAGE_ERROR_ACTION(ms,ms);
+  }
+  return internal_mallinfo(ms);
+}
+#endif /* NO_MALLINFO */
+
+int mspace_mallopt(int param_number, int value) {
+  return change_mparam(param_number, value);
+}
+
+#endif /* MSPACES */
+
+/* -------------------- Alternative MORECORE functions ------------------- */
+
+/*
+  Guidelines for creating a custom version of MORECORE:
+
+  * For best performance, MORECORE should allocate in multiples of pagesize.
+  * MORECORE may allocate more memory than requested. (Or even less,
+      but this will usually result in a malloc failure.)
+  * MORECORE must not allocate memory when given argument zero, but
+      instead return one past the end address of memory from previous
+      nonzero call.
+  * For best performance, consecutive calls to MORECORE with positive
+      arguments should return increasing addresses, indicating that
+      space has been contiguously extended.
+  * Even though consecutive calls to MORECORE need not return contiguous
+      addresses, it must be OK for malloc'ed chunks to span multiple
+      regions in those cases where they do happen to be contiguous.
+  * MORECORE need not handle negative arguments -- it may instead
+      just return MFAIL when given negative arguments.
+      Negative arguments are always multiples of pagesize. MORECORE
+      must not misinterpret negative args as large positive unsigned
+      args. You can suppress all such calls from even occurring by defining
+      MORECORE_CANNOT_TRIM,
+
+  As an example alternative MORECORE, here is a custom allocator
+  kindly contributed for pre-OSX macOS.  It uses virtually but not
+  necessarily physically contiguous non-paged memory (locked in,
+  present and won't get swapped out).  You can use it by uncommenting
+  this section, adding some #includes, and setting up the appropriate
+  defines above:
+
+      #define MORECORE osMoreCore
+
+  There is also a shutdown routine that should somehow be called for
+  cleanup upon program exit.
+
+  #define MAX_POOL_ENTRIES 100
+  #define MINIMUM_MORECORE_SIZE  (64 * 1024U)
+  static int next_os_pool;
+  void *our_os_pools[MAX_POOL_ENTRIES];
+
+  void *osMoreCore(int size)
+  {
+    void *ptr = 0;
+    static void *sbrk_top = 0;
+
+    if (size > 0)
+    {
+      if (size < MINIMUM_MORECORE_SIZE)
+         size = MINIMUM_MORECORE_SIZE;
+      if (CurrentExecutionLevel() == kTaskLevel)
+         ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0);
+      if (ptr == 0)
+      {
+        return (void *) MFAIL;
+      }
+      // save ptrs so they can be freed during cleanup
+      our_os_pools[next_os_pool] = ptr;
+      next_os_pool++;
+      ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK);
+      sbrk_top = (char *) ptr + size;
+      return ptr;
+    }
+    else if (size < 0)
+    {
+      // we don't currently support shrink behavior
+      return (void *) MFAIL;
+    }
+    else
+    {
+      return sbrk_top;
+    }
+  }
+
+  // cleanup any allocated memory pools
+  // called as last thing before shutting down driver
+
+  void osCleanupMem(void)
+  {
+    void **ptr;
+
+    for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++)
+      if (*ptr)
+      {
+         PoolDeallocate(*ptr);
+         *ptr = 0;
+      }
+  }
+
+*/
+
+
+/* -----------------------------------------------------------------------
+History:
+    V2.8.3 Thu Sep 22 11:16:32 2005  Doug Lea  (dl at gee)
+      * Add max_footprint functions
+      * Ensure all appropriate literals are size_t
+      * Fix conditional compilation problem for some #define settings
+      * Avoid concatenating segments with the one provided
+        in create_mspace_with_base
+      * Rename some variables to avoid compiler shadowing warnings
+      * Use explicit lock initialization.
+      * Better handling of sbrk interference.
+      * Simplify and fix segment insertion, trimming and mspace_destroy
+      * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x
+      * Thanks especially to Dennis Flanagan for help on these.
+
+    V2.8.2 Sun Jun 12 16:01:10 2005  Doug Lea  (dl at gee)
+      * Fix memalign brace error.
+
+    V2.8.1 Wed Jun  8 16:11:46 2005  Doug Lea  (dl at gee)
+      * Fix improper #endif nesting in C++
+      * Add explicit casts needed for C++
+
+    V2.8.0 Mon May 30 14:09:02 2005  Doug Lea  (dl at gee)
+      * Use trees for large bins
+      * Support mspaces
+      * Use segments to unify sbrk-based and mmap-based system allocation,
+        removing need for emulation on most platforms without sbrk.
+      * Default safety checks
+      * Optional footer checks. Thanks to William Robertson for the idea.
+      * Internal code refactoring
+      * Incorporate suggestions and platform-specific changes.
+        Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas,
+        Aaron Bachmann,  Emery Berger, and others.
+      * Speed up non-fastbin processing enough to remove fastbins.
+      * Remove useless cfree() to avoid conflicts with other apps.
+      * Remove internal memcpy, memset. Compilers handle builtins better.
+      * Remove some options that no one ever used and rename others.
+
+    V2.7.2 Sat Aug 17 09:07:30 2002  Doug Lea  (dl at gee)
+      * Fix malloc_state bitmap array misdeclaration
+
+    V2.7.1 Thu Jul 25 10:58:03 2002  Doug Lea  (dl at gee)
+      * Allow tuning of FIRST_SORTED_BIN_SIZE
+      * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte.
+      * Better detection and support for non-contiguousness of MORECORE.
+        Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger
+      * Bypass most of malloc if no frees. Thanks To Emery Berger.
+      * Fix freeing of old top non-contiguous chunk im sysmalloc.
+      * Raised default trim and map thresholds to 256K.
+      * Fix mmap-related #defines. Thanks to Lubos Lunak.
+      * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield.
+      * Branch-free bin calculation
+      * Default trim and mmap thresholds now 256K.
+
+    V2.7.0 Sun Mar 11 14:14:06 2001  Doug Lea  (dl at gee)
+      * Introduce independent_comalloc and independent_calloc.
+        Thanks to Michael Pachos for motivation and help.
+      * Make optional .h file available
+      * Allow > 2GB requests on 32bit systems.
+      * new WIN32 sbrk, mmap, munmap, lock code from <Walter@GeNeSys-e.de>.
+        Thanks also to Andreas Mueller <a.mueller at paradatec.de>,
+        and Anonymous.
+      * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for
+        helping test this.)
+      * memalign: check alignment arg
+      * realloc: don't try to shift chunks backwards, since this
+        leads to  more fragmentation in some programs and doesn't
+        seem to help in any others.
+      * Collect all cases in malloc requiring system memory into sysmalloc
+      * Use mmap as backup to sbrk
+      * Place all internal state in malloc_state
+      * Introduce fastbins (although similar to 2.5.1)
+      * Many minor tunings and cosmetic improvements
+      * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK
+      * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS
+        Thanks to Tony E. Bennett <tbennett@nvidia.com> and others.
+      * Include errno.h to support default failure action.
+
+    V2.6.6 Sun Dec  5 07:42:19 1999  Doug Lea  (dl at gee)
+      * return null for negative arguments
+      * Added Several WIN32 cleanups from Martin C. Fong <mcfong at yahoo.com>
+         * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h'
+          (e.g. WIN32 platforms)
+         * Cleanup header file inclusion for WIN32 platforms
+         * Cleanup code to avoid Microsoft Visual C++ compiler complaints
+         * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing
+           memory allocation routines
+         * Set 'malloc_getpagesize' for WIN32 platforms (needs more work)
+         * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to
+           usage of 'assert' in non-WIN32 code
+         * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to
+           avoid infinite loop
+      * Always call 'fREe()' rather than 'free()'
+
+    V2.6.5 Wed Jun 17 15:57:31 1998  Doug Lea  (dl at gee)
+      * Fixed ordering problem with boundary-stamping
+
+    V2.6.3 Sun May 19 08:17:58 1996  Doug Lea  (dl at gee)
+      * Added pvalloc, as recommended by H.J. Liu
+      * Added 64bit pointer support mainly from Wolfram Gloger
+      * Added anonymously donated WIN32 sbrk emulation
+      * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen
+      * malloc_extend_top: fix mask error that caused wastage after
+        foreign sbrks
+      * Add linux mremap support code from HJ Liu
+
+    V2.6.2 Tue Dec  5 06:52:55 1995  Doug Lea  (dl at gee)
+      * Integrated most documentation with the code.
+      * Add support for mmap, with help from
+        Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
+      * Use last_remainder in more cases.
+      * Pack bins using idea from  colin@nyx10.cs.du.edu
+      * Use ordered bins instead of best-fit threshhold
+      * Eliminate block-local decls to simplify tracing and debugging.
+      * Support another case of realloc via move into top
+      * Fix error occuring when initial sbrk_base not word-aligned.
+      * Rely on page size for units instead of SBRK_UNIT to
+        avoid surprises about sbrk alignment conventions.
+      * Add mallinfo, mallopt. Thanks to Raymond Nijssen
+        (raymond@es.ele.tue.nl) for the suggestion.
+      * Add `pad' argument to malloc_trim and top_pad mallopt parameter.
+      * More precautions for cases where other routines call sbrk,
+        courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
+      * Added macros etc., allowing use in linux libc from
+        H.J. Lu (hjl@gnu.ai.mit.edu)
+      * Inverted this history list
+
+    V2.6.1 Sat Dec  2 14:10:57 1995  Doug Lea  (dl at gee)
+      * Re-tuned and fixed to behave more nicely with V2.6.0 changes.
+      * Removed all preallocation code since under current scheme
+        the work required to undo bad preallocations exceeds
+        the work saved in good cases for most test programs.
+      * No longer use return list or unconsolidated bins since
+        no scheme using them consistently outperforms those that don't
+        given above changes.
+      * Use best fit for very large chunks to prevent some worst-cases.
+      * Added some support for debugging
+
+    V2.6.0 Sat Nov  4 07:05:23 1995  Doug Lea  (dl at gee)
+      * Removed footers when chunks are in use. Thanks to
+        Paul Wilson (wilson@cs.texas.edu) for the suggestion.
+
+    V2.5.4 Wed Nov  1 07:54:51 1995  Doug Lea  (dl at gee)
+      * Added malloc_trim, with help from Wolfram Gloger
+        (wmglo@Dent.MED.Uni-Muenchen.DE).
+
+    V2.5.3 Tue Apr 26 10:16:01 1994  Doug Lea  (dl at g)
+
+    V2.5.2 Tue Apr  5 16:20:40 1994  Doug Lea  (dl at g)
+      * realloc: try to expand in both directions
+      * malloc: swap order of clean-bin strategy;
+      * realloc: only conditionally expand backwards
+      * Try not to scavenge used bins
+      * Use bin counts as a guide to preallocation
+      * Occasionally bin return list chunks in first scan
+      * Add a few optimizations from colin@nyx10.cs.du.edu
+
+    V2.5.1 Sat Aug 14 15:40:43 1993  Doug Lea  (dl at g)
+      * faster bin computation & slightly different binning
+      * merged all consolidations to one part of malloc proper
+         (eliminating old malloc_find_space & malloc_clean_bin)
+      * Scan 2 returns chunks (not just 1)
+      * Propagate failure in realloc if malloc returns 0
+      * Add stuff to allow compilation on non-ANSI compilers
+          from kpv@research.att.com
+
+    V2.5 Sat Aug  7 07:41:59 1993  Doug Lea  (dl at g.oswego.edu)
+      * removed potential for odd address access in prev_chunk
+      * removed dependency on getpagesize.h
+      * misc cosmetics and a bit more internal documentation
+      * anticosmetics: mangled names in macros to evade debugger strangeness
+      * tested on sparc, hp-700, dec-mips, rs6000
+          with gcc & native cc (hp, dec only) allowing
+          Detlefs & Zorn comparison study (in SIGPLAN Notices.)
+
+    Trial version Fri Aug 28 13:14:29 1992  Doug Lea  (dl at g.oswego.edu)
+      * Based loosely on libg++-1.2X malloc. (It retains some of the overall
+         structure of old version,  but most details differ.)
+*/
diff --git a/malloc.h b/malloc.h
new file mode 100644 (file)
index 0000000..242a8a5
--- /dev/null
+++ b/malloc.h
@@ -0,0 +1,529 @@
+/*
+  Default header file for malloc-2.8.x, written by Doug Lea
+  and released to the public domain, as explained at
+  http://creativecommons.org/licenses/publicdomain. 
+  last update: Mon Aug 15 08:55:52 2005  Doug Lea  (dl at gee)
+
+  This header is for ANSI C/C++ only.  You can set any of
+  the following #defines before including:
+
+  * If USE_DL_PREFIX is defined, it is assumed that malloc.c 
+    was also compiled with this option, so all routines
+    have names starting with "dl".
+
+  * If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this
+    file will be #included AFTER <malloc.h>. This is needed only if
+    your system defines a struct mallinfo that is incompatible with the
+    standard one declared here.  Otherwise, you can include this file
+    INSTEAD of your system system <malloc.h>.  At least on ANSI, all
+    declarations should be compatible with system versions
+
+  * If MSPACES is defined, declarations for mspace versions are included.
+*/
+
+#ifndef MALLOC_280_H
+#define MALLOC_280_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//#include <stddef.h>   /* for size_t */
+
+#if !ONLY_MSPACES
+
+#ifndef USE_DL_PREFIX
+#define dlcalloc               calloc
+#define dlfree                 free
+#define dlmalloc               malloc
+#define dlmemalign             memalign
+#define dlrealloc              realloc
+#define dlvalloc               valloc
+#define dlpvalloc              pvalloc
+#define dlmallinfo             mallinfo
+#define dlmallopt              mallopt
+#define dlmalloc_trim          malloc_trim
+#define dlmalloc_stats         malloc_stats
+#define dlmalloc_usable_size   malloc_usable_size
+#define dlmalloc_footprint     malloc_footprint
+#define dlindependent_calloc   independent_calloc
+#define dlindependent_comalloc independent_comalloc
+#endif /* USE_DL_PREFIX */
+
+
+/*
+  malloc(size_t n)
+  Returns a pointer to a newly allocated chunk of at least n bytes, or
+  null if no space is available, in which case errno is set to ENOMEM
+  on ANSI C systems.
+
+  If n is zero, malloc returns a minimum-sized chunk. (The minimum
+  size is 16 bytes on most 32bit systems, and 32 bytes on 64bit
+  systems.)  Note that size_t is an unsigned type, so calls with
+  arguments that would be negative if signed are interpreted as
+  requests for huge amounts of space, which will often fail. The
+  maximum supported value of n differs across systems, but is in all
+  cases less than the maximum representable value of a size_t.
+*/
+void* dlmalloc(size_t);
+
+/*
+  free(void* p)
+  Releases the chunk of memory pointed to by p, that had been previously
+  allocated using malloc or a related routine such as realloc.
+  It has no effect if p is null. If p was not malloced or already
+  freed, free(p) will by default cuase the current program to abort.
+*/
+void  dlfree(void*);
+
+/*
+  calloc(size_t n_elements, size_t element_size);
+  Returns a pointer to n_elements * element_size bytes, with all locations
+  set to zero.
+*/
+void* dlcalloc(size_t, size_t);
+
+/*
+  realloc(void* p, size_t n)
+  Returns a pointer to a chunk of size n that contains the same data
+  as does chunk p up to the minimum of (n, p's size) bytes, or null
+  if no space is available.
+
+  The returned pointer may or may not be the same as p. The algorithm
+  prefers extending p in most cases when possible, otherwise it
+  employs the equivalent of a malloc-copy-free sequence.
+
+  If p is null, realloc is equivalent to malloc.
+
+  If space is not available, realloc returns null, errno is set (if on
+  ANSI) and p is NOT freed.
+
+  if n is for fewer bytes than already held by p, the newly unused
+  space is lopped off and freed if possible.  realloc with a size
+  argument of zero (re)allocates a minimum-sized chunk.
+
+  The old unix realloc convention of allowing the last-free'd chunk
+  to be used as an argument to realloc is not supported.
+*/
+
+void* dlrealloc(void*, size_t);
+
+/*
+  memalign(size_t alignment, size_t n);
+  Returns a pointer to a newly allocated chunk of n bytes, aligned
+  in accord with the alignment argument.
+
+  The alignment argument should be a power of two. If the argument is
+  not a power of two, the nearest greater power is used.
+  8-byte alignment is guaranteed by normal malloc calls, so don't
+  bother calling memalign with an argument of 8 or less.
+
+  Overreliance on memalign is a sure way to fragment space.
+*/
+void* dlmemalign(size_t, size_t);
+
+/*
+  valloc(size_t n);
+  Equivalent to memalign(pagesize, n), where pagesize is the page
+  size of the system. If the pagesize is unknown, 4096 is used.
+*/
+void* dlvalloc(size_t);
+
+/*
+  mallopt(int parameter_number, int parameter_value)
+  Sets tunable parameters The format is to provide a
+  (parameter-number, parameter-value) pair.  mallopt then sets the
+  corresponding parameter to the argument value if it can (i.e., so
+  long as the value is meaningful), and returns 1 if successful else
+  0.  SVID/XPG/ANSI defines four standard param numbers for mallopt,
+  normally defined in malloc.h.  None of these are use in this malloc,
+  so setting them has no effect. But this malloc also supports other
+  options in mallopt:
+
+  Symbol            param #  default    allowed param values
+  M_TRIM_THRESHOLD     -1   2*1024*1024   any   (-1U disables trimming)
+  M_GRANULARITY        -2     page size   any power of 2 >= page size
+  M_MMAP_THRESHOLD     -3      256*1024   any   (or 0 if no MMAP support)
+*/
+int dlmallopt(int, int);
+
+#define M_TRIM_THRESHOLD     (-1)
+#define M_GRANULARITY        (-2)
+#define M_MMAP_THRESHOLD     (-3)
+
+
+/*
+  malloc_footprint();
+  Returns the number of bytes obtained from the system.  The total
+  number of bytes allocated by malloc, realloc etc., is less than this
+  value. Unlike mallinfo, this function returns only a precomputed
+  result, so can be called frequently to monitor memory consumption.
+  Even if locks are otherwise defined, this function does not use them,
+  so results might not be up to date.
+*/
+size_t dlmalloc_footprint();
+
+#if !NO_MALLINFO
+/*
+  mallinfo()
+  Returns (by copy) a struct containing various summary statistics:
+
+  arena:     current total non-mmapped bytes allocated from system
+  ordblks:   the number of free chunks
+  smblks:    always zero.
+  hblks:     current number of mmapped regions
+  hblkhd:    total bytes held in mmapped regions
+  usmblks:   the maximum total allocated space. This will be greater
+                than current total if trimming has occurred.
+  fsmblks:   always zero
+  uordblks:  current total allocated space (normal or mmapped)
+  fordblks:  total free space
+  keepcost:  the maximum number of bytes that could ideally be released
+               back to system via malloc_trim. ("ideally" means that
+               it ignores page restrictions etc.)
+
+  Because these fields are ints, but internal bookkeeping may
+  be kept as longs, the reported values may wrap around zero and
+  thus be inaccurate.
+*/
+#ifndef HAVE_USR_INCLUDE_MALLOC_H
+#ifndef _MALLOC_H
+#ifndef MALLINFO_FIELD_TYPE
+#define MALLINFO_FIELD_TYPE size_t
+#endif /* MALLINFO_FIELD_TYPE */
+struct mallinfo {
+  MALLINFO_FIELD_TYPE arena;    /* non-mmapped space allocated from system */
+  MALLINFO_FIELD_TYPE ordblks;  /* number of free chunks */
+  MALLINFO_FIELD_TYPE smblks;   /* always 0 */
+  MALLINFO_FIELD_TYPE hblks;    /* always 0 */
+  MALLINFO_FIELD_TYPE hblkhd;   /* space in mmapped regions */
+  MALLINFO_FIELD_TYPE usmblks;  /* maximum total allocated space */
+  MALLINFO_FIELD_TYPE fsmblks;  /* always 0 */
+  MALLINFO_FIELD_TYPE uordblks; /* total allocated space */
+  MALLINFO_FIELD_TYPE fordblks; /* total free space */
+  MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */
+};
+#endif  /* _MALLOC_H */
+#endif  /* HAVE_USR_INCLUDE_MALLOC_H */
+
+struct mallinfo dlmallinfo(void);
+#endif  /* NO_MALLINFO */
+
+/*
+  independent_calloc(size_t n_elements, size_t element_size, void* chunks[]);
+
+  independent_calloc is similar to calloc, but instead of returning a
+  single cleared space, it returns an array of pointers to n_elements
+  independent elements that can hold contents of size elem_size, each
+  of which starts out cleared, and can be independently freed,
+  realloc'ed etc. The elements are guaranteed to be adjacently
+  allocated (this is not guaranteed to occur with multiple callocs or
+  mallocs), which may also improve cache locality in some
+  applications.
+
+  The "chunks" argument is optional (i.e., may be null, which is
+  probably the most typical usage). If it is null, the returned array
+  is itself dynamically allocated and should also be freed when it is
+  no longer needed. Otherwise, the chunks array must be of at least
+  n_elements in length. It is filled in with the pointers to the
+  chunks.
+
+  In either case, independent_calloc returns this pointer array, or
+  null if the allocation failed.  If n_elements is zero and "chunks"
+  is null, it returns a chunk representing an array with zero elements
+  (which should be freed if not wanted).
+
+  Each element must be individually freed when it is no longer
+  needed. If you'd like to instead be able to free all at once, you
+  should instead use regular calloc and assign pointers into this
+  space to represent elements.  (In this case though, you cannot
+  independently free elements.)
+
+  independent_calloc simplifies and speeds up implementations of many
+  kinds of pools.  It may also be useful when constructing large data
+  structures that initially have a fixed number of fixed-sized nodes,
+  but the number is not known at compile time, and some of the nodes
+  may later need to be freed. For example:
+
+  struct Node { int item; struct Node* next; };
+
+  struct Node* build_list() {
+    struct Node** pool;
+    int n = read_number_of_nodes_needed();
+    if (n <= 0) return 0;
+    pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0);
+    if (pool == 0) die();
+    // organize into a linked list...
+    struct Node* first = pool[0];
+    for (i = 0; i < n-1; ++i)
+      pool[i]->next = pool[i+1];
+    free(pool);     // Can now free the array (or not, if it is needed later)
+    return first;
+  }
+*/
+void** dlindependent_calloc(size_t, size_t, void**);
+
+/*
+  independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
+
+  independent_comalloc allocates, all at once, a set of n_elements
+  chunks with sizes indicated in the "sizes" array.    It returns
+  an array of pointers to these elements, each of which can be
+  independently freed, realloc'ed etc. The elements are guaranteed to
+  be adjacently allocated (this is not guaranteed to occur with
+  multiple callocs or mallocs), which may also improve cache locality
+  in some applications.
+
+  The "chunks" argument is optional (i.e., may be null). If it is null
+  the returned array is itself dynamically allocated and should also
+  be freed when it is no longer needed. Otherwise, the chunks array
+  must be of at least n_elements in length. It is filled in with the
+  pointers to the chunks.
+
+  In either case, independent_comalloc returns this pointer array, or
+  null if the allocation failed.  If n_elements is zero and chunks is
+  null, it returns a chunk representing an array with zero elements
+  (which should be freed if not wanted).
+
+  Each element must be individually freed when it is no longer
+  needed. If you'd like to instead be able to free all at once, you
+  should instead use a single regular malloc, and assign pointers at
+  particular offsets in the aggregate space. (In this case though, you
+  cannot independently free elements.)
+
+  independent_comallac differs from independent_calloc in that each
+  element may have a different size, and also that it does not
+  automatically clear elements.
+
+  independent_comalloc can be used to speed up allocation in cases
+  where several structs or objects must always be allocated at the
+  same time.  For example:
+
+  struct Head { ... }
+  struct Foot { ... }
+
+  void send_message(char* msg) {
+    int msglen = strlen(msg);
+    size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) };
+    void* chunks[3];
+    if (independent_comalloc(3, sizes, chunks) == 0)
+      die();
+    struct Head* head = (struct Head*)(chunks[0]);
+    char*        body = (char*)(chunks[1]);
+    struct Foot* foot = (struct Foot*)(chunks[2]);
+    // ...
+  }
+
+  In general though, independent_comalloc is worth using only for
+  larger values of n_elements. For small values, you probably won't
+  detect enough difference from series of malloc calls to bother.
+
+  Overuse of independent_comalloc can increase overall memory usage,
+  since it cannot reuse existing noncontiguous small chunks that
+  might be available for some of the elements.
+*/
+void** dlindependent_comalloc(size_t, size_t*, void**);
+
+
+/*
+  pvalloc(size_t n);
+  Equivalent to valloc(minimum-page-that-holds(n)), that is,
+  round up n to nearest pagesize.
+ */
+void*  dlpvalloc(size_t);
+
+/*
+  malloc_trim(size_t pad);
+
+  If possible, gives memory back to the system (via negative arguments
+  to sbrk) if there is unused memory at the `high' end of the malloc
+  pool or in unused MMAP segments. You can call this after freeing
+  large blocks of memory to potentially reduce the system-level memory
+  requirements of a program. However, it cannot guarantee to reduce
+  memory. Under some allocation patterns, some large free blocks of
+  memory will be locked between two used chunks, so they cannot be
+  given back to the system.
+
+  The `pad' argument to malloc_trim represents the amount of free
+  trailing space to leave untrimmed. If this argument is zero, only
+  the minimum amount of memory to maintain internal data structures
+  will be left. Non-zero arguments can be supplied to maintain enough
+  trailing space to service future expected allocations without having
+  to re-obtain memory from the system.
+
+  Malloc_trim returns 1 if it actually released any memory, else 0.
+*/
+int  dlmalloc_trim(size_t);
+
+/*
+  malloc_usable_size(void* p);
+
+  Returns the number of bytes you can actually use in
+  an allocated chunk, which may be more than you requested (although
+  often not) due to alignment and minimum size constraints.
+  You can use this many bytes without worrying about
+  overwriting other allocated objects. This is not a particularly great
+  programming practice. malloc_usable_size can be more useful in
+  debugging and assertions, for example:
+
+  p = malloc(n);
+  assert(malloc_usable_size(p) >= 256);
+*/
+size_t dlmalloc_usable_size(void*);
+
+/*
+  malloc_stats();
+  Prints on stderr the amount of space obtained from the system (both
+  via sbrk and mmap), the maximum amount (which may be more than
+  current if malloc_trim and/or munmap got called), and the current
+  number of bytes allocated via malloc (or realloc, etc) but not yet
+  freed. Note that this is the number of bytes allocated, not the
+  number requested. It will be larger than the number requested
+  because of alignment and bookkeeping overhead. Because it includes
+  alignment wastage as being in use, this figure may be greater than
+  zero even when no user-level chunks are allocated.
+
+  The reported current and maximum system memory can be inaccurate if
+  a program makes other calls to system memory allocation functions
+  (normally sbrk) outside of malloc.
+
+  malloc_stats prints only the most commonly interesting statistics.
+  More information can be obtained by calling mallinfo.
+*/
+void  dlmalloc_stats();
+
+#endif /* !ONLY_MSPACES */
+
+#if MSPACES
+
+/*
+  mspace is an opaque type representing an independent
+  region of space that supports mspace_malloc, etc.
+*/
+typedef void* mspace;
+
+/*
+  create_mspace creates and returns a new independent space with the
+  given initial capacity, or, if 0, the default granularity size.  It
+  returns null if there is no system memory available to create the
+  space.  If argument locked is non-zero, the space uses a separate
+  lock to control access. The capacity of the space will grow
+  dynamically as needed to service mspace_malloc requests.  You can
+  control the sizes of incremental increases of this space by
+  compiling with a different DEFAULT_GRANULARITY or dynamically
+  setting with mallopt(M_GRANULARITY, value).
+*/
+mspace create_mspace(size_t capacity, int locked);
+
+/*
+  destroy_mspace destroys the given space, and attempts to return all
+  of its memory back to the system, returning the total number of
+  bytes freed. After destruction, the results of access to all memory
+  used by the space become undefined.
+*/
+size_t destroy_mspace(mspace msp);
+
+/*
+  create_mspace_with_base uses the memory supplied as the initial base
+  of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
+  space is used for bookkeeping, so the capacity must be at least this
+  large. (Otherwise 0 is returned.) When this initial space is
+  exhausted, additional memory will be obtained from the system.
+  Destroying this space will deallocate all additionally allocated
+  space (if possible) but not the initial base.
+*/
+mspace create_mspace_with_base(void* base, size_t capacity, int locked);
+
+/*
+  mspace_malloc behaves as malloc, but operates within
+  the given space.
+*/
+void* mspace_malloc(mspace msp, size_t bytes);
+
+/*
+  mspace_free behaves as free, but operates within
+  the given space.
+
+  If compiled with FOOTERS==1, mspace_free is not actually needed.
+  free may be called instead of mspace_free because freed chunks from
+  any space are handled by their originating spaces.
+*/
+void mspace_free(mspace msp, void* mem);
+
+/*
+  mspace_realloc behaves as realloc, but operates within
+  the given space.
+
+  If compiled with FOOTERS==1, mspace_realloc is not actually
+  needed.  realloc may be called instead of mspace_realloc because
+  realloced chunks from any space are handled by their originating
+  spaces.
+*/
+void* mspace_realloc(mspace msp, void* mem, size_t newsize);
+
+/*
+  mspace_calloc behaves as calloc, but operates within
+  the given space.
+*/
+void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
+
+/*
+  mspace_memalign behaves as memalign, but operates within
+  the given space.
+*/
+void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
+
+/*
+  mspace_independent_calloc behaves as independent_calloc, but
+  operates within the given space.
+*/
+void** mspace_independent_calloc(mspace msp, size_t n_elements,
+                                 size_t elem_size, void* chunks[]);
+
+/*
+  mspace_independent_comalloc behaves as independent_comalloc, but
+  operates within the given space.
+*/
+void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+                                   size_t sizes[], void* chunks[]);
+
+/*
+  mspace_footprint() returns the number of bytes obtained from the
+  system for this space.
+*/
+size_t mspace_footprint(mspace msp);
+
+
+#if !NO_MALLINFO
+/*
+  mspace_mallinfo behaves as mallinfo, but reports properties of
+  the given space.
+*/
+struct mallinfo mspace_mallinfo(mspace msp);
+#endif /* NO_MALLINFO */
+
+/*
+  mspace_malloc_stats behaves as malloc_stats, but reports
+  properties of the given space.
+*/
+void mspace_malloc_stats(mspace msp);
+
+/*
+  mspace_trim behaves as malloc_trim, but
+  operates within the given space.
+*/
+int mspace_trim(mspace msp, size_t pad);
+
+/*
+  An alias for mallopt.
+*/
+int mspace_mallopt(int, int);
+
+#endif  /* MSPACES */
+
+#ifdef __cplusplus
+};  /* end of extern "C" */
+#endif
+
+#endif /* MALLOC_280_H */
diff --git a/mini.ld b/mini.ld
new file mode 100644 (file)
index 0000000..437b74a
--- /dev/null
+++ b/mini.ld
@@ -0,0 +1,53 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2009                     Andre Heider "dhewg" <dhewg@wiibrew.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+OUTPUT_FORMAT("elf32-powerpc")
+OUTPUT_ARCH(powerpc:common)
+
+ENTRY(_realmode_vector)
+
+PHDRS {
+       realmode        PT_LOAD FLAGS(5);
+       app                     PT_LOAD FLAGS(7);
+}
+
+SECTIONS {
+       . = 0x00003400;
+
+       .realmode : { *(.realmode) } :realmode = 0
+
+       . = 0x80004000;
+
+       .start : AT(ADDR(.start) & 0x3fffffff) { crt0.o(*) } :app = 0
+
+       .text : { *(.text) *(.text.*) . = ALIGN(32); }
+
+       .data : { *(.data) *(.data.*) . = ALIGN(32); }
+       .sdata : { *(.sdata) *(.sdata.*) . = ALIGN(32); }
+       .rodata : { *(.rodata) *(.rodata.*) . = ALIGN(32); }
+       .stack : {
+               _stack_top = .;
+               . += 32768;
+               _stack_bot = .;
+       }
+
+       . = ALIGN(32);
+
+       __bss_start = .;
+       .bss : { *(.bss) }
+       .sbss : { *(.sbss) }
+       __bss_end = .;
+
+       . = ALIGN(0x10000);
+
+       _sbrk_start = .;
+       _sbrk_end = 0x816ffff0;
+}
+
diff --git a/mini_ipc.c b/mini_ipc.c
new file mode 100644 (file)
index 0000000..dcf9e9f
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+       mini_ipc.c -- public PowerPC-side interface to mini.  Part of the
+       BootMii project.
+
+Copyright (C) 2009                     Andre Heider "dhewg" <dhewg@wiibrew.org>
+Copyright (C) 2009                     Haxx Enterprises <bushing@gmail.com>
+Copyright (C) 2009                     John Kelley <wiidev@kelley.ca>
+Copyright (C) 2008, 2009       Sven Peter <svenpeter@gmail.com>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+#include "ipc.h"
+#include "mini_ipc.h"
+#include "string.h"
+
+int ipc_powerpc_boot(const void *addr, u32 len)
+{
+       ipc_request *req;
+
+       sync_after_write(addr, len);
+       req =  ipc_exchange(IPC_PPC_BOOT, 3, 0, virt_to_phys(addr), len);
+       return req->args[0];
+}
+
+u32 boot2_run(u32 hi, u32 lo)
+{
+       ipc_request * req;
+       req =  ipc_exchange(IPC_BOOT2_RUN, 2, hi, lo);
+       return req->args[0];
+}
+
+tmd *boot2_tmd(void)
+{
+       tmd *ret = phys_to_virt(ipc_exchange(IPC_BOOT2_TMD, 0)->args[0]);
+       sync_before_read(ret, sizeof(tmd));
+       return ret;
+}
+
+void getotp(otp_t *otp)
+{
+       sync_before_read(otp, sizeof(*otp));
+       ipc_exchange(IPC_KEYS_GETOTP, 1, virt_to_phys(otp));
+}
+
+void getMiniGitVer(char *buf, u16 len)
+{
+       if (len < 32)
+       {
+               memset((void *)buf, 0, 32);
+               return;
+       }
+       sync_before_read(buf, len);
+       ipc_exchange(IPC_SYS_GETGITS, 1, virt_to_phys(buf));
+}
+
+void getseeprom(seeprom_t *seeprom)
+{
+       sync_before_read(seeprom, sizeof(*seeprom));
+       ipc_exchange(IPC_KEYS_GETEEP, 1, virt_to_phys(seeprom));
+}
+
+void aes_reset(void)
+{
+       ipc_exchange(IPC_AES_RESET, 0);
+}
+
+void aes_set_key(u8 *key)
+{
+       u32 *keyptr = (u32 *)key;
+       ipc_exchange(IPC_AES_SETKEY, 4, keyptr[0], keyptr[1], keyptr[2], keyptr[3]);
+}
+
+void aes_set_iv(u8 *iv)
+{
+       u32 *ivptr = (u32 *)iv;
+       ipc_exchange(IPC_AES_SETIV, 4, ivptr[0], ivptr[1], ivptr[2], ivptr[3]);
+}
+
+void aes_decrypt(u8 *src, u8 *dst, u32 blocks, u8 keep_iv)
+{
+       sync_after_write(src, (blocks+1)*16);
+       ipc_exchange(IPC_AES_DECRYPT, 4, virt_to_phys(src), virt_to_phys(dst), blocks, keep_iv);
+       sync_before_read(dst, (blocks+1)*16);
+}
+
+void nand_reset(void)
+{
+       ipc_exchange(IPC_NAND_RESET, 0);
+}
+
+u32 nand_getid(void)
+{
+       static u8 idbuf[64] __attribute__((aligned(64)));
+
+       ipc_exchange(IPC_NAND_GETID, 1, virt_to_phys(&idbuf));
+       sync_before_read(idbuf, 0x40);
+
+       return idbuf[0] << 24 | idbuf[1] << 16 | idbuf[2] << 8 | idbuf[3];
+}
+
+u8 nand_status(void)
+{
+       static u8 buf[64] __attribute__((aligned(64)));
+
+       ipc_exchange(IPC_NAND_STATUS, 1, virt_to_phys(&buf));
+       sync_before_read(buf, 0x40);
+
+       return buf[0];
+}
+
+int nand_read(u32 pageno, void *data, void *ecc)
+{
+       if (data)
+               sync_before_read(data, 0x800);
+       if (ecc)
+               sync_before_read(ecc, 0x40);
+       return ipc_exchange(IPC_NAND_READ, 3, pageno,
+               (!data ? (u32)-1 : virt_to_phys(data)),
+               (!ecc ? (u32)-1 : virt_to_phys(ecc)))->args[0];
+}
+
+void nand_write(u32 pageno, void *data, void *ecc)
+{
+       if (data)
+               sync_after_write(data, 0x800);
+       if (ecc)
+               sync_after_write(ecc, 0x40);
+       ipc_exchange(IPC_NAND_WRITE, 3, pageno,
+               (!data ? (u32)-1 : virt_to_phys(data)),
+               (!ecc ? (u32)-1 : virt_to_phys(ecc)));
+}
+
+void nand_erase(u32 pageno)
+{
+       ipc_exchange(IPC_NAND_ERASE, 1, pageno);
+}
+
+int sd_mount(void)
+{
+       return ipc_exchange(IPC_SDMMC_ACK, 0)->args[0];
+}
+
+int sd_get_state(void)
+{
+       return ipc_exchange(IPC_SDMMC_STATE, 0)->args[0];
+}
+
+int sd_protected(void)
+{
+//     return (ipc_exchange(IPC_SD_GETSTATE, 0)->args[0] & SDHC_WRITE_PROTECT) == SDHC_WRITE_PROTECT;
+       return 0;
+}
+
+int sd_select(void)
+{
+       return 1;
+//     return ipc_exchange(IPC_SD_SELECT, 0)->args[0];
+}
+
+int sd_read(u32 start_block, u32 blk_cnt, void *buffer)
+{
+       int retval;
+       sync_before_read(buffer, blk_cnt * 512);
+       retval = ipc_exchange(IPC_SDMMC_READ, 3, start_block, blk_cnt, virt_to_phys(buffer))->args[0];
+       return retval;
+}
+
+int sd_write(u32 start_block, u32 blk_cnt, const void *buffer)
+{
+       int retval;
+       sync_after_write(buffer, blk_cnt * 512);
+       retval = ipc_exchange(IPC_SDMMC_WRITE, 3, start_block, blk_cnt, virt_to_phys(buffer))->args[0];
+
+       return retval;
+}
+
+u32 sd_getsize(void)
+{
+       return ipc_exchange(IPC_SDMMC_SIZE, 0)->args[0];
+}
+
diff --git a/mini_ipc.h b/mini_ipc.h
new file mode 100644 (file)
index 0000000..bbeba44
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+       mini_ipc.h -- public PowerPC-side interface to mini.  Part of the
+       BootMii project.
+
+Copyright (C) 2009                     Andre Heider "dhewg" <dhewg@wiibrew.org>
+Copyright (C) 2009                     Haxx Enterprises <bushing@gmail.com>
+Copyright (C) 2009                     John Kelley <wiidev@kelley.ca>
+Copyright (C) 2008, 2009       Sven Peter <svenpeter@gmail.com>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#ifndef __MINI_IPC_H__
+#define __MINI_IPC_H__
+
+#define SDHC_ENOCARD    -0x1001
+#define SDHC_ESTRANGE   -0x1002
+#define SDHC_EOVERFLOW  -0x1003
+#define SDHC_ETIMEDOUT  -0x1004
+#define SDHC_EINVAL     -0x1005
+#define SDHC_EIO        -0x1006
+
+#define SDMMC_NO_CARD   1
+#define SDMMC_NEW_CARD  2
+#define SDMMC_INSERTED  3
+
+#define NAND_ECC_OK 0
+#define NAND_ECC_CORRECTED 1
+#define NAND_ECC_UNCORRECTABLE -1
+
+int sd_get_state(void);
+int sd_protected(void);
+int sd_mount(void);
+int sd_select(void);
+int sd_read(u32 start_block, u32 blk_cnt, void *buffer);
+int sd_write(u32 start_block, u32 blk_cnt, const void *buffer);
+u32 sd_getsize(void);
+
+int ipc_powerpc_boot(const void *addr, u32 len);
+
+#define TMD_BM_MARK(x) ((u16*)&(x->reserved[4]))
+// 'BM'
+#define TMD_BM_MAGIC 0x424d
+
+typedef struct {
+       u32 type;
+       u8 sig[256];
+       u8 fill[60];
+} __attribute__((packed)) sig_rsa2048;
+
+typedef struct {
+       u32 cid;
+       u16 index;
+       u16 type;
+       u64 size;
+       u8 hash[20];
+} __attribute__((packed)) tmd_content;
+
+typedef struct {
+       sig_rsa2048 signature;
+       char issuer[0x40];
+       u8 version;
+       u8 ca_crl_version;
+       u8 signer_crl_version;
+       u8 fill2;
+       u64 sys_version;
+       u64 title_id;
+       u32 title_type;
+       u16 group_id;
+       u16 zero;
+       u16 region;
+       u8 ratings[16];
+       u8 reserved[42];
+       u32 access_rights;
+       u16 title_version;
+       u16 num_contents;
+       u16 boot_index;
+       u16 fill3;
+       tmd_content boot_content;
+} __attribute__((packed)) tmd;
+
+u32 boot2_run(u32 hi, u32 lo);
+tmd *boot2_tmd(void);
+
+typedef struct
+{
+       u8 boot1_hash[20];
+       u8 common_key[16];
+       u32 ng_id;
+       union { // first two bytes of nand_hmac overlap last two bytes of ng_priv. no clue why
+               struct {
+                       u8 ng_priv[30];
+                       u8 _wtf1[18];
+               };
+               struct {
+                       u8 _wtf2[28];
+                       u8 nand_hmac[20];
+               };
+       };
+       u8 nand_key[16];
+       u8 rng_key[16];
+       u32 unk1;
+       u32 unk2; // 0x00000007
+} __attribute__((packed)) otp_t;
+
+typedef struct
+{
+       u8 boot2version;
+       u8 unknown1;
+       u8 unknown2;
+       u8 pad;
+       u32 update_tag;
+       u16 checksum;
+} __attribute__((packed)) eep_ctr_t;
+
+typedef struct
+{
+       union {
+               struct {
+                       u32 ms_key_id;
+                       u32 ca_key_id;
+                       u32 ng_key_id;
+                       u8 ng_sig[60];
+                       eep_ctr_t counters[2];
+                       u8 fill[0x18];
+                       u8 korean_key[16];
+               };
+               u8 data[256];
+       };
+} __attribute__((packed)) seeprom_t;
+
+void getotp(otp_t *otp);
+void getseeprom(seeprom_t *seeprom);
+void getMiniGitVer(char *buf, u16 len);
+       
+void aes_reset(void);
+void aes_set_key(u8 *key);
+void aes_set_iv(u8 *iv);
+void aes_decrypt(u8 *src, u8 *dst, u32 blocks, u8 keep_iv);
+
+void nand_reset(void);
+u32 nand_getid(void);
+u8 nand_status(void);
+int nand_read(u32 pageno, void *data, void *ecc);
+void nand_write(u32 pageno, void *data, void *ecc);
+void nand_erase(u32 pageno);
+
+#endif
diff --git a/nandfs.c b/nandfs.c
new file mode 100644 (file)
index 0000000..c9404b9
--- /dev/null
+++ b/nandfs.c
@@ -0,0 +1,261 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn IOS.
+       Requires mini.
+
+       NAND filesystem support
+
+Copyright (C) 2008, 2009       Sven Peter <svenpeter@gmail.com>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+#include "ipc.h"
+#include "mini_ipc.h"
+#include "nandfs.h"
+#include "string.h"
+
+#define        PAGE_SIZE       2048
+#define NANDFS_FREE    0xFFFE
+
+static otp_t otp;
+
+struct _nandfs_file_node {
+       char name[NANDFS_NAME_LEN];
+       u8 attr;
+       u8 wtf;
+       union {
+               u16 first_child;
+               u16 first_cluster;
+       };
+       u16 sibling;
+       u32 size;
+       u32 uid;
+       u16 gid;
+       u32 dummy;
+} __attribute__((packed));
+
+struct _nandfs_sffs {
+       u8 magic[4];
+       u32 version;
+       u32 dummy;
+
+       u16 cluster_table[32768];
+       struct _nandfs_file_node files[6143];
+} __attribute__((packed));
+
+
+union _sffs_t {
+       u8 buffer[16*8*2048];
+       struct _nandfs_sffs sffs;
+};
+
+static union _sffs_t sffs __attribute__((aligned(32)));
+
+/*static u8 _sffs_buffer[16*8*2048] __attribute__((aligned(32)));
+static struct _nandfs_sffs *sffs = (struct _nandfs_sffs *)&_sffs_buffer;*/
+static u8 buffer[8*2048] __attribute__((aligned(32)));
+static s32 initialized = 0;
+
+void nand_read_cluster(u32 pageno, u8 *buffer)
+{
+       int i;
+       for (i = 0; i < 8; i++)
+               nand_read(pageno + i, buffer + (i * PAGE_SIZE), NULL);
+}
+
+void nand_read_decrypted_cluster(u32 pageno, u8 *buffer)
+{
+       u8 iv[16] = {0,};
+       nand_read_cluster(pageno, buffer);
+
+       aes_reset();
+       aes_set_iv(iv);
+       aes_set_key(otp.nand_key);
+       aes_decrypt(buffer, buffer, 0x400, 0);
+}
+
+s32 nandfs_initialize(void)
+{
+       u32 i;
+       u32 supercluster = 0;
+       u32 supercluster_version = 0;
+
+       getotp(&otp);
+
+       nand_reset();
+
+       for(i = 0x7F00; i < 0x7fff; i++) {
+               nand_read(i*8, sffs.buffer, NULL);
+               if(memcmp(sffs.sffs.magic, "SFFS", 4) != 0)
+                       continue;
+               if(supercluster == 0 ||
+                  sffs.sffs.version > supercluster_version) {
+                       supercluster = i*8;
+                       supercluster_version = sffs.sffs.version;
+               }
+       }
+
+       if(supercluster == 0) {
+               printf("no supercluster found. "
+                            " your nand filesystem is seriously broken...\n");
+               return -1;
+       }
+
+       for(i = 0; i < 16; i++) {
+               printf("reading...\n");
+               nand_read_cluster(supercluster + i*8,
+                               (sffs.buffer) + (i * PAGE_SIZE * 8));
+       }
+
+       initialized = 1;
+       return 0;
+}
+
+u32 nandfs_get_usage(void) {
+       u32 i;
+       int used_clusters = 0;
+       for (i=0; i < sizeof(sffs.sffs.cluster_table) / sizeof(u16); i++)
+               if(sffs.sffs.cluster_table[i] != NANDFS_FREE) used_clusters++;
+               
+       printf("Used clusters: %d\n", used_clusters);
+       return 1000 * used_clusters / (sizeof(sffs.sffs.cluster_table)/sizeof(u16));
+}
+
+s32 nandfs_open(struct nandfs_fp *fp, const char *path)
+{
+       char *ptr, *ptr2;
+       u32 len;
+       struct _nandfs_file_node *cur = sffs.sffs.files;
+
+       if (initialized != 1)
+               return -1;
+
+       memset(fp, 0, sizeof(*fp));
+
+       if(strcmp(cur->name, "/") != 0) {
+               printf("your nandfs is corrupted. fixit!\n");
+               return -1;
+       }
+
+       cur = &sffs.sffs.files[cur->first_child];
+
+       ptr = (char *)path;
+       do {
+               ptr++;
+               ptr2 = strchr(ptr, '/');
+               if (ptr2 == NULL)
+                       len = strlen(ptr);
+               else {
+                       ptr2++;
+                       len = ptr2 - ptr - 1;
+               }
+               if (len > 12)
+               {
+                       printf("invalid length: %s %s %s [%d]\n",
+                                       ptr, ptr2, path, len);
+                       return -1;
+               }
+
+               for (;;) {
+                       if(ptr2 != NULL && strncmp(cur->name, ptr, len) == 0
+                            && strnlen(cur->name, 12) == len
+                            && (cur->attr&3) == 2
+                            && (s16)(cur->first_child&0xffff) != (s16)0xffff) {
+                               cur = &sffs.sffs.files[cur->first_child];
+                               ptr = ptr2-1;
+                               break;
+                       } else if(ptr2 == NULL &&
+                                  strncmp(cur->name, ptr, len) == 0 &&
+                                  strnlen(cur->name, 12) == len &&
+                                  (cur->attr&3) == 1) {
+                               break;
+                       } else if((cur->sibling&0xffff) != 0xffff) {
+                               cur = &sffs.sffs.files[cur->sibling];
+                       } else {
+                               return -1;
+                       }
+               }
+               
+       } while(ptr2 != NULL);
+
+       fp->first_cluster = cur->first_cluster;
+       fp->cur_cluster = fp->first_cluster;
+       fp->offset = 0;
+       fp->size = cur->size;
+       return 0;
+}
+
+s32 nandfs_read(void *ptr, u32 size, u32 nmemb, struct nandfs_fp *fp)
+{
+       u32 total = size*nmemb;
+       u32 copy_offset, copy_len;
+
+       if (initialized != 1)
+               return -1;
+
+       if (fp->offset + total > fp->size)
+               total = fp->size - fp->offset;
+
+       if (total == 0)
+               return 0;
+
+       while(total > 0) {
+               nand_read_decrypted_cluster(fp->cur_cluster*8, buffer);
+               copy_offset = fp->offset % (PAGE_SIZE * 8);
+               copy_len = (PAGE_SIZE * 8) - copy_offset;
+               if(copy_len > total)
+                       copy_len = total;
+               memcpy(ptr, buffer + copy_offset, copy_len);
+               total -= copy_len;
+               fp->offset += copy_len;
+
+               if ((copy_offset + copy_len) >= (PAGE_SIZE * 8))
+                       fp->cur_cluster = sffs.sffs.cluster_table[fp->cur_cluster];
+       }
+
+       return size*nmemb;
+}
+
+s32 nandfs_seek(struct nandfs_fp *fp, s32 offset, u32 whence)
+{
+       if (initialized != 1)
+               return -1;
+
+       switch (whence) {
+       case NANDFS_SEEK_SET:
+               if (offset < 0)
+                       return -1;
+               if ((u32)offset > fp->size)
+                       return -1;
+
+               fp->offset = offset;
+               break;
+
+       case NANDFS_SEEK_CUR:
+               if ((fp->offset + offset) > fp->size ||
+                   (s32)(fp->offset + offset) < 0)
+                       return -1;
+               fp->offset += offset;
+               break;
+
+       case NANDFS_SEEK_END:
+       default:
+               if ((fp->size + offset) > fp->size ||
+                   (s32)(fp->size + offset) < 0)
+                       return -1;
+               fp->offset = fp->size + offset;
+               break;
+       }
+
+       int skip = fp->offset;
+       fp->cur_cluster = fp->first_cluster;
+       while (skip > (2048*8)) {
+               fp->cur_cluster = sffs.sffs.cluster_table[fp->cur_cluster];
+               skip -= 2048*8;
+       }
+
+       return 0;
+}
+
diff --git a/nandfs.h b/nandfs.h
new file mode 100644 (file)
index 0000000..1bf5527
--- /dev/null
+++ b/nandfs.h
@@ -0,0 +1,35 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2008, 2009       Sven Peter <svenpeter@gmail.com>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#ifndef __NANDFS_H__
+#define __NANDFS_H__
+
+#define        NANDFS_NAME_LEN 12
+
+#define        NANDFS_SEEK_SET 0
+#define        NANDFS_SEEK_CUR 1
+#define        NANDFS_SEEK_END 2
+
+struct nandfs_fp {
+       s16 first_cluster;
+       s32 cur_cluster;
+       u32 size;
+       u32 offset;
+};
+
+s32 nandfs_initialize(void);
+u32 nandfs_get_usage(void);
+
+s32 nandfs_open(struct nandfs_fp *fp, const char *path);
+s32 nandfs_read(void *ptr, u32 size, u32 nmemb, struct nandfs_fp *fp);
+s32 nandfs_seek(struct nandfs_fp *fp, s32 offset, u32 whence);
+
+#endif
+
diff --git a/ppcboot.elf b/ppcboot.elf
new file mode 100755 (executable)
index 0000000..060a8e8
Binary files /dev/null and b/ppcboot.elf differ
diff --git a/ppcboot.elf.map b/ppcboot.elf.map
new file mode 100644 (file)
index 0000000..4cb0abe
--- /dev/null
@@ -0,0 +1,578 @@
+
+Allocating common symbols
+Common symbol       size              file
+
+otp                 0x80              main.o
+font_yuv            0x3fc             console.o
+seeprom             0x100             main.o
+req_recv            0x20              ipc.o
+
+Discarded input sections
+
+ .data          0x00000000        0x0 crt0.o
+ .bss           0x00000000        0x0 crt0.o
+ .text          0x00000000        0x0 realmode.o
+ .data          0x00000000        0x0 realmode.o
+ .bss           0x00000000        0x0 realmode.o
+ .text          0x00000000        0x0 main.o
+ .data          0x00000000        0x0 main.o
+ .bss           0x00000000        0x0 main.o
+ .text          0x00000000        0x0 string.o
+ .data          0x00000000        0x0 string.o
+ .bss           0x00000000        0x0 string.o
+ .text.strnlen  0x00000000        0x0 string.o
+ .text.strcmp   0x00000000        0x0 string.o
+ .text.strncmp  0x00000000        0x0 string.o
+ .text.strlcpy  0x00000000        0x0 string.o
+ .text.strlcat  0x00000000        0x0 string.o
+ .text.strchr   0x00000000        0x0 string.o
+ .text.strspn   0x00000000        0x0 string.o
+ .text.strcspn  0x00000000        0x0 string.o
+ .text          0x00000000        0x0 sync.o
+ .data          0x00000000        0x0 sync.o
+ .bss           0x00000000        0x0 sync.o
+ .text          0x00000000        0x0 time.o
+ .data          0x00000000        0x0 time.o
+ .bss           0x00000000        0x0 time.o
+ .text.mftb     0x00000000        0x0 time.o
+ .text          0x00000000        0x0 printf.o
+ .data          0x00000000        0x0 printf.o
+ .bss           0x00000000        0x0 printf.o
+ .text.sprintf  0x00000000        0x0 printf.o
+ .text          0x00000000        0x0 input.o
+ .data          0x00000000        0x0 input.o
+ .text.pad_read
+                0x00000000        0x0 input.o
+ .text.gpio_read
+                0x00000000        0x0 input.o
+ .text.input_read
+                0x00000000        0x0 input.o
+ .text.input_wait
+                0x00000000        0x0 input.o
+ .text          0x00000000        0x0 exception.o
+ .data          0x00000000        0x0 exception.o
+ .bss           0x00000000        0x0 exception.o
+ .data          0x00000000        0x0 exception_2200.o
+ .bss           0x00000000        0x0 exception_2200.o
+ .text          0x00000000        0x0 malloc.o
+ .data          0x00000000        0x0 malloc.o
+ .text.sbrk     0x00000000        0x0 malloc.o
+ .text.malloc_footprint
+                0x00000000        0x0 malloc.o
+ .text.malloc_max_footprint
+                0x00000000        0x0 malloc.o
+ .text.malloc_usable_size
+                0x00000000        0x0 malloc.o
+ .text.mallopt  0x00000000        0x0 malloc.o
+ .text.T.235    0x00000000        0x0 malloc.o
+ .text.T.240    0x00000000        0x0 malloc.o
+ .text.T.242    0x00000000        0x0 malloc.o
+ .text.mallinfo
+                0x00000000        0x0 malloc.o
+ .text.malloc_stats
+                0x00000000        0x0 malloc.o
+ .text.malloc_trim
+                0x00000000        0x0 malloc.o
+ .text.calloc   0x00000000        0x0 malloc.o
+ .text.T.231    0x00000000        0x0 malloc.o
+ .text.independent_calloc
+                0x00000000        0x0 malloc.o
+ .text.independent_comalloc
+                0x00000000        0x0 malloc.o
+ .text.realloc  0x00000000        0x0 malloc.o
+ .text.pvalloc  0x00000000        0x0 malloc.o
+ .text.valloc   0x00000000        0x0 malloc.o
+ .text          0x00000000        0x0 gecko.o
+ .data          0x00000000        0x0 gecko.o
+ .text          0x00000000        0x0 video_low.o
+ .data          0x00000000        0x0 video_low.o
+ .text.VIDEO_WaitVSync
+                0x00000000        0x0 video_low.o
+ .text.VIDEO_BlackOut
+                0x00000000        0x0 video_low.o
+ .text.VIDEO_Shutdown
+                0x00000000        0x0 video_low.o
+ .text          0x00000000        0x0 ipc.o
+ .data          0x00000000        0x0 ipc.o
+ .text.ipc_process_unhandled
+                0x00000000        0x0 ipc.o
+ .text.ipc_flush
+                0x00000000        0x0 ipc.o
+ .text.ipc_shutdown
+                0x00000000        0x0 ipc.o
+ .text.ipc_post
+                0x00000000        0x0 ipc.o
+ .text          0x00000000        0x0 mini_ipc.o
+ .data          0x00000000        0x0 mini_ipc.o
+ .bss           0x00000000        0x0 mini_ipc.o
+ .text.sd_protected
+                0x00000000        0x0 mini_ipc.o
+ .text.sd_select
+                0x00000000        0x0 mini_ipc.o
+ .text.sd_getsize
+                0x00000000        0x0 mini_ipc.o
+ .text.sd_get_state
+                0x00000000        0x0 mini_ipc.o
+ .text.sd_mount
+                0x00000000        0x0 mini_ipc.o
+ .text.nand_erase
+                0x00000000        0x0 mini_ipc.o
+ .text.nand_reset
+                0x00000000        0x0 mini_ipc.o
+ .text.aes_set_iv
+                0x00000000        0x0 mini_ipc.o
+ .text.aes_set_key
+                0x00000000        0x0 mini_ipc.o
+ .text.aes_reset
+                0x00000000        0x0 mini_ipc.o
+ .text.boot2_run
+                0x00000000        0x0 mini_ipc.o
+ .text.sd_write
+                0x00000000        0x0 mini_ipc.o
+ .text.nand_write
+                0x00000000        0x0 mini_ipc.o
+ .text.ipc_powerpc_boot
+                0x00000000        0x0 mini_ipc.o
+ .text.sd_read  0x00000000        0x0 mini_ipc.o
+ .text.nand_read
+                0x00000000        0x0 mini_ipc.o
+ .text.nand_status
+                0x00000000        0x0 mini_ipc.o
+ .text.nand_getid
+                0x00000000        0x0 mini_ipc.o
+ .text.aes_decrypt
+                0x00000000        0x0 mini_ipc.o
+ .text.boot2_tmd
+                0x00000000        0x0 mini_ipc.o
+ .text.getMiniGitVer
+                0x00000000        0x0 mini_ipc.o
+ .text          0x00000000        0x0 nandfs.o
+ .data          0x00000000        0x0 nandfs.o
+ .bss           0x00000000        0x0 nandfs.o
+ .text.nandfs_seek
+                0x00000000        0x0 nandfs.o
+ .text.nandfs_get_usage
+                0x00000000        0x0 nandfs.o
+ .text.nandfs_open
+                0x00000000        0x0 nandfs.o
+ .text.nand_read_cluster
+                0x00000000        0x0 nandfs.o
+ .text.nandfs_initialize
+                0x00000000        0x0 nandfs.o
+ .text.nand_read_decrypted_cluster
+                0x00000000        0x0 nandfs.o
+ .text.nandfs_read
+                0x00000000        0x0 nandfs.o
+ .rodata.str1.1
+                0x00000000        0x0 nandfs.o
+ .text          0x00000000        0x0 ff.o
+ .data          0x00000000        0x0 ff.o
+ .bss           0x00000000        0x0 ff.o
+ .text.clust2sect
+                0x00000000        0x0 ff.o
+ .text.get_fileinfo
+                0x00000000        0x0 ff.o
+ .text.f_mount  0x00000000        0x0 ff.o
+ .text.f_mkfs   0x00000000        0x0 ff.o
+ .text.validate
+                0x00000000        0x0 ff.o
+ .text.move_window
+                0x00000000        0x0 ff.o
+ .text.get_cluster
+                0x00000000        0x0 ff.o
+ .text.dir_seek
+                0x00000000        0x0 ff.o
+ .text.f_check_contig
+                0x00000000        0x0 ff.o
+ .text.put_cluster
+                0x00000000        0x0 ff.o
+ .text.create_chain
+                0x00000000        0x0 ff.o
+ .text.remove_chain
+                0x00000000        0x0 ff.o
+ .text.f_truncate
+                0x00000000        0x0 ff.o
+ .text.dir_next
+                0x00000000        0x0 ff.o
+ .text.dir_register
+                0x00000000        0x0 ff.o
+ .text.dir_remove
+                0x00000000        0x0 ff.o
+ .text.sync     0x00000000        0x0 ff.o
+ .text.dir_read
+                0x00000000        0x0 ff.o
+ .text.f_readdir
+                0x00000000        0x0 ff.o
+ .text.f_sync   0x00000000        0x0 ff.o
+ .text.f_close  0x00000000        0x0 ff.o
+ .text.f_lseek  0x00000000        0x0 ff.o
+ .text.f_write  0x00000000        0x0 ff.o
+ .text.f_read   0x00000000        0x0 ff.o
+ .text.check_fs
+                0x00000000        0x0 ff.o
+ .text.auto_mount
+                0x00000000        0x0 ff.o
+ .text.f_getfree
+                0x00000000        0x0 ff.o
+ .text.follow_path
+                0x00000000        0x0 ff.o
+ .text.f_rename
+                0x00000000        0x0 ff.o
+ .text.f_utime  0x00000000        0x0 ff.o
+ .text.f_chmod  0x00000000        0x0 ff.o
+ .text.f_mkdir  0x00000000        0x0 ff.o
+ .text.f_unlink
+                0x00000000        0x0 ff.o
+ .text.f_stat   0x00000000        0x0 ff.o
+ .text.f_opendir
+                0x00000000        0x0 ff.o
+ .text.f_open   0x00000000        0x0 ff.o
+ .rodata        0x00000000        0x0 ff.o
+ .rodata.str1.1
+                0x00000000        0x0 ff.o
+ .text          0x00000000        0x0 diskio.o
+ .data          0x00000000        0x0 diskio.o
+ .bss           0x00000000        0x0 diskio.o
+ .text.get_fattime
+                0x00000000        0x0 diskio.o
+ .text.disk_ioctl
+                0x00000000        0x0 diskio.o
+ .text.disk_write
+                0x00000000        0x0 diskio.o
+ .text.disk_read
+                0x00000000        0x0 diskio.o
+ .text.disk_status
+                0x00000000        0x0 diskio.o
+ .text.disk_initialize
+                0x00000000        0x0 diskio.o
+ .rodata        0x00000000        0x0 diskio.o
+ .text          0x00000000        0x0 fat.o
+ .data          0x00000000        0x0 fat.o
+ .bss           0x00000000        0x0 fat.o
+ .text.fat_clust2sect
+                0x00000000        0x0 fat.o
+ .text.fat_umount
+                0x00000000        0x0 fat.o
+ .text.fat_mount
+                0x00000000        0x0 fat.o
+ .text          0x00000000        0x0 font.o
+ .bss           0x00000000        0x0 font.o
+ .text          0x00000000        0x0 console.o
+ .data          0x00000000        0x0 console.o
+ .text.pal_idx  0x00000000        0x0 console.o
+ .text.fill_rect
+                0x00000000        0x0 console.o
+ .text.scroll   0x00000000        0x0 console.o
+ .text.print_str
+                0x00000000        0x0 console.o
+ .text.gfx_printf
+                0x00000000        0x0 console.o
+ .rodata.str1.1
+                0x00000000        0x0 console.o
+
+Memory Configuration
+
+Name             Origin             Length             Attributes
+*default*        0x00000000         0xffffffff
+
+Linker script and memory map
+
+                0x00003400                . = 0x3400
+
+.realmode       0x00003400      0x140
+ *(.realmode)
+ .realmode      0x00003400      0x140 realmode.o
+                0x00003400                _realmode_vector
+                0x80004000                . = 0x80004000
+
+.start          0x80004000      0x370 load address 0x00004000
+ crt0.o(*)
+ .text          0x80004000      0x370 crt0.o
+                0x80004000                _start
+ .rela.text     0x00000000        0x0 crt0.o
+ .rela.text.testOTP
+                0x00000000        0x0 crt0.o
+ .rela.text.exception_init
+                0x00000000        0x0 crt0.o
+ .rela.text.sbrk
+                0x00000000        0x0 crt0.o
+ .rela.text.T.248
+                0x00000000        0x0 crt0.o
+ .rela.text.malloc
+                0x00000000        0x0 crt0.o
+ .rela.text.ipc_receive
+                0x00000000        0x0 crt0.o
+ .rela.text.print_str
+                0x00000000        0x0 crt0.o
+ .rela.text.font_to_yuv
+                0x00000000        0x0 crt0.o
+ .rela.text.print_str_noscroll
+                0x00000000        0x0 crt0.o
+
+.text           0x80004370     0x6450 load address 0x00004370
+ *(.text)
+ .text          0x80004370       0x5c exception_2200.o
+                0x80004370                exception_2200_start
+                0x800043cc                exception_2200_end
+ *(.text.*)
+ .text.hexdump  0x800043cc      0x170 main.o
+                0x800043cc                hexdump
+ .text.testOTP  0x8000453c       0xb8 main.o
+                0x8000453c                testOTP
+ .text.main     0x800045f4      0x144 main.o
+                0x800045f4                main
+ .text.strlen   0x80004738       0x20 string.o
+                0x80004738                strlen
+ .text.memset   0x80004758       0x2c string.o
+                0x80004758                memset
+ .text.memcpy   0x80004784       0x30 string.o
+                0x80004784                memcpy
+ .text.memcmp   0x800047b4       0x44 string.o
+                0x800047b4                memcmp
+ .text.sync_before_read
+                0x800047f8       0x30 sync.o
+                0x800047f8                sync_before_read
+ .text.sync_after_write
+                0x80004828       0x30 sync.o
+                0x80004828                sync_after_write
+ .text.sync_before_exec
+                0x80004858       0x38 sync.o
+                0x80004858                sync_before_exec
+ .text.udelay   0x80004890       0x6c time.o
+                0x80004890                udelay
+ .text.fmtstr   0x800048fc      0x130 printf.o
+ .text.fmtint   0x80004a2c      0x378 printf.o
+ .text.vsnprintf
+                0x80004da4      0x928 printf.o
+                0x80004da4                vsnprintf
+ .text.vsprintf
+                0x800056cc       0x18 printf.o
+                0x800056cc                vsprintf
+ .text.input_init
+                0x800056e4      0x1d4 input.o
+                0x800056e4                input_init
+ .text.exception_init
+                0x800058b8       0xac exception.o
+                0x800058b8                exception_init
+ .text.exception_handler
+                0x80005964      0x108 exception.o
+                0x80005964                exception_handler
+ .text.init_mparams
+                0x80005a6c       0x60 malloc.o
+ .text.T.237    0x80005acc      0x1cc malloc.o
+ .text.T.238    0x80005c98       0x80 malloc.o
+ .text.T.233    0x80005d18       0xcc malloc.o
+ .text.T.236    0x80005de4       0xec malloc.o
+ .text.T.232    0x80005ed0      0x228 malloc.o
+ .text.T.248    0x800060f8      0x230 malloc.o
+ .text.free     0x80006328      0xab0 malloc.o
+                0x80006328                free
+ .text.malloc   0x80006dd8     0x2108 malloc.o
+                0x80006dd8                malloc
+ .text.memalign
+                0x80008ee0      0x24c malloc.o
+                0x80008ee0                memalign
+ .text.gecko_init
+                0x8000912c      0x1cc gecko.o
+                0x8000912c                gecko_init
+ .text.printf   0x800092f8      0x148 gecko.o
+                0x800092f8                printf
+ .text.VIDEO_SetFrameBuffer
+                0x80009440       0x4c video_low.o
+                0x80009440                VIDEO_SetFrameBuffer
+ .text.VIDEO_Init
+                0x8000948c      0x118 video_low.o
+                0x8000948c                VIDEO_Init
+ .text.T.15     0x800095a4      0x3fc video_low.o
+ .text.T.13     0x800099a0       0x4c video_low.o
+ .text.__VIWriteI2CRegister8
+                0x800099ec       0x38 video_low.o
+ .text.__VIWriteI2CRegister16
+                0x80009a24       0x44 video_low.o
+ .text.__VIWriteI2CRegisterBuf
+                0x80009a68       0x50 video_low.o
+ .text.VISetupEncoder
+                0x80009ab8      0x168 video_low.o
+                0x80009ab8                VISetupEncoder
+ .text.ipc_receive
+                0x80009c20      0x104 ipc.o
+                0x80009c20                ipc_receive
+ .text.ipc_receive_tagged
+                0x80009d24       0xa4 ipc.o
+                0x80009d24                ipc_receive_tagged
+ .text.ipc_vpost
+                0x80009dc8      0x23c ipc.o
+                0x80009dc8                ipc_vpost
+ .text.ipc_exchange
+                0x8000a004       0xc4 ipc.o
+                0x8000a004                ipc_exchange
+ .text.ipc_initialize
+                0x8000a0c8      0x138 ipc.o
+                0x8000a0c8                ipc_initialize
+ .text.getseeprom
+                0x8000a200       0x48 mini_ipc.o
+                0x8000a200                getseeprom
+ .text.getotp   0x8000a248       0x44 mini_ipc.o
+                0x8000a248                getotp
+ .text.get_xfb  0x8000a28c        0xc console.o
+                0x8000a28c                get_xfb
+ .text.make_yuv
+                0x8000a298      0x124 console.o
+                0x8000a298                make_yuv
+ .text.gfx_draw_rect
+                0x8000a3bc       0xe0 console.o
+                0x8000a3bc                gfx_draw_rect
+ .text.font_to_yuv
+                0x8000a49c      0x12c console.o
+                0x8000a49c                font_to_yuv
+ .text.init_fb  0x8000a5c8      0x10c console.o
+                0x8000a5c8                init_fb
+ .text.print_str_noscroll
+                0x8000a6d4       0xe0 console.o
+                0x8000a6d4                print_str_noscroll
+                0x8000a7c0                . = ALIGN (0x20)
+ *fill*         0x8000a7b4        0xc 00
+
+.data           0x8000a7c0     0x1000 load address 0x0000a7c0
+ *(.data)
+ .data          0x8000a7c0     0x1000 font.o
+                0x8000a7c0                console_font_8x16
+ *(.data.*)
+                0x8000b7c0                . = ALIGN (0x20)
+
+.sdata          0x8000b7c0        0x0
+ *(.sdata)
+ *(.sdata.*)
+                0x8000b7c0                . = ALIGN (0x20)
+
+.rodata         0x8000b7c0      0x680 load address 0x0000b7c0
+ *(.rodata)
+ .rodata        0x8000b7c0       0xc0 printf.o
+ .rodata        0x8000b880      0x22c video_low.o
+ *(.rodata.*)
+ .rodata.str1.1
+                0x8000baac       0xec main.o
+                                 0xf0 (size before relaxing)
+ .rodata.str1.1
+                0x8000bb98       0x2f printf.o
+ .rodata.str1.1
+                0x8000bbc7       0x85 exception.o
+ .rodata.str1.1
+                0x8000bc4c       0x75 malloc.o
+ .rodata.str1.1
+                0x8000bcc1      0x178 ipc.o
+                0x8000be40                . = ALIGN (0x20)
+ *fill*         0x8000be39        0x7 00
+
+.stack          0x8000be40     0x8000 load address 0x0000be40
+                0x8000be40                _stack_top = .
+                0x80013e40                . = (. + 0x8000)
+ *fill*         0x8000be40     0x8000 00
+                0x80013e40                _stack_bot = .
+                0x80013e40                . = ALIGN (0x20)
+                0x80013e40                __bss_start = .
+
+.bss            0x80013e40      0x7c0 load address 0x00013e40
+ *(.bss)
+ .bss           0x80013e40        0xc input.o
+ .bss           0x80013e4c      0x1e4 malloc.o
+ .bss           0x80014030        0x4 gecko.o
+ .bss           0x80014034        0x4 video_low.o
+ .bss           0x80014038       0x24 ipc.o
+ .bss           0x8001405c        0x8 console.o
+ COMMON         0x80014064      0x180 main.o
+                0x80014064                otp
+                0x800140e4                seeprom
+ COMMON         0x800141e4       0x20 ipc.o
+                0x800141e4                req_recv
+ COMMON         0x80014204      0x3fc console.o
+                0x80014204                font_yuv
+
+.sbss
+ *(.sbss)
+                0x80014600                __bss_end = .
+                0x80020000                . = ALIGN (0x10000)
+                0x80020000                _sbrk_start = .
+                0x816ffff0                _sbrk_end = 0x816ffff0
+LOAD realmode.o
+LOAD crt0.o
+LOAD main.o
+LOAD string.o
+LOAD sync.o
+LOAD time.o
+LOAD printf.o
+LOAD input.o
+LOAD exception.o
+LOAD exception_2200.o
+LOAD malloc.o
+LOAD gecko.o
+LOAD video_low.o
+LOAD ipc.o
+LOAD mini_ipc.o
+LOAD nandfs.o
+LOAD ff.o
+LOAD diskio.o
+LOAD fat.o
+LOAD font.o
+LOAD console.o
+LOAD /opt/wiidev/lib/gcc/powerpc-elf/4.4.0/libgcc.a
+OUTPUT(ppcboot.elf elf32-powerpc)
+
+.comment        0x00000000      0x144
+ .comment       0x00000000       0x12 main.o
+ .comment       0x00000012       0x12 string.o
+ .comment       0x00000024       0x12 sync.o
+ .comment       0x00000036       0x12 time.o
+ .comment       0x00000048       0x12 printf.o
+ .comment       0x0000005a       0x12 input.o
+ .comment       0x0000006c       0x12 exception.o
+ .comment       0x0000007e       0x12 malloc.o
+ .comment       0x00000090       0x12 gecko.o
+ .comment       0x000000a2       0x12 video_low.o
+ .comment       0x000000b4       0x12 ipc.o
+ .comment       0x000000c6       0x12 mini_ipc.o
+ .comment       0x000000d8       0x12 nandfs.o
+ .comment       0x000000ea       0x12 ff.o
+ .comment       0x000000fc       0x12 diskio.o
+ .comment       0x0000010e       0x12 fat.o
+ .comment       0x00000120       0x12 font.o
+ .comment       0x00000132       0x12 console.o
+
+.gnu.attributes
+                0x00000000       0x12
+ .gnu.attributes
+                0x00000000       0x14 main.o
+ .gnu.attributes
+                0x00000014       0x14 string.o
+ .gnu.attributes
+                0x00000028       0x14 sync.o
+ .gnu.attributes
+                0x0000003c       0x14 time.o
+ .gnu.attributes
+                0x00000050       0x14 printf.o
+ .gnu.attributes
+                0x00000064       0x14 input.o
+ .gnu.attributes
+                0x00000078       0x14 exception.o
+ .gnu.attributes
+                0x0000008c       0x14 malloc.o
+ .gnu.attributes
+                0x000000a0       0x14 gecko.o
+ .gnu.attributes
+                0x000000b4       0x14 video_low.o
+ .gnu.attributes
+                0x000000c8       0x14 ipc.o
+ .gnu.attributes
+                0x000000dc       0x14 mini_ipc.o
+ .gnu.attributes
+                0x000000f0       0x14 nandfs.o
+ .gnu.attributes
+                0x00000104       0x14 ff.o
+ .gnu.attributes
+                0x00000118       0x14 diskio.o
+ .gnu.attributes
+                0x0000012c       0x14 fat.o
+ .gnu.attributes
+                0x00000140       0x14 font.o
+ .gnu.attributes
+                0x00000154       0x14 console.o
diff --git a/printf.c b/printf.c
new file mode 100644 (file)
index 0000000..77bf0c5
--- /dev/null
+++ b/printf.c
@@ -0,0 +1,798 @@
+/*
+ * Copyright (c) 1995 Patrick Powell.
+ *
+ * This code is based on code written by Patrick Powell <papowell@astart.com>.
+ * It may be used for any purpose as long as this notice remains intact on all
+ * source code distributions.
+ */
+
+/*
+ * Copyright (c) 2008 Holger Weiss.
+ *
+ * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
+ * My changes to the code may freely be used, modified and/or redistributed for
+ * any purpose.  It would be nice if additions and fixes to this file (including
+ * trivial code cleanups) would be sent back in order to let me include them in
+ * the version available at <http://www.jhweiss.de/software/snprintf.html>.
+ * However, this is not a requirement for using or redistributing (possibly
+ * modified) versions of this file, nor is leaving this notice intact mandatory.
+ */
+
+/*
+ * History
+ *
+ * 2009-03-05 Hector Martin <hector@marcansoft.com>
+ *
+ *     Hacked up and removed a lot of stuff including floating-point support,
+ *     a bunch of ifs and defines, locales, and tests
+ *
+ * 2008-01-20 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.1:
+ *
+ *     Fixed the detection of infinite floating point values on IRIX (and
+ *     possibly other systems) and applied another few minor cleanups.
+ *
+ * 2008-01-06 Holger Weiss <holger@jhweiss.de> for C99-snprintf 1.0:
+ *
+ *     Added a lot of new features, fixed many bugs, and incorporated various
+ *     improvements done by Andrew Tridgell <tridge@samba.org>, Russ Allbery
+ *     <rra@stanford.edu>, Hrvoje Niksic <hniksic@xemacs.org>, Damien Miller
+ *     <djm@mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
+ *     projects.  The additions include: support the "e", "E", "g", "G", and
+ *     "F" conversion specifiers (and use conversion style "f" or "F" for the
+ *     still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
+ *     "t", and "z" length modifiers; support the "#" flag and the (non-C99)
+ *     "'" flag; use localeconv(3) (if available) to get both the current
+ *     locale's decimal point character and the separator between groups of
+ *     digits; fix the handling of various corner cases of field width and
+ *     precision specifications; fix various floating point conversion bugs;
+ *     handle infinite and NaN floating point values; don't attempt to write to
+ *     the output buffer (which may be NULL) if a size of zero was specified;
+ *     check for integer overflow of the field width, precision, and return
+ *     values and during the floating point conversion; use the OUTCHAR() macro
+ *     instead of a function for better performance; provide asprintf(3) and
+ *     vasprintf(3) functions; add new test cases.  The replacement functions
+ *     have been renamed to use an "rpl_" prefix, the function calls in the
+ *     main project (and in this file) must be redefined accordingly for each
+ *     replacement function which is needed (by using Autoconf or other means).
+ *     Various other minor improvements have been applied and the coding style
+ *     was cleaned up for consistency.
+ *
+ * 2007-07-23 Holger Weiss <holger@jhweiss.de> for Mutt 1.5.13:
+ *
+ *     C99 compliant snprintf(3) and vsnprintf(3) functions return the number
+ *     of characters that would have been written to a sufficiently sized
+ *     buffer (excluding the '\0').  The original code simply returned the
+ *     length of the resulting output string, so that's been fixed.
+ *
+ * 1998-03-05 Michael Elkins <me@mutt.org> for Mutt 0.90.8:
+ *
+ *     The original code assumed that both snprintf(3) and vsnprintf(3) were
+ *     missing.  Some systems only have snprintf(3) but not vsnprintf(3), so
+ *     the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * 1998-01-27 Thomas Roessler <roessler@does-not-exist.org> for Mutt 0.89i:
+ *
+ *     The PGP code was using unsigned hexadecimal formats.  Unfortunately,
+ *     unsigned formats simply didn't work.
+ *
+ * 1997-10-22 Brandon Long <blong@fiction.net> for Mutt 0.87.1:
+ *
+ *     Ok, added some minimal floating point support, which means this probably
+ *     requires libm on most operating systems.  Don't yet support the exponent
+ *     (e,E) and sigfig (g,G).  Also, fmtint() was pretty badly broken, it just
+ *     wasn't being exercised in ways which showed it, so that's been fixed.
+ *     Also, formatted the code to Mutt conventions, and removed dead code left
+ *     over from the original.  Also, there is now a builtin-test, run with:
+ *     gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
+ *
+ * 2996-09-15 Brandon Long <blong@fiction.net> for Mutt 0.43:
+ *
+ *     This was ugly.  It is still ugly.  I opted out of floating point
+ *     numbers, but the formatter understands just about everything from the
+ *     normal C string format, at least as far as I can tell from the Solaris
+ *     2.5 printf(3S) man page.
+ */
+
+#include "types.h"
+
+#include <limits.h>
+#include <stdarg.h>
+
+#define VA_START(ap, last) va_start(ap, last)
+#define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */
+
+#if HAVE_INTTYPES_H
+#include <inttypes.h>  /* For intmax_t (if not defined in <stdint.h>). */
+#endif /* HAVE_INTTYPES_H */
+
+#if HAVE_STDDEF_H
+#include <stddef.h>    /* For ptrdiff_t. */
+#endif /* HAVE_STDDEF_H */
+
+#if HAVE_STDINT_H
+#include <stdint.h>    /* For intmax_t. */
+#endif /* HAVE_STDINT_H */
+
+/* Support for unsigned long long int.  We may also need ULLONG_MAX. */
+#ifndef ULONG_MAX      /* We may need ULONG_MAX as a fallback. */
+#ifdef UINT_MAX
+#define ULONG_MAX UINT_MAX
+#else
+#define ULONG_MAX INT_MAX
+#endif /* defined(UINT_MAX) */
+#endif /* !defined(ULONG_MAX) */
+#ifdef ULLONG
+#undef ULLONG
+#endif /* defined(ULLONG) */
+#if HAVE_UNSIGNED_LONG_LONG_INT
+#define ULLONG unsigned long long int
+#ifndef ULLONG_MAX
+#define ULLONG_MAX ULONG_MAX
+#endif /* !defined(ULLONG_MAX) */
+#else
+#define ULLONG unsigned long int
+#ifdef ULLONG_MAX
+#undef ULLONG_MAX
+#endif /* defined(ULLONG_MAX) */
+#define ULLONG_MAX ULONG_MAX
+#endif /* HAVE_LONG_LONG_INT */
+
+/* Support for uintmax_t.  We also need UINTMAX_MAX. */
+#ifdef UINTMAX_T
+#undef UINTMAX_T
+#endif /* defined(UINTMAX_T) */
+#if HAVE_UINTMAX_T || defined(uintmax_t)
+#define UINTMAX_T uintmax_t
+#ifndef UINTMAX_MAX
+#define UINTMAX_MAX ULLONG_MAX
+#endif /* !defined(UINTMAX_MAX) */
+#else
+#define UINTMAX_T ULLONG
+#ifdef UINTMAX_MAX
+#undef UINTMAX_MAX
+#endif /* defined(UINTMAX_MAX) */
+#define UINTMAX_MAX ULLONG_MAX
+#endif /* HAVE_UINTMAX_T || defined(uintmax_t) */
+
+/* Support for long long int. */
+#ifndef LLONG
+#if HAVE_LONG_LONG_INT
+#define LLONG long long int
+#else
+#define LLONG long int
+#endif /* HAVE_LONG_LONG_INT */
+#endif /* !defined(LLONG) */
+
+/* Support for intmax_t. */
+#ifndef INTMAX_T
+#if HAVE_INTMAX_T || defined(intmax_t)
+#define INTMAX_T intmax_t
+#else
+#define INTMAX_T LLONG
+#endif /* HAVE_INTMAX_T || defined(intmax_t) */
+#endif /* !defined(INTMAX_T) */
+
+/* Support for uintptr_t. */
+#ifndef UINTPTR_T
+#if HAVE_UINTPTR_T || defined(uintptr_t)
+#define UINTPTR_T uintptr_t
+#else
+#define UINTPTR_T unsigned long int
+#endif /* HAVE_UINTPTR_T || defined(uintptr_t) */
+#endif /* !defined(UINTPTR_T) */
+
+/* Support for ptrdiff_t. */
+#ifndef PTRDIFF_T
+#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
+#define PTRDIFF_T ptrdiff_t
+#else
+#define PTRDIFF_T long int
+#endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
+#endif /* !defined(PTRDIFF_T) */
+
+/*
+ * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
+ * 7.19.6.1, 7).  However, we'll simply use PTRDIFF_T and convert it to an
+ * unsigned type if necessary.  This should work just fine in practice.
+ */
+#ifndef UPTRDIFF_T
+#define UPTRDIFF_T PTRDIFF_T
+#endif /* !defined(UPTRDIFF_T) */
+
+/*
+ * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
+ * However, we'll simply use size_t and convert it to a signed type if
+ * necessary.  This should work just fine in practice.
+ */
+#ifndef SSIZE_T
+#define SSIZE_T size_t
+#endif /* !defined(SSIZE_T) */
+
+
+/*
+ * Buffer size to hold the octal string representation of UINT128_MAX without
+ * nul-termination ("3777777777777777777777777777777777777777777").
+ */
+#ifdef MAX_CONVERT_LENGTH
+#undef MAX_CONVERT_LENGTH
+#endif /* defined(MAX_CONVERT_LENGTH) */
+#define MAX_CONVERT_LENGTH      43
+
+/* Format read states. */
+#define PRINT_S_DEFAULT         0
+#define PRINT_S_FLAGS           1
+#define PRINT_S_WIDTH           2
+#define PRINT_S_DOT             3
+#define PRINT_S_PRECISION       4
+#define PRINT_S_MOD             5
+#define PRINT_S_CONV            6
+
+/* Format flags. */
+#define PRINT_F_MINUS           (1 << 0)
+#define PRINT_F_PLUS            (1 << 1)
+#define PRINT_F_SPACE           (1 << 2)
+#define PRINT_F_NUM             (1 << 3)
+#define PRINT_F_ZERO            (1 << 4)
+#define PRINT_F_QUOTE           (1 << 5)
+#define PRINT_F_UP              (1 << 6)
+#define PRINT_F_UNSIGNED        (1 << 7)
+#define PRINT_F_TYPE_G          (1 << 8)
+#define PRINT_F_TYPE_E          (1 << 9)
+
+/* Conversion flags. */
+#define PRINT_C_CHAR            1
+#define PRINT_C_SHORT           2
+#define PRINT_C_LONG            3
+#define PRINT_C_LLONG           4
+//#define PRINT_C_LDOUBLE         5
+#define PRINT_C_SIZE            6
+#define PRINT_C_PTRDIFF         7
+#define PRINT_C_INTMAX          8
+
+#ifndef MAX
+#define MAX(x, y) ((x >= y) ? x : y)
+#endif /* !defined(MAX) */
+#ifndef CHARTOINT
+#define CHARTOINT(ch) (ch - '0')
+#endif /* !defined(CHARTOINT) */
+#ifndef ISDIGIT
+#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
+#endif /* !defined(ISDIGIT) */
+
+#define OUTCHAR(str, len, size, ch)                                          \
+do {                                                                         \
+       if (len + 1 < size)                                                  \
+               str[len] = ch;                                               \
+       (len)++;                                                             \
+} while (/* CONSTCOND */ 0)
+
+static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
+static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
+static void printsep(char *, size_t *, size_t);
+static int getnumsep(int);
+static int convert(UINTMAX_T, char *, size_t, int, int);
+
+int vsnprintf(char *str, size_t size, const char *format, va_list args)
+{
+       INTMAX_T value;
+       unsigned char cvalue;
+       const char *strvalue;
+       INTMAX_T *intmaxptr;
+       PTRDIFF_T *ptrdiffptr;
+       SSIZE_T *sizeptr;
+       LLONG *llongptr;
+       long int *longptr;
+       int *intptr;
+       short int *shortptr;
+       signed char *charptr;
+       size_t len = 0;
+       int overflow = 0;
+       int base = 0;
+       int cflags = 0;
+       int flags = 0;
+       int width = 0;
+       int precision = -1;
+       int state = PRINT_S_DEFAULT;
+       char ch = *format++;
+
+       /*
+        * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
+        * pointer." (7.19.6.5, 2)  We're forgiving and allow a NULL pointer
+        * even if a size larger than zero was specified.  At least NetBSD's
+        * snprintf(3) does the same, as well as other versions of this file.
+        * (Though some of these versions will write to a non-NULL buffer even
+        * if a size of zero was specified, which violates the standard.)
+        */
+       if (str == NULL && size != 0)
+               size = 0;
+
+       while (ch != '\0')
+               switch (state) {
+               case PRINT_S_DEFAULT:
+                       if (ch == '%')
+                               state = PRINT_S_FLAGS;
+                       else
+                               OUTCHAR(str, len, size, ch);
+                       ch = *format++;
+                       break;
+               case PRINT_S_FLAGS:
+                       switch (ch) {
+                       case '-':
+                               flags |= PRINT_F_MINUS;
+                               ch = *format++;
+                               break;
+                       case '+':
+                               flags |= PRINT_F_PLUS;
+                               ch = *format++;
+                               break;
+                       case ' ':
+                               flags |= PRINT_F_SPACE;
+                               ch = *format++;
+                               break;
+                       case '#':
+                               flags |= PRINT_F_NUM;
+                               ch = *format++;
+                               break;
+                       case '0':
+                               flags |= PRINT_F_ZERO;
+                               ch = *format++;
+                               break;
+                       case '\'':      /* SUSv2 flag (not in C99). */
+                               flags |= PRINT_F_QUOTE;
+                               ch = *format++;
+                               break;
+                       default:
+                               state = PRINT_S_WIDTH;
+                               break;
+                       }
+                       break;
+               case PRINT_S_WIDTH:
+                       if (ISDIGIT(ch)) {
+                               ch = CHARTOINT(ch);
+                               if (width > (INT_MAX - ch) / 10) {
+                                       overflow = 1;
+                                       goto out;
+                               }
+                               width = 10 * width + ch;
+                               ch = *format++;
+                       } else if (ch == '*') {
+                               /*
+                                * C99 says: "A negative field width argument is
+                                * taken as a `-' flag followed by a positive
+                                * field width." (7.19.6.1, 5)
+                                */
+                               if ((width = va_arg(args, int)) < 0) {
+                                       flags |= PRINT_F_MINUS;
+                                       width = -width;
+                               }
+                               ch = *format++;
+                               state = PRINT_S_DOT;
+                       } else
+                               state = PRINT_S_DOT;
+                       break;
+               case PRINT_S_DOT:
+                       if (ch == '.') {
+                               state = PRINT_S_PRECISION;
+                               ch = *format++;
+                       } else
+                               state = PRINT_S_MOD;
+                       break;
+               case PRINT_S_PRECISION:
+                       if (precision == -1)
+                               precision = 0;
+                       if (ISDIGIT(ch)) {
+                               ch = CHARTOINT(ch);
+                               if (precision > (INT_MAX - ch) / 10) {
+                                       overflow = 1;
+                                       goto out;
+                               }
+                               precision = 10 * precision + ch;
+                               ch = *format++;
+                       } else if (ch == '*') {
+                               /*
+                                * C99 says: "A negative precision argument is
+                                * taken as if the precision were omitted."
+                                * (7.19.6.1, 5)
+                                */
+                               if ((precision = va_arg(args, int)) < 0)
+                                       precision = -1;
+                               ch = *format++;
+                               state = PRINT_S_MOD;
+                       } else
+                               state = PRINT_S_MOD;
+                       break;
+               case PRINT_S_MOD:
+                       switch (ch) {
+                       case 'h':
+                               ch = *format++;
+                               if (ch == 'h') {        /* It's a char. */
+                                       ch = *format++;
+                                       cflags = PRINT_C_CHAR;
+                               } else
+                                       cflags = PRINT_C_SHORT;
+                               break;
+                       case 'l':
+                               ch = *format++;
+                               if (ch == 'l') {        /* It's a long long. */
+                                       ch = *format++;
+                                       cflags = PRINT_C_LLONG;
+                               } else
+                                       cflags = PRINT_C_LONG;
+                               break;
+                       case 'j':
+                               cflags = PRINT_C_INTMAX;
+                               ch = *format++;
+                               break;
+                       case 't':
+                               cflags = PRINT_C_PTRDIFF;
+                               ch = *format++;
+                               break;
+                       case 'z':
+                               cflags = PRINT_C_SIZE;
+                               ch = *format++;
+                               break;
+                       }
+                       state = PRINT_S_CONV;
+                       break;
+               case PRINT_S_CONV:
+                       switch (ch) {
+                       case 'd':
+                               /* FALLTHROUGH */
+                       case 'i':
+                               switch (cflags) {
+                               case PRINT_C_CHAR:
+                                       value = (signed char)va_arg(args, int);
+                                       break;
+                               case PRINT_C_SHORT:
+                                       value = (short int)va_arg(args, int);
+                                       break;
+                               case PRINT_C_LONG:
+                                       value = va_arg(args, long int);
+                                       break;
+                               case PRINT_C_LLONG:
+                                       value = va_arg(args, LLONG);
+                                       break;
+                               case PRINT_C_SIZE:
+                                       value = va_arg(args, SSIZE_T);
+                                       break;
+                               case PRINT_C_INTMAX:
+                                       value = va_arg(args, INTMAX_T);
+                                       break;
+                               case PRINT_C_PTRDIFF:
+                                       value = va_arg(args, PTRDIFF_T);
+                                       break;
+                               default:
+                                       value = va_arg(args, int);
+                                       break;
+                               }
+                               fmtint(str, &len, size, value, 10, width,
+                                   precision, flags);
+                               break;
+                       case 'X':
+                               flags |= PRINT_F_UP;
+                               /* FALLTHROUGH */
+                       case 'x':
+                               base = 16;
+                               /* FALLTHROUGH */
+                       case 'o':
+                               if (base == 0)
+                                       base = 8;
+                               /* FALLTHROUGH */
+                       case 'u':
+                               if (base == 0)
+                                       base = 10;
+                               flags |= PRINT_F_UNSIGNED;
+                               switch (cflags) {
+                               case PRINT_C_CHAR:
+                                       value = (unsigned char)va_arg(args,
+                                           unsigned int);
+                                       break;
+                               case PRINT_C_SHORT:
+                                       value = (unsigned short int)va_arg(args,
+                                           unsigned int);
+                                       break;
+                               case PRINT_C_LONG:
+                                       value = va_arg(args, unsigned long int);
+                                       break;
+                               case PRINT_C_LLONG:
+                                       value = va_arg(args, ULLONG);
+                                       break;
+                               case PRINT_C_SIZE:
+                                       value = va_arg(args, size_t);
+                                       break;
+                               case PRINT_C_INTMAX:
+                                       value = va_arg(args, UINTMAX_T);
+                                       break;
+                               case PRINT_C_PTRDIFF:
+                                       value = va_arg(args, UPTRDIFF_T);
+                                       break;
+                               default:
+                                       value = va_arg(args, unsigned int);
+                                       break;
+                               }
+                               fmtint(str, &len, size, value, base, width,
+                                   precision, flags);
+                               break;
+                       case 'c':
+                               cvalue = va_arg(args, int);
+                               OUTCHAR(str, len, size, cvalue);
+                               break;
+                       case 's':
+                               strvalue = va_arg(args, char *);
+                               fmtstr(str, &len, size, strvalue, width,
+                                   precision, flags);
+                               break;
+                       case 'p':
+                               /*
+                                * C99 says: "The value of the pointer is
+                                * converted to a sequence of printing
+                                * characters, in an implementation-defined
+                                * manner." (C99: 7.19.6.1, 8)
+                                */
+                               if ((strvalue = va_arg(args, void *)) == NULL)
+                                       /*
+                                        * We use the glibc format.  BSD prints
+                                        * "0x0", SysV "0".
+                                        */
+                                       fmtstr(str, &len, size, "(nil)", width,
+                                           -1, flags);
+                               else {
+                                       /*
+                                        * We use the BSD/glibc format.  SysV
+                                        * omits the "0x" prefix (which we emit
+                                        * using the PRINT_F_NUM flag).
+                                        */
+                                       flags |= PRINT_F_NUM;
+                                       flags |= PRINT_F_UNSIGNED;
+                                       fmtint(str, &len, size,
+                                           (UINTPTR_T)strvalue, 16, width,
+                                           precision, flags);
+                               }
+                               break;
+                       case 'n':
+                               switch (cflags) {
+                               case PRINT_C_CHAR:
+                                       charptr = va_arg(args, signed char *);
+                                       *charptr = len;
+                                       break;
+                               case PRINT_C_SHORT:
+                                       shortptr = va_arg(args, short int *);
+                                       *shortptr = len;
+                                       break;
+                               case PRINT_C_LONG:
+                                       longptr = va_arg(args, long int *);
+                                       *longptr = len;
+                                       break;
+                               case PRINT_C_LLONG:
+                                       llongptr = va_arg(args, LLONG *);
+                                       *llongptr = len;
+                                       break;
+                               case PRINT_C_SIZE:
+                                       /*
+                                        * C99 says that with the "z" length
+                                        * modifier, "a following `n' conversion
+                                        * specifier applies to a pointer to a
+                                        * signed integer type corresponding to
+                                        * size_t argument." (7.19.6.1, 7)
+                                        */
+                                       sizeptr = va_arg(args, SSIZE_T *);
+                                       *sizeptr = len;
+                                       break;
+                               case PRINT_C_INTMAX:
+                                       intmaxptr = va_arg(args, INTMAX_T *);
+                                       *intmaxptr = len;
+                                       break;
+                               case PRINT_C_PTRDIFF:
+                                       ptrdiffptr = va_arg(args, PTRDIFF_T *);
+                                       *ptrdiffptr = len;
+                                       break;
+                               default:
+                                       intptr = va_arg(args, int *);
+                                       *intptr = len;
+                                       break;
+                               }
+                               break;
+                       case '%':       /* Print a "%" character verbatim. */
+                               OUTCHAR(str, len, size, ch);
+                               break;
+                       default:        /* Skip other characters. */
+                               break;
+                       }
+                       ch = *format++;
+                       state = PRINT_S_DEFAULT;
+                       base = cflags = flags = width = 0;
+                       precision = -1;
+                       break;
+               }
+out:
+       if (len < size)
+               str[len] = '\0';
+       else if (size > 0)
+               str[size - 1] = '\0';
+
+       if (overflow || len >= INT_MAX) {
+               return -1;
+       }
+       return (int)len;
+}
+
+static void
+fmtstr(char *str, size_t *len, size_t size, const char *value, int width,
+       int precision, int flags)
+{
+       int padlen, strln;      /* Amount to pad. */
+       int noprecision = (precision == -1);
+
+       if (value == NULL)      /* We're forgiving. */
+               value = "(null)";
+
+       /* If a precision was specified, don't read the string past it. */
+       for (strln = 0; value[strln] != '\0' &&
+           (noprecision || strln < precision); strln++)
+               continue;
+
+       if ((padlen = width - strln) < 0)
+               padlen = 0;
+       if (flags & PRINT_F_MINUS)      /* Left justify. */
+               padlen = -padlen;
+
+       while (padlen > 0) {    /* Leading spaces. */
+               OUTCHAR(str, *len, size, ' ');
+               padlen--;
+       }
+       while (*value != '\0' && (noprecision || precision-- > 0)) {
+               OUTCHAR(str, *len, size, *value);
+               value++;
+       }
+       while (padlen < 0) {    /* Trailing spaces. */
+               OUTCHAR(str, *len, size, ' ');
+               padlen++;
+       }
+}
+
+static void
+fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width,
+       int precision, int flags)
+{
+       UINTMAX_T uvalue;
+       char iconvert[MAX_CONVERT_LENGTH];
+       char sign = 0;
+       char hexprefix = 0;
+       int spadlen = 0;        /* Amount to space pad. */
+       int zpadlen = 0;        /* Amount to zero pad. */
+       int pos;
+       int separators = (flags & PRINT_F_QUOTE);
+       int noprecision = (precision == -1);
+
+       if (flags & PRINT_F_UNSIGNED)
+               uvalue = value;
+       else {
+               uvalue = (value >= 0) ? value : -value;
+               if (value < 0)
+                       sign = '-';
+               else if (flags & PRINT_F_PLUS)  /* Do a sign. */
+                       sign = '+';
+               else if (flags & PRINT_F_SPACE)
+                       sign = ' ';
+       }
+
+       pos = convert(uvalue, iconvert, sizeof(iconvert), base,
+           flags & PRINT_F_UP);
+
+       if (flags & PRINT_F_NUM && uvalue != 0) {
+               /*
+                * C99 says: "The result is converted to an `alternative form'.
+                * For `o' conversion, it increases the precision, if and only
+                * if necessary, to force the first digit of the result to be a
+                * zero (if the value and precision are both 0, a single 0 is
+                * printed).  For `x' (or `X') conversion, a nonzero result has
+                * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
+                */
+               switch (base) {
+               case 8:
+                       if (precision <= pos)
+                               precision = pos + 1;
+                       break;
+               case 16:
+                       hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
+                       break;
+               }
+       }
+
+       if (separators) /* Get the number of group separators we'll print. */
+               separators = getnumsep(pos);
+
+       zpadlen = precision - pos - separators;
+       spadlen = width                         /* Minimum field width. */
+           - separators                        /* Number of separators. */
+           - MAX(precision, pos)               /* Number of integer digits. */
+           - ((sign != 0) ? 1 : 0)             /* Will we print a sign? */
+           - ((hexprefix != 0) ? 2 : 0);       /* Will we print a prefix? */
+
+       if (zpadlen < 0)
+               zpadlen = 0;
+       if (spadlen < 0)
+               spadlen = 0;
+
+       /*
+        * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
+        * ignored.  For `d', `i', `o', `u', `x', and `X' conversions, if a
+        * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
+        */
+       if (flags & PRINT_F_MINUS)      /* Left justify. */
+               spadlen = -spadlen;
+       else if (flags & PRINT_F_ZERO && noprecision) {
+               zpadlen += spadlen;
+               spadlen = 0;
+       }
+       while (spadlen > 0) {   /* Leading spaces. */
+               OUTCHAR(str, *len, size, ' ');
+               spadlen--;
+       }
+       if (sign != 0)  /* Sign. */
+               OUTCHAR(str, *len, size, sign);
+       if (hexprefix != 0) {   /* A "0x" or "0X" prefix. */
+               OUTCHAR(str, *len, size, '0');
+               OUTCHAR(str, *len, size, hexprefix);
+       }
+       while (zpadlen > 0) {   /* Leading zeros. */
+               OUTCHAR(str, *len, size, '0');
+               zpadlen--;
+       }
+       while (pos > 0) {       /* The actual digits. */
+               pos--;
+               OUTCHAR(str, *len, size, iconvert[pos]);
+               if (separators > 0 && pos > 0 && pos % 3 == 0)
+                       printsep(str, len, size);
+       }
+       while (spadlen < 0) {   /* Trailing spaces. */
+               OUTCHAR(str, *len, size, ' ');
+               spadlen++;
+       }
+}
+
+
+static void
+printsep(char *str, size_t *len, size_t size)
+{
+       OUTCHAR(str, *len, size, ',');
+}
+
+static int
+getnumsep(int digits)
+{
+       int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
+       return separators;
+}
+
+static int
+convert(UINTMAX_T value, char *buf, size_t size, int base, int caps)
+{
+       const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
+       size_t pos = 0;
+
+       /* We return an unterminated buffer with the digits in reverse order. */
+       do {
+               buf[pos++] = digits[value % base];
+               value /= base;
+       } while (value != 0 && pos < size);
+
+       return (int)pos;
+}
+
+int vsprintf(char *buf, const char *fmt, va_list args)
+{
+       return vsnprintf(buf, INT_MAX, fmt, args);
+}
+
+int sprintf(char *buffer, const char *fmt, ...)
+{
+       va_list args;
+       int i;
+
+       va_start(args, fmt);
+       i = vsprintf(buffer, fmt,args);
+       va_end(args);
+       return i;
+}
+
diff --git a/printf.h b/printf.h
new file mode 100644 (file)
index 0000000..b01a17c
--- /dev/null
+++ b/printf.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 1995 Patrick Powell.
+ *
+ * This code is based on code written by Patrick Powell <papowell@astart.com>.
+ * It may be used for any purpose as long as this notice remains intact on all
+ * source code distributions.
+ */
+
+/*
+ * Copyright (c) 2008 Holger Weiss.
+ *
+ * This version of the code is maintained by Holger Weiss <holger@jhweiss.de>.
+ * My changes to the code may freely be used, modified and/or redistributed for
+ * any purpose.  It would be nice if additions and fixes to this file (including
+ * trivial code cleanups) would be sent back in order to let me include them in
+ * the version available at <http://www.jhweiss.de/software/snprintf.html>.
+ * However, this is not a requirement for using or redistributing (possibly
+ * modified) versions of this file, nor is leaving this notice intact mandatory.
+ */
+#ifndef __VSPRINTF_H__
+#define __VSPRINTF_H__
+
+#include <stdarg.h>
+
+int vsprintf(char *buf, const char *fmt, va_list args);
+int vsnprintf(char *str, size_t size, const char *format, va_list args);
+int sprintf(char *buffer, const char *fmt, ...);
+
+#endif
+
diff --git a/realmode.S b/realmode.S
new file mode 100644 (file)
index 0000000..69995f3
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+       
+Copyright (C) 2008             Segher Boessenkool <segher@kernel.crashing.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#define IBAT0U          528
+#define IBAT0L          529
+#define IBAT1U          530
+#define IBAT1L          531
+#define IBAT2U          532
+#define IBAT2L          533
+#define IBAT3U          534
+#define IBAT3L          535
+#define IBAT4U          560
+#define IBAT4L          561
+#define IBAT5U          562
+#define IBAT5L          563
+#define IBAT6U          564
+#define IBAT6L          565
+#define IBAT7U          566
+#define IBAT7L          567
+
+#define DBAT0U          536
+#define DBAT0L          537
+#define DBAT1U          538
+#define DBAT1L          539
+#define DBAT2U          540
+#define DBAT2L          541
+#define DBAT3U          542
+#define DBAT3L          543
+#define DBAT4U          568
+#define DBAT4L          569
+#define DBAT5U          570
+#define DBAT5L          571
+#define DBAT6U          572
+#define DBAT6L          573
+#define DBAT7U          574
+#define DBAT7L          575
+
+
+       .text
+       .section .realmode,"ax",@progbits
+       .extern _start
+       .align 2
+       .globl _realmode_vector
+
+_realmode_vector:
+       // HID0 = 00110c64:
+       // bus checkstops off, sleep modes off,
+       // caches off, caches invalidate,
+       // store gathering off, enable data cache
+       // flush assist, enable branch target cache,
+       // enable branch history table
+       lis 3,0x0011 ; ori 3,3,0x0c64 ; mtspr 1008,3 ; isync
+
+       // MSR = 00002000 (FP on)
+       li 4,0x2000 ; mtmsr 4
+
+       // HID0 |= 0000c000 (caches on)
+       ori 3,3,0xc000 ; mtspr 1008,3 ; isync
+
+       // clear all BATs
+       li 0,0
+       mtspr 528,0 ; mtspr 530,0 ; mtspr 532,0 ; mtspr 534,0 // IBATU 0..3
+       mtspr 536,0 ; mtspr 538,0 ; mtspr 540,0 ; mtspr 542,0 // DBATU 0..3
+       mtspr 560,0 ; mtspr 562,0 ; mtspr 564,0 ; mtspr 566,0 // IBATU 4..7
+       mtspr 568,0 ; mtspr 570,0 ; mtspr 572,0 ; mtspr 574,0 // DBATU 4..7
+       isync
+
+       // clear all SRs
+       lis 0,0x8000
+       mtsr  0,0 ; mtsr  1,0 ; mtsr  2,0 ; mtsr  3,0
+       mtsr  4,0 ; mtsr  5,0 ; mtsr  6,0 ; mtsr  7,0
+       mtsr  8,0 ; mtsr  9,0 ; mtsr 10,0 ; mtsr 11,0
+       mtsr 12,0 ; mtsr 13,0 ; mtsr 14,0 ; mtsr 15,0
+       isync
+
+       // set [DI]BAT0 for 256MB@80000000,
+       // real 00000000, WIMG=0000, R/W
+       li 3,2 ; lis 4,0x8000 ; ori 4,4,0x1fff
+       mtspr IBAT0L,3 ; mtspr IBAT0U,4 ; mtspr DBAT0L,3 ; mtspr DBAT0U,4 ; isync
+
+       // set [DI]BAT4 for 256MB@90000000,
+       // real 10000000, WIMG=0000, R/W
+       addis 3,3,0x1000 ; addis 4,4,0x1000
+       mtspr IBAT4L,3 ; mtspr IBAT4U,4 ; mtspr DBAT4L,3 ; mtspr DBAT4U,4 ; isync
+
+       // set DBAT1 for 256MB@c0000000,
+       // real 00000000, WIMG=0101, R/W
+       li 3,0x2a ; lis 4,0xc000 ; ori 4,4,0x1fff
+       mtspr DBAT1L,3 ; mtspr DBAT1U,4 ; isync
+
+       // set DBAT5 for 256MB@d0000000,
+       // real 10000000, WIMG=0101, R/W
+       addis 3,3,0x1000 ; addis 4,4,0x1000
+       mtspr DBAT5L,3 ; mtspr DBAT5U,4 ; isync
+
+       // enable [DI]BAT4-7 in HID4
+       lis 3, 0x8200
+       mtspr 1011,3
+
+       // set MSR[DR:IR] = 11, jump to _start
+       lis 3,_start@h ; ori 3,3,_start@l ; mtsrr0 3
+
+       mfmsr 3 ; ori 3,3,0x30 ; mtsrr1 3
+       rfi
+
diff --git a/string.c b/string.c
new file mode 100644 (file)
index 0000000..5b867b0
--- /dev/null
+++ b/string.c
@@ -0,0 +1,157 @@
+/*  string.c -- standard C string-manipulation functions.
+
+Copyright (C) 2008             Segher Boessenkool <segher@kernel.crashing.org>
+Copyright (C) 2009             Haxx Enterprises <bushing@gmail.com>
+
+Portions taken from the Public Domain C Library (PDCLib).
+https://negix.net/trac/pdclib
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "string.h"
+
+size_t strlen(const char *s)
+{
+       size_t len;
+
+       for (len = 0; s[len]; len++)
+               ;
+
+       return len;
+}
+
+size_t strnlen(const char *s, size_t count)
+{
+       size_t len;
+
+       for (len = 0; s[len] && len < count; len++)
+               ;
+
+       return len;
+}
+
+void *memset(void *b, int c, size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; i++)
+               ((unsigned char *)b)[i] = c;
+
+       return b;
+}
+
+void *memcpy(void *dst, const void *src, size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; i++)
+               ((unsigned char *)dst)[i] = ((unsigned char *)src)[i];
+
+       return dst;
+}
+
+int memcmp(const void *s1, const void *s2, size_t len)
+{
+       size_t i;
+       const unsigned char * p1 = (const unsigned char *) s1;
+       const unsigned char * p2 = (const unsigned char *) s2;
+
+       for (i = 0; i < len; i++)
+               if (p1[i] != p2[i]) return p1[i] - p2[i];
+       
+       return 0;
+}
+
+int strcmp(const char *s1, const char *s2)
+{
+       size_t i;
+
+       for (i = 0; s1[i] && s1[i] == s2[i]; i++)
+               ;
+       
+       return s1[i] - s2[i];
+}
+
+int strncmp(const char *s1, const char *s2, size_t n)
+{
+       size_t i;
+
+       for (i = 0; i < n && s1[i] && s1[i] == s2[i]; i++)
+               ;
+       if (i == n) return 0;
+       return s1[i] - s2[i];
+}
+
+size_t strlcpy(char *dest, const char *src, size_t maxlen)
+{
+       size_t len,needed;
+
+       len = needed = strnlen(src, maxlen-1) + 1;
+       if (len >= maxlen)
+               len = maxlen-1;
+
+       memcpy(dest, src, len);
+       dest[len]='\0';
+
+       return needed-1;
+}
+
+size_t strlcat(char *dest, const char *src, size_t maxlen)
+{
+       size_t used;
+
+    used = strnlen(dest, maxlen-1);
+       return used + strlcpy(dest + used, src, maxlen - used);
+}
+
+char * strchr(const char *s, int c)
+{
+       size_t i;
+       
+       for (i = 0; s[i]; i++)
+               if (s[i] == (char)c) return (char *)s + i;
+
+       return NULL;
+}
+
+size_t strspn(const char *s1, const char *s2)
+{
+       size_t len = 0;
+       const char *p;
+
+       while (s1[len]) {
+               p = s2;
+               while (*p) {
+                       if (s1[len] == *p)
+                               break;
+
+                       ++p;
+               }
+               if (!*p)
+                       return len;
+
+               ++len;
+       }
+
+       return len;
+}
+
+size_t strcspn(const char *s1, const char *s2)
+{
+       size_t len = 0;
+       const char *p;
+
+       while (s1[len]) {
+               p = s2;
+               while (*p)
+                       if (s1[len] == *p++)
+                               return len;
+
+               ++len;
+       }
+
+       return len;
+}
+
diff --git a/string.h b/string.h
new file mode 100644 (file)
index 0000000..da87683
--- /dev/null
+++ b/string.h
@@ -0,0 +1,29 @@
+/*  string.c -- standard C string-manipulation functions.
+
+Copyright (C) 2008             Segher Boessenkool <segher@kernel.crashing.org>
+Copyright (C) 2009             Haxx Enterprises <bushing@gmail.com>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#ifndef _STRING_H
+#define _STRING_H
+
+#include "types.h"
+
+size_t strlen(const char *);
+size_t strnlen(const char *, size_t);
+void *memset(void *, int, size_t);
+void *memcpy(void *, const void *, size_t);
+int memcmp(const void *, const void *, size_t);
+int strcmp(const char *, const char *);
+int strncmp(const char *, const char *, size_t);
+size_t strlcpy(char *, const char *, size_t);
+size_t strlcat(char *, const char *, size_t);
+char *strchr(const char *, int);
+size_t strspn(const char *, const char *);
+size_t strcspn(const char *, const char *);
+
+#endif
+
diff --git a/sync.c b/sync.c
new file mode 100644 (file)
index 0000000..a0e27a0
--- /dev/null
+++ b/sync.c
@@ -0,0 +1,51 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2008             Segher Boessenkool <segher@kernel.crashing.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+
+void sync_before_read(void *p, u32 len)
+{
+       u32 a, b;
+
+       a = (u32)p & ~0x1f;
+       b = ((u32)p + len + 0x1f) & ~0x1f;
+
+       for ( ; a < b; a += 32)
+               asm("dcbi 0,%0" : : "b"(a));
+
+       asm("sync ; isync");
+}
+
+void sync_after_write(const void *p, u32 len)
+{
+       u32 a, b;
+
+       a = (u32)p & ~0x1f;
+       b = ((u32)p + len + 0x1f) & ~0x1f;
+
+       for ( ; a < b; a += 32)
+               asm("dcbst 0,%0" : : "b"(a));
+
+       asm("sync ; isync");
+}
+
+void sync_before_exec(const void *p, u32 len)
+{
+       u32 a, b;
+
+       a = (u32)p & ~0x1f;
+       b = ((u32)p + len + 0x1f) & ~0x1f;
+
+       for ( ; a < b; a += 32)
+               asm("dcbst 0,%0 ; sync ; icbi 0,%0" : : "b"(a));
+
+       asm("sync ; isync");
+}
+
diff --git a/time.c b/time.c
new file mode 100644 (file)
index 0000000..52c7c3a
--- /dev/null
+++ b/time.c
@@ -0,0 +1,38 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+Copyright (C) 2008             Segher Boessenkool <segher@kernel.crashing.org>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#include "bootmii_ppc.h"
+
+// Timebase frequency is bus frequency / 4.  Ignore roundoff, this
+// doesn't have to be very accurate.
+#define TICKS_PER_USEC (243/4)
+
+u64 mftb(void)
+{
+  u32 hi, lo, dum;
+  
+  asm("0: mftbu %0 ; mftb %1 ; mftbu %2 ; cmplw %0,%2 ; bne 0b" 
+      : "=r"(hi), "=r"(lo), "=r"(dum)); 
+  return ((u64)hi << 32) | lo;
+}
+
+static void __delay(u64 ticks)
+{
+       u64 start = mftb();
+
+       while (mftb() - start < ticks)
+               ;
+}
+
+void udelay(u32 us)
+{
+       __delay(TICKS_PER_USEC * (u64)us);
+}
+
diff --git a/types.h b/types.h
new file mode 100644 (file)
index 0000000..2e1330a
--- /dev/null
+++ b/types.h
@@ -0,0 +1,61 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       Requires mini.
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+*/
+
+#ifndef __TYPES_H__
+#define __TYPES_H__
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+
+typedef signed char s8;
+typedef signed short s16;
+typedef signed int s32;
+typedef signed long long s64;
+
+typedef volatile unsigned char vu8;
+typedef volatile unsigned short vu16;
+typedef volatile unsigned int vu32;
+typedef volatile unsigned long long vu64;
+
+typedef volatile signed char vs8;
+typedef volatile signed short vs16;
+typedef volatile signed int vs32;
+typedef volatile signed long long vs64;
+
+typedef unsigned int size_t;
+typedef signed int ssize_t;
+
+#define NULL ((void *)0)
+
+/* These types must be 16-bit, 32-bit or larger integer */
+typedef int                            INT;
+typedef unsigned int   UINT;
+
+/* These types must be 8-bit integer */
+typedef signed char            CHAR;
+typedef unsigned char  UCHAR;
+typedef unsigned char  BYTE;
+
+/* These types must be 16-bit integer */
+typedef short                  SHORT;
+typedef unsigned short USHORT;
+typedef unsigned short WORD;
+typedef unsigned short WCHAR;
+
+/* These types must be 32-bit integer */
+typedef long                   LONG;
+typedef unsigned long  ULONG;
+typedef unsigned long  DWORD;
+
+/* Boolean type */
+typedef enum { FALSE = 0, TRUE } BOOL;
+
+#endif
+
diff --git a/video_low.c b/video_low.c
new file mode 100644 (file)
index 0000000..cf21cf5
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       low-level video support for the BootMii UI
+
+Copyright (C) 2008, 2009       Hector Martin "marcan" <marcan@marcansoft.com>
+Copyright (C) 2009                     Haxx Enterprises <bushing@gmail.com>
+Copyright (c) 2009             Sven Peter <svenpeter@gmail.com>
+
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+Some routines and initialization constants originally came from the
+"GAMECUBE LOW LEVEL INFO" document and sourcecode released by Titanik
+of Crazy Nation and the GC Linux project.
+*/
+
+#include "bootmii_ppc.h"
+#include "video_low.h"
+#include "string.h"
+
+#ifdef VI_DEBUG
+#define  VI_debug(f, arg...) printf("VI: " f, ##arg);
+#else
+#define  VI_debug(f, arg...) while(0)
+#endif
+
+// hardcoded VI init states
+static const u16 VIDEO_Mode640X480NtsciYUV16[64] = {
+  0x0F06, 0x0001, 0x4769, 0x01AD, 0x02EA, 0x5140, 0x0003, 0x0018,
+  0x0002, 0x0019, 0x410C, 0x410C, 0x40ED, 0x40ED, 0x0043, 0x5A4E,
+  0x0000, 0x0000, 0x0043, 0x5A4E, 0x0000, 0x0000, 0x0000, 0x0000,
+  0x1107, 0x01AE, 0x1001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+  0x0000, 0x0000, 0x0000, 0x0000, 0x2850, 0x0100, 0x1AE7, 0x71F0,
+  0x0DB4, 0xA574, 0x00C1, 0x188E, 0xC4C0, 0xCBE2, 0xFCEC, 0xDECF,
+  0x1313, 0x0F08, 0x0008, 0x0C0F, 0x00FF, 0x0000, 0x0000, 0x0000,
+  0x0280, 0x0000, 0x0000, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF};
+
+static const u16 VIDEO_Mode640X480Pal50YUV16[64] = {
+  0x11F5, 0x0101, 0x4B6A, 0x01B0, 0x02F8, 0x5640, 0x0001, 0x0023,
+  0x0000, 0x0024, 0x4D2B, 0x4D6D, 0x4D8A, 0x4D4C, 0x0043, 0x5A4E,
+  0x0000, 0x0000, 0x0043, 0x5A4E, 0x0000, 0x0000, 0x013C, 0x0144,
+  0x1139, 0x01B1, 0x1001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+  0x0000, 0x0000, 0x0000, 0x0000, 0x2850, 0x0100, 0x1AE7, 0x71F0,
+  0x0DB4, 0xA574, 0x00C1, 0x188E, 0xC4C0, 0xCBE2, 0xFCEC, 0xDECF,
+  0x1313, 0x0F08, 0x0008, 0x0C0F, 0x00FF, 0x0000, 0x0000, 0x0000,
+  0x0280, 0x0000, 0x0000, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF};
+
+static const u16 VIDEO_Mode640X480Pal60YUV16[64] = {
+  0x0F06, 0x0001, 0x4769, 0x01AD, 0x02EA, 0x5140, 0x0003, 0x0018,
+  0x0002, 0x0019, 0x410C, 0x410C, 0x40ED, 0x40ED, 0x0043, 0x5A4E,
+  0x0000, 0x0000, 0x0043, 0x5A4E, 0x0000, 0x0000, 0x0005, 0x0176,
+  0x1107, 0x01AE, 0x1001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+  0x0000, 0x0000, 0x0000, 0x0000, 0x2850, 0x0100, 0x1AE7, 0x71F0,
+  0x0DB4, 0xA574, 0x00C1, 0x188E, 0xC4C0, 0xCBE2, 0xFCEC, 0xDECF,
+  0x1313, 0x0F08, 0x0008, 0x0C0F, 0x00FF, 0x0000, 0x0000, 0x0000,
+  0x0280, 0x0000, 0x0000, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF};
+
+static const u16 VIDEO_Mode640X480NtscpYUV16[64] = {
+  0x1E0C, 0x0005, 0x4769, 0x01AD, 0x02EA, 0x5140, 0x0006, 0x0030,
+  0x0006, 0x0030, 0x81D8, 0x81D8, 0x81D8, 0x81D8, 0x0015, 0x77A0,
+  0x0000, 0x0000, 0x0015, 0x77A0, 0x0000, 0x0000, 0x022A, 0x01D6,
+  0x120E, 0x0001, 0x1001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+  0x0000, 0x0000, 0x0000, 0x0000, 0x2828, 0x0100, 0x1AE7, 0x71F0,
+  0x0DB4, 0xA574, 0x00C1, 0x188E, 0xC4C0, 0xCBE2, 0xFCEC, 0xDECF,
+  0x1313, 0x0F08, 0x0008, 0x0C0F, 0x00FF, 0x0000, 0x0001, 0x0001,
+  0x0280, 0x807A, 0x019C, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF};
+
+static int video_mode;
+
+void VIDEO_Init(int VideoMode)
+{
+       u32 Counter=0;
+       const u16 *video_initstate=NULL;
+
+       VI_debug("Resetting VI...\n");
+       write16(R_VIDEO_STATUS1, 2);
+       udelay(2);
+       write16(R_VIDEO_STATUS1, 0);
+       VI_debug("VI reset...\n");
+
+       switch(VideoMode)
+       {
+       case VIDEO_640X480_NTSCi_YUV16:
+               video_initstate = VIDEO_Mode640X480NtsciYUV16;
+               break;
+
+       case VIDEO_640X480_PAL50_YUV16:
+               video_initstate = VIDEO_Mode640X480Pal50YUV16;
+               break;
+
+       case VIDEO_640X480_PAL60_YUV16:
+               video_initstate = VIDEO_Mode640X480Pal60YUV16;
+               break;
+
+       case VIDEO_640X480_NTSCp_YUV16:
+               video_initstate = VIDEO_Mode640X480NtscpYUV16;
+               break;
+
+       /* Use NTSC as default */
+       default:
+               VideoMode = VIDEO_640X480_NTSCi_YUV16;
+               video_initstate = VIDEO_Mode640X480NtsciYUV16;
+               break;
+       }
+       
+       VI_debug("Configuring VI...\n");
+       for(Counter=0; Counter<64; Counter++)
+       {
+               if(Counter==1)
+                       write16(MEM_VIDEO_BASE + 2*Counter, video_initstate[Counter] & 0xFFFE);
+               else
+                       write16(MEM_VIDEO_BASE + 2*Counter, video_initstate[Counter]);
+       }
+
+       video_mode = VideoMode;
+
+       write16(R_VIDEO_STATUS1, video_initstate[1]);
+#ifdef VI_DEBUG
+       VI_debug("VI dump:\n");
+       for(Counter=0; Counter<32; Counter++)
+               printf("%02x: %04x %04x,\n", Counter*4, read16(MEM_VIDEO_BASE + Counter*4), read16(MEM_VIDEO_BASE + Counter*4+2));
+
+       printf("---\n");
+#endif
+}
+
+void VIDEO_SetFrameBuffer(void *FrameBufferAddr)
+{
+       u32 fb = virt_to_phys(FrameBufferAddr);
+
+       write32(R_VIDEO_FRAMEBUFFER_1, (fb >> 5) | 0x10000000);
+       if(video_mode != VIDEO_640X480_NTSCp_YUV16)
+               fb += 2 * 640; // 640 pixels == 1 line
+       write32(R_VIDEO_FRAMEBUFFER_2, (fb >> 5) | 0x10000000);
+}
+
+void VIDEO_WaitVSync(void)
+{
+       while(read16(R_VIDEO_HALFLINE_1) >= 200);
+       while(read16(R_VIDEO_HALFLINE_1) <  200);
+}
+
+/* black out video (not reversible!) */
+void VIDEO_BlackOut(void)
+{
+       VIDEO_WaitVSync();
+
+       int active = read32(R_VIDEO_VTIMING) >> 4;
+
+       write32(R_VIDEO_PRB_ODD, read32(R_VIDEO_PRB_ODD) + ((active<<1)-2));
+       write32(R_VIDEO_PRB_EVEN, read32(R_VIDEO_PRB_EVEN) + ((active<<1)-2));
+       write32(R_VIDEO_PSB_ODD, read32(R_VIDEO_PSB_ODD) + 2);
+       write32(R_VIDEO_PSB_EVEN, read32(R_VIDEO_PSB_EVEN) + 2);
+
+       mask32(R_VIDEO_VTIMING, 0xfffffff0, 0);
+}
+
+//static vu16* const _viReg = (u16*)0xCC002000;
+
+void VIDEO_Shutdown(void)
+{
+       VIDEO_BlackOut();
+       write16(R_VIDEO_STATUS1, 0);
+}
+
+#define                HW_REG_BASE             0xd800000
+
+// PPC side of GPIO1 (Starlet can access this too)
+// Output state
+#define                HW_GPIO1BOUT            (HW_REG_BASE + 0x0c0)
+// Direction (1=output)
+#define                HW_GPIO1BDIR            (HW_REG_BASE + 0x0c4)
+// Input state
+#define                HW_GPIO1BIN                     (HW_REG_BASE + 0x0c8)
+
+#define SLAVE_AVE 0xe0
+
+static inline void aveSetDirection(u32 dir)
+{
+       u32 val = (read32(HW_GPIO1BDIR)&~0x8000)|0x4000;
+       if(dir) val |= 0x8000;
+       write32(HW_GPIO1BDIR, val);
+}
+
+static inline void aveSetSCL(u32 scl)
+{
+       u32 val = read32(HW_GPIO1BOUT)&~0x4000;
+       if(scl) val |= 0x4000;
+       write32(HW_GPIO1BOUT, val);
+}
+
+static inline void aveSetSDA(u32 sda)
+{
+       u32 val = read32(HW_GPIO1BOUT)&~0x8000;
+       if(sda) val |= 0x8000;
+       write32(HW_GPIO1BOUT, val);
+}
+
+static inline u32 aveGetSDA()
+{
+       if(read32(HW_GPIO1BIN)&0x8000)
+               return 1;
+       else
+               return 0;
+}
+
+static u32 __sendSlaveAddress(u8 addr)
+{
+       u32 i;
+
+       aveSetSDA(0);
+       udelay(2);
+
+       aveSetSCL(0);
+       for(i=0;i<8;i++) {
+               if(addr&0x80) aveSetSDA(1);
+               else aveSetSDA(0);
+               udelay(2);
+
+               aveSetSCL(1);
+               udelay(2);
+
+               aveSetSCL(0);
+               addr <<= 1;
+       }
+
+       aveSetDirection(0);
+       udelay(2);
+
+       aveSetSCL(1);
+       udelay(2);
+
+       if(aveGetSDA()!=0) {
+               VI_debug("No ACK\n");
+               return 0;
+       }
+
+       aveSetSDA(0);
+       aveSetDirection(1);
+       aveSetSCL(0);
+
+       return 1;
+}
+
+static u32 __VISendI2CData(u8 addr,void *val,u32 len)
+{
+       u8 c;
+       u32 i,j;
+       u32 ret;
+
+       VI_debug("I2C[%02x]:",addr);
+       for(i=0;i<len;i++)
+               VI_debug(" %02x", ((u8*)val)[i]);
+       VI_debug("\n");
+
+       aveSetDirection(1);
+       aveSetSCL(1);
+
+       aveSetSDA(1);
+       udelay(4);
+
+       ret = __sendSlaveAddress(addr);
+       if(ret==0) {
+               return 0;
+       }
+
+       aveSetDirection(1);
+       for(i=0;i<len;i++) {
+               c = ((u8*)val)[i];
+               for(j=0;j<8;j++) {
+                       if(c&0x80) aveSetSDA(1);
+                       else aveSetSDA(0);
+                       udelay(2);
+
+                       aveSetSCL(1);
+                       udelay(2);
+                       aveSetSCL(0);
+
+                       c <<= 1;
+               }
+               aveSetDirection(0);
+               udelay(2);
+               aveSetSCL(1);
+               udelay(2);
+
+               if(aveGetSDA()!=0) {
+                       VI_debug("No ACK\n");
+                       return 0;
+               }
+
+               aveSetSDA(0);
+               aveSetDirection(1);
+               aveSetSCL(0);
+       }
+
+       aveSetDirection(1);
+       aveSetSDA(0);
+       udelay(2);
+       aveSetSDA(1);
+
+       return 1;
+}
+
+static void __VIWriteI2CRegister8(u8 reg, u8 data)
+{
+       u8 buf[2];
+       buf[0] = reg;
+       buf[1] = data;
+       __VISendI2CData(SLAVE_AVE,buf,2);
+       udelay(2);
+}
+
+static void __VIWriteI2CRegister16(u8 reg, u16 data)
+{
+       u8 buf[3];
+       buf[0] = reg;
+       buf[1] = data >> 8;
+       buf[2] = data & 0xFF;
+       __VISendI2CData(SLAVE_AVE,buf,3);
+       udelay(2);
+}
+
+static void __VIWriteI2CRegister32(u8 reg, u32 data)
+{
+       u8 buf[5];
+       buf[0] = reg;
+       buf[1] = data >> 24;
+       buf[2] = (data >> 16) & 0xFF;
+       buf[3] = (data >> 8) & 0xFF;
+       buf[4] = data & 0xFF;
+       __VISendI2CData(SLAVE_AVE,buf,5);
+       udelay(2);
+}
+
+static void __VIWriteI2CRegisterBuf(u8 reg, int size, u8 *data)
+{
+       u8 buf[0x100];
+       buf[0] = reg;
+       memcpy(&buf[1], data, size);
+       __VISendI2CData(SLAVE_AVE,buf,size+1);
+       udelay(2);
+}
+
+static void __VISetYUVSEL(u8 dtvstatus)
+{
+       int vdacFlagRegion;
+       switch(video_mode) {
+       case VIDEO_640X480_NTSCi_YUV16:
+       case VIDEO_640X480_NTSCp_YUV16:
+       default:
+               vdacFlagRegion = 0;
+               break;
+       case VIDEO_640X480_PAL50_YUV16:
+       case VIDEO_640X480_PAL60_YUV16:
+               vdacFlagRegion = 2;
+               break;
+       }
+       __VIWriteI2CRegister8(0x01, (dtvstatus<<5) | (vdacFlagRegion&0x1f));
+}
+
+static void __VISetFilterEURGB60(u8 enable)
+{
+       __VIWriteI2CRegister8(0x6e, enable);
+}
+
+void VISetupEncoder(void)
+{
+       u8 macrobuf[0x1a];
+
+       u8 gamma[0x21] = {
+               0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00,
+               0x10, 0x00, 0x10, 0x00, 0x10, 0x20, 0x40, 0x60,
+               0x80, 0xa0, 0xeb, 0x10, 0x00, 0x20, 0x00, 0x40,
+               0x00, 0x60, 0x00, 0x80, 0x00, 0xa0, 0x00, 0xeb,
+               0x00
+       };
+
+       u8 dtv;
+
+       //tv = VIDEO_GetCurrentTvMode();
+       dtv = read16(R_VIDEO_VISEL) & 1;
+       //oldDtvStatus = dtv;
+
+       // SetRevolutionModeSimple
+
+       VI_debug("DTV status: %d\n", dtv);
+
+       memset(macrobuf, 0, 0x1a);
+
+       __VIWriteI2CRegister8(0x6a, 1);
+       __VIWriteI2CRegister8(0x65, 1);
+       __VISetYUVSEL(dtv);
+       __VIWriteI2CRegister8(0x00, 0);
+       __VIWriteI2CRegister16(0x71, 0x8e8e);
+       __VIWriteI2CRegister8(0x02, 7);
+       __VIWriteI2CRegister16(0x05, 0x0000);
+       __VIWriteI2CRegister16(0x08, 0x0000);
+       __VIWriteI2CRegister32(0x7A, 0x00000000);
+
+       // Macrovision crap
+       __VIWriteI2CRegisterBuf(0x40, sizeof(macrobuf), macrobuf);
+
+       // Sometimes 1 in RGB mode? (reg 1 == 3)
+       __VIWriteI2CRegister8(0x0A, 0);
+
+       __VIWriteI2CRegister8(0x03, 1);
+
+       __VIWriteI2CRegisterBuf(0x10, sizeof(gamma), gamma);
+
+       __VIWriteI2CRegister8(0x04, 1);
+       __VIWriteI2CRegister32(0x7A, 0x00000000);
+       __VIWriteI2CRegister16(0x08, 0x0000);
+       __VIWriteI2CRegister8(0x03, 1);
+
+       //if(tv==VI_EURGB60) __VISetFilterEURGB60(1);
+       //else
+       __VISetFilterEURGB60(0);
+
+       //oldTvStatus = tv;
+}
+
diff --git a/video_low.h b/video_low.h
new file mode 100644 (file)
index 0000000..96f15c2
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+       BootMii - a Free Software replacement for the Nintendo/BroadOn bootloader.
+       low-level video support for the BootMii UI
+
+Copyright (C) 2008, 2009       Hector Martin "marcan" <marcan@marcansoft.com>
+Copyright (C) 2009                     Haxx Enterprises <bushing@gmail.com>
+Copyright (C) 2009             Sven Peter <svenpeter@gmail.com>
+# This code is licensed to you under the terms of the GNU GPL, version 2;
+# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
+
+Some routines and initialization constants originally came from the
+"GAMECUBE LOW LEVEL INFO" document and sourcecode released by Titanik
+of Crazy Nation and the GC Linux project.
+*/
+
+#ifndef VIDEO_LOW_H
+#define VIDEO_LOW_H
+
+#define MEM_VIDEO_BASE               (0xCC002000)           ///< Memory address of Video Interface
+#define MEM_VIDEO_BASE_PTR           (u32*)MEM_VIDEO_BASE   ///< Pointer to Video Interface
+
+// 32-bit-wide registers
+#define R_VIDEO_VTIMING              (MEM_VIDEO_BASE+0x00)   ///< Vertical timing.
+#define R_VIDEO_STATUS1              (MEM_VIDEO_BASE+0x02)   ///< Status? register location.
+#define R_VIDEO_PSB_ODD              (MEM_VIDEO_BASE+0x00)   ///< Postblank odd.
+#define R_VIDEO_PRB_ODD              (MEM_VIDEO_BASE+0x00)   ///< Preblank odd.
+#define R_VIDEO_PSB_EVEN             (MEM_VIDEO_BASE+0x00)   ///< Postblank even.
+#define R_VIDEO_PRB_EVEN             (MEM_VIDEO_BASE+0x00)   ///< Preblank even.
+#define R_VIDEO_FRAMEBUFFER_1        (MEM_VIDEO_BASE+0x1C)   ///< Framebuffer1 register location.
+#define R_VIDEO_FRAMEBUFFER_2        (MEM_VIDEO_BASE+0x24)   ///< Framebuffer2 register location.
+// 16-bit-wide registers
+#define R_VIDEO_HALFLINE_1           (MEM_VIDEO_BASE+0x2C)   ///< HalfLine1 register location.
+#define R_VIDEO_HALFLINE_2           (MEM_VIDEO_BASE+0x2E)   ///< HalfLine2 register location.
+#define R_VIDEO_STATUS               (MEM_VIDEO_BASE+0x6C)   ///< VideoStatus register location.
+#define R_VIDEO_VISEL                (MEM_VIDEO_BASE+0x6E)   // cable detect
+
+// Constants for VIDEO_Init()
+#define VIDEO_640X480_NTSCi_YUV16    (0)
+#define VIDEO_640X480_PAL50_YUV16    (1)
+#define VIDEO_640X480_PAL60_YUV16    (2)
+#define VIDEO_640X480_NTSCp_YUV16    (3)
+
+// Constants for VIDEO_SetFrameBuffer
+#define VIDEO_FRAMEBUFFER_1          (1)
+#define VIDEO_FRAMEBUFFER_2          (2)
+#define VIDEO_FRAMEBUFFER_BOTH       (0)
+
+void VIDEO_Init            (int VideoMode);
+void VIDEO_SetFrameBuffer  (void *FrameBufferAddr);
+void VIDEO_WaitVSync       (void);
+void VIDEO_BlackOut        (void);
+void VIDEO_Shutdown        (void);
+void VISetupEncoder        (void);
+
+#endif /* VIDEO_H */
+