;;   Calculator for MenuetOS (c) Ville Turjanmaa
;;  
;;   Compile with FASM
;;   
;;   Pavel Rymovski (Heavyiron) - version for KolibriOS
;;
;; What's new:
;;   Calc 1.1
;;           1) changed design
;;           2) new procedure of draw window (10 decimal digits, 23 binary, "+" not displayed now)
;;           3) window with skin
;;   Calc 1.2
;;           1) added some useful functions, such as arcsin, arccos, arctg, 1/x, x^2
;;   Calc 1.31
;;           1) optimised program
;;           2) new type of window (you need kernel 114 revision or higher)
;;   Calc 1.32
;;           1) fixed arccos
;;   Calc 1.33
;;           1) align button captions in proper way, finally!


use32
        org     0x0
        db      'MENUET01'      ; 8 byte id
        dd      0x01            ; header version
        dd      START           ; start of code
        dd      I_END           ; size of image
        dd      E_END           ; memory for app
        dd      E_END           ; esp
        dd      0x0,0x0         ; I_Param , I_Icon

include '../../../macros.inc'

START:
red:
        call    draw_window
still:  
        mcall   10

        dec     eax
        jz      red
        dec     eax 
        jz      key 

button:
        mcall   17      ; get button id
        shr     eax, 8
        jmp     testbut

key:
        mcall   2       ; get ASCII key code
        shr     eax, 8
        mov     edi, asci       ; convert ASCII into button id
        mov     ecx, 18
        cld
        repne   scasb
        jne     still
        sub     edi, asci
        dec     edi
        mov     esi, butid
        add     esi, edi
        lodsb

testbut:
        cmp     eax, 1  ; button 1 -- exit
        jne     noexit
        mcall   -1
 
noexit:
        cmp     eax, 2
        jne     no_reset
        call    clear_all
        jmp     still

no_reset:
        finit
        mov     ebx, muuta1     ; convert to FPU format
        mov     esi, 18
        call    atof
        fstp    [trans1]
        mov     ebx, muuta2
        mov     esi, 18
        call    atof
        fst     [trans2]

        cmp     eax, 33
        jne     no_sign
        cmp     [dsign], byte '-'
        jne     no_m
        mov     [dsign], byte '+'
        call    print_display
        jmp     still

no_m:
        mov     [dsign], byte '-'
        call    print_display
        jmp     still

no_sign:
        cmp     eax, 3
        jne     no_display_change
        inc     [display_type]
        cmp     [display_type], 2
        jbe     display_continue
        mov     [display_type], 0

display_continue:
        mov     eax, [display_type]
        mov     eax, [multipl + eax*4]
        mov     [entry_multiplier], eax
        call    print_display
        jmp     still

no_display_change:
        cmp     eax, 6
        jb      no_a_f
        cmp     eax, 11
        jg      no_a_f
        add     eax, 4
        call    number_entry
        jmp     still

no_a_f:
        cmp     eax, 12
        jb      no_13
        cmp     eax, 14
        jg      no_13
        sub     eax, 11
        call    number_entry
        jmp     still

no_13:
        cmp     eax, 19
        jb      no_46
        cmp     eax, 21
        jg      no_46
        sub     eax, 15
        call    number_entry
        jmp     still

no_46:
        cmp     eax, 26
        jb      no_79
        cmp     eax, 28
        jg      no_79
        sub     eax, 19
        call    number_entry
        jmp     still

no_79:
        cmp     eax, 34
        jne     no_0
        xor     eax, eax
        call    number_entry
        jmp     still

no_0:
        cmp     eax, 35
        jne     no_id
        inc     [id]
        and     [id], 1
        mov     [new_dec], 100000
        jmp     still

no_id:
        cmp     eax, 17
        jne     no_sin
        fld     [trans1]
        fsin
        jmp     show_result

no_sin:
        cmp     eax, 18
        jne     no_asin
        fld     [trans1]
        fld     st0
        fmul    st, st1
        fld1
        fsubrp  st1, st0
        fsqrt
        fpatan
        jmp     show_result

no_asin:
        cmp     eax, 16
        jne     no_int
        fld     [trans1]
        frndint
        jmp     show_result

no_int:
        cmp     eax, 23
        jne     no_1x
        fld1
        fdiv    [trans1]
        jmp     show_result

no_1x:  
        cmp     eax, 24
        jne     no_cos
        fld     [trans1]
        fcos
        jmp     show_result

no_cos:
        cmp     eax, 25
        jne     no_acos
        fld     [trans1]
        fld     st0
        fmul    st, st1
        fld1
        fsubrp  st1, st0
        fsqrt
        fxch    st1
        fpatan
        jmp     show_result

no_acos:   
        cmp     eax, 30
        jne     no_x2
        fld     [trans1]
        fmul    st, st0
        jmp     show_result

no_x2:  
        cmp     eax, 31
        jne     no_tan
        fld     [trans1]
        fptan
        fstp    st2
        jmp     show_result

no_tan:
        cmp     eax, 32
        jne     no_atan
        fld     [trans1]
        fld1
        fpatan
        jmp     show_result

no_atan:
        cmp     eax, 38
        jne     no_pi
        fldpi
        jmp     show_result

no_pi:
        cmp     eax, 37
        jne     no_sqrt
        fld     [trans1]
        fsqrt
        jmp     show_result

no_sqrt:
        cmp     eax, 15
        jne     no_add
        call    calculate
        call    new_entry
        mov     [calc], '+'
        jmp     still

no_add:
        cmp     eax, 22
        jne     no_sub
        call    calculate
        call    new_entry
        mov     [calc], '-'
        jmp     still
  
no_sub:
        cmp     eax, 29
        jne     no_div
        call    calculate
        call    new_entry
        mov     [calc], '/'
        jmp     still

no_div:
        cmp     eax, 36
        jne     no_mul
        call    calculate
        mov     [calc], '*'
        call    new_entry
        jmp     still

no_mul:
        cmp     eax, 39
        jne     no_calc
        call    calculate
        jmp     still

no_calc:
        jmp     still

show_result:
        call    ftoa
        call    print_display
        jmp     still

error:
        jmp     still

calculate:
        pusha
        cmp     [calc], ' '
        je      no_calculation
        cmp     [calc], '/'
        jne     no_cdiv
        fdiv    [trans1]

no_cdiv:
        cmp     [calc], '*'
        jne     no_cmul
        fmul    [trans1]

no_cmul:
        cmp     [calc], '+'
        jne     no_cadd
        fadd    [trans1]

no_cadd:
        cmp     [calc], '-'
        jne     no_cdec
        fsub    [trans1]

no_cdec:
        call    ftoa

no_calculation:
        call    print_display
        popa
        ret

number_entry:

        pusha

        cmp     eax, [entry_multiplier]
        jge     no_entry
        cmp     [id], 1
        je      decimal_entry
        mov     ebx, [integer]
        test    ebx, 0xf0000000
        jnz     no_entry
        mov     ebx, eax
        mov     eax, [integer]
        mov     ecx, [entry_multiplier]
        mul     ecx
        add     eax, ebx
        mov     [integer], eax
        call    print_display
        call    to_muuta
        popa
        ret

decimal_entry:

        imul    eax, [new_dec]
        add     [decimal], eax
        mov     eax, [new_dec]
        xor     edx, edx
        mov     ebx, [entry_multiplier]
        div     ebx
        mov     [new_dec], eax
        call    print_display
        call    to_muuta
        popa
        ret

no_entry:

        call    print_display
        call    to_muuta
        popa
        ret

to_muuta:

        pusha
        mov     al, [dsign]
        mov     esi, muuta0
        mov     edi, muuta1
        mov     ecx, 18
        cld
        rep     movsb
        mov     [muuta1], al
        mov     edi, muuta1+10     ; []
        mov     eax, [integer]

new_to_muuta1:

        mov     ebx, 10
        xor     edx, edx
        div     ebx
        mov     [edi], dl
        add     [edi], byte 48
        dec     edi
        cmp     edi, muuta1+1
        jge     new_to_muuta1
        mov     edi, muuta1+17     ; {}
        mov     eax, [decimal]

new_to_muuta2:

        mov     ebx, 10
        xor     edx, edx
        div     ebx
        mov     [edi], dl
        add     [edi], byte 48
        dec     edi
        cmp     edi, muuta1+12
        jge     new_to_muuta2
        popa
        ret

new_entry:

        pusha
        mov     esi, muuta1
        mov     edi, muuta2
        mov     ecx, 18
        cld
        rep     movsb
        mov     esi, muuta0
        mov     edi, muuta1
        mov     ecx, 18
        cld
        rep     movsb
        mov     [integer], 0
        mov     [decimal], 0
        mov     [id], 0
        mov     [new_dec], 100000
        mov     [sign], byte '+'
        popa
        ret


ftoa:                         ; fpu st0 -> [integer],[decimal]
        pusha
        fst     [tmp2]
        fstcw   [controlWord]      ; set truncate integer mode
        mov     ax, [controlWord]
        mov     [tmp], ax
        or      [tmp], word 0x0c00
        fldcw   [tmp]
        ftst                      ; test if st0 is negative
        fstsw   ax
        and     ax, 0x4500
        mov     [sign], 0
        cmp     ax, 0x0100
        jne     no_neg
        mov     [sign], 1

no_neg:
        fld     [tmp2]
        cmp     byte [sign], 0     ; change fraction to positive
        je      no_neg2
        fchs

no_neg2:
        fadd    [smallValueForRounding]
        fist    [integer]
        fisub   [integer]
        mov     [res], 0     ; convert 6 decimal numbers
        mov     edi, 6

newd:
        fimul   [kymppi]
        fist    [decimal]
        mov     ebx, [res]
        imul    ebx, 10
        mov     [res], ebx
        mov     eax, [decimal]
        add     [res], eax
        fisub   [decimal]
        fst     [tmp2]
        ftst
        fstsw   ax
        test    ax, 1
        jnz     real_done
        fld     [tmp2]
        dec     edi
        jz      real_done
        jmp     newd

real_done:
        fldcw   [controlWord]
        mov     eax, [res]
        mov     [decimal], eax
        cmp     [integer], 0x80000000
        jne     no_error
        call    clear_all
        mov     [calc], 'E'

no_error:
        mov     [dsign], byte '+'
        cmp     [sign], byte 0      ; convert negative result
        je      no_negative
;       mov     eax, [integer]
;       not     eax
;       inc     eax
;       mov     [integer], eax
        mov     [dsign], byte '-'

no_negative:
        call    to_muuta
        popa
        ret


atof:
        push    ax
        push    di
        fldz
        mov     di, 0
        cmp     si, 0
        je      .error            ; Jump if string has 0 length.
        mov     byte [sign], 0
        cmp     byte [bx], '+'   ; Take care of leading '+' or '-'.
        jne     .noPlus
        inc     di
        jmp     .noMinus

  .noPlus:
        cmp     byte [bx], '-'
        jne     .noMinus
        mov     byte [sign], 1   ; Number is negative.
        inc     di

  .noMinus:
        cmp     si, di
        je      .error
        call    atof_convertWholePart
        jc      .error
        call    atof_convertFractionalPart
        jc      .error
        cmp     byte [sign], 0
        je      .dontNegate
        fchs    ; Negate value

  .dontNegate:
        mov     bh, 0    ; Set bh to indicate the string is a valid number.
        jmp     .exit

  .error:
        mov     bh, 1    ; Set error code.
;       fstp    st0    ; Pop top of fpu stack.

  .exit:
        pop     di
        pop     ax
        ret

atof_convertWholePart:

    ; Convert the whole number part (the part preceding the decimal
    ; point) by reading a digit at a time, multiplying the current
    ; value by 10, and adding the digit.

  .mainLoop:
        mov     al, [bx + di]
        cmp     al, '.'
        je      .exit
        cmp     al, '0'       ; Make sure character is a digit.
        jb      .error
        cmp     al, '9'
        ja      .error

    ; Convert single character to digit and save to memory for
    ; transfer to the FPU.

        sub     al, '0'
        mov     ah, 0
        mov     [tmp], ax

    ; Multiply current value by 10 and add in digit.

        fmul    dword [ten]
        fiadd   word [tmp]
        inc     di
        cmp     si, di         ; Jump if end of string has been reached.
        je      .exit
        jmp     .mainLoop

  .error:
        stc                ; Set error (carry) flag.
        ret

  .exit:
        clc                ; Clear error (carry) flag.
        ret


atof_convertFractionalPart:
        fld1               ; Load 1 to TOS.  This will be the value of the decimal place.

  .mainLoop:
        cmp     si, di         ; Jump if end of string has been reached.
        je      .exit
        inc     di             ; Move past the decimal point.
        cmp     si, di         ; Jump if end of string has been reached.
        je      .exit
        mov     al, [bx + di]
        cmp     al, '0'        ; Make sure character is a digit.
        jb      .error
        cmp     al, '9'
        ja      .error
        fdiv    dword [ten]   ; Next decimal place
        sub     al, '0'
        mov     ah, 0
        mov     [tmp], ax

    ; Load digit, multiply by value for appropriate decimal place,
    ; and add to current total.

        fild    word [tmp]
        fmul    st0, st1
        faddp   st2, st0
        jmp     .mainLoop

  .error:
        stc           ; Set error (carry) flag.
        fstp    st0    ; Pop top of fpu stack.
        ret

  .exit:
        clc              ; Clear error (carry) flag.
        fstp    st0    ; Pop top of fpu stack.
        ret

;   *********************************************
;   ******* WINDOW DEFINITIONS AND DRAW *********
;   *********************************************

draw_window:
        mcall   12, 1

        mcall   48, 3, sc, 192

        mcall   48, 4

        mov     ecx, eax
        xor     eax, eax                     
        mov     ebx, 200 shl 16 + 256
        add     ecx, 200 shl 16 + 158
        mov     edx, [sc.win_body]
        or      edx, 0x34000000
        mov     edi, title
        mcall

        mov     eax, 8
        mov     ebx, 19 shl 16 + 28
        mov     ecx, 49 shl 16 + 18
        mov     edx, 6
        mov     esi, [sc.btn_face]
        mov     edi, 7
newbutton:
        dec     edi
        jnz     no_new_row
        mov     edi, 7
        mov     ebx, 19 shl 16 + 28
        add     ecx, 20 shl 16
no_new_row:
        mcall
        add     ebx, 30 shl 16
        inc     edx
        cmp     edx, 39
        jbe     newbutton

        mcall   , <199, 28>, <49, 18>, 2        ; 'C'
        mcall   , <220,  8>, < 7,  8>, 3        ; 'dec-bin-hex'


        mov     ecx, [sc.btn_text]
        mov     edx, text
        mov     edi, 55 - 20
next_line:
        inc     edx
        and     edi, 0x0000ffff
        add     edi, 20 SHL 16 + 20
next_button:
        movzx   esi, byte[edx - 1]
        imul    eax, esi, 6
        neg     eax
        add     eax, 29
        shr     eax, 1
        shl     eax, 16
        mov     ebx, edi
        add     ebx, eax
        mcall   4
        add     edx, esi
        inc     edx
        add     edi, 30 SHL 16
        cmp     [edx - 1], byte 0
        jne     next_button
        cmp     [edx], byte 'x'
        jne     next_line

        call    print_display

        mcall   12, 2
        ret

print_display:
        pusha
        mcall   13, < 20, 207>, <21, 17>, [sc.gui_face]
        mcall   38, < 19, 227>, <20, 20>, [sc.gui_frame]
        mcall   38, < 19, 227>, <38, 38>, [sc.gui_frame]
        mcall   38, < 19,  19>, <21, 37>, [sc.gui_frame]
        mcall   38, <227, 227>, <21, 37>, [sc.gui_frame]

        mov     eax, 4
        mov     ebx, 135 shl 16 + 7
        mov     ecx, [sc.gui_text]
        or      ecx, 0x40000000
        mov     edx, calc
        mov     esi, 1
        mov     edi, [sc.win_body]
        mcall

        mov     ebx, 198 shl 16 + 8
        mov     edx, [display_type]
        shl     edx, 2
        add     edx, display_type_text
        mov     esi, 3
        mov     edi, [sc.win_body]
        mcall

        cmp     [dsign], byte '+'
        je      positive
        mcall   , <23, 26>, 0, dsign, 1

positive:  
        cmp     [display_type], 0
        jne     no_display_decimal
        cmp     [decimal], 0
        je      whole

        mcall   , <180, 26>, 0, dot, 1
        mcall   47, <10, 0>, [integer], <120, 22>, 0
        mcall   , <6, 0>, [decimal], <187, 22>, 0

        popa
        ret

whole:
        mcall   , <220, 26>, 0, dot, 1

        cmp     [integer], 0
        je      null
        mcall   47, <10, 0>, [integer], <160, 26>, 0
        popa
        ret

no_display_decimal:
        cmp     [display_type], 1
        jne     no_display_hexadecimal
        cmp     [integer], 0
        je      null
        mcall   47, <8, 256>, [integer], <173, 26>, 0
        popa
        ret

no_display_hexadecimal:
        cmp     [integer], 0
        je      null
        mcall   47, <32, 2*256>, [integer], <32, 26>, 0
        popa
        ret

null:
        mcall   47, <1, 0>, 0, <214, 26>, 0
        popa
        ret

clear_all:
        pusha
        mov     [calc], ' '
        mov     [integer], 0
        mov     [decimal], 0
        mov     [id], 0
        mov     [dsign], byte '+'
        mov     esi, muuta0
        mov     edi, muuta1
        mov     ecx, 18
        cld
        rep     movsb
        mov     esi, muuta0
        mov     edi, muuta2
        mov     ecx, 18
        cld
        rep     movsb
        call    print_display
        popa
        ret


;data

title   db 'Calc 1.33', 0

display_type            dd  0    ; 0 = decimal, 1 = hexadecimal, 2= binary
entry_multiplier        dd  10
display_type_text       db  'dec hex bin'

dot             db  '.'
calc            db  ' '
integer         dd  0
decimal         dd  0
kymppi          dd  10
ten             dd  10.0, 0
tmp             dw  1, 0
sign            db  1, 0
tmp2            dq  0, 0
exp             dd  0, 0
new_dec         dd  100000, 0
id              db  0, 0
res             dd  0
trans1          dq  0
trans2          dq  0
controlWord     dw  1
smallValueForRounding dq        0.0000005 ; 1/2 from last significant digit
multipl         dd  10,16,2

dsign:
muuta1          db  '+0000000000.000000'
muuta2          db  '+0000000000.000000'
muuta0          db  '+0000000000.000000'

text:
        db 1,'A',   1,'B', 1,'C', 1,'D', 1,'E',   1,'F',   3,'CLR', 0
        db 1,'1',   1,'2', 1,'3', 1,'+', 3,'Int', 3,'Sin', 4,'Asin', 0
        db 1,'4',   1,'5', 1,'6', 1,'-', 3,'1/x', 3,'Cos', 4,'Acos', 0
        db 1,'7',   1,'8', 1,'9', 1,'/', 3,'x^2', 3,'Tan', 4,'Atan', 0
        db 3,'+/-', 1,'0', 1,'.', 1,'*', 3,'Sqr', 2,'Pi',  1,'=', 0
        db 'x'

asci:   db 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 43, 61, 45, 42, 47, 44, 46, 27
butid:  db 12, 13, 14, 19, 20, 21, 26, 27, 28, 34, 15, 39, 22, 36, 29, 35, 35, 1

I_END:

sc      sys_colors_new
rb      0x200	; stack
E_END: