Basic Univers
; Maths en ASM

;- des fonctions de trigo

Procedure.f WrapValue(Angle.f) ;[0, 2Pi[
  ; charge 2Pi
  !FLDPI
  !FADD  st, st
 
  ; charge Angle
  !FLD dword [p.v_Angle]
 
  ; calcule WrapValue
  !FPREM
  !FSTP  st1
 
  ; l'angle est négatif ?
  !FLDZ
  !FCOMP
  !FNSTSW ax
  !TEST   ah, $41
  !JNE    l_wrapvalue_ok
 
  ; on lui ajoute 2Pi
  !FLDPI
  !FADD  st, st
  !FADDP
 
  wrapvalue_ok:
  !RET 4
EndProcedure

Procedure.f ATan2(y.f, x.f)    ;[-Pi, Pi[
  !FLD dword [p.v_y]
  !FLD dword [p.v_x]
  !FPATAN
  !RET 8
EndProcedure

Procedure.f ATanFull(y.f, x.f) ;[0, 2Pi[
  !FLD dword [p.v_y]
  !FLD dword [p.v_x]
  !FPATAN
 
  ; l'angle est négatif ?
  !FLDZ
  !FCOMP
  !FNSTSW ax
  !TEST   ah, $41
  !JNE    l_atanfull_ok
 
  ; on lui ajoute 2Pi
  !FLDPI
  !FADD  st, st
  !FADDP
 
  atanfull_ok:
  !RET 8
EndProcedure



;- la fonction exponentielle (j'ai galéré mais c'est NICKEL =)

Procedure.f Exp(x.f)
  Protected i.l
  !NewCW equ p.v_i + 0
  !OldCW equ p.v_i + 2
 
  ; charge x sans s'occuper du signe
  !FLD dword [p.v_x]
  !FABS
 
  ; n = x / Log(2)
  !FLDLN2
  !FDIVR st0, st1
 
  ; récupère le CW
  !FNSTCW word [NewCW]
  !FNSTCW word [OldCW]
 
  ; crée le CW pour arrondi par défaut
  !AND   word [NewCW], $F3FF
  !OR    word [NewCW], $0400
  !FLDCW word [NewCW]
 
  ; n = Floor(n)
  !FRNDINT
 
  ; restore le CW
  !FLDCW word [OldCW]
 
  ; u = x - n * Log(2)
  !FXCH
  !FLD st1 ; charge n
  !FLDLN2
  !FMULP
  !FSUBP
 
  ; m = Exp(u)
  !FLD1 ; m
  !FLD1 ; t
  !MOV  dword [p.v_i], 1
 
  exp_loop:
  ; tant que t > 0
  !FLDZ
  !FCOMP
  !FNSTSW ax
  !TEST   ah, $40
  !JNE    l_exp_end_loop
 
  ; t * u / i
  !FMUL  st0, st2
  !FIDIV dword [p.v_i]
 
  ; i + 1
  !INC   dword [p.v_i]
 
  ; m + t
  !FADD  st1, st0
 
  !JMP l_exp_loop
  exp_end_loop:
 
  !FSTP st0 ; retire t
  !FSTP st1 ; retire u
  !FSCALE   ; r = m * Pow(2, n)
  !FSTP st1 ; retire n
 
  ; si x négatif
  !TEST dword [p.v_x], $80000000
  !JE   l_exp_end
 
  ; r = 1.0 / r
  !FLD1
  !FDIVRP
 
  exp_end:
 
  !ADD esp, 4
  !RET 4
EndProcedure





;- fonctions de probabilités (étrangement ca a été super simple =)

Procedure.d Factorial(n.l) ; n!
  ; si n négatif factorielle est indéfinie
  !TEST dword [p.v_n], $80000000
  !JNE  l_factorial_end
 
  !FLD1 ; le résultat dans st0
 
  factorial_loop:
  ; tant que n > 1
  !CMP dword [p.v_n], 1
  !JLE l_factorial_end
 
  ; calcule la factorielle
  !FIMUL dword [p.v_n]
  !DEC   dword [p.v_n]
 
  !JMP l_factorial_loop
  factorial_end:
 
  !RET 4
EndProcedure

Procedure.d Permutations(n.l, p.l) ; A(n, p)
  ; si n négatif pas d'arrangements
  !TEST dword [p.v_n], $80000000
  !JNE  l_permutations_end
 
  ; si p négatif pas d'arrangements
  !TEST dword [p.v_p], $80000000
  !JNE  l_permutations_end
 
  ; si n inférieur à p pas d'arrangements
  !MOV eax, dword [p.v_n]
  !CMP eax, dword [p.v_p]
  !JL  l_permutations_end
 
  !FLD1                   ; résultat dans st0
  !SUB eax, dword [p.v_p] ; eax = n - p
 
  permutations_loop:
  ; tant que n > eax
  !CMP dword [p.v_n], eax
  !JE  l_permutations_end
 
  ; calcule les arrangements
  !FIMUL dword [p.v_n]
  !DEC   dword [p.v_n]
 
  !JMP l_permutations_loop
  permutations_end:
 
  !RET 8
EndProcedure

Procedure.d Combinations(n.l, p.l) ; C(n, p)
  ; si n négatif pas de combinaisons
  !TEST dword [p.v_n], $80000000
  !JNE  l_combinations_end
 
  ; si p négatif pas de combinaisons
  !TEST dword [p.v_p], $80000000
  !JNE  l_combinations_end
 
  ; si n inférieur à p pas de combinaisons
  !MOV eax, dword [p.v_n]
  !CMP eax, dword [p.v_p]
  !JL  l_combinations_end
 
  !FLD1 ; résultat dans st0
 
  ; si p > n/2
  !SHR eax, 1
  !CMP eax, dword [p.v_p]
  !JG l_combinations_loop
 
  ; on prend n - p à la place de p
  !MOV eax, dword [p.v_n]
  !SUB eax, dword [p.v_p]
  !MOV dword [p.v_p], eax
 
  combinations_loop:
  ; tant que p > 0
  !CMP dword [p.v_p], 0
  !JLE l_combinations_end_loop
 
  ; calcule les combinaisons
  !FIMUL dword [p.v_n]
  !FIDIV dword [p.v_p]
  !DEC   dword [p.v_n]
  !DEC   dword [p.v_p]
 
  !JMP l_combinations_loop
  combinations_end_loop:
 
  !FRNDINT
  combinations_end:
 
  !RET 8
EndProcedure


;- Le modulo version nombres réels

Procedure.f Mod(x.f, y.f) ; x % y
  !FLD  dword [p.v_x]
  !FLD  dword [p.v_y]
  !FPREM
  !FSTP st1
  !RET  8
EndProcedure





;- Des fonctions d'arrondis (je sais y'a Round...)

Procedure.f Ceil(Value.f)
  Protected CW.l
  !NewCW equ p.v_CW + 0
  !OldCW equ p.v_CW + 2
 
  ; récupère le CW
  !FNSTCW word [OldCW]
  !FNSTCW word [NewCW]
 
  ; crée le CW pour arrondi par excès
  !AND   word [NewCW], $F3FF
  !OR    word [NewCW], $0800
  !FLDCW word [NewCW]
 
  ; calcule l'arrondi
  !FLD dword [p.v_Value]
  !FRNDINT
 
  ; restore le CW
  !FLDCW word [OldCW]
 
  !ADD esp, 4
  !RET 4
EndProcedure

Procedure.f Floor(Value.f)
  Protected CW.l
  !NewCW equ p.v_CW + 0
  !OldCW equ p.v_CW + 2
 
  ; récupère le CW
  !FNSTCW word [OldCW]
  !FNSTCW word [NewCW]
 
  ; crée le CW pour arrondi par défaut
  !AND   word [NewCW], $F3FF
  !OR    word [NewCW], $0400
  !FLDCW word [NewCW]
 
  ; calcule l'arrondi
  !FLD dword [p.v_Value]
  !FRNDINT
 
  ; restore le CW
  !FLDCW word [OldCW]
 
  !ADD esp, 4
  !RET 4
EndProcedure




;- fonctions à part sur la représentation des nombres réels

Procedure.f Significand(Value.f)
  !FLD  dword [p.v_Value]
  !FXTRACT
  !FSTP st1
  !RET  4
EndProcedure

Procedure.f Exponent(Value.f)
  !FLD  dword [p.v_Value]
  !FXTRACT
  !FSTP st0
  !RET  4
EndProcedure

Procedure.f Scale(Significand.f, Exponent.f)
  !FLD  dword [p.v_Exponent]
  !FLD  dword [p.v_Significand]
  !FSCALE
  !FSTP st1
  !RET  8
EndProcedure



;- PGCD

Procedure GCD(a.l, b.l)
  !MOV  eax, dword [p.v_a]
  !MOV  ebx, dword [p.v_b]
 
  gcd_loop:
 
  ; tant que b <> 0
  !CMP ebx, 0
  !JE  l_gcd_end
   
  ; calcule le PGCD
  !CDQ
  !IDIV ebx
  !MOV  eax, ebx
  !MOV  ebx, edx
 
  !JMP l_gcd_loop
 
  gcd_end:
 
  !RET 8
EndProcedure