Difference between revisions of "Z80 Routines:Math:Advance Math"
From WikiTI
(Created page with 'Advanced_Math Advanced_Math == Introduction == These are routines designed for math of a slightly higher level. These d…') |
m ((using real name for credits)) |
||
(3 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | [[Category:Z80 Routines:Math| | + | [[Category:Z80 Routines:Math|Advanced Math]] |
− | [[Category:Z80 Routines| | + | [[Category:Z80 Routines|Advanced Math]] |
== Introduction == | == Introduction == | ||
Line 8: | Line 8: | ||
=== nCr Algorithm === | === nCr Algorithm === | ||
− | This computes "n choose r" | + | This computes "n choose r" in such a way as to avoid overflow unless the final result would overflow 16 bits. |
<nowiki> | <nowiki> | ||
− | ; | + | ;Written by Zeda |
− | + | ||
− | ;==== | + | ; Requires |
− | + | ; mul16 ;BC*DE ==> DEHL | |
− | ; | + | ; DEHL_Div_BC ;DEHL/BC ==> DEHL |
− | ; | + | |
+ | ncr_HL_DE: | ||
+ | ;"n choose r", defined as n!/(r!(n-r)!) | ||
+ | ;Computes "HL choose DE" | ||
+ | ;Inputs: HL,DE | ||
;Outputs: | ;Outputs: | ||
− | ; | + | ; HL is the result |
− | + | ; "HL choose DE" | |
− | + | ; carry flag reset means overflow | |
− | ; | + | ;Destroys: |
− | ; | + | ; A,BC,DE,IX |
− | ; | + | ;Notes: |
− | ; | + | ; Overflow is returned as 0 |
− | ; | + | ; Overflow happens if HL choose DE exceeds 65535 |
− | ; | + | ; This algorithm is constructed in such a way that intermediate |
− | ; | + | ; operations won't erroneously trigger overflow. |
− | + | ;66 bytes | |
− | + | ld bc,1 | |
− | + | or a | |
− | + | sbc hl,de | |
− | + | jr c,ncr_oob | |
− | + | jr z,ncr_exit | |
− | + | sbc hl,de | |
− | + | add hl,de | |
− | + | jr c,$+3 | |
− | + | ex de,hl | |
− | + | ld a,h | |
− | + | or l | |
− | + | push hl | |
− | + | pop ix | |
− | + | ncr_exit: | |
− | + | ld h,b | |
− | + | ld l,c | |
− | + | scf | |
− | + | ret z | |
− | + | ncr_loop: | |
− | + | push bc \ push de | |
− | + | push hl \ push bc | |
− | + | ld b,h | |
− | + | ld c,l | |
− | + | call mul16 ;BC*DE ==> DEHL | |
− | + | pop bc | |
− | + | call DEHL_Div_BC ;result in DEHL | |
− | + | ld a,d | |
− | + | or e | |
− | + | pop bc | |
− | + | pop de | |
+ | jr nz,ncr_overflow | ||
+ | add hl,bc | ||
+ | jr c,ncr_overflow | ||
+ | pop bc | ||
+ | inc bc | ||
+ | ld a,b | ||
+ | cp ixh | ||
+ | jr c,ncr_loop | ||
+ | ld a,ixl | ||
+ | cp c | ||
+ | jr nc,ncr_loop | ||
+ | ret | ||
+ | ncr_overflow: | ||
+ | pop bc | ||
+ | xor a | ||
+ | ld b,a | ||
+ | ncr_oob: | ||
+ | ld h,b | ||
+ | ld l,b | ||
+ | ret | ||
+ | |||
</nowiki> | </nowiki> | ||
− | === | + | === GCDHL_DE === |
− | This finds the greatest common divisor (GCD) of HL and | + | This finds the greatest common divisor (GCD) of HL and DE using the binary GCD algorithm to improve performance. |
<nowiki> | <nowiki> | ||
− | + | gcdHL_DE: | |
− | ; | + | ;gcd(HL,DE)->HL |
− | + | ;Output: | |
− | ; | + | ; B=0 |
− | + | ; HL is the GCD of the inputs | |
− | ; | + | |
− | ; | + | |
− | + | ||
;Destroys: | ;Destroys: | ||
− | ; | + | ; A,DE |
− | ; | + | ; DE is guaranteed 0 unless the output is 0 (which only happens if one of the inputs is 0). |
− | ; | + | ;Uses the binary GCD algorithm |
− | ; | + | ;65 bytes |
− | + | ||
− | ; | + | ;B is our cofactor-of-2 counter |
− | + | ld b,0 | |
− | + | ||
− | ; | + | ;If HL=0, return 0 |
− | + | ld a,h \ or l \ ret z | |
− | + | ||
− | + | ;If DE=0, return 0 | |
− | + | ex de,hl | |
− | + | ld a,h \ or l \ jr nz,gcd_test_cofactor_of_2 | |
− | + | ret | |
− | + | ||
− | + | gcd_cofactor_2_loop: | |
− | + | inc b | |
− | + | srl h \ rr l | |
− | + | srl d \ rr e | |
− | + | gcd_test_cofactor_of_2: | |
− | + | inc b | |
− | + | ld a,e | |
− | + | or l | |
− | + | rra | |
− | + | jr nc,gcd_cofactor_2_loop | |
− | + | ||
− | + | gcd_remove_factors_of_2_op2: | |
+ | srl h \ rr l \ jr nc,gcd_remove_factors_of_2_op2 | ||
+ | adc hl,hl | ||
+ | jr gcd_swap_ops | ||
+ | |||
+ | gcd_swap_ops_negate: | ||
+ | ;At this point, HL needs to be negated and swapped with DE | ||
+ | xor a \ sub l \ ld l,a \ sbc a,a \ sub h \ ld h,a | ||
+ | gcd_swap_ops: | ||
+ | ex de,hl | ||
+ | gcd_remove_factors_of_2_op1: | ||
+ | srl h \ rr l \ jr nc,gcd_remove_factors_of_2_op1 | ||
+ | adc hl,hl | ||
+ | sbc hl,de | ||
+ | jr c,gcd_swap_ops_negate | ||
+ | jp nz,gcd_remove_factors_of_2_op1 | ||
+ | |||
+ | ;DE is the GCD, need to shift it left B-1 times. | ||
+ | ex de,hl | ||
+ | dec b | ||
+ | ret z | ||
+ | add hl,hl \ djnz $-1 | ||
+ | ret | ||
</nowiki> | </nowiki> | ||
Line 109: | Line 153: | ||
== Credits and Contributions == | == Credits and Contributions == | ||
− | * '''Zeda | + | * '''Zeda Thomas''' for the nCr and GCD algorithm |
Latest revision as of 15:08, 30 September 2019
Introduction
These are routines designed for math of a slightly higher level. These don't necessarily contribute to everyday coding, but might be useful for an OS that handles such math (or programming language).
nCr Algorithm
This computes "n choose r" in such a way as to avoid overflow unless the final result would overflow 16 bits.
;Written by Zeda ; Requires ; mul16 ;BC*DE ==> DEHL ; DEHL_Div_BC ;DEHL/BC ==> DEHL ncr_HL_DE: ;"n choose r", defined as n!/(r!(n-r)!) ;Computes "HL choose DE" ;Inputs: HL,DE ;Outputs: ; HL is the result ; "HL choose DE" ; carry flag reset means overflow ;Destroys: ; A,BC,DE,IX ;Notes: ; Overflow is returned as 0 ; Overflow happens if HL choose DE exceeds 65535 ; This algorithm is constructed in such a way that intermediate ; operations won't erroneously trigger overflow. ;66 bytes ld bc,1 or a sbc hl,de jr c,ncr_oob jr z,ncr_exit sbc hl,de add hl,de jr c,$+3 ex de,hl ld a,h or l push hl pop ix ncr_exit: ld h,b ld l,c scf ret z ncr_loop: push bc \ push de push hl \ push bc ld b,h ld c,l call mul16 ;BC*DE ==> DEHL pop bc call DEHL_Div_BC ;result in DEHL ld a,d or e pop bc pop de jr nz,ncr_overflow add hl,bc jr c,ncr_overflow pop bc inc bc ld a,b cp ixh jr c,ncr_loop ld a,ixl cp c jr nc,ncr_loop ret ncr_overflow: pop bc xor a ld b,a ncr_oob: ld h,b ld l,b ret
GCDHL_DE
This finds the greatest common divisor (GCD) of HL and DE using the binary GCD algorithm to improve performance.
gcdHL_DE: ;gcd(HL,DE)->HL ;Output: ; B=0 ; HL is the GCD of the inputs ;Destroys: ; A,DE ; DE is guaranteed 0 unless the output is 0 (which only happens if one of the inputs is 0). ;Uses the binary GCD algorithm ;65 bytes ;B is our cofactor-of-2 counter ld b,0 ;If HL=0, return 0 ld a,h \ or l \ ret z ;If DE=0, return 0 ex de,hl ld a,h \ or l \ jr nz,gcd_test_cofactor_of_2 ret gcd_cofactor_2_loop: inc b srl h \ rr l srl d \ rr e gcd_test_cofactor_of_2: inc b ld a,e or l rra jr nc,gcd_cofactor_2_loop gcd_remove_factors_of_2_op2: srl h \ rr l \ jr nc,gcd_remove_factors_of_2_op2 adc hl,hl jr gcd_swap_ops gcd_swap_ops_negate: ;At this point, HL needs to be negated and swapped with DE xor a \ sub l \ ld l,a \ sbc a,a \ sub h \ ld h,a gcd_swap_ops: ex de,hl gcd_remove_factors_of_2_op1: srl h \ rr l \ jr nc,gcd_remove_factors_of_2_op1 adc hl,hl sbc hl,de jr c,gcd_swap_ops_negate jp nz,gcd_remove_factors_of_2_op1 ;DE is the GCD, need to shift it left B-1 times. ex de,hl dec b ret z add hl,hl \ djnz $-1 ret
LCM
This is as simple as multiplying the two numbers and dividing by the GCD.
Credits and Contributions
- Zeda Thomas for the nCr and GCD algorithm