123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- {
- 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}
|