Quadratic Equations Builder

Quadratic Equations demo

program

; Name:         quadraticequations.asm
; Build:        nasm -felf64 quadraticequations.asm -l quadraticequations.lst -o quadraticequations.o
;               ld -s -melf_x86_64 -o quadraticequations quadraticequations.o
; Description:  Generates a quadratic equation from a given squareroot of D,x1 and x2, all integers.

bits 64

[list -]
    %include "unistd.inc"
    %include "fraction.class"
[list +]

section .bss
    contentlength:  resq    1
    envparams:      resq    1
    signbuffer:     resb    1
    integerbuffer:  resb    20
    
section .data
request_method: db  "REQUEST_METHOD"
.len:           equ $-request_method
content_length: db  "CONTENT_LENGTH"
.len:           equ $-content_length
post:           db  "POST"
.len:           equ $-post
d:              db  "d="
.len:           equ $-d
.start:         dq  0
.size:          dq  0
.value:         dq  0
.ascii:         times 20 db 0
.ascstart:      dq  0
.asclen:        dq  0

x1:             db  "x1="
.len:           equ $-x1
.start:         dq  0
.size:          dq  0
.value:         dq  0
.ascii:         times 20 db 0
.ascstart:      dq  0
.asclen:        dq  0

x2:             db  "x2="
.len:           equ $-x2
.start:         dq  0
.size:          dq  0
.value:         dq  0
.ascii:         times 20 db 0
.ascstart:      dq  0
.asclen:        dq  0

ampersand:      db  "&"

minus:          db  "-"
.len:           equ $-minus
plus:           db  "+"
.len:           equ $-plus
division:       db  "/"
.len:           equ $-division
xx:             db  "x²"
.len:           equ $-xx
x:              db  "x"
.len:           equ $-x
equals:         db  "=0"
.len:           equ $-equals



httpheader:     db  'Content-type: text/html',0x0A,0x0A
.len:           equ $-httpheader
JSONObject:
    .part1:     db  '{"d":"'
    .part1.len: equ $-JSONObject.part1
    .part2:     db  '","x1":"'
    .part2.len: equ $-JSONObject.part2
    .part3:     db  '","x2":"'
    .part3.len: equ $-JSONObject.part3
    .part4:     db  '","equation":"'
    .part4.len: equ $-JSONObject.part4
    .part5:     db  '"}'
    .part5.len: equ $-JSONObject.part5
htmleol:        db  "<br><br>"
.len:           equ $-htmleol

middle:
          db   '":"'
.length:  equ  $-middle    

separator:
          db   '","'
.length:  equ  $-separator

bottom:
          db   '"}'
.length:  equ  $-bottom

FRACTION a
FRACTION b
FRACTION c

equation:
    .xx:        db  "x²"
    .xxlen:     equ $-equation.xx
    .x:         db  "x"
    .xlen:      equ $-equation.x
    .zero:      db  "=0"
    .zerolen:   equ $-equation.zero
    .astart:    db  "a("
    .astartlen: equ $-equation.astart
    .aend:      db  ")=0 for any a in R"
    .aendlen:   equ $-equation.aend
    .basic:     db  "ax²=0"
    .basiclen:  equ $-equation.basic

rooterror:
.unequal:       db  "if D=0, x1 and x2 cannot be the different."
.unequallen:    equ $-rooterror.unequal
.equal:         db  "if D<>0, x1 and x2 cannot be the equal."
.equallen:      equ $-rooterror.equal
    
    
divsign: db "/"
eol:     db 10

section .text
     global _start

_start:
    ;initialize the cgi environment
    call    CgiInit
    call    IsPostBack
    and     rax,rax
    jz      .nopostback
    ;syscall write,stdout,postback,postback.len
    call    GetContentLength
    ;content length is in rax, reserve memory on heap to load the posted parameters
    mov     r15,rax                                     ;save content length
    inc     r15                                         ;extra byte for trailing &
    syscall brk,0                                       ;get start of heap
    mov     r14,rax                                     ;save start of heap
    add     rax,r15                                     ;add bytes to heapaddress
    syscall brk,rax                                     ;reserve memory
    syscall read,stdin,r14,r15                          ;read posted parameterstring
    add     rax,r14
    mov     byte[rax],"&"                               ;trailing ampersand at the end
    
    ;get d
    mov     rdi,d
    mov     rsi,d.len
    mov     rdx,r14                                     ;start of postparams
    mov     rcx,r15                                     ;length of postparams
    call    GetPostParam       
    mov     qword[d.start],rax
    mov     rdi,rax
    call    GetPostValueLength
    mov     qword[d.size],rax
    mov     rdi,qword[d.start]                          ;start of integer in rdi
    mov     rsi,rax                                     ;length of integer in rsi
    call    CheckParameter
    mov     qword[d.start],rax
    mov     qword[d.size],rdx
    mov     rdi,qword[d.start]                          ;convert string to integer
    mov     rsi,qword[d.size]
    
    call    StringToInteger
    mov     qword[d.value],rax
    mov     rdi,qword[d.value]
    mov     rsi,d.ascii
    call    IntegerToString
    mov     qword[d.ascstart],rax
    mov     qword[d.asclen],rdx
    
    ;get x1
    mov     rdi,x1
    mov     rsi,x1.len
    mov     rdx,r14                                     ;start of postparams
    mov     rcx,r15                                     ;length of postparams
    call    GetPostParam       
    mov     qword[x1.start],rax                         ;rsi points to value of post parameter
    mov     rdi,rax
    call    GetPostValueLength
    mov     qword[x1.size],rax
    mov     rdi,qword[x1.start]                         ;start of integer in rdi
    mov     rsi,rax                                     ;length of integer in rsi
    call    CheckParameter
    mov     qword[x1.start],rax
    mov     qword[x1.size],rdx
    mov     rdi,qword[x1.start]                          ;convert string to integer
    mov     rsi,qword[x1.size]
    call    StringToInteger
    mov     qword[x1.value],rax
    mov     rdi,qword[x1.value]
    mov     rsi,x1.ascii
    call    IntegerToString
    mov     qword[x1.ascstart],rax
    mov     qword[x1.asclen],rdx
    
    ;get x2
    mov     rdi,x2
    mov     rsi,x2.len
    mov     rdx,r14                                     ;start of postparams
    mov     rcx,r15                                     ;length of postparams
    call    GetPostParam       
    mov     qword[x2.start],rax                         ;rsi points to value of post parameter
    mov     rdi,rax
    call    GetPostValueLength
    mov     qword[x2.size],rax
    mov     rdi,qword[x2.start]                         ;start of integer in rdi
    mov     rsi,rax                                     ;length of integer in rsi
    call    CheckParameter
    mov     qword[x2.start],rax
    mov     qword[x2.size],rdx
    mov     rdi,qword[x2.start]                          ;convert string to integer
    mov     rsi,qword[x2.size]
    call    StringToInteger
    mov     qword[x2.value],rax
    mov     rdi,qword[x2.value]
    mov     rsi,x2.ascii
    call    IntegerToString
    mov     qword[x2.ascstart],rax
    mov     qword[x2.asclen],rdx
    
       
    syscall write,stdout,httpheader,httpheader.len
    syscall write,stdout,JSONObject.part1,JSONObject.part1.len   
    syscall write,stdout,qword[d.ascstart],qword[d.asclen]
    syscall write,stdout,JSONObject.part2,JSONObject.part2.len
    syscall write,stdout,qword[x1.ascstart],qword[x1.asclen]
    syscall write,stdout,JSONObject.part3,JSONObject.part3.len
    syscall write,stdout,qword[x2.ascstart],qword[x2.asclen]
    syscall write,stdout,JSONObject.part4,JSONObject.part4.len

    call    QuadraticEquation
    
.endjson:    
    syscall write,stdout,JSONObject.part5,JSONObject.part5.len
    
    syscall brk,r14                                     ;release allocated memory
.nopostback:    
    syscall exit,0

    
;Initialize the cgi environment
CgiInit:
    push    rbp
    add     rsp,40
    mov     rbp,rsp
    mov     qword[envparams],rbp
    sub     rsp,40
    pop     rbp
    ret

; check if a webpage is posted back or not.  If posted back then rax=1 otherwise 0
IsPostBack:
    push    rdi
    push    rsi
    push    rdx
    mov     rdi,request_method
    mov     rsi,qword[envparams]
    mov     rdx,request_method.len
    call    GetWebVar
    and     rax,rax
    jz      .done
    mov     rsi,rax
    lodsd                                   ;get "POST" or "GET"
    cmp     eax,"POST"
    jne     .done
    mov     rax,1
.done:
    pop     rdx
    pop     rsi
    pop     rdi
    ret

GetContentLength:
    push    rdi
    push    rsi
    push    rdx
    mov     rdi,content_length
    mov     rsi,qword[envparams]
    mov     rdx,content_length.len
    call    GetWebVar
    and     rax,rax
    jz      .done
    mov     rsi,rax
    ; initialise rcx, rcx wil contain the hexadecimal value of CONTENT_LENGTH
    xor     rcx,rcx
.nextdigit:
    xor     rax,rax
    lodsb                                ;get digit
    and     al,al                        ;if 0 then no digits
    je      .end
    xor     rdx,rdx
    mov     rbx,10
    imul    rcx,rbx
    and     al,0x0F
    add     rcx,rax                      ;previous digit x 10 + current digit     
    jmp     .nextdigit
.end:
    mov     rax,rcx                     ;content length in rax
.done:
    pop     rdx
    pop     rsi
    pop     rdi
    ret
    
;RSI has first item in the list with strings

GetWebVar:
    ;RSI = pointer to first item in zero terminated list of webvars
    ;RDI = pointer to string to find
    ;RDX = length of string to find
    ;on return: RAX is pointer to value of webvar or zero if not found
    push    r14
    push    r15
    push    rcx
    push    rdx
    push    rsi
    push    rdi
    push    rbp
    mov     rbp,rsp
    mov     r14,rdi
    mov     r15,rdx
    mov     rsp,rsi                         ;stackpointer points to webserver variables
    xor     rdx,rdx                         ;assume not found
.search:
    pop     rsi
    and     rsi, rsi                        ;done yet?
    jz      .done
    mov     rcx,r15
    mov     rdi,r14
    cld
    rep     cmpsb                           ;compare RCX bytes
    jne     .search                         ;no match get the next variable, if any
    inc     rsi                             ;point to value of webvar
.done:
    mov     rax,rsi
    mov     rsp,rbp
    pop     rbp
    pop     rdi
    pop     rsi
    pop     rdx
    pop     rcx
    pop     r15
    pop     r14
    ret
    
GetPostValueLength:
    ;RDI = pointer to post parameter value
    ;on return RAX = length of parametervalue string
    push    rdi
    push    rcx
    xor     rcx,rcx
    not     rcx
    mov     al,"&"
    repne   scasb
    neg     rcx                                         ;length of parameter value
    dec     rcx
    dec     rcx
    mov     rax,rcx
    pop     rcx
    pop     rdi
    ret
    
GetPostParam:
    ;RDI = start of substring
    ;RSI = length of substring
    ;RDX = start of string to look in
    ;RCX = length of string to look in
    ;on return RAX = pointer to start of substring or 0 if not found
    push    rdi
    push    rsi
    push    rdx
    push    rcx
    push    r8                                          ;help registers
    push    r9
    xor     rax,rax                                     ;assume not present
    mov     r8,rdi                                      ;start of substring
    mov     r9,rsi                                      ;length of substring
    mov     rsi,rdx                                     ;start of string to look in, in rsi
    add     rdx,rcx                                     ;calculate last offset of string    
    ;we now have: RDI = start of substring
    ;             RCX = length of substring
    ;             RSI = start of entire string
    ;             RDX = end of entire string
    ;now let's search for the substring
    cld
.search:
    cmp     rsi,rdx                                     ;already at the end?
    ja      .done
    mov     rdi,r8                                      ;start of postparam
    mov     rcx,r9                                      ;length of postparam
    rep     cmpsb                                       ;compare RCX bytes
    jne     .search                                     ;no match get the next variable, if any
    mov     rax,rsi                                     ;offset of substring in entire string in rax
.done:
    pop     r9
    pop     r8
    pop     rcx
    pop     rdx
    pop     rsi
    pop     rdi
    ret
    
CheckParameter:
    ;RDI = address of ascii string integer
    ;RSI = length of ascii string integer
    ;on return: RAX = modified pointer if string starts with %2B
    ;           RDX = modified length of the parameter string
    push    rsi
    push    rdi
    push    rcx
    mov     rcx,rsi                                     ;length of integer in rcx
    cmp     rcx,0
    jz      .done   
    mov     rsi,rdi                                     ;start of string in rsi
    lodsb                                               ;first byte in AL
    dec     rsi                                         ;adjust pointer
    cmp     al,"-"
    je      .done
    cmp     al,"%"
    jne     .done                                       ;no sign in number
    ;here we can have %2B or some other combination with %
    ;in case %2B the number is positive, otherwise we leave the pointer to the string which will later result
    ;in a convert error.
    cmp     rcx,2
    jl      .done
    lodsd                                               ;load byte in AL
    and     eax,0xFFFFFF                                ;mask off high 8 bits
    sub     rsi,4                                       ;adjust pointer   
    cmp     eax,"%2B"                                   ;plus sign
    je      .ispositive
    jmp     .done
.ispositive:
    inc     rsi
    inc     rsi                                         ;adjust pointer to positive number
    inc     rsi
    dec     rcx
    dec     rcx
    dec     rcx
.done:
    mov     rax,rsi                                     ;result in rax
    mov     rdx,rcx
    pop     rcx
    pop     rdi
    pop     rsi
    ret
    
StringToInteger:
    push    rbx
    push    rcx
    push    rdx
    push    r8
    push    r9
    push    rdi
    push    rsi
    mov     rcx,rsi                                     ;length in RCX
    mov     rsi,rdi                                     ;pointer in RSI
    xor     r8,r8                                       ;assume positive integer
    xor     r9,r9                                       ;temp value in R9
    xor     rax,rax                                     ;RAX = 0
    mov     al,byte[rsi]                                ;read first byte, must be minus sign or digit
    cmp     al,"-"
    jne     .checkdigits
    inc     r8
    inc     rsi                                         ;point to next byte
    dec     rcx                                         ;adjust loop counter
    cmp     rcx,0                                       ;check if any bytes left in string
    jz      .done                                       ;we're done
.checkdigits:
    mov     rax,r9                                      ;temp value in rax
    xor     rdx,rdx                                     ;prepare multiplication
    mov     rbx,10
    mul     rbx                                         ;RDX:RAX = RAX * RBX
    mov     r9,rax                                      ;store temp value again
    xor     rax,rax
    lodsb                                               ;read byte
    cmp     al,"0"
    jl      .done
    cmp     al,"9"
    jg      .done
    and     al,0x0F                                     ;unascii
    add     r9,rax                                      ;add to temp value
    loop    .checkdigits
.done:
    mov     rax,r9
    and     r8,r8
    jz      .exit
    neg     rax                                         ;make RAX negative
.exit:    
    pop     rsi
    pop     rdi
    pop     r9
    pop     r8
    pop     rdx
    pop     rcx
    pop     rbx
    ret

IntegerToString:
    push    rdi                         ;save value on stack
    push    rsi
    push    rbx
    push    r8
    
    mov     r8,rdi                      ;save value in r8
    ;initialize the buffer
    mov     rdi,rsi
    xor     rax,rax
    stosq                               ;clear the buffer
    stosq
    stosd
    mov     rdi,rsi
    add     rdi,19                      ;point to end of buffer
    xor     rdx,rdx
    mov     rax,r8                      ;get the hexadecimal value in rax
    and     rax,rax                     ;is it zero?
    jz      .iszero
    jns     .convert
    neg     rax                         ;get absolute value of rax
.convert:
    ;rax has the absolute value,rdi points to the last byte in the buffer
    ;start to convert.
    mov     rbx,10                      ;decimal base in rbx
.repeat:
    xor     rdx,rdx                     ;clear rdx
    div     rbx                         ;divide by ten, rdx has remainder, rax quotient
    or      dl,0x30                     ;make human readable
    mov     byte[rdi],dl                ;save digit
    dec     rdi                         ;adjust buffer pointer for next digit
    and     rax,rax                     ;is quotient zero?
    jnz     .repeat                     ;if not repeat the whole loop
    and     r8,r8                       ;test if value is negative
    jns     .ispositive
    mov     byte[rdi],'-'               ;value is negative, copy sign byte
    dec     rdi                         ;adjust buffer and continue calculating length
.ispositive:
    mov     rdx,rsi
    add     rdx,19                      ;point to last position in buffer
    sub     rdx,rdi                     ;subtract start position to get length of number
    inc     rdi                         ;adjust buffer pointer
    jmp     .exit
.iszero:
    or      byte[rdi],'0'               ;make ascii
    inc     rdx                         ;length of decimal value is 1
.exit:
    mov     rax,rdi                     ;return pointer in rax
    pop     r8
    pop     rbx
    pop     rsi
    pop     rdi                         ;restore hexadecimal value in rdi
    ret

QuadraticEquation:
    mov     rdi,1
    mov     rsi,1
    a.Load
    b.Load
    c.Load
    mov     rdi,qword[d.value]
    cmp     rdi,0
    je      .discriminantiszero
    ;discriminant is not zero, x1 and x2 may not be equal
    mov     rdi,qword[x1.value]
    sub     rdi,qword[x2.value]
    cmp     rdi,0
    je      .errorrootsequal
    ;discriminant is not zero and roots x1 and x2 are different
    ;the equation is 0 = a(x-x1)(x-x2) = ax²-a(x1+x2)x+ax1x2
    ;
    ;D = d² = b²-4ac = (-a(x1+x2))²-4a²x1x2
    ;                = a²((x1+x2)²-4x1x2)
    ;                = a²(x1²+2x1x2+x2²-4x1x2)
    ;                = a²(x1²-2x1x2+x2²)
    ;                = a²(x1-x2)²
    ;we can calculate a because all other terms are known:
    ;a = d/(x1-x2) , because x1<>x2 we don't have division by zero
    ;b = -d(x1+x2)/(x1-x2)
    ;c = dx1x2/(x1-x2)
    ;because a is the square root of a², the terms a,b and c can be
    ;negative to which means that we have two equations which are opposite
    ;in sign.
    ;
    ;calculate x1-x2
    mov     rax,qword[x1.value]
    sub     rax,qword[x2.value]
    mov     r8,rax                      ;save x1-x2
    mov     rdi,qword[d.value]
    mov     rsi,r8
    a.Load                              ;load values in fraction a
        
    ;calculate -d(x1+x2)
    mov     rax,qword[x1.value]               ;rax = x1
    add     rax,qword[x2.value]               ;rax = x1+x2
    mov     rbx,qword[d.value]        
    xor     rdx,rdx                     
    imul    rbx                         ;rax = d(x1+x2)
    neg     rax                         ;rax = -d(x1+x2)
    mov     rdi,rax
    mov     rsi,r8
    b.Load
    
    ;calculate dx1x2
    mov     rax,qword[x1.value]
    mov     rbx,qword[x2.value]
    xor     rdx,rdx
    imul    rbx
    mov     rbx,qword[d.value]
    imul    rbx
    mov     rdi,rax
    mov     rsi,r8
    c.Load
    ;print the equation
    call    PrintEquation
    syscall write,stdout,equation.zero,equation.zerolen
    syscall write,stdout,htmleol,htmleol.len
    neg     byte[a.sign]
    neg     byte[b.sign]
    neg     byte[c.sign]
    call    PrintEquation
    syscall write,stdout,equation.zero,equation.zerolen
    syscall write,stdout,htmleol,htmleol.len
    ret  
    
.discriminantiszero:
    ;if the disriminant is zero then both roots x1 and x2 must be equal, if not
    ;then we have an error in the input.
    mov     rax,qword[x1.value]
    sub     rax,qword[x2.value]
    cmp     rax,0
    jne     .errorrootsnotequal
    ;discriminant is zero and x1 = x2
    ;check if x1 and x2 are different from zero, in this case we have a very
    ;basic equation: ax²=0.
    mov     rax,qword[x1.value]
    cmp     rax,0
    je      .basicequation
    ;the equation now is a(x-x1)(x-x2) with x1=x2
    ;                    a(x-x1)(x-x1)
    ;                    a(x²-xx1-xx1+x1²)
    ;                    a(x²-2xx1+x1²)
    ;because the discriminant is zero, we can't calculate a value for a.
    ;What we can do however is calculate b=-2x1 and c=x1² and keep a=1 in
    ;x²-2x1x+x1².  Then for any value of 'a' the equation is a solution.
    ;
    ;fill a with ones
    mov     rdi,1
    mov     rsi,1
    a.Load
    ;now calculate -2x1
    mov     rax,qword[x1.value]                   ;rax = x1
    sal     rax,1                           ;rax = 2x1
    neg     rax
    mov     rdi,rax
    mov     rsi,1
    b.Load
    ;calculate x1²
    mov     rax,qword[x1.value]                   ;rax = x1
    xor     rdx,rdx
    imul    rax                             ;rax = x1²
    mov     rdi,rax
    mov     rsi,1
    c.Load
    ;print the equation
    syscall write,stdout,equation.astart,equation.astartlen
    call    PrintEquation
    syscall write,stdout,equation.aend,equation.aendlen
    syscall write,stdout,htmleol,htmleol.len
    neg     byte[a.sign]
    neg     byte[b.sign]
    neg     byte[c.sign]
    syscall write,stdout,equation.astart,equation.astartlen
    call    PrintEquation
    syscall write,stdout,equation.aend,equation.aendlen
    syscall write,stdout,htmleol,htmleol.len
    ret
.basicequation:
    syscall write,stdout,equation.basic,equation.basiclen
    ret
.errorrootsequal:
    syscall write,stdout,rooterror.equal,rooterror.equallen
    ret
.errorrootsnotequal:
    syscall write,stdout,rooterror.unequal,rooterror.unequallen
    ret

PrintEquation:
    a.Simplify
    b.Simplify
    c.Simplify
    call    PrintTermA
    call    PrintTermB
    call    PrintTermC
    ret

PrintTermA:
    mov     rax,qword[a.numerator]
    cmp     rax,0                       ;a.numerator = 0 then we don't print
    je      .done
    mov     cl,byte[a.sign]
    cmp     cl,1
    je      .nosign
    mov     byte[signbuffer],"-"
    syscall write,stdout,signbuffer,1
.nosign:    
    mov     rax,qword[a.numerator]
    cmp     rax,1
    je      .nonumerator
    mov     rdi,rax
    mov     rsi,integerbuffer
    call    HexToDecAscii
    syscall write,stdout,rax,rdx
.nonumerator:
    syscall write,stdout,equation.xx,equation.xxlen
    mov     rax,qword[a.denominator]
    cmp     rax,1
    je      .done
    mov     byte[signbuffer],"/"
    syscall write,stdout,signbuffer,1
    mov     rdi,qword[a.denominator]
    mov     rsi,integerbuffer
    call    HexToDecAscii
    syscall write,stdout,rax,rdx
.done:
    ret
    
PrintTermB:
    mov     rax,qword[b.numerator]
    cmp     rax,0                       ;a.numerator = 0 then we don't print
    je      .done
    mov     byte[signbuffer],"+"
    mov     cl,byte[b.sign]
    cmp     cl,1
    je      .printsign
    mov     byte[signbuffer],"-"
.printsign:    
    syscall write,stdout,signbuffer,1
    mov     rax,qword[b.numerator]
    cmp     rax,1
    je      .nonumerator
    mov     rdi,rax
    mov     rsi,integerbuffer
    call    HexToDecAscii
    syscall write,stdout,rax,rdx
.nonumerator:
    syscall write,stdout,equation.x,equation.xlen
    mov     rax,qword[b.denominator]
    cmp     rax,1
    je      .done
    mov     byte[signbuffer],"/"
    syscall write,stdout,signbuffer,1
    mov     rdi,qword[b.denominator]
    mov     rsi,integerbuffer
    call    HexToDecAscii
    syscall write,stdout,rax,rdx
.done:
    ret

PrintTermC:
    mov     rax,qword[c.numerator]
    cmp     rax,0
    je      .done
    mov     byte[signbuffer],"+"
    mov     cl,byte[c.sign]
    cmp     cl,1
    je      .printsign
    mov     byte[signbuffer],"-"
.printsign:    
    syscall write,stdout,signbuffer,1
    mov     rax,qword[c.numerator]
    mov     rdi,rax
    mov     rsi,integerbuffer
    call    HexToDecAscii
    syscall write,stdout,rax,rdx
    mov     rax,qword[c.denominator]
    cmp     rax,1
    je      .done
    mov     byte[signbuffer],"/"
    syscall write,stdout,signbuffer,1
    mov     rdi,qword[c.denominator]
    mov     rsi,integerbuffer
    call    HexToDecAscii
    syscall write,stdout,rax,rdx
.done:
    ret

math.inc

%ifndef _MATH_ASM_
%define _MATH_ASM_

section .text
    GreatestCommonDivisor:
        push    rbx
        mov     rax,rdi                 ;rax=u
        mov     rbx,rsi                 ;rbx=v
        ;make rdi and rsi positive if not
        and     rax,rax
        jns     .uispositive
        neg     rax
    .uispositive:
        and     rbx,rbx
        jns     .vispositive
        neg     rbx
    .vispositive:
        ;gcd(0,v)=v, gcd(u,0)=u,gcd(0,0)=0
        ;if v==0 return u
        and     rbx,rbx
        jz      .returnu
        ;if u==0 return v
        and     rax,rax
        jz      .returnv
        ;save used registers except rax = result
        ;remember rbx is already saved on stack
        push    rcx
        push    rdx
        push    r8
        push    r9
        ;initialize registers
        mov     r8,rax                  ;save u
        mov     r9,rbx                  ;save v
        xor     rcx,rcx                 ;shift=0
        mov     rdx,1                   ;help register containing 1
        ;left shift := lg k, where k is greatest power of 2 dividing both u and v
    .l1:
        ;for(shift=0;((u|v)&1)==0;shift++)
        mov     rax,r8                  ;rax=u
        mov     rbx,r9                  ;rbx=v
        or      rax,rbx                 ;u | v
        and     rax,rdx                 ;(u | v) & 1
        jnz     .l2                     ;(u | v) & 1 == 0 ?
        shr     r8,1                    ;yes, right shift u
        shr     r9,1                    ;     right shift v
        inc     rcx                     ;increment shift
        jmp     .l1
    .l2:
        ;while (u & 1) == 0
        mov     rax,r8                  ;rax=u
        and     rax,rdx                 ;u & 1
        jnz     .l3                     ; (u & 1) == 0
        shr     r8,1                    ; yes, right shift u
        jmp     .l2
    .l3:
        ;u is always odd
        ;remove all factors of two in v
        ;while ((v & 1) == 0)
        mov     rax,r9                  ;rax=v
        and     rax,rdx                 ;v & 1
        jnz     .l4                     ;(v & 1) == 0
        shr     r9,1                    ;yes right shift r9
        jmp     .l3
    .l4:
        ;u and v are odd, swap if necessary so u <= u
        mov     rax,r8                  ;rax=u
        mov     rbx,r9                  ;rbx=v
        cmp     rax,rbx                 ;u <= v
        jle     .l5                     ;yes, skip swapping
        xor     rax,rbx                 ;swap u and v
        xor     rbx,rax
        xor     rax,rbx
    .l5:
        ;v = v - u
        sub     rbx,rax
        mov     r8,rax                  ;store values
        mov     r9,rbx
        and     rbx,rbx                 ;v != 0
        jnz     .l3
        ;gcd = u << shift
        mov     rax,r8
        shl     rax,cl                  ;return gcd
        pop     r9
        pop     r8
        pop     rdx
        pop     rcx
        pop     rbx
        ret
    .returnv:
        mov     rax,rbx
    .returnu:
        pop     rbx
        ret

HexToDecAscii:
    push    rbx
    push    rsi
    push    rdi                         ;save value on stack    
    push    r8   
    mov     r8,rdi                      ;save value in r8
    ;initialize the buffer
    mov     rdi,rsi
    xor     rax,rax
    stosq                               ;clear the buffer
    stosq
    stosd
    mov     rdi,rsi
    add     rdi,19                      ;point to end of buffer
    mov     rax,r8                      ;get the hexadecimal value in rax
    and     rax,rax                     ;is it zero?
    jz      .iszero
    ;rax has the absolute value,rdi points to the last byte in the buffer
    ;start to convert.
    mov     rbx,10                      ;decimal base in rbx
.repeat:
    xor     rdx,rdx                     ;clear rdx
    div     rbx                         ;divide by ten, rdx has remainder, rax quotient
    or      dl,0x30                     ;make human readable
    mov     byte[rdi],dl                ;save digit
    dec     rdi                         ;adjust buffer pointer for next digit
    and     rax,rax                     ;is quotient zero?
    jnz     .repeat                     ;if not repeat the loop    
    mov     rdx,rsi
    add     rdx,19                      ;point to last position in buffer
    sub     rdx,rdi                     ;subtract start position to get length of number
    inc     rdi                         ;adjust buffer pointer
    jmp     .exit
.iszero:
    or      byte[rdi],'0'               ;make ascii
    inc     rdx                         ;length of decimal value is 1
.exit:
    mov     rax,rdi                     ;return pointer in rax
    pop     r8
    pop     rdi
    pop     rsi
    pop     rbx
    ret
    
%endif

fraction.class

%ifndef _FRACTION_CLASS_
%define _FRACTION_CLASS_

%include "math.inc"

struc FRACTION_STRUC
    .numerator:     resq    1
    .denominator:   resq    1
    .sign:          resb    1
    .Load:          resq    1
    .Simplify:      resq    1
    .Write:         resq    1
endstruc

%macro FRACTION 1

    %ifndef FRACTIONMACROS
    %define FRACTIONMACROS
    
        %macro FRACTIONLOAD 1
            mov     rax, %1+FRACTION_STRUC.Load
            call    qword[rax]
        %endmacro
        
        %macro FRACTIONSIMPLIFY 1
            mov     rax, %1+FRACTION_STRUC.Simplify
            call    qword[rax]
        %endmacro
        
        %macro FRACTIONWRITE 1
            mov     rax, %1+FRACTION_STRUC.Write
            call    qword[rax]
        %endmacro
        
        [section .data]
        @fractionerror:     db  "error: denominator cannot be zero",10
        .length:            equ  $-@fractionerror
        @fractionbuffer:    times 20 db 0
        
    %endif
    
    %define %1.numerator    %1+FRACTION_STRUC.numerator
    %define %1.denominator  %1+FRACTION_STRUC.denominator
    %define %1.sign         %1+FRACTION_STRUC.sign
    %define %1.Load         FRACTIONLOAD %1
    %define %1.Simplify     FRACTIONSIMPLIFY %1
    %define %1.Write        FRACTIONWRITE %1
    
    [section .data]
        %1: istruc FRACTION_STRUC
            at  FRACTION_STRUC.numerator,    dq  0
            at  FRACTION_STRUC.denominator,  dq  0
            at  FRACTION_STRUC.sign,         db  0
            at  FRACTION_STRUC.Load,         dq  FractionLOAD
            at  FRACTION_STRUC.Simplify,     dq  FractionSIMPLIFY
            at  FRACTION_STRUC.Write,        dq  FractionWRITE
        iend   
    
%endmacro

section .text

FractionLOAD:
    ;loads the numerator and denominator in a fraction pointed by rcx (this pointer).
    ;when one or both values are negative, the absolute value will be stored in the
    ;fraction and the sign byte is set accordingly.
    ;
    ;calling:
    ;   section .data
    ;       FRACTION objectname
    ;   section .text
    ;       mov     rdi,value
    ;       mov     rsi,value
    ;       objectname.Load
    cmp     rsi,0
    jne     .continue
    ;here we have a problem, indicating that the denominator can't be zero
    ;we will end with an exception
    syscall write,stderr,@fractionerror,@fractionerror.length
    syscall exit,0
.continue:    
    push    rax
    push    rcx
    push    rdi
    push    rsi   
    mov     rcx,rax
    sub     rcx,FRACTION_STRUC.Load - FRACTION_STRUC    ;points to 'this' fraction
    xor     al,al
    inc     al                                          ;positive sign
    cmp     rdi,0
    jge     .np
    neg     rdi                                         ;get absolute value
    neg     al                                          ;invert sign
.np:    
    mov     qword[rcx+FRACTION_STRUC.numerator],rdi
    cmp     rsi,0
    jge     .dp
    neg     rsi
    neg     al                                          ;invert sign
.dp:    
    mov     qword[rcx+FRACTION_STRUC.denominator],rsi
    mov     byte[rcx+FRACTION_STRUC.sign],al    
    pop     rsi
    pop     rdi
    pop     rcx
    pop     rax
.done:    
    ret

FractionSIMPLIFY:
    ;simplifies a fraction pointed by rcx (this).
    ;makes use of he GreatestCommonDivisor subroutine.
    ;note that the values for the numerator and denominator are treated as positive numbers.
    ;
    ;calling:
    ;   section .data
    ;       FRACTION objectname
    ;   section .text
    ;       objectname.Simplify   
    push    rax
    push    rbx                         
    push    rcx
    push    rdx
    mov     rcx,rax
    sub     rcx,FRACTION_STRUC.Simplify - FRACTION_STRUC   
    mov     rdi,qword[rcx+FRACTION_STRUC.numerator]
    mov     rsi,qword[rcx+FRACTION_STRUC.denominator]
    call    GreatestCommonDivisor
    and     rax,rax
    jz      .done                       ;if gcd is zero, meaning rdi and rsi is zero then just exit
    mov     rbx,rax                     ;gcd in rbx for divisions
    mov     rax,rdi
    xor     rdx,rdx
    idiv    rbx                        ;divide numerator by gcd, gives new numerator
    mov     qword[rcx+FRACTION_STRUC.numerator],rax
    mov     rax,rsi
    idiv    rbx                         ;divide denominator by gcd, gives new denominator
    mov     qword[rcx+FRACTION_STRUC.denominator],rax
.done:
    pop     rdx
    pop     rcx
    pop     rbx                         ;restore rbx
    pop     rax
    ret    
    
FractionWRITE:
    push    rax
    push    rcx
    push    rdi
    push    rsi
    push    r8
    mov     rcx,rax
    sub     rcx,FRACTION_STRUC.Write - FRACTION_STRUC
    mov     byte[@fractionbuffer],"+"                       ;assume positive fraction
    mov     al,byte[rcx+FRACTION_STRUC.sign]
    cmp     al,1                                            ;fraction positive
    je      .printplussign                                  ;yes, print plus sign if rdi<>0
    mov     byte[@fractionbuffer],"-"
    jmp     .printsign
.printplussign:
    and     rdi,rdi
    jz      .numerator
.printsign:
    push    rcx                                             ;save 'this' pointer
    syscall write,stdout,@fractionbuffer,1
    pop     rcx
.numerator:
    mov     rdi,qword[rcx+FRACTION_STRUC.numerator]
    mov     rsi,@fractionbuffer
    call    HexToDecAscii
    push    rcx
    syscall write,stdout,rax,rdx
    mov     byte[@fractionbuffer],"/"
    syscall write,stdout,@fractionbuffer,1
    pop     rcx
    mov     rdi,qword[rcx+FRACTION_STRUC.denominator]
    mov     rsi,@fractionbuffer
    call    HexToDecAscii
    syscall write,stdout,rax,rdx       
.done:
    pop     r8
    pop     rsi
    pop     rdi
    pop     rcx
    pop     rax
    ret
%endif

Webpage quadequations.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Quadratic Equation builder</title>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
        <script type="text/javascript">
        $(document).ready(function(){
            $(".button").click(function(){
                    $("form").submit();
            });  // end $(".button").click(function()
            $( "form" ).submit(function( event ) {
                var d  = $("#d").val();
                var x1 = $("#x1").val();
                var x2 = $("#x2").val();
                    event.preventDefault();
                    var posting = $.post(
                    "https://www.agguro.be/cgi-bin/quadraticequations",
                    {
                        "d" : d,
                        "x1" : x1,
                        "x2" : x2
                    });
                    posting.done(function( data,status ) {
                        $( "#data" ).empty().append(data);
                        $( "#status" ).empty().append( status );
                        var result = JSON.parse(data);   
                        $("#vald").empty().append(result.d);
                        $("#valx1").empty().append(result.x1);
                        $("#valx2").empty().append(result.x2);
                        $("#equation").empty().append(result.equation);
                    });  // end posting.done(function( data,status )
            });  // end $( "form" ).submit(function( event )
            $(function(){   
                $('.number-only').keyup(function(e){
                    if(this.value!='-')
                        while(isNaN(this.value))
                            this.value = this.value.split('').reverse().join('').replace(/[\D]/i,'').split('').reverse().join('');
                }).on("cut copy paste",function(e){
                    e.preventDefault();
                });
            });
            $(function(){   
                $('.positive-number-only').keyup(function(e){
                    //if(this.value!='-')
                        while(isNaN(this.value))
                            this.value = this.value.split('').reverse().join('').replace(/[\D]/i,'').split('').reverse().join('');
                }).on("cut copy paste",function(e){
                    e.preventDefault();
                });
            });
        });  // end $(document).ready(function(){
        
</script>
</head>
<body>
<pre>
<h1>Quadratic Equation builder</h1>
<form action="" method="post">
<label><span style="white-space: nowrap; font-size:larger">&radic;<span style="text-decoration:overline;">d</span></span> : </label><input class="positive-number-only" type="textbox" id="d"><label> x1 : </label><input class="number-only" type="textbox" id="x1"><label> x2 : </label><input class="number-only" type='textbox' id="x2">

<button id="post" class="button" type="submit">Build equation</button>
<span><h3>For</h3></span>
<span style="white-space: nowrap; font-size:larger">&radic;<span style="text-decoration:overline;">d</span></span> = <span style="font-style: italic;" id="vald"></span>
x1 = <span style="font-style: italic;" id="valx1"></span>
x2 = <span style="font-style: italic;" id="valx2"></span>
<span><h3>next equations are legal</h3></span><h3><span style="font-style: italic;" id="equation"></span></h3>
</form></pre>
</body>    
</html>