assemblyx86x87hla

I'm having trouble finding my error on my Quadratic Formula Assembly Program (HLA)


When I try to execute my code I get a error telling me "Value out of range".

program solveQuadratic;
#include( "stdlib.hhf" );

static
  a: real32;
  b: real32;
  c: real32;
  discriminant: real32;
  root1: real32;
  root2: real32;

begin solveQuadratic;

  // Prompt and read values for a, b, and c
  stdout.put("Gimme a value for a: ");
  stdin.get(a);
  stdout.put("Gimme a value for b: ");
  stdin.get(b);
  stdout.put("Gimme a value for c: ");
  stdin.get(c);

  // Calculate the discriminant
  fld(b);
  fmul(st0, st1);      // b^2
  fld(a);
  fld(c);
  fmul(st0, st1);      // a * c
  fadd(st0, st1);      // 2ac
  fsub();              // b^2 - 4ac
  fstp(discriminant);

    // Compute the roots
    fld(discriminant);
    fsqrt();
    fstp(root1);       // store sqrt(discriminant) in root1

    // Calculate the positive root
    fld(b);
    fchs();            // -b
    fld(root1);
    fadd();            // -b + sqrt(discriminant)
    fld(a);
    fadd(st0, st0);    // 2a
    fdiv();            // (-b + sqrt(discriminant)) / (2a)
    fstp(root1);

    // Calculate the negative root
    fld(b);
    fchs();            // -b
    fld(root1);
    fsub();            // -b - sqrt(discriminant)
    fld(a);
    fadd(st0, st0);    // 2a
    fdiv();            // (-b - sqrt(discriminant)) / (2a)
    fstp(root2);

    // Output the roots
    stdout.put("x is ", root1:0:5, " and x is also ", root2:0:5, "\n");

end solveQuadratic;

I was expecting this to be outputted

Gimme a value for a: 2.1
Gimme a value for b: 3.1
Gimme a value for c: -5.0
x is -2.44857 and x is also 0.97238

but all I get is a conversion error and I don't know why.


Solution

  • Flaws in your fpu operations.

    You have stored sqrt(discriminant) in root1, but in doing so you have made the calculation for the negative root impossible!

    fld(b);
    fmul(st0, st1);      // b^2
    

    You can't multiply by ST1 if you didn't load that register! fmul(b) would be fine.

    fld(a);
    fld(c);
    fmul(st0, st1);      // a * c
    fadd(st0, st1);      // 2ac
    

    This multiplication is ok but the addition really does a * c + a because ST1 still holds the a value, that you should also remove else the fpu stack will overflow at some point. Also, why do you go for 2ac when you should be going for 4ac?

    Use faddp(), fsubp, and fdivp to discard values you no longer need.

    This is very important since the fpu stack has only 8 registers to play with!

    fld(b);
    fmul(b);            // b^2
    fld(a);
    fmul(c);            // a * c
    fadd(st0, st0);     // 2ac
    fadd(st0, st0);     // 4ac
    fsubp();            // b^2 - 4ac
    // fstp(discriminant);
    
    // fld(discriminant);
    fsqrt();
    fstp(root2);       // store sqrt(discriminant) in root2
    
    // Calculate the positive root
    fld(b);
    fchs();            // -b
    fld(root2);
    faddp();           // -b + sqrt(discriminant)
    fld(a);
    fadd(st0, st0);    // 2a
    fdivp();           // (-b + sqrt(discriminant)) / (2a)
    fstp(root1);
    
    // Calculate the negative root
    fld(b);
    fchs();            // -b
    fld(root2);
    fsubp();           // -b - sqrt(discriminant)
    fld(a);
    fadd(st0, st0);    // 2a
    fdivp();           // (-b - sqrt(discriminant)) / (2a)
    fstp(root2);
    

    Shorter.

    You can replace:

    fld(root2);
    faddp();           // -b + sqrt(discriminant)
    

    by

    fadd(root2);
    

    Also, you can replace:

    fld(root2);
    fsubp();           // -b - sqrt(discriminant)
    

    by

    fsub(root2);