From b2f4e0c1b4129b40e1fcf3a1de53f9a5de4d1350 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 25 Oct 2020 21:10:20 -0700 Subject: [PATCH] exo: add sdmmc test program --- exosphere/sdmmc_test/Makefile | 108 ++++++++++ exosphere/sdmmc_test/sdmmc_test.ld | 194 ++++++++++++++++++ exosphere/sdmmc_test/sdmmc_test.specs | 7 + .../sdmmc_test/source/sdmmc_test_main.cpp | 93 +++++++++ .../sdmmc_test/source/sdmmc_test_start.s | 37 ++++ exosphere/warmboot/source/warmboot_start.s | 2 +- libraries/libexosphere/arm.mk | 1 + .../libexosphere/source/libc/libexo_cxx.cpp | 27 +++ .../source/libc/libgcc_division.arch.arm.c | 160 +++++++++++++++ .../libc/libgcc_division_asm.arch.arm.s | 30 +++ .../source/libc/libgcc_thumb_case.arch.arm.s | 35 ++++ 11 files changed, 693 insertions(+), 1 deletion(-) create mode 100644 exosphere/sdmmc_test/Makefile create mode 100644 exosphere/sdmmc_test/sdmmc_test.ld create mode 100644 exosphere/sdmmc_test/sdmmc_test.specs create mode 100644 exosphere/sdmmc_test/source/sdmmc_test_main.cpp create mode 100644 exosphere/sdmmc_test/source/sdmmc_test_start.s create mode 100644 libraries/libexosphere/source/libc/libexo_cxx.cpp create mode 100644 libraries/libexosphere/source/libc/libgcc_division.arch.arm.c create mode 100644 libraries/libexosphere/source/libc/libgcc_division_asm.arch.arm.s create mode 100644 libraries/libexosphere/source/libc/libgcc_thumb_case.arch.arm.s diff --git a/exosphere/sdmmc_test/Makefile b/exosphere/sdmmc_test/Makefile new file mode 100644 index 000000000..4bfed891e --- /dev/null +++ b/exosphere/sdmmc_test/Makefile @@ -0,0 +1,108 @@ +#--------------------------------------------------------------------------------- +# Define the atmosphere board and cpu +#--------------------------------------------------------------------------------- +export ATMOSPHERE_BOARD := nx-hac-001 +export ATMOSPHERE_CPU := arm7tdmi + +#--------------------------------------------------------------------------------- +# pull in common atmosphere configuration +#--------------------------------------------------------------------------------- +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 ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) +export DEPSDIR := $(CURDIR)/$(BUILD) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(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) + +#--------------------------------------------------------------------------------- +# 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)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) check_libexo + +$(BUILD): check_libexo + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +check_libexo: + @$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).bin + +$(OUTPUT).bin : $(OUTPUT).elf + $(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@ + @echo built ... $(notdir $@) + +$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.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/exosphere/sdmmc_test/sdmmc_test.ld b/exosphere/sdmmc_test/sdmmc_test.ld new file mode 100644 index 000000000..181d33330 --- /dev/null +++ b/exosphere/sdmmc_test/sdmmc_test.ld @@ -0,0 +1,194 @@ +OUTPUT_ARCH(arm) +ENTRY(_ZN3ams10sdmmc_test5StartEv) + +MEMORY +{ + NULL : ORIGIN = 0, LENGTH = 4K + test_fw : ORIGIN = 0x40010000, LENGTH = 32K +} + + +SECTIONS +{ + /* =========== CODE section =========== */ + PROVIDE(__start__ = ORIGIN(test_fw)); + . = __start__; + __code_start = . ; + + .crt0 : + { + KEEP (*(.crt0 .crt0.*)) + . = ALIGN(8); + } >test_fw + + .vectors : + { + KEEP (*(.vectors .vectors.*)) + . = ALIGN(8); + } >test_fw + + .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); + } >test_fw + + .init : + { + KEEP( *(.init) ) + . = ALIGN(8); + } >test_fw + + .plt : + { + *(.plt) + *(.iplt) + . = ALIGN(8); + } >test_fw + + .fini : + { + KEEP( *(.fini) ) + . = ALIGN(8); + } >test_fw + + + /* =========== RODATA section =========== */ + . = ALIGN(8); + __rodata_start = . ; + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + . = ALIGN(8); + } >test_fw + + .eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >test_fw + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >test_fw + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >test_fw + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >test_fw + + .hash : { *(.hash) } >test_fw + + /* =========== DATA section =========== */ + . = ALIGN(8); + __data_start = . ; + + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >test_fw + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >test_fw + .gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >test_fw + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >test_fw + + .preinit_array ALIGN(8) : + { + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + } >test_fw + + .init_array ALIGN(8) : + { + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + } >test_fw + + .fini_array ALIGN(8) : + { + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + } >test_fw + + .ctors ALIGN(8) : + { + KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >test_fw + + .dtors ALIGN(8) : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >test_fw + + __got_start__ = .; + + .got : { *(.got) *(.igot) } >test_fw + .got.plt : { *(.got.plt) *(.igot.plt) } >test_fw + + __got_end__ = .; + + .data ALIGN(8) : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } >test_fw + + __bss_start__ = .; + .bss ALIGN(8) : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(16); + } >test_fw + __bss_end__ = .; + + __end__ = ABSOLUTE(.) ; + + __total_size__ = (__end__ - __start__); + + __stack_top__ = 0x40031000; + __stack_bottom__ = 0x40030000; + + /* ================== + ==== 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/exosphere/sdmmc_test/sdmmc_test.specs b/exosphere/sdmmc_test/sdmmc_test.specs new file mode 100644 index 000000000..72d846e06 --- /dev/null +++ b/exosphere/sdmmc_test/sdmmc_test.specs @@ -0,0 +1,7 @@ +%rename link old_link + +*link: +%(old_link) -T %:getenv(TOPDIR /sdmmc_test.ld) --gc-sections --nmagic -nostdlib -nostartfiles + +*startfile: +crti%O%s crtbegin%O%s diff --git a/exosphere/sdmmc_test/source/sdmmc_test_main.cpp b/exosphere/sdmmc_test/source/sdmmc_test_main.cpp new file mode 100644 index 000000000..e03e672d5 --- /dev/null +++ b/exosphere/sdmmc_test/source/sdmmc_test_main.cpp @@ -0,0 +1,93 @@ +/* + * 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::sdmmc_test { + + namespace { + + constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress(); + + constexpr inline auto Port = sdmmc::Port_Mmc0; + alignas(8) constinit u8 g_mmc_work_buffer[sdmmc::MmcWorkBufferSize]; + + constexpr inline u32 SectorIndex = 0; + constexpr inline u32 SectorCount = 2; + + NORETURN void PmcMainReboot() { + /* Write enable to MAIN_RESET. */ + reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE)); + + /* Wait forever until we're reset. */ + AMS_INFINITE_LOOP(); + } + + void CheckResult(const Result result) { + volatile u32 * const DEBUG = reinterpret_cast(0x4003C000); + if (R_FAILED(result)) { + DEBUG[1] = result.GetValue(); + PmcMainReboot(); + } + } + + } + + void Main() { + /* Debug signaler. */ + volatile u32 * const DEBUG = reinterpret_cast(0x4003C000); + DEBUG[0] = 0; + DEBUG[1] = 0xAAAAAAAA; + + /* Initialize sdmmc library. */ + sdmmc::Initialize(Port); + DEBUG[0] = 1; + + sdmmc::SetMmcWorkBuffer(Port, g_mmc_work_buffer, sizeof(g_mmc_work_buffer)); + DEBUG[0] = 2; + + Result result = sdmmc::Activate(Port); + DEBUG[0] = 3; + CheckResult(result); + + /* Select user data partition. */ + result = sdmmc::SelectMmcPartition(Port, sdmmc::MmcPartition_UserData); + DEBUG[0] = 4; + CheckResult(result); + + /* Read the first two sectors from disk. */ + void * const sector_dst = reinterpret_cast(0x40038000); + result = sdmmc::Read(sector_dst, SectorCount * sdmmc::SectorSize, Port, SectorIndex, SectorCount); + DEBUG[0] = 5; + CheckResult(result); + + /* Perform a reboot. */ + DEBUG[1] = 0; + PmcMainReboot(); + } + + NORETURN void ExceptionHandler() { + PmcMainReboot(); + } + +} + +namespace ams::diag { + + void AbortImpl() { + sdmmc_test::ExceptionHandler(); + } + +} diff --git a/exosphere/sdmmc_test/source/sdmmc_test_start.s b/exosphere/sdmmc_test/source/sdmmc_test_start.s new file mode 100644 index 000000000..69c1204f7 --- /dev/null +++ b/exosphere/sdmmc_test/source/sdmmc_test_start.s @@ -0,0 +1,37 @@ +/* + * 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.text._ZN3ams10sdmmc_test5StartEv, "ax", %progbits +.align 3 +.global _ZN3ams10sdmmc_test5StartEv +_ZN3ams10sdmmc_test5StartEv: + /* Switch to system mode, mask all interrupts, clear all flags */ + msr cpsr_cxsf, #0xDF + + /* Set the stack pointer. */ + ldr sp, =__stack_top__ + + /* Set our link register to the exception handler. */ + ldr lr, =_ZN3ams10sdmmc_test16ExceptionHandlerEv + + /* Call init array functions. */ + bl __libc_init_array + + /* Invoke main. */ + b _ZN3ams10sdmmc_test4MainEv + + /* Infinite loop. */ + 2: b 2b \ No newline at end of file diff --git a/exosphere/warmboot/source/warmboot_start.s b/exosphere/warmboot/source/warmboot_start.s index 18070e04a..dc6a56c2e 100644 --- a/exosphere/warmboot/source/warmboot_start.s +++ b/exosphere/warmboot/source/warmboot_start.s @@ -30,7 +30,7 @@ _ZN3ams8warmboot5StartEv: /* Invoke main. */ ldr r0, =_metadata - bl _ZN3ams8warmboot4MainEPKNS0_8MetadataE + b _ZN3ams8warmboot4MainEPKNS0_8MetadataE /* Infinite loop. */ 1: b 1b \ No newline at end of file diff --git a/libraries/libexosphere/arm.mk b/libraries/libexosphere/arm.mk index f82386e0f..a9b50909c 100644 --- a/libraries/libexosphere/arm.mk +++ b/libraries/libexosphere/arm.mk @@ -112,6 +112,7 @@ $(OFILES) : $(GCH_FILES) $(OFILES_SRC) : $(HFILES_BIN) libc.o: CFLAGS += -fno-builtin -fno-lto +libgcc_division.arch.arm.o: CFLAGS += -fno-builtin -fno-lto #--------------------------------------------------------------------------------- %_bin.h %.bin.o : %.bin diff --git a/libraries/libexosphere/source/libc/libexo_cxx.cpp b/libraries/libexosphere/source/libc/libexo_cxx.cpp new file mode 100644 index 000000000..64efc6786 --- /dev/null +++ b/libraries/libexosphere/source/libc/libexo_cxx.cpp @@ -0,0 +1,27 @@ +/* + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +/* cxx implementation details to be stubbed here, as needed. */ +void __cxa_pure_virtual() { AMS_ABORT("pure virtual function call"); } + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/libraries/libexosphere/source/libc/libgcc_division.arch.arm.c b/libraries/libexosphere/source/libc/libgcc_division.arch.arm.c new file mode 100644 index 000000000..22bc673a1 --- /dev/null +++ b/libraries/libexosphere/source/libc/libgcc_division.arch.arm.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2014, STMicroelectronics International N.V. + */ + +/* + * Form ABI specifications: + * int __aeabi_idiv(int numerator, int denominator); + * unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator); + * + * typedef struct { int quot; int rem; } idiv_return; + * typedef struct { unsigned quot; unsigned rem; } uidiv_return; + * + * __value_in_regs idiv_return __aeabi_idivmod(int numerator, + * int *denominator); + * __value_in_regs uidiv_return __aeabi_uidivmod(unsigned *numerator, + * unsigned denominator); + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* struct qr - stores qutient/remainder to handle divmod EABI interfaces. */ +struct qr { + unsigned q; /* computed quotient */ + unsigned r; /* computed remainder */ + unsigned q_n; /* specficies if quotient shall be negative */ + unsigned r_n; /* specficies if remainder shall be negative */ +}; + +static void uint_div_qr(unsigned numerator, unsigned denominator, + struct qr *qr); + +/* returns in R0 and R1 by tail calling an asm function */ +unsigned __aeabi_uidivmod(unsigned numerator, unsigned denominator); + +unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator); + +/* returns in R0 and R1 by tail calling an asm function */ +signed __aeabi_idivmod(signed numerator, signed denominator); + +signed __aeabi_idiv(signed numerator, signed denominator); + +/* + * __ste_idivmod_ret_t __aeabi_idivmod(signed numerator, signed denominator) + * Numerator and Denominator are received in R0 and R1. + * Where __ste_idivmod_ret_t is returned in R0 and R1. + * + * __ste_uidivmod_ret_t __aeabi_uidivmod(unsigned numerator, + * unsigned denominator) + * Numerator and Denominator are received in R0 and R1. + * Where __ste_uidivmod_ret_t is returned in R0 and R1. + */ +#ifdef __GNUC__ +signed ret_idivmod_values(signed quotient, signed remainder); +unsigned ret_uidivmod_values(unsigned quotient, unsigned remainder); +#else +#error "Compiler not supported" +#endif + +static void division_qr(unsigned n, unsigned p, struct qr *qr) +{ + unsigned i = 1, q = 0; + if (p == 0) { + qr->r = 0xFFFFFFFF; /* division by 0 */ + return; + } + + while ((p >> 31) == 0) { + i = i << 1; /* count the max division steps */ + p = p << 1; /* increase p until it has maximum size*/ + } + + while (i > 0) { + q = q << 1; /* write bit in q at index (size-1) */ + if (n >= p) + { + n -= p; + q++; + } + p = p >> 1; /* decrease p */ + i = i >> 1; /* decrease remaining size in q */ + } + qr->r = n; + qr->q = q; +} + +static void uint_div_qr(unsigned numerator, unsigned denominator, struct qr *qr) +{ + + division_qr(numerator, denominator, qr); + + /* negate quotient and/or remainder according to requester */ + if (qr->q_n) + qr->q = -qr->q; + if (qr->r_n) + qr->r = -qr->r; +} + +unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator) +{ + struct qr qr = { .q_n = 0, .r_n = 0 }; + + uint_div_qr(numerator, denominator, &qr); + + return qr.q; +} + +unsigned __aeabi_uidivmod(unsigned numerator, unsigned denominator) +{ + struct qr qr = { .q_n = 0, .r_n = 0 }; + + uint_div_qr(numerator, denominator, &qr); + + return ret_uidivmod_values(qr.q, qr.r); +} + +signed __aeabi_idiv(signed numerator, signed denominator) +{ + struct qr qr = { .q_n = 0, .r_n = 0 }; + + if (((numerator < 0) && (denominator > 0)) || + ((numerator > 0) && (denominator < 0))) + qr.q_n = 1; /* quotient shall be negate */ + if (numerator < 0) { + numerator = -numerator; + qr.r_n = 1; /* remainder shall be negate */ + } + if (denominator < 0) + denominator = -denominator; + + uint_div_qr(numerator, denominator, &qr); + + return qr.q; +} + +signed __aeabi_idivmod(signed numerator, signed denominator) +{ + struct qr qr = { .q_n = 0, .r_n = 0 }; + + if (((numerator < 0) && (denominator > 0)) || + ((numerator > 0) && (denominator < 0))) + qr.q_n = 1; /* quotient shall be negate */ + if (numerator < 0) { + numerator = -numerator; + qr.r_n = 1; /* remainder shall be negate */ + } + if (denominator < 0) + denominator = -denominator; + + uint_div_qr(numerator, denominator, &qr); + + return ret_idivmod_values(qr.q, qr.r); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif \ No newline at end of file diff --git a/libraries/libexosphere/source/libc/libgcc_division_asm.arch.arm.s b/libraries/libexosphere/source/libc/libgcc_division_asm.arch.arm.s new file mode 100644 index 000000000..a0acbace0 --- /dev/null +++ b/libraries/libexosphere/source/libc/libgcc_division_asm.arch.arm.s @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2014, STMicroelectronics International N.V. + */ + +/* + * signed ret_idivmod_values(signed quot, signed rem); + * return quotient and remaining the EABI way (regs r0,r1) + */ +.section .text.ret_idivmod_values, "ax", %progbits +.globl ret_idivmod_values +.align 0 +.syntax unified +ret_idivmod_values: + bx lr +.type ret_idivmod_values, %function +.size ret_idivmod_values, .-ret_idivmod_values + +/* + * unsigned ret_uidivmod_values(unsigned quot, unsigned rem); + * return quotient and remaining the EABI way (regs r0,r1) + */ +.section .text.ret_uidivmod_values, "ax", %progbits +.globl ret_uidivmod_values +.align 0 +.syntax unified +ret_uidivmod_values: + bx lr +.type ret_uidivmod_values, %function +.size ret_uidivmod_values, .-ret_uidivmod_values diff --git a/libraries/libexosphere/source/libc/libgcc_thumb_case.arch.arm.s b/libraries/libexosphere/source/libc/libgcc_thumb_case.arch.arm.s new file mode 100644 index 000000000..2658ad61b --- /dev/null +++ b/libraries/libexosphere/source/libc/libgcc_thumb_case.arch.arm.s @@ -0,0 +1,35 @@ +/* Copyright (C) 1995-2018 Free Software Foundation, Inc. +This file 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 3, or (at your option) any +later version. +This file 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. +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + + +.section .text.__gnu_thumb1_case_uqi, "ax", %progbits +.globl __gnu_thumb1_case_uqi +.align 0 +.thumb_func +.syntax unified +__gnu_thumb1_case_uqi: + push {r1} + mov r1, lr + lsrs r1, r1, #1 + lsls r1, r1, #1 + ldrb r1, [r1, r0] + lsls r1, r1, #1 + add lr, lr, r1 + pop {r1} + bx lr +.type __gnu_thumb1_case_uqi, %function +.size __gnu_thumb1_case_uqi, .-__gnu_thumb1_case_uqi