Ok... next question. :grin:

Now I've split 2 floating point numbers (strings) into two parts, whole and decimal.

So if the user enters: '123.999' and '1.111'


FPVal_W1 is '123' and FPVal_D1 is '999'
FPVal_W2 is '1__' and FPVal_D2 is '111'


So I add like this:

123.999
001.1


Now, I'm having some trouble adding the carry from the addition of ( FPVal_D1 + FPVal_D2 ) to FPVal_W1 like in a normal arithmitec problem...

Here is what I got so far:


.data

FPSum_W1 BYTE 60 dup ('#'),0
FPSum_D1 BYTE 60 dup ('#'),0

.
.
.
.

.code
.
.
.
Addition1:

setPointer esi,FPVal_D1 ;points esi to last digit in FPVal_D1
setPointer edi,FPVal_D2 ;points edi to last digit in FPVal_D2
setPointer ebx,FPSum_D1 ;points ebx to last space in FPSum_D1

dec ebx

mov ecx, lengthof FPVal_D1
clc

L1:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf

loop L1

setPointer esi,FPVal_W1

jnc Addition2

carry_over:
mov al, '1'
add al,

aaa

pushf

add al, 30h
mov , al

popf

Addition2:


setPointer esi,FPVal_W1
setPointer edi,FPVal_W2
setPointer ebx,FPSum_W1

dec ebx

mov ecx, lengthof FPVal_W1
clc

L2:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf

loop L2


jnc Print

mov byte ptr , '1'


I wanna say in advance thanks to anyone who takes their time to look at my code and help me out, :)

:) :) :) :) :)
Posted on 2003-04-23 00:52:00 by locke
I know you are probably wanting to do this the hard way, but.... did you know that there is a StrToFloat function in masm32.lib? You could convert both your strings to floats, then put them on the fpu and let it do the addition for you.
Posted on 2003-04-23 03:03:22 by sluggy
His project requires him to use the Ascii commands ;-)


IIRC AAA will convert the byte in AL into two decimal digits in AH and AL... can't be sure though.
Posted on 2003-04-23 03:36:10 by AmkG
I think you need to use FPU for the code. Not too sure anyway since I do not use much of FPU.
Posted on 2003-04-23 04:01:12 by roticv
locke

There are several ways to learn something new. One of them is to read on the subject. Sometimes, the information is not sufficient nor too clear. By trial and error, you may be able to figure out what the text was trying to explain. Whatever you can learn that way will be remembered for a long time, and possibly apply to other situations, thus making your learning curve much steeper if you have the capabilities to absorb it.

I would strongly suggest that you get a debugger and see for yourself what happens to the registers (including the flag register) under various scenarios of input with the instructions that are not familiar to you.

Another way of learning is to expect to be "spoon-fed". You won't really learn much that way unless responders go to extremes to explain all the details of their reasonning. And, even then, each programmer has his/her own style which may not suit yours.

For your current assignment, forget the FPU because you have to learn the usage of operations with packed/unpacked decimals. It may come handy to you some day.

The very first part of your assignment will be to learn what those operations require as input. Only then can you proceed further.

Raymond
Posted on 2003-04-23 15:34:46 by Raymond
I've already read about Unpacked BCD, Packed BCD, and ASCII numbers. My debugger (VC++) isn't working for whatever reason... and I don't wanna deal w/ uninstalling and reinstalling it.... just wanted to see if anyone could see a real obvious mistake I might have made w/ my code or not. And no I don't want to deal w/ the FPU, cause thats not what I'm doing right now. THis whole project is to learn about ASCII OPERATIONS AAA, AAS ... and yes I know what they do. I've just had some problem w/ formating the strings (padding), now that I got that right (thanks Raymond :) ), I'm just trying to finish this proj up.

Jumping from C++ to Asm, I've learned a lot about major concepts of coding, which hopefully I can translate to whatever language.

Thanks again everyone for the help.
Posted on 2003-04-23 21:38:41 by locke
I don't know what you have read about BCDs (and I won't assume anything), but it appears that you have not learned that they are not ASCII. The AAA, AAD, etc. do not operate on ASCII characters but on binary numbers.

Therefore, you have to convert your ASCII characters to binary numbers before you do any of the maths, and then convert the binary answer back to ASCII for display.

Have fun

Raymond
Posted on 2003-04-23 21:59:23 by Raymond
Forgot to mention one trick for your assignment.

After you make both numbers the same length (integers and decimals), join the integer digits and decimal digits together for each of the numbers (simulating a multiplication by a factor of 10 to eliminate the decimals. Then do the addition and reposition your decimal point in the answer for display (simulating a division by the same factor of 10).

Have more fun

Raymond
Posted on 2003-04-23 22:18:15 by Raymond
This is straight outta my book (Assembly Language For Intel Based Computers):

"The CPU calculates in binary, but it is also possible to perform arithmetic on ASCII decimal strings."

"Suppose that we would like to input two numbers from the user and add them together. The following is a sample of the program's output, in which the user has input 3402 and 1256:

Enter first number: 3402
Enter second number: 1256
The sum is: 4658 "

"We have two options when calculating and displaying the sum:

1. Convert both operands to binary (what you said Raymond), add the binary values, and convert the sum from binary to ASCII strings.

2. Add the digits strings directly by successively adding each pair of ASCII digits (2+6, 0+5, 4+2, 3+1). The sum is an ASCII digit, so it can be directly displayed on the screen "

"The second option requires specialized instructions that adjust the sum after adding each pair of ASCII digits. The instruction set has four instructions that deal with ASCII addition, subtraction, multiplication, and division: AAA, AAS, AAM and AAD."

"When executing ASCII addition and subtraction, operand s can be in either ASCII format or in unpacked decimal format. Only unpacked decimal numbers can be used for multiplication and division."

"AAA (ASCII adjust after addition) and AAS adjust the binary result of and ADD or ADC instruction. IT makes the result in AL consisten with ASCII digit representation."

Here is the example they give:

mov ah, 0
mov al, '8' ; AX = 0038h
add al, '2' ; AX = 006Ah
aaa ; AX = 0100h
or ax, 3030h ; AX = 3130h = '10'

and basically thats what I'm doing...
Posted on 2003-04-24 13:39:52 by locke
or ax, 3030h ; AX = 3130h = '10'
One minor observation. If you store that AX value as a word and try to display it as a string, it would come out as '01'!

More food for thought since you are showing a very sincere interest in learning.

This may not be a specified part of your current assignment, but it's never too early to review the subject. A good program never assumes that the user will always provide valid input. For a program to return valid results, code must be added to verify the validity of the input and provide some additional code to handle invalid and/or problematic input.

The following are some examples of input which could be entered by the user:

123.45 ;the obvious valid input which you currently expect
123.5 ;mathematically valid with one or more leading (or trailing) spaces but could insert needless 0's in your output.
+12.4567 ;mathematically valid but the + sign would be treated as a value of 11 if not removed.
-5.789 ;also mathematically valid but you would have to transfer control to a subtraction procedure unless both numbers were negative. In the latter case, you would need to remove the - signs before adding and later reinsert it in the output.
765 ;mathematically valid but without any decimal point or digits.
84.K/L ;obviously invalid.
12.56.98 ;also obviously invalid with more than 1 decimal point.

You could also imagine a few more unusual kinds of input. Not to mention the absence of input.

Sounds like more fun

And, as the saying goes, there are more than one way to skin a cat. As algorithms go, there are innumerable ways to achieve the desired result in assembler. After you have verified the input and lined it up, you could also look at the following:

EDI pointing to the terminating 0 of the1st string
ESI pointing to the terminating 0 of the 2nd string
EBX pointing to the end of the buffer where you store the result
ECX containing the sum of integer and decimal digits to be added
  mov byte ptr [ebx],0 ;store the terminating 0

clc
lahf ;keep the flags in AH, starting with NO carry
toploop:
dec edi
dec esi
dec ebx
mov al,[edi]
cmp al,"."
jz @F ;store the decimal point in result
sahf ;recover the flags (primarily the carry flag)
adc al,[esi] ;adds the two digits + any carryover
aaa
lahf ;keep the latest carry flag in AH
add al,30h
@@:
mov [ebx],al
dec ecx
jnz toploop

sahf
jnc @F ;jump if last addition had no carry
dec ebx
mov byte ptr [ebx],"1"
@@:

;if you have to insert a "-" sign, you would now need the following 2 lines

dec ebx
mov byte ptr [ebx],"-"

;EBX now points to the first character of the null-terminated result string.
You can be sure that every programmer would come up with something different to arrive at the same result.

Raymond
Posted on 2003-04-24 20:05:50 by Raymond
Raymond... my program as it is, already seperates a floating point number into two parts, than padds it if neccessary. If a user enters '123.999' and '1.111' it seperates and padds like this:

FPVal_W1 holds -> '123'
FPVal_W2 holds -> '001'

FPVal_D1 holds -> '999'
FPVal_D2 holds -> '111'

But my problem is... I can't get it to add the carry from adding '999' and '111' to '123'.


After I get this case done, I'm going to add later code to check for signs (+/-) and other invalid input. But right now, I have to do this first.

Here is my entire code so far (kinda long):



;===============================================

INCLUDE Irvine32.inc

includelib KERNEL32.lib
includelib USER32.lib



wsprintfA PROTO C :DWORD,:DWORD,:VARARG
wsprintf equ <wsprintfA>

.data

str1 BYTE "Assignment4: Addition of two Floating Point Numbers ",0Ah, 0Dh,0
str2 BYTE "Enter your name: ",0
str3 BYTE "Please enter two Floating Point Numbers, ",0
str4 BYTE "Enter first Floating Point Number: ",0
str5 BYTE "Enter second Floating Point Number: ",0
str6 BYTE ".",0
str7 BYTE "The sum is: ",0

buffer BYTE 50 dup (0), 0

FPVal1 BYTE 50 dup ('0'),0
FPVal2 BYTE 50 dup ('0'),0

FPVal_W1 BYTE 50 dup ('0'),0
FPVal_D1 BYTE 50 dup ('0'),0

FPVal_W2 BYTE 50 dup ('0'),0
FPVal_D2 BYTE 50 dup ('0'),0

FPSum_W1 BYTE 51 dup ('0'),0
FPSum_D1 BYTE 51 dup ('0'),0

setPointer MACRO reg, var ; Macro to set pointer
lea reg, var ; Load Effective Address of register
add reg, lengthof var ; Move pointer to end of string
dec reg ; Move back one
ENDM ; End of Macro definition

EnterString MACRO FP_Buffer ; Readstring Macro
lea edx, FP_Buffer ; Load Effective Address of FP_Buffer
mov ecx, (SIZEOF FP_Buffer) - 1 ; Maximum number of characters
call Readstring ; Call Readstring function
ENDM


.code

main PROC

mov eax, lightGreen + (black * 16) ;light green text on black background
call SetTextColor
call Clrscr

lea edx, str1 ; load effective address str1
call WriteString ; "Assignment4: Addition of two Floating Point Numbers"
call Crlf ; end line

call WaitMsg ; "Press..."

lea edx, str2 ; load effective offset str2
call WriteString ; "Enter your name: "

lea edx, buffer ; load effective address buffer
mov ecx, (SIZEOF buffer) - 1 ; maximum # of characters
call ReadString ; read string input from user

lea edx, str3 ; load effective address str3
call WriteString ; "Please enter two Floating Point Numbers, "

lea edx, buffer ; load effective offset buffer
call WriteString ; display user name
call Crlf ; end line

Again:
call Crlf ; end line

lea edx, str4 ; load effective address str4
call WriteString ; "Enter first Floating Point Number: "
Enterstring FPVal1 ; read floating point value from user
call Crlf ; end line

lea edx, str5 ; load effective offset str5
call WriteString ; "Enter second Floating Point Number: "
Enterstring FPVal2 ; read floating point value from user
call Crlf ; end line

Mov_Num1:
mov ecx, LENGTHOF FPVal1 ; get length of string
lea edi, FPVal_W1 ; load effective address of FPVal_W1
lea esi, FPVal1 ; load effective address of FPVal1
cld ; clear direction flag

check_loop1:

lodsb ; load byte from memory at ESI into AL
cmp al, '.' ; compare to see if it is decimal point
jne no_change_dest1 ; jump if not equal to no_change_dest1
mov byte ptr , 0 ; set null terminator
lea edi, FPVal_D1 ; load effective address FPVal_D1
jmp loop_next1 ; jump to loop_next1

no_change_dest1:

stosb ; store content of al into memory at EDI

loop_next1:

dec ecx ; decrement counter
jnz check_loop1 ; jump if not zero to check_loop1
xor al, al ; clear al register
stosb ; store content of al into memory at EDI

Mov_Num2:
mov ecx, LENGTHOF FPVal2
lea edi, FPVal_W2
lea esi, FPVal2
cld

check_loop2:

lodsb
cmp al, '.'
jne no_change_dest2
mov byte ptr , 0
lea edi, FPVal_D2
jmp loop_next2

no_change_dest2:

stosb

loop_next2:

dec ecx
jnz check_loop2
xor al, al
stosb




Prepare:

INVOKE Str_length, ADDR FPVal_W2
push eax ; preserve that value on the stack
INVOKE Str_length, ADDR FPVal_W1
pop ecx ; retrieve W2 length

std ; do everything backwards

.if ( eax > ecx ) ; if (W1 > W2)
lea edi, ;
lea esi, ; points to the terminating 0
push ecx
inc ecx
rep movsb ; moves the terminating 0 and the digits

mov ecx,eax ; longer string length
pop eax ; shorter string length
sub ecx,eax ; difference between the two
mov al,'0'
rep stosb
.elseif ( ecx > eax ) ; if (W2 > W1)
lea edi,
lea esi, ;points to the terminating 0

push ecx
mov ecx, eax
inc ecx
rep movsb ; moves the terminating 0 and the digits


pop ecx ; restore original value of ecx
sub ecx, eax ; difference between the two
mov al,'0'
rep stosb
.endif ; if both are equal, you don't change anything
cld ; clean-up the direction flag



Add_Decimal_Part:

setPointer esi,FPVal_D1
setPointer edi,FPVal_D2
setPointer ebx,FPSum_D1

dec ebx

mov ecx, lengthof FPVal_D1
clc

L1:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf

loop L1

setPointer esi,FPVal_W1
setPointer edi,FPVal_W2
setPointer ebx,FPSum_W1

dec ebx

jnc Add_Whole_Part

Carry_Over:

mov al,
adc al, '1'
aaa
pushf
add al, 30h
mov , al



Add_Whole_Part:


setPointer esi,FPVal_W1
setPointer edi,FPVal_W2
setPointer ebx,FPSum_W1

dec ebx

mov ecx, lengthof FPVal_W1
clc

L2:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf

loop L2


jnc Remove_Extra

mov byte ptr , '1'



comment %

Subtraction:

setPointer esi,num1
setPointer edi,num2
setPointer ebx,dif

dec ebx

mov ecx, lengthof num1
clc

L3:
mov al,
sbb al,

aas

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf
loop L3

%

Remove_Extra:

;INVOKE Str_trim, ADDR FPSum_W1, '0'
;INVOKE Str_trim, ADDR FPSUm_D1, '0'


Print:
call Crlf
lea edx, FPVal_W1
call Writestring
call Crlf
lea edx, FPVal_W2
call Writestring
call Crlf

call Crlf
lea edx, FPVal_D1
call Writestring
call Crlf
lea edx, FPVal_D2
call Writestring
call Crlf

call Crlf
lea edx, str7
call Writestring

lea edx, FPSum_W1
call Writestring
lea edx, str6
call Writestring
lea edx, FPSum_D1
call Writestring
call Crlf

INVOKE ExitProcess,0 ; exit program

main ENDP
END main




Also when I print out the sum... I get excess zeros '0' ... like if the sum should be 259.112 its prints like: '25900000000000000000000000.1120000000000000000000000'

I tried adding the code: mov byte ptr , 0
( pointing to the buffer where I save the sum ) right before adding... but that didn't help.
Posted on 2003-04-24 23:50:24 by locke
I would recommend that you convert the numbers to a fixed point format and add fixed point numbers. I saw from your previous thread that you were having problems with it. Position the decimal place for example after the 64th digit in both 128 digit numbers (for example). This way you just need to add two numbers BCD numbers...

so, position esi to last digit of FPVal1
edi to last position of FPVal2
ebx to target

clc
L1:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf

loop L1

The carry will cross the decimal point just as it crosses everywhere else..

If you insist on adding the whole part and decimal separately, however, forget testing for a carry from the decimal part!!!!!!
Just add it into the first digit of the whole part!!!!

;(*****leave out this part because now you are altering your ;source number... Actually, what are you altering here? esi was ;defined to point to the decimal part, but now that the decimal ;addition is gone, and esi has not yet been set to point to the ;whole part, esi should be undefined!!!! So, leave out all of this

;jnc Addition2

;carry_over:
;mov al, '1'
;***** Next problem, you should add 1, not $31 ASCII 49('1')
;***** ie use mov al, 1
;add al,
;***** Plus in your latest program I see you use
;***** mov al,
;***** adc al, '1'
;***** this also adds $31 ASCII 49 ('1')
;***** but since the carry flag is set, adc adds an extra 1
;***** none of this matters since I have already said to omit
;***** this entire section that checks for the carry flag
;***** straingt from the {***** to *****)


;aaa

;pushf

;add al, 30h
;mov , al

;popf
;*******)

;(***** Edit this section as follows
;pushf ***** - should not be necessary because none of the ;following instructions affect the carry
Addition2:

setPointer esi,FPVal_W1
setPointer edi,FPVal_W2
setPointer ebx,FPSum_W1

;***** dec ebx - leave out this I think

mov ecx, lengthof FPVal_W1
;***** clc - leave out the clc because you are simply using the ;carry flag from the last addition
;popf ***** - should not be necessary because none of the ;preceding instructions affect the carry
L2:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf
;*****)


Hope this helps..... I put in the brackets and stars to delineate the program... for commenting purposes...

What this does is simply move from adding the decimals to adding the whole part. The carry flag is added in normally with adc.
Posted on 2003-04-25 00:40:05 by V Coder
As V Coder explained, go directly from adding the decimals to adding the integers.

HOWEVER, as you exit Loop1, you must save the flags because the carry flag will be affected by your setPointer macro instructions. You then retrieve the flags just before entering the L2 loop.

Raymond
Posted on 2003-04-25 08:57:29 by Raymond
Did as you said V_Coder and Raymond, but didn't work... hmmmm

Actually it kinda works, but the other problem comes up(excess zeros '0') which I don't know where it comes form.

When I added: '123.999' and '1.111'

123
001

999
111


I got the sum as: '124000000000000000000000000000000000000001' . '1100000000000000000000000000000000000000'

so instead of adding the carry from '999' and '111' to the '124', it added it to the excess zeros...

Any ideas? :confused:



here is the code:



Addition1:

setPointer esi,FPVal_D1
setPointer edi,FPVal_D2
setPointer ebx,FPSum_D1

dec ebx

mov ecx, lengthof FPVal_D1
clc

L1:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf

loop L1

pushf

Addition2:

setPointer esi,FPVal_W1
setPointer edi,FPVal_W2
setPointer ebx,FPSum_W1

;dec ebx

mov ecx, lengthof FPVal_W1

popf

L2:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf

loop L2


jnc Remove_Extra

mov byte ptr , '1'

Posted on 2003-04-25 15:58:56 by locke
For your add routine to work, you need to "right justify" your whole number part. Don't change the fraction part.

Store it as

00000000000xxx

rather than as

xxx00000000000

This way you also get zeros in the correct places. Then for printing, you just need to skip over zeros on the correct ends of the two strings.
Posted on 2003-04-25 16:45:11 by tenkey
The whole number part is already right justified...

If the two numbers '123.999' and '1.111' are entered it adds in '0's to the shorter string of the whole number part, so it becomes:

'123' is saved in FPVal_W1
'001' is saved in FPVal_W2

than their sum is suppose to be saved in FPSum_W1...


its just that... after adding it and saving the sum... for some reason it adds or there are already extra zeros... :(
Posted on 2003-04-25 17:34:42 by locke
No, it's not right justified.

In your macros, you use LENGTHOF which starts your pointer not at the end of the number, but at the end of the string buffer. You have '0' chars at the end of the buffer. The (binary) zero byte that ends the string is at the very end of the buffer, so your print routines display everything to that point. You're adding the carry to the digit at the end of the buffer, not the end of the number.

What your strings look like are:

123000...000
001000...000

not as I suggest:

000...000123
000...000001
Posted on 2003-04-25 20:34:12 by tenkey
Let's go back to basics. Your setPointer macro uses the "lengthof" pseudo code. This returns the number of bytes you reserved for the variable used with it. If you would have reserved 256 bytes for that variable, it returns a value of 256 regardless of the number of bytes you would have effectively used at the start of that buffer, AND it doesn't care about terminating 0's.

If you want to know the number of characters at the beginning of a buffer before a terminating 0, the masm32.lib has a function to do just that: lnstr.

Secondly. Getting the end address of the buffer where you want to store the result can easily be obtained by getting the address of the following variable and decrementing it by 1.

answer db 100 dup(0)
nexone db xx dup(xx)

mov ebx,offset nexone-1 ;->address of last byte of answer buffer

Store your terminating 0 immediately at that point and "dec ebx"

Thirdly. After you add the decimals, store immediately your decimal point and don't try to store the integers into another buffer. Then, when you're finished with the integers, EBX will point to the start of your full answer.

You're getting there. Don't despair.

Raymond
Posted on 2003-04-25 22:10:52 by Raymond
I'm not familiar w/ lnstr is it the same as Str_length function I've been using?


Is this correct?

.data

FPSum_D1 BYTE 60 dup ('0'),0
FPSum_W1 BYTE 60 dup ('0'),0

.code

mov ebx, offset FPSum_W1-1;
mov byte ptr ,0

dec ebx
Posted on 2003-04-25 23:01:53 by locke
this is how I updated my code, still isn't working quite right... : /


It actually adds it correctly w/ the the carry ... but now I have extra '0's on the left of the number



setPointer MACRO reg, var ; Macro to set pointer
lea reg, var ; Load Effective Address of register
INVOKE Str_length, ADDR var ; Receive length of string in eax
add reg, eax ; Move pointer to end of string
dec reg ; Move back one
ENDM ; End of Macro definition


.
.
.
.


Addition1:

setPointer esi, FPVal_D1
setPointer edi, FPVal_D2

mov ebx, offset FPSum_W1-1;
mov byte ptr ,0

dec ebx

INVOKE Str_length, ADDR FPVal_D1
mov ecx, eax

;mov ecx, lengthof FPVal_D1
clc

L1:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf

loop L1

pushf

mov byte ptr , '.'
dec ebx

Addition2:

setPointer esi, FPVal_W1
setPointer edi, FPVal_W2

INVOKE Str_length, ADDR FPVal_W1
mov ecx, eax
;mov ecx, lengthof FPVal_W1

popf

L2:
mov al,
adc al,

aaa

pushf

add al, 30h
mov , al

dec esi
dec edi
dec ebx

popf

loop L2


jnc Remove_Extra

mov byte ptr , '1'




So if I add '900.999' and '100.111' I get: '0000000000000000001001.110'

well I guess thats not such a big gripe... maybe I should start working on a case for negative signs

hmmm... actually I get an error of memory could not be written if I enter a whole number alone, like: '2' and '5'
Posted on 2003-04-25 23:17:41 by locke