diff --git a/fusee_cpp/loader_stub/Makefile b/fusee_cpp/loader_stub/Makefile
new file mode 100644
index 000000000..8cd08d11f
--- /dev/null
+++ b/fusee_cpp/loader_stub/Makefile
@@ -0,0 +1,154 @@
+#---------------------------------------------------------------------------------
+# Define the atmosphere board and cpu
+#---------------------------------------------------------------------------------
+export ATMOSPHERE_BOARD := nx-hac-001
+export ATMOSPHERE_CPU := arm7tdmi
+
+#---------------------------------------------------------------------------------
+# pull in common atmosphere configuration
+#---------------------------------------------------------------------------------
+THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
+CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(__RECURSIVE__),1)
+#---------------------------------------------------------------------------------
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \
+ $(foreach dir,$(DATA),$(CURDIR)/$(dir))
+
+CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
+CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
+SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
+BINFILES :=
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES_BIN := $(addsuffix .o,$(BINFILES))
+export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+export OFILES := $(OFILES_BIN) $(OFILES_SRC) program.lz4.o
+export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES))))
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I.
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
+
+export TOPDIR := $(CURRENT_DIRECTORY)
+
+OUTPUT_BASE := $(TOPDIR)/$(notdir $(TOPDIR))
+
+#---------------------------------------------------------------------------------
+
+ATMOSPHERE_BUILD_CONFIGS :=
+all: release
+
+define ATMOSPHERE_ADD_TARGET
+
+ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
+
+$(strip $1): check_libexo_$(strip $1) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1)
+ @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(OUTPUT_BASE)$(strip $2) $(3) \
+ ATMOSPHERE_BUILD_TARGET_IDENTIFIER=$(strip $1) \
+ ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX=$(strip $2) \
+ DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \
+ LIBEXOSPHERE_NAME=exosphere$(strip $2) \
+ --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \
+ -f $$(THIS_MAKEFILE)
+
+check_libexo_$(strip $1):
+ @$$(MAKE) --no-print-directory -C $$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere $$(ATMOSPHERE_ARCH_NAME)-$(strip $1)
+
+clean-$(strip $1):
+ @echo clean $(strip $1) ...
+ @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(OUTPUT_BASE)$(strip $2).bin $$(OUTPUT_BASE)$(strip $2).elf
+
+endef
+
+$(eval $(call ATMOSPHERE_ADD_TARGET, release, , \
+ ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS" \
+))
+
+$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug, \
+ ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \
+))
+
+$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \
+ ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \
+))
+
+$(ATMOSPHERE_BUILD_DIR)/%:
+ @[ -d $@ ] || mkdir -p $@
+
+#---------------------------------------------------------------------------------
+clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
+
+.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config))
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+
+$(OUTPUT).bin : $(OUTPUT).elf
+ $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@
+ @echo built ... $(notdir $@)
+
+$(OUTPUT).elf : $(OFILES)
+
+$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/lib$(LIBEXOSPHERE_NAME).a
+
+program.lz4.o: program_lz4.h
+
+program_lz4.h: $(TOPDIR)/../program/program$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4
+ @echo $(notdir $<)
+ @rm -rf tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)
+ @mkdir -p tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)
+ @cp $(TOPDIR)/../program/program$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4 tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/program.lz4
+ @bin2s -a 8 -H program_lz4.h tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/program.lz4 | $(AS) -o program.lz4.o
+ @rm -rf tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)
+
+$(TOPDIR)/../program/program$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4:
+ @$(MAKE) __RECURSIVE__=0 --no-print-directory -C $(TOPDIR)/../program $(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)
+
+%.elf:
+ @echo linking $(notdir $@)
+ $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
+ @$(NM) -CSn $@ > $(notdir $*.lst)
+
+$(OFILES_SRC) : $(HFILES_BIN) program_lz4.h
+
+#---------------------------------------------------------------------------------
+# you need a rule like this for each extension you use as binary data
+#---------------------------------------------------------------------------------
+%.bin.o %_bin.h: %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/fusee_cpp/loader_stub/loader_stub.ld b/fusee_cpp/loader_stub/loader_stub.ld
new file mode 100644
index 000000000..97a1d9ae5
--- /dev/null
+++ b/fusee_cpp/loader_stub/loader_stub.ld
@@ -0,0 +1,125 @@
+OUTPUT_ARCH(arm)
+ENTRY(_ZN3ams6nxboot6loader5StartEv)
+
+MEMORY
+{
+ NULL : ORIGIN = 0, LENGTH = 4K
+ data : ORIGIN = 0x40010000, LENGTH = 0x28000
+ loader_stub : ORIGIN = 0x4003D000, LENGTH = 4K
+}
+
+
+SECTIONS
+{
+ /* =========== CODE section =========== */
+
+ . = ORIGIN(data);
+ __data_start__ = . ;
+
+ .crt0 :
+ {
+ KEEP(*(.crt0 .crt0.*))
+ . = ALIGN(8);
+ } >data
+
+ /* =========== RODATA section =========== */
+ . = ALIGN(8);
+
+ .rodata :
+ {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ . = ALIGN(8);
+ } >data
+
+ .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >data
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >data
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >data
+ .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >data
+
+ .hash : { *(.hash) } >data
+
+ /* =========== DATA section =========== */
+ . = ALIGN(8);
+
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >data
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >data
+ .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >data
+ .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >data
+
+ .data ALIGN(8) :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ } >data
+
+ __bss_start__ = .;
+ .bss (NOLOAD) :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(16);
+ } >data :NONE
+ __bss_end__ = .;
+
+ __data_end__ = ABSOLUTE(.);
+
+
+ .loader_stub :
+ {
+ . = ALIGN(32);
+ PROVIDE (__loader_stub_start__ = ABSOLUTE(.));
+ PROVIDE (__loader_stub_lma__ = LOADADDR(.loader_stub));
+ fusee_loader_main.o(.text*)
+ fusee_loader_uncompress.o(.text*)
+ fusee_loader_error.o(.text*)
+ fusee_loader_main.o(.rodata*)
+ fusee_loader_uncompress.o(.rodata*)
+ fusee_loader_error.o(.rodata*)
+ fusee_loader_main.o(.data*)
+ fusee_loader_uncompress.o(.data*)
+ fusee_loader_error.o(.data*)
+ . = ALIGN(32);
+ PROVIDE (__loader_stub_end__ = ABSOLUTE(.));
+ } >loader_stub AT>data
+
+ /* ==================
+ ==== Metadata ====
+ ================== */
+
+ /* Discard sections that difficult post-processing */
+ /DISCARD/ : { *(.group .comment .note .interp) }
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+}
\ No newline at end of file
diff --git a/fusee_cpp/loader_stub/loader_stub.specs b/fusee_cpp/loader_stub/loader_stub.specs
new file mode 100644
index 000000000..261cffccd
--- /dev/null
+++ b/fusee_cpp/loader_stub/loader_stub.specs
@@ -0,0 +1,4 @@
+%rename link old_link
+
+*link:
+%(old_link) -T %:getenv(TOPDIR /loader_stub.ld) --gc-sections --nmagic
\ No newline at end of file
diff --git a/fusee_cpp/loader_stub/source/fusee_loader_error.cpp b/fusee_cpp/loader_stub/source/fusee_loader_error.cpp
new file mode 100644
index 000000000..93d6b1859
--- /dev/null
+++ b/fusee_cpp/loader_stub/source/fusee_loader_error.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "fusee_loader_error.hpp"
+
+namespace ams::diag {
+
+ NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
+ AMS_UNUSED(file, line, func, expr, value, format);
+ ams::nxboot::loader::ErrorStop();
+ }
+
+ NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
+ AMS_UNUSED(file, line, func, expr, value);
+ ams::nxboot::loader::ErrorStop();
+ }
+
+ NORETURN void AbortImpl() {
+ ams::nxboot::loader::ErrorStop();
+ }
+
+}
+
+namespace ams::nxboot::loader {
+
+ NORETURN void ErrorStop() {
+ /* Halt ourselves. */
+ while (true) {
+ reg::Write(secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress() + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
+ FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, ENABLED));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/fusee_cpp/loader_stub/source/fusee_loader_error.hpp b/fusee_cpp/loader_stub/source/fusee_loader_error.hpp
new file mode 100644
index 000000000..83bfe6008
--- /dev/null
+++ b/fusee_cpp/loader_stub/source/fusee_loader_error.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#pragma once
+
+namespace ams::nxboot::loader {
+
+ NORETURN void ErrorStop();
+
+}
\ No newline at end of file
diff --git a/fusee_cpp/loader_stub/source/fusee_loader_main.cpp b/fusee_cpp/loader_stub/source/fusee_loader_main.cpp
new file mode 100644
index 000000000..f58d5e1bd
--- /dev/null
+++ b/fusee_cpp/loader_stub/source/fusee_loader_main.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "fusee_loader_uncompress.hpp"
+#include "program_lz4.h"
+
+namespace ams::nxboot::loader {
+
+ namespace {
+
+ constexpr uintptr_t ProgramImageBase = 0x40001000;
+ constexpr uintptr_t ProgramImageEnd = 0x4003D000;
+ constexpr size_t ProgramImageSizeMax = ProgramImageEnd - ProgramImageBase;
+
+ void CopyBackwards(void *dst, const void *src, size_t size) {
+ /* We want to copy 32-bits at a time from destination to source. */
+ const size_t words = util::DivideUp(size, sizeof(u32));
+
+ /* Convert to 32-bit pointers. */
+ u32 *dst_32 = static_cast(dst) + words;
+ const u32 *src_32 = static_cast(src) + words;
+
+ /* Copy data. */
+ for (size_t i = 0; i < words; ++i) {
+ *(--dst_32) = *(--src_32);
+ }
+ }
+
+ }
+
+ NORETURN void UncompressAndExecute(const void *program, size_t program_size) {
+ /* Relocate the compressed binary to a place where we can safely decompress it. */
+ void *relocated_program = reinterpret_cast(util::AlignDown(ProgramImageEnd - program_size, sizeof(u32)));
+ if (relocated_program != program) {
+ CopyBackwards(relocated_program, program, program_size);
+ }
+
+ /* Uncompress the program image. */
+ Uncompress(reinterpret_cast(ProgramImageBase), ProgramImageSizeMax, relocated_program, program_size);
+
+ /* Jump to the boot image. */
+ reinterpret_cast(ProgramImageBase)();
+
+ /* We will never reach this point. */
+ __builtin_unreachable();
+ }
+
+}
\ No newline at end of file
diff --git a/fusee_cpp/loader_stub/source/fusee_loader_start.s b/fusee_cpp/loader_stub/source/fusee_loader_start.s
new file mode 100644
index 000000000..70ced975e
--- /dev/null
+++ b/fusee_cpp/loader_stub/source/fusee_loader_start.s
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+
+.section .crt0._ZN3ams6nxboot6loader5StartEv, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot6loader5StartEv
+.type _ZN3ams6nxboot6loader5StartEv, %function
+_ZN3ams6nxboot6loader5StartEv:
+ /* Switch to system mode, mask all interrupts, clear all flags */
+ msr cpsr_cxsf, #0xDF
+
+ /* Relocate loader stub. */
+ ldr r0, =_ZN3ams6nxboot6loader5StartEv
+ adr r1, _ZN3ams6nxboot6loader5StartEv
+ ldr r2, =__loader_stub_lma__
+ sub r2, r2, r0
+ add r2, r2, r1
+
+ ldr r3, =__loader_stub_start__
+ ldr r4, =__loader_stub_end__
+ sub r4, r4, r3
+ 0:
+ ldmia r2!, {r5-r12}
+ stmia r3!, {r5-r12}
+ subs r4, #0x20
+ bne 0b
+
+ /* Set the stack pointer */
+ ldr sp, =0x40001000
+ mov fp, #0
+
+ /* Generate arguments. */
+ ldr r3, =program_lz4
+ ldr r4, =program_lz4_end
+ sub r4, r4, r3
+ sub r3, r3, r0
+ add r3, r3, r1
+ mov r0, r3
+ mov r1, r4
+
+ /* Jump to the loader stub. */
+ ldr r3, =_ZN3ams6nxboot6loader20UncompressAndExecuteEPKvj
+ bx r3
\ No newline at end of file
diff --git a/fusee_cpp/loader_stub/source/fusee_loader_uncompress.cpp b/fusee_cpp/loader_stub/source/fusee_loader_uncompress.cpp
new file mode 100644
index 000000000..6926c2046
--- /dev/null
+++ b/fusee_cpp/loader_stub/source/fusee_loader_uncompress.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "fusee_loader_uncompress.hpp"
+
+namespace ams::nxboot::loader {
+
+ namespace {
+
+ class Lz4Uncompressor {
+ private:
+ const u8 *src;
+ size_t src_size;
+ size_t src_offset;
+ u8 *dst;
+ size_t dst_size;
+ size_t dst_offset;
+ public:
+ Lz4Uncompressor(void *dst, size_t dst_size, const void *src, size_t src_size) : src(static_cast(src)), src_size(src_size), src_offset(0), dst(static_cast(dst)), dst_size(dst_size), dst_offset(0) {
+ /* ... */
+ }
+
+ void Uncompress() {
+ while (true) {
+ /* Read a control byte. */
+ const u8 control = this->ReadByte();
+
+ /* Copy what it specifies we should copy. */
+ this->Copy(this->GetCopySize(control >> 4));
+
+ /* If we've exceeded size, we're done. */
+ if (this->src_offset >= this->src_size) {
+ break;
+ }
+
+ /* Read the wide copy offset. */
+ u16 wide_offset = this->ReadByte();
+ AMS_ABORT_UNLESS(this->CanRead());
+ wide_offset |= (this->ReadByte() << 8);
+
+ /* Determine the copy size. */
+ const size_t wide_copy_size = this->GetCopySize(control & 0xF);
+
+ /* Copy bytes. */
+ const size_t end_offset = this->dst_offset + wide_copy_size + 4;
+ for (size_t cur_offset = this->dst_offset; cur_offset < end_offset; this->dst_offset = (++cur_offset)) {
+ AMS_ABORT_UNLESS(wide_offset <= cur_offset);
+
+ this->dst[cur_offset] = this->dst[cur_offset - wide_offset];
+ }
+ }
+ }
+ private:
+ u8 ReadByte() {
+ return this->src[this->src_offset++];
+ }
+
+ bool CanRead() const {
+ return this->src_offset < this->src_size;
+ }
+
+ size_t GetCopySize(u8 control) {
+ size_t size = control;
+
+ if (control >= 0xF) {
+ do {
+ AMS_ABORT_UNLESS(this->CanRead());
+ control = this->ReadByte();
+ size += control;
+ } while (control == 0xFF);
+ }
+
+ return size;
+ }
+
+ void Copy(size_t size) {
+ __builtin_memcpy(this->dst + this->dst_offset, this->src + this->src_offset, size);
+ this->dst_offset += size;
+ this->src_offset += size;
+ }
+ };
+
+ }
+
+ void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size) {
+ /* Create an execute a decompressor. */
+ Lz4Uncompressor(dst, dst_size, src, src_size).Uncompress();
+ }
+
+}
\ No newline at end of file
diff --git a/fusee_cpp/loader_stub/source/fusee_loader_uncompress.hpp b/fusee_cpp/loader_stub/source/fusee_loader_uncompress.hpp
new file mode 100644
index 000000000..802e77180
--- /dev/null
+++ b/fusee_cpp/loader_stub/source/fusee_loader_uncompress.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#pragma once
+
+namespace ams::nxboot::loader {
+
+ void Uncompress(void *dst, size_t dst_size, const void *src, size_t src_size);
+
+}
\ No newline at end of file
diff --git a/fusee_cpp/program/Makefile b/fusee_cpp/program/Makefile
new file mode 100644
index 000000000..f87cead16
--- /dev/null
+++ b/fusee_cpp/program/Makefile
@@ -0,0 +1,143 @@
+#---------------------------------------------------------------------------------
+# Define the atmosphere board and cpu
+#---------------------------------------------------------------------------------
+export ATMOSPHERE_BOARD := nx-hac-001
+export ATMOSPHERE_CPU := arm7tdmi
+
+#---------------------------------------------------------------------------------
+# pull in common atmosphere configuration
+#---------------------------------------------------------------------------------
+THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
+CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
+include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(__RECURSIVE__),1)
+#---------------------------------------------------------------------------------
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \
+ $(foreach dir,$(DATA),$(CURDIR)/$(dir))
+
+CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
+CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
+SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
+BINFILES :=
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES_BIN := $(addsuffix .o,$(BINFILES))
+export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+export OFILES := $(OFILES_BIN) $(OFILES_SRC)
+export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES))))
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I.
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
+
+export TOPDIR := $(CURRENT_DIRECTORY)
+
+OUTPUT_BASE := $(TOPDIR)/$(notdir $(TOPDIR))
+
+#---------------------------------------------------------------------------------
+
+ATMOSPHERE_BUILD_CONFIGS :=
+all: release
+
+define ATMOSPHERE_ADD_TARGET
+
+ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
+
+$(strip $1): check_libexo_$(strip $1) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1)
+ @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(OUTPUT_BASE)$(strip $2) $(3) \
+ DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \
+ LIBEXOSPHERE_NAME=exosphere$(strip $2) \
+ --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \
+ -f $$(THIS_MAKEFILE)
+
+check_libexo_$(strip $1):
+ @$$(MAKE) --no-print-directory -C $$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere $$(ATMOSPHERE_ARCH_NAME)-$(strip $1)
+
+clean-$(strip $1):
+ @echo clean $(strip $1) ...
+ @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(OUTPUT_BASE)$(strip $2).bin $$(OUTPUT_BASE)$(strip $2).elf
+
+endef
+
+$(eval $(call ATMOSPHERE_ADD_TARGET, release, , \
+ ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS" \
+))
+
+$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug, \
+ ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \
+))
+
+$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \
+ ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \
+))
+
+$(ATMOSPHERE_BUILD_DIR)/%:
+ @[ -d $@ ] || mkdir -p $@
+
+#---------------------------------------------------------------------------------
+clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
+
+.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config))
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+
+$(OUTPUT).lz4 : $(OUTPUT).bin
+ @python $(TOPDIR)/lz4_compress.py $(OUTPUT).bin $(OUTPUT).lz4
+ @echo built ... $(notdir $@)
+
+$(OUTPUT).bin : $(OUTPUT).elf
+ $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@
+ @echo built ... $(notdir $@)
+
+$(OUTPUT).elf : $(OFILES)
+
+$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/lib$(LIBEXOSPHERE_NAME).a
+
+%.elf:
+ @echo linking $(notdir $@)
+ $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
+ @$(NM) -CSn $@ > $(notdir $*.lst)
+
+$(OFILES_SRC) : $(HFILES_BIN)
+
+#---------------------------------------------------------------------------------
+# you need a rule like this for each extension you use as binary data
+#---------------------------------------------------------------------------------
+%.bin.o %_bin.h: %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/fusee_cpp/program/lz4_compress.py b/fusee_cpp/program/lz4_compress.py
new file mode 100644
index 000000000..2bf17a64d
--- /dev/null
+++ b/fusee_cpp/program/lz4_compress.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+import sys, lz4
+from struct import unpack as up
+
+def lz4_compress(data):
+ try:
+ import lz4.block as block
+ except ImportError:
+ block = lz4.LZ4_compress
+ return block.compress(data, 'high_compression', store_size=False)
+
+def main(argc, argv):
+ if argc != 3:
+ print('Usage: %s in out' % argv[0])
+ return 1
+ with open(argv[1], 'rb') as f:
+ data = f.read()
+ with open(argv[2], 'wb') as f:
+ f.write(lz4_compress(data))
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(len(sys.argv), sys.argv))
diff --git a/fusee_cpp/program/program.ld b/fusee_cpp/program/program.ld
new file mode 100644
index 000000000..5a6ea3adf
--- /dev/null
+++ b/fusee_cpp/program/program.ld
@@ -0,0 +1,184 @@
+OUTPUT_ARCH(arm)
+ENTRY(_ZN3ams6nxboot5StartEv)
+
+MEMORY
+{
+ NULL : ORIGIN = 0, LENGTH = 4K
+ main : ORIGIN = 0x40001000, LENGTH = 0x2B000
+ ovl : ORIGIN = 0x4002C000, LENGTH = 0x14000
+}
+
+
+SECTIONS
+{
+ /* =========== CODE section =========== */
+ PROVIDE(__start__ = ORIGIN(main));
+ . = __start__;
+ __main_start__ = . ;
+
+ .crt0 :
+ {
+ KEEP (*(.crt0 .crt0.*))
+ . = ALIGN(8);
+ } >main
+
+ .text :
+ {
+ *(.text.unlikely .text.*_unlikely .text.unlikely.*)
+ *(.text.exit .text.exit.*)
+ *(.text.startup .text.startup.*)
+ *(.text.hot .text.hot.*)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ . = ALIGN(8);
+ } >main
+
+ .init :
+ {
+ KEEP( *(.init) )
+ . = ALIGN(8);
+ } >main
+
+ .plt :
+ {
+ *(.plt)
+ *(.iplt)
+ . = ALIGN(8);
+ } >main
+
+ .fini :
+ {
+ KEEP( *(.fini) )
+ . = ALIGN(8);
+ } >main
+
+
+ /* =========== RODATA section =========== */
+ . = ALIGN(8);
+ __rodata_start = . ;
+
+ .rodata :
+ {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ . = ALIGN(8);
+ } >main
+
+ .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >main
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >main
+ .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >main
+
+ .hash : { *(.hash) } >main
+
+ /* =========== DATA section =========== */
+ . = ALIGN(8);
+ __data_start = . ;
+
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >main
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >main
+ .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >main
+ .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >main
+
+ .preinit_array ALIGN(8) :
+ {
+ PROVIDE (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE (__preinit_array_end = .);
+ } >main
+
+ .init_array ALIGN(8) :
+ {
+ PROVIDE (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE (__init_array_end = .);
+ } >main
+
+ .fini_array ALIGN(8) :
+ {
+ PROVIDE (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE (__fini_array_end = .);
+ } >main
+
+ .ctors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ } >main
+
+ .dtors ALIGN(8) :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ } >main
+
+ __got_start__ = .;
+
+ .got : { *(.got) *(.igot) } >main
+ .got.plt : { *(.got.plt) *(.igot.plt) } >main
+
+ __got_end__ = .;
+
+ .data ALIGN(8) :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ } >main
+
+ __bss_start__ = .;
+ .bss ALIGN(8) :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(16);
+ } >main
+ __bss_end__ = .;
+
+ __main_end__ = ABSOLUTE(.) ;
+
+ /* ==================
+ ==== Metadata ====
+ ================== */
+
+ /* Discard sections that difficult post-processing */
+ /DISCARD/ : { *(.group .comment .note .interp) }
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+}
\ No newline at end of file
diff --git a/fusee_cpp/program/program.specs b/fusee_cpp/program/program.specs
new file mode 100644
index 000000000..7d174b721
--- /dev/null
+++ b/fusee_cpp/program/program.specs
@@ -0,0 +1,4 @@
+%rename link old_link
+
+*link:
+%(old_link) -T %:getenv(TOPDIR /program.ld) --gc-sections --nmagic
\ No newline at end of file
diff --git a/fusee_cpp/program/source/fusee_crt0.cpp b/fusee_cpp/program/source/fusee_crt0.cpp
new file mode 100644
index 000000000..791faa796
--- /dev/null
+++ b/fusee_cpp/program/source/fusee_crt0.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+extern "C" void __libc_init_array();
+
+namespace ams::nxboot::crt0 {
+
+ void Initialize(uintptr_t bss_start, uintptr_t bss_end) {
+ /* TODO: Collect timing information? */
+
+ /* Clear bss. */
+ std::memset(reinterpret_cast(bss_start), 0, bss_end - bss_start);
+
+ /* Call init array. */
+ __libc_init_array();
+ }
+
+}
diff --git a/fusee_cpp/program/source/fusee_exception_handler.cpp b/fusee_cpp/program/source/fusee_exception_handler.cpp
new file mode 100644
index 000000000..d9cc0810c
--- /dev/null
+++ b/fusee_cpp/program/source/fusee_exception_handler.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "fusee_exception_handler.hpp"
+
+namespace ams::nxboot {
+
+ NORETURN void ErrorStop() {
+ /* Halt ourselves. */
+ while (true) {
+ reg::Write(secmon::MemoryRegionPhysicalDeviceFlowController.GetAddress() + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
+ FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, ENABLED));
+ }
+ }
+
+ NORETURN void ExceptionHandlerImpl(s32 which, u32 lr, u32 svc_lr) {
+ /* TODO */
+ ErrorStop();
+ }
+
+}
+
+namespace ams::diag {
+
+ NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
+ AMS_UNUSED(file, line, func, expr, value, format);
+ ams::nxboot::ErrorStop();
+ }
+
+ NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
+ AMS_UNUSED(file, line, func, expr, value);
+ ams::nxboot::ErrorStop();
+ }
+
+ NORETURN void AbortImpl() {
+ ams::nxboot::ErrorStop();
+ }
+
+}
\ No newline at end of file
diff --git a/fusee_cpp/program/source/fusee_exception_handler.hpp b/fusee_cpp/program/source/fusee_exception_handler.hpp
new file mode 100644
index 000000000..6695f1e5f
--- /dev/null
+++ b/fusee_cpp/program/source/fusee_exception_handler.hpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#pragma once
+
+namespace ams::nxboot::loader {
+
+ NORETURN void ExceptionHandler();
+
+ NORETURN void ExceptionHandler0();
+ NORETURN void ExceptionHandler1();
+ NORETURN void ExceptionHandler2();
+ NORETURN void ExceptionHandler3();
+ NORETURN void ExceptionHandler4();
+ NORETURN void ExceptionHandler5();
+ NORETURN void ExceptionHandler6();
+ NORETURN void ExceptionHandler7();
+
+}
\ No newline at end of file
diff --git a/fusee_cpp/program/source/fusee_exception_handler_asm.s b/fusee_cpp/program/source/fusee_exception_handler_asm.s
new file mode 100644
index 000000000..55836bf1d
--- /dev/null
+++ b/fusee_cpp/program/source/fusee_exception_handler_asm.s
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+.section .text._ZN3ams6nxboot17ExceptionHandlerNEl, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot17ExceptionHandlerNEl
+.type _ZN3ams6nxboot17ExceptionHandlerNEl, %function
+_ZN3ams6nxboot17ExceptionHandlerNEl:
+ /* Get the normal return address. */
+ mov r1, lr
+
+ /* Get the SVC return address. */
+ msr cpsr_cf, #0xD3
+ mov r2, lr
+
+ /* Invoke the exception handler in abort mode. */
+ msr cpsr_cf, #0xD7
+ b _ZN3ams6nxboot20ExceptionHandlerImplElmm
+
+
+.section .text._ZN3ams6nxboot16ExceptionHandlerEv, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot16ExceptionHandlerEv
+.type _ZN3ams6nxboot16ExceptionHandlerEv, %function
+_ZN3ams6nxboot16ExceptionHandlerEv:
+ mov r0, #-1
+ b _ZN3ams6nxboot17ExceptionHandlerNEl
+
+.section .text._ZN3ams6nxboot17ExceptionHandler0Ev, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot17ExceptionHandler0Ev
+.type _ZN3ams6nxboot17ExceptionHandler0Ev, %function
+_ZN3ams6nxboot17ExceptionHandler0Ev:
+ mov r0, #0
+ b _ZN3ams6nxboot17ExceptionHandlerNEl
+
+.section .text._ZN3ams6nxboot17ExceptionHandler1Ev, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot17ExceptionHandler1Ev
+.type _ZN3ams6nxboot17ExceptionHandler1Ev, %function
+_ZN3ams6nxboot17ExceptionHandler1Ev:
+ mov r0, #1
+ b _ZN3ams6nxboot17ExceptionHandlerNEl
+
+.section .text._ZN3ams6nxboot17ExceptionHandler2Ev, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot17ExceptionHandler2Ev
+.type _ZN3ams6nxboot17ExceptionHandler2Ev, %function
+_ZN3ams6nxboot17ExceptionHandler2Ev:
+ mov r0, #2
+ b _ZN3ams6nxboot17ExceptionHandlerNEl
+
+.section .text._ZN3ams6nxboot17ExceptionHandler3Ev, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot17ExceptionHandler3Ev
+.type _ZN3ams6nxboot17ExceptionHandler3Ev, %function
+_ZN3ams6nxboot17ExceptionHandler3Ev:
+ mov r0, #3
+ b _ZN3ams6nxboot17ExceptionHandlerNEl
+
+.section .text._ZN3ams6nxboot17ExceptionHandler4Ev, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot17ExceptionHandler4Ev
+.type _ZN3ams6nxboot17ExceptionHandler4Ev, %function
+_ZN3ams6nxboot17ExceptionHandler4Ev:
+ mov r0, #4
+ b _ZN3ams6nxboot17ExceptionHandlerNEl
+
+.section .text._ZN3ams6nxboot17ExceptionHandler5Ev, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot17ExceptionHandler5Ev
+.type _ZN3ams6nxboot17ExceptionHandler5Ev, %function
+_ZN3ams6nxboot17ExceptionHandler5Ev:
+ mov r0, #5
+ b _ZN3ams6nxboot17ExceptionHandlerNEl
+
+.section .text._ZN3ams6nxboot17ExceptionHandler6Ev, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot17ExceptionHandler6Ev
+.type _ZN3ams6nxboot17ExceptionHandler6Ev, %function
+_ZN3ams6nxboot17ExceptionHandler6Ev:
+ mov r0, #6
+ b _ZN3ams6nxboot17ExceptionHandlerNEl
+
+.section .text._ZN3ams6nxboot17ExceptionHandler7Ev, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot17ExceptionHandler7Ev
+.type _ZN3ams6nxboot17ExceptionHandler7Ev, %function
+_ZN3ams6nxboot17ExceptionHandler7Ev:
+ mov r0, #7
+ b _ZN3ams6nxboot17ExceptionHandlerNEl
diff --git a/fusee_cpp/program/source/fusee_main.cpp b/fusee_cpp/program/source/fusee_main.cpp
new file mode 100644
index 000000000..f57175850
--- /dev/null
+++ b/fusee_cpp/program/source/fusee_main.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+
+namespace ams::nxboot {
+
+ void Main() {
+ /* TODO */
+ AMS_INFINITE_LOOP();
+ }
+
+}
diff --git a/fusee_cpp/program/source/fusee_start.s b/fusee_cpp/program/source/fusee_start.s
new file mode 100644
index 000000000..d1af31440
--- /dev/null
+++ b/fusee_cpp/program/source/fusee_start.s
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018-2020 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+.section .crt0._ZN3ams6nxboot5StartEv, "ax", %progbits
+.arm
+.align 5
+.global _ZN3ams6nxboot5StartEv
+.type _ZN3ams6nxboot5StartEv, %function
+_ZN3ams6nxboot5StartEv:
+ /* Setup all registers as = 0. */
+ msr cpsr_f, #0xC0
+ mov r0, #0
+ mov r1, #0
+ mov r2, #0
+ mov r3, #0
+ mov r4, #0
+ mov r5, #0
+ mov r6, #0
+ mov r7, #0
+ mov r8, #0
+ mov r9, #0
+ mov r10, #0
+ mov r11, #0
+ mov r12, #0
+
+ /* Setup all modes as pointing to our exception handler. */
+ msr cpsr_cf, #0xDF
+ ldr sp, =0x40001000
+ ldr lr, =_ZN3ams6nxboot16ExceptionHandlerEv
+
+ msr cpsr_cf, #0xD2
+ ldr sp, =0x40001000
+ ldr lr, =_ZN3ams6nxboot16ExceptionHandlerEv
+
+ msr cpsr_cf, #0xD1
+ ldr sp, =0x40001000
+ ldr lr, =_ZN3ams6nxboot16ExceptionHandlerEv
+
+ msr cpsr_cf, #0xD7
+ ldr sp, =0x40001000
+ ldr lr, =_ZN3ams6nxboot16ExceptionHandlerEv
+
+ msr cpsr_cf, #0xDB
+ ldr sp, =0x40001000
+ ldr lr, =_ZN3ams6nxboot16ExceptionHandlerEv
+
+ msr cpsr_cf, #0xD3
+ ldr sp, =0x40001000
+ ldr lr, =_ZN3ams6nxboot16ExceptionHandlerEv
+
+ /* Perform runtime initialization. */
+ ldr r0, =__bss_start__
+ ldr r1, =__bss_end__
+ bl _ZN3ams6nxboot4crt010InitializeEjj
+
+ /* Perform nx boot procedure. */
+ bl _ZN3ams6nxboot4MainEv
diff --git a/libraries/config/templates/exosphere.mk b/libraries/config/templates/exosphere.mk
index 91d3ce84d..27bd718be 100644
--- a/libraries/config/templates/exosphere.mk
+++ b/libraries/config/templates/exosphere.mk
@@ -17,7 +17,7 @@ CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
else ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm)
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
-SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -fno-non-call-exceptions \
+SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -Werror -fno-non-call-exceptions \
-Wno-array-bounds \
-Wno-stringop-overflow \
-Wno-stringop-overread
diff --git a/libraries/libexosphere/arm.mk b/libraries/libexosphere/arm.mk
index 35592ce4b..6a42c37c6 100644
--- a/libraries/libexosphere/arm.mk
+++ b/libraries/libexosphere/arm.mk
@@ -16,7 +16,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk
#---------------------------------------------------------------------------------
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE
-SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Wextra -Werror -flto -fno-non-call-exceptions \
+SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -Wextra -Werror -flto -fno-non-call-exceptions \
-Wno-array-bounds \
-Wno-stringop-overflow \
-Wno-stringop-overread