#include "mtl/target.hpp" #include "mtl/fixed.hpp" TARGET_ARM_MODE namespace mtl { fixed fixed::operator/(fixed rhs) const { int32_t raw_result; asm( // This division implementation has two methods it can use. // The fastest uses a left shift followed by a single division. The value is shifted // first to preserve the decimal part. Unfortunately, this means large numerators // will cause the operation to overflow. In this case, a compatible method will be // used. This method uses two divisions, one to calculate the integral quotient, // and one to calculate the decimal part. Both these methods work for negative numbers as well. ".arm;" "movs r1, %[d];" // Load numerator and denominator, and check if negative or zero "beq 4f;" "movs r0, %[n];" "blt 1f;" "tst r0, #0x7e000000;" // Check if the numerator is large enough to overflow "bne 3f;" "b 2f;" "1:" // check_negative "mvn r2, r0;" // Check if the numerator is large enough to overflow. "tst r2, #0x7e000000;" "bne 3f;" "2:" // fast_div // Fast method "lsl r0, #6;" // Shift first to avoid truncation "swi #0x60000;" // GBA Div syscall "mov %[res], r0;" "b 5f;" "3:" // compat_div // Compatible method "swi #0x60000;" // Compute quotient and shift "lsl r2, r0, #6;" "mov r0, r1;" // Div syscall puts the modulus in r1, use it as the numerator "lsr r1, %[d], #6;" // Load the denominator again, shifted right to calculate decimal part "swi #0x60000;" "mov %[res], r2;" // Calculate the final result "add %[res], r0;" "b 5f;" "4:" // zero_div "teq %[n], %[d];" // Set result to largest possible negative/positive value. "movmi %[res], #0x80000000;" "movpl %[res], #0x7FFFFFFF;" "5:" : [res] "=r" (raw_result) : [n] "r" (x), [d] "r" (rhs.x) : "r0", "r1", "r2", "r3" ); return from_raw(raw_result); } } // namespace mtl TARGET_END_MODE