fpc/rtl/avr/math.inc
2019-06-02 15:50:41 +00:00

270 lines
6.7 KiB
PHP

{
This file is part of the Free Pascal run time library.
Copyright (c) 2008 by the Free Pascal development team.
Implementation of mathematical Routines (only for real)
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
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.
**********************************************************************}
// Based on restoring division algorithm
// Algorithm source document: Lecture notes by S. Galal and D. Pham, Division algorithms and hardware implementations.
// Link to documentation http://www.seas.ucla.edu/~ingrid/ee213a/lectures/division_presentV2.pdf
// Also refer to description on Wikipedia: https://en.wikipedia.org/wiki/Division_algorithm#Restoring_division
// Note that the algorithm automatically yields the following results for special cases:
// z div 0 = MAX(type)
// 0 div 0 = MAX(type)
// 0 div n = 0
// Checks for z = 0; n = [0,1]; n = z and n > z could shortcut the algorithm for speed-ups
// but would add extra code
// Perhaps add the checks depending on optimization settings?
// z (dividend) = q(quotient) x n(divisor) + p(remainder)
{$ifndef FPC_SYSTEM_HAS_DIV_BYTE}
{$define FPC_SYSTEM_HAS_DIV_BYTE}
// z in Ra, n in Rb, 0 in Rp
function fpc_div_byte(n, z: byte): byte; assembler; nostackframe;
{$ifdef FPC_IS_SYSTEM}[public,alias: 'FPC_DIV_BYTE'];{$endif}
label
start, div1, div2, div3, finish;
asm
// Symbol Name Register(s)
// z (A) dividend R22
// n (B) divisor R24
// p (P) remainder R20
// i counter R18
cp R24, R1
brne start
{$ifdef CPUAVR_HAS_JMP_CALL}
call get_pc_addr
{$else CPUAVR_HAS_JMP_CALL}
rcall get_pc_addr
{$endif CPUAVR_HAS_JMP_CALL}
movw R20, R24
{$ifdef CPUAVR_HAS_JMP_CALL}
call get_frame
{$else CPUAVR_HAS_JMP_CALL}
rcall get_frame
{$endif CPUAVR_HAS_JMP_CALL}
movw R18, R24
ldi R22, 200
clr R23
clr R24
clr R25
{$ifdef CPUAVR_HAS_JMP_CALL}
call HandleErrorAddrFrameInd
{$else CPUAVR_HAS_JMP_CALL}
rcall HandleErrorAddrFrameInd
{$endif CPUAVR_HAS_JMP_CALL}
start:
clr R20 // clear remainder
ldi R18, 8 // iterate over 8 bits
div1:
lsl R22 // shift left A
rol R20 // shift left P with carry from A shift
sub R20, R24 // Subtract B from P, P <= P - B
brlo div2
ori R22, 1 // Set A[0] = 1
rjmp div3
div2: // negative branch, A[0] = 0 (default after shift), restore P
add R20, R24 // restore old value of P
div3:
dec R18
brne div1
finish:
mov R24, R22 // Move result from R22 to R24
end;
{It is a compilerproc (systemh.inc), make an alias for internal use.}
{$ifdef FPC_IS_SYSTEM}
function fpc_div_byte(n, z: byte): byte; external name 'FPC_DIV_BYTE';
{$endif FPC_IS_SYSTEM}
{$endif FPC_SYSTEM_HAS_DIV_BYTE}
{$ifndef FPC_SYSTEM_HAS_DIV_WORD}
{$define FPC_SYSTEM_HAS_DIV_WORD}
// z in Ra, n in Rb, 0 in Rp
function fpc_div_word(n, z: word): word; assembler; nostackframe;
{$ifdef FPC_IS_SYSTEM}[public,alias: 'FPC_DIV_WORD'];{$endif}
label
start, div1, div2, div3, finish;
asm
// Symbol Name Register(s)
// z (A) dividend R23, R22
// n (B) divisor R25, R24
// p (P) remainder R21, R20
// i counter R18
cp R24, R1
brne start
{$ifdef CPUAVR_HAS_JMP_CALL}
call get_pc_addr
{$else CPUAVR_HAS_JMP_CALL}
rcall get_pc_addr
{$endif CPUAVR_HAS_JMP_CALL}
movw R20, R24
{$ifdef CPUAVR_HAS_JMP_CALL}
call get_frame
{$else CPUAVR_HAS_JMP_CALL}
rcall get_frame
{$endif CPUAVR_HAS_JMP_CALL}
movw R18, R24
ldi R22, 200
clr R23
clr R24
clr R25
{$ifdef CPUAVR_HAS_JMP_CALL}
call HandleErrorAddrFrameInd
{$else CPUAVR_HAS_JMP_CALL}
rcall HandleErrorAddrFrameInd
{$endif CPUAVR_HAS_JMP_CALL}
start: // Start of division...
clr R20 // clear remainder low
clr R21 // clear remainder hi
ldi R18, 16 // iterate over 16 bits
div1:
lsl R22 // shift left A_L
rol R23
rol R20 // shift left P with carry from A shift
rol R21
sub R20, R24 // Subtract B from P, P <= P - B
sbc R21, R25
brlo div2
ori R22, 1 // Set A[0] = 1
rjmp div3
div2: // negative branch, A[0] = 0 (default after shift), restore P
add R20, R24 // restore old value of P
adc R21, R25
div3:
dec R18
brne div1
finish:
movw R24, R22 // Move result from R22:R23 to R24:R25
end;
{It is a compilerproc (systemh.inc), make an alias for internal use.}
{$ifdef FPC_IS_SYSTEM}
function fpc_div_word(n, z: word): word; external name 'FPC_DIV_WORD';
{$endif FPC_IS_SYSTEM}
{$endif FPC_SYSTEM_HAS_DIV_WORD}
{$ifndef FPC_SYSTEM_HAS_DIV_DWORD}
{$define FPC_SYSTEM_HAS_DIV_DWORD}
// z in Ra, n in Rb, 0 in Rp
function fpc_div_dword(n, z: dword): dword; assembler; nostackframe;
{$ifdef FPC_IS_SYSTEM}[public,alias: 'FPC_DIV_DWORD'];{$endif}
label
start, div1, div2, div3, finish;
asm
// Symbol Name Register(s)
// z (A) dividend R21, R20, R19, R18
// n (B) divisor R25, R24, R23, R22
// p (P) remainder R17, R16, R15, R14
// i counter R26
cp R24, R1
cpc R25, R1
cpc R22, R1
cpc R23, R1
brne .LNonZero
{$ifdef CPUAVR_HAS_JMP_CALL}
call get_pc_addr
{$else CPUAVR_HAS_JMP_CALL}
rcall get_pc_addr
{$endif CPUAVR_HAS_JMP_CALL}
movw R20, R24
{$ifdef CPUAVR_HAS_JMP_CALL}
call get_frame
{$else CPUAVR_HAS_JMP_CALL}
rcall get_frame
{$endif CPUAVR_HAS_JMP_CALL}
movw R18, R24
ldi R22, 200
clr R23
clr R24
clr R25
{$ifdef CPUAVR_HAS_JMP_CALL}
call HandleErrorAddrFrameInd
{$else CPUAVR_HAS_JMP_CALL}
rcall HandleErrorAddrFrameInd
{$endif CPUAVR_HAS_JMP_CALL}
.LNonZero:
push R17
push R16
push R15
push R14
start: // Start of division...
clr R14 // clear remainder
clr R15 // clear remainder
clr R16
clr R17
ldi R26, 32 // iterate over 32 bits
div1:
lsl R18 // shift left A_L
rol R19
rol R20
rol R21
rol R14 // shift left P with carry from A shift
rol R15
rol R16
rol R17
sub R14, R22 // Subtract B from P, P <= P - B
sbc R15, R23
sbc R16, R24
sbc R17, R25
brlo div2
ori R18, 1 // Set A[0] = 1
rjmp div3
div2: // negative branch, A[0] = 0 (default after shift), restore P
add R14, R22 // restore old value of P
adc R15, R23
adc R16, R24
adc R17, R25
div3:
dec R26
brne div1
finish:
movw R22, R18 // Move result from R18:R21 to R22:R25
movw R24, R20
pop R14
pop R15
pop R16
pop R17
end;
{It is a compilerproc (systemh.inc), make an alias for internal use.}
{$ifdef FPC_IS_SYSTEM}
function fpc_div_dword(n, z: dword): dword; external name 'FPC_DIV_DWORD';
{$endif FPC_IS_SYSTEM}
{$endif FPC_SYSTEM_HAS_DIV_DWORD}