14 Replies - 1528 Views - Last Post: 25 June 2012 - 07:22 PM

#1 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 384
  • Joined: 18-September 11

How to use >32 bit numbers in x86

Posted 24 June 2012 - 06:01 PM

How do I represent big numbers on x86?

I've tried the following but it's giving me an incorrect value:

	movl	$0x0000008b, %eax
	movl	$0xe589eac7, %ebx



where eax:ebx would be 600851475143 (= 8be589eac7). I know that the second line is treating the number as signed, but I can't figure out how to handle it as an unsigned integer.

Thanks!

This post has been edited by carnivroar: 24 June 2012 - 06:02 PM

Is This A Good Question/Topic? 0
  • +

Replies To: How to use >32 bit numbers in x86

#2 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 859
  • View blog
  • Posts: 2,307
  • Joined: 28-March 11

Re: How to use >32 bit numbers in x86

Posted 24 June 2012 - 07:10 PM

What you are doing is correct. I get the correct output:

Attached Image

This is the code, I used esi instead of eax so the value does not get clobbered by printf
.data
szFmt       db  "%I64d%c", 0
szFmtHex    db  "%I64x%c", 0
.code
start:         
    mov     esi, 00000008bh
    mov     ebx, 0e589eac7h

    push    10
    push    esi
    push    ebx
    push    offset szFmt
    call    crt_printf
    add     esp, 4 * 4

    push    10
    push    esi
    push    ebx
    push    offset szFmtHex
    call    crt_printf
    add     esp, 4 * 4
    inkey
    ret
end start

It is MASM code and INTEL syntax, but you should get the idea. Unless I am misunderstanding what you want to do.
Was This Post Helpful? 0
  • +
  • -

#3 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 384
  • Joined: 18-September 11

Re: How to use >32 bit numbers in x86

Posted 24 June 2012 - 07:30 PM

That's exactly what I'm trying to do, but doesn't seem to work. What does your "push 10" line do?

int_format:
	.string "%d\n"

.section .text
.globl main

main:
	movl	$0x0000008b, %eax
	movl	$0xe589eac7, %ebx
	jmp		main_exit

print_num:
	pushl	%eax
	pushl	$int_format
	call	printf
	pushl	%ebx
	pushl	$int_format
	call	printf
	addl	$16, %esp
	ret

main_exit:
	call	print_num
	movl	$1, %eax
	movl	$1,	%ebx
	int		$0x80



And my output:

Quote

139
-443946297

Was This Post Helpful? 0
  • +
  • -

#4 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 859
  • View blog
  • Posts: 2,307
  • Joined: 28-March 11

Re: How to use >32 bit numbers in x86

Posted 24 June 2012 - 07:40 PM

You are getting the correct output for what you are passing to printf and the format string you are using.

Remember, you can pass many parameters to printf. See my code, I pass both esi and ebx which is esi:ebx (or is it the otherway around) and I tell it to expect a 64bit number with %I64 not sure what you use on Linux, according to wiki you use q
Was This Post Helpful? 1
  • +
  • -

#5 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 859
  • View blog
  • Posts: 2,307
  • Joined: 28-March 11

Re: How to use >32 bit numbers in x86

Posted 24 June 2012 - 08:10 PM

Ok, fired up the Linux VM and used NASM instead:

Attached Image

global  main
extern  printf

section .data
fmt:        db  "%qd", 13, 10, 0

section .text
main:
    mov     esi, 00000008bh
    mov     ebx, 0e589eac7h
    
    push    esi
    push    ebx
    push    fmt
    call    printf
    add     esp, 4 * 3
    ret

Was This Post Helpful? 1
  • +
  • -

#6 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 384
  • Joined: 18-September 11

Re: How to use >32 bit numbers in x86

Posted 24 June 2012 - 08:16 PM

View PostGunnerInc, on 24 June 2012 - 07:40 PM, said:

(or is it the otherway around)


I'm glad I read this. You are correct, I was putting them in reverse order. I thought that printf read the parameters from bottom to top.

This works now (I took out the things from your code that I didn't understand)

int64_format:
	.string "%qd\n"

.section .text
.globl main

main:
	movl	$0x0000008b, %eax
	movl	$0xe589eac7, %ebx
	jmp     main_exit

print_num:
	pushl	%eax
	pushl	%ebx
	pushl	$int64_format
	call	printf
	addl	$12, %esp
	ret

main_exit:
	call	print_num
	movl	$1, %eax
	movl	$1, %ebx
	int     $0x80



Output is 600851475143.

Thanks, I understand how functions work a little better now.

Now to do operations with it! I've only done it in MIPS before, I think it should be easier in x86.

This post has been edited by carnivroar: 24 June 2012 - 08:19 PM

Was This Post Helpful? 1
  • +
  • -

#7 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 384
  • Joined: 18-September 11

Re: How to use >32 bit numbers in x86

Posted 24 June 2012 - 10:34 PM

I'm almost done with my program.

Only problem is when I divide the big number - "If quotient does not fit into quotient register, arithmetic overflow interrupt occurs."

Is there any way to deal with this, since I don't care about the quotient, only the remainder? I tried jo (jump if overflow) but it doesn't seem to work.

Here is the piece of code:

	movl	4(%esp), %edx	# higher part of dividend
	movl	(%esp), %eax	# lower part of dividend
	divl	%edi		# divides number by index



After that it throws the Floating point exception (core dumped).

Thanks!

This post has been edited by carnivroar: 24 June 2012 - 10:34 PM

Was This Post Helpful? 0
  • +
  • -

#8 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 384
  • Joined: 18-September 11

Re: How to use >32 bit numbers in x86

Posted 25 June 2012 - 08:11 AM

Never mind, I found a solution. :)

Here's the code I was working on:

# PROJECT EULER
# PROBLEM 3
#
# The prime factors of 13195 are 5, 7, 13 and 29.
#
# What is the largest prime factor of the number 600851475143 ?

dec32_format:
	.string "%d\n"

dec64_format:
	.string "%qd\n"

.section .text
.globl main


main:
	movl	$0x0000008b, %ebx	# high part of number
	movl	$0xe589eac7, %ecx	# low part of number
	pushl	%ebx				# passes high part of number as parameter
	pushl	%ecx				# passes low part of number as parameter
	call	grt_prime_fctr		# calls greatest prime factor function
	popl	%ecx				# restores low part of number
	popl	%ebx				# restores high part of number
	movl	%eax, %edx			# moves result to edx register
	pushl	%edx				# passes result as a parameter
	call	print32				# prints result
	popl	%edx				# restores result
	call	main_exit

#-----------------------------------------------------------------------------
main_exit:
	movl	$1, %eax			
	movl	$1,	%ebx
	int		$0x80
#------------------------------------------------------------------------------
.type print_num, @function

print32:
	pushl	4(%esp)
	pushl	$dec32_format
	call	printf
	addl	$8, %esp
	ret
#------------------------------------------------------------------------------
.type print_num, @function

print64:
	pushl	8(%esp)
	pushl	8(%esp)
	pushl	$dec64_format
	call	printf
	addl	$12, %esp
	ret
#------------------------------------------------------------------------------
.type grt_prime_fctr, @ function

grt_prime_fctr:
	pushl	%ebp			# save old base pointer
	movl	%esp, %ebp		# create new stack pointer
	movl	12(%ebp), %edx  # high part of num
	movl	8(%ebp), %eax	# low part of num
	pushl	%edx			# push high part into stack
	pushl	%eax			# push low part into stack
	call	sqrt_approx		# calls sqrt function
	movl	%eax, %ebx		# edx is the sqrt
	popl	%eax			# restores low part
	popl	%edx			# restores high part
	movl	$1, %edi		# index/divisor	

gpf_loop:
	cmpl	%ebx, %edi		# compares index with sqrt of num
	ja		gpf_exit		# leave if greater
	pushl	%edx			# save high
	pushl	%eax			# save low
	pushl	%ecx			# save gpf result
	pushl	%ebx			# save sqrt
	pushl	%edi			# save and passes index as parameter
	call	is_prime		# call is_prime function
	cmpl	$1, %eax		# check prime result
	popl	%edi			# restore index
	popl	%ebx			# restore sqrt
	popl	%ecx			# restore gpf result
	popl	%eax			# restore low
	popl	%edx			# restore high
	jne		skip			# skip next statements if it was not a prime
	pushl	%edx			# save high
	pushl	%eax			# save low
	movl	$0, %edx		# high part of dividend = 0
	movl	4(%esp), %eax	# low part of dividend = high part of number
	div		%edi			# divides number by index
	movl	(%esp), %eax	# low part of dividend = low part of number
	div		%edi			# divides number by index
	cmpl	$0, %edx		# checks if remainder is 0
	popl	%eax			# restores low part of number
	popl	%edx			# restores high part of number
	jne		skip			# skip if it's not
	movl	%edi, %ecx		# index must be the current greatest prime factor

skip:
	addl	$2, %edi		# index becomes next odd number
	jmp		gpf_loop		# do it again

gpf_exit:
	movl	%ecx, %eax		# moves result into return register
	popl	%ebp			# restores base pointer
	ret
#------------------------------------------------------------------------------
.type sqrt_approx, @function

sqrt_approx:
	movl	8(%esp), %ebx	# high
	movl	4(%esp), %ecx	# low
	movl	$1, %edi		# index/sqrt candidate

sqrt_loop:
	movl	%edi, %eax		# move sqrt into required mul register
	mul		%eax			# multiply said register by itself
	cmpl	%ebx, %edx 		# compares high part of result with high part of number
	jl		continue		# skip next statements if it's less than
	cmpl	%ecx, %eax		# compares low part of result with low part of number
	jae		sqrt_exit		# if it's greater than or equal leave

continue:
	incl	%edi			# else increment sqrt
	jmp		sqrt_loop		# do it again

sqrt_exit:
	movl	%edi, %eax		# moves sqrt value into return register
	ret
#------------------------------------------------------------------------------
.type is_prime, @function

is_prime:
	pushl	%ebp			# save old base pointer
	movl	%esp, %ebp		# create new stack pointer
	movl	8(%ebp), %ebx	# arg
	cmpl	$2, %ebx		# compares argument to 2
	je		yes				# if it's equal it's prime
	jl		no				# if it's less than it's not prime
	pushl	$0				# high part of parameter
	pushl	%ebx			# low part of parameter
	call	sqrt_approx		# call sqrt function
	popl	%ebx			# restores register
	addl	$4, %esp		# restores sp
	movl	%eax, %ecx		# assigns sqrt to ecx
	movl	$3, %edi		# i = 3

prime_loop:
	cmpl	%ecx, %edi 		# i >= sqrt(arg) ?
	jge		yes				# quit if it is
	movl	$0, %edx		# higher part of dividend
	movl	%ebx, %eax		# lower part of dividend
	divl	%edi			# divides current by index
	cmpl	$0, %edx		# checks if remainder is 0
	je		no				# if it is it's not a prime
	addl	$2, %edi		# index+=2
	jmp		prime_loop		# do it again

yes:
	movl	$1, %eax		# sets result to "true"
	jmp		prime_exit		# exits

no:
	movl	$0, %eax		# sets result to "false"

prime_exit:
	movl	%ebp, %esp		# stack pointer
	popl	%ebp			# restores base pointer
	ret



Very inefficient but gives me the correct answer. If you could read through my comments and tell if if it looks like I'm doing the right thing, I would be very thankful!

This post has been edited by carnivroar: 25 June 2012 - 12:25 PM

Was This Post Helpful? 0
  • +
  • -

#9 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 859
  • View blog
  • Posts: 2,307
  • Joined: 28-March 11

Re: How to use >32 bit numbers in x86

Posted 25 June 2012 - 02:56 PM

I changed a few things around for you
changes/comments are below - #############################

Not sure of AT&T syntax for xor, inc, test , they might be the same.

# PROJECT EULER
# PROBLEM 3
#
# The prime factors of 13195 are 5, 7, 13 and 29.
#
# What is the largest prime factor of the number 600851475143 ?

dec32_format:
	.string "%d\n"

dec64_format:
	.string "%qd\n"

.section .text
.globl main


main:
    # the following are not needed
	# movl	$0x0000008b, %ebx	# high part of number
	# movl	$0xe589eac7, %ecx	# low part of number
	# change these two 
	# pushl	%ebx				# passes high part of number as parameter
	# pushl	%ecx				# passes low part of number as parameter
	#############################
	pushl   $0x0000008b         # I think pushl is correct
	pushl   $0xe589eac7
	call	grt_prime_fctr		# calls greatest prime factor function
	#############################
	
	# popl	%ecx				# restores low part of number
	# popl	%ebx				# restores high part of number
	# movl	%eax, %edx			# moves result to edx register
	# pushl	%eax				# passes result as a parameter
	pushl   %eax
	call	print32				# prints result
	# no need for this
	# popl	%edx				# restores result
	call	main_exit

#-----------------------------------------------------------------------------
main_exit:
    # lets save some bytes by not using these
    # these 2 are 10 bytes
	# movl	$1, %eax			
	# movl	$1,	%ebx
	
	# The following 4 instructions are 6 bytes
	# not sure of AT&T syntax for them
	xor    %eax, %eax
	inc    %eax
	
	xor     %ebx, %ebx
	inc     %ebx
	int		$0x80
#------------------------------------------------------------------------------
.type print_num, @function

print32:
	pushl	4(%esp)
	pushl	$dec32_format
	call	printf
	addl	$8, %esp
	# we pushed 1 param on the stack, remove it
	ret     4
#------------------------------------------------------------------------------
.type print_num, @function

print64:
	pushl	8(%esp)
	pushl	8(%esp)
	pushl	$dec64_format
	call	printf
	addl	$12, %esp
	ret
#------------------------------------------------------------------------------
.type grt_prime_fctr, @ function

grt_prime_fctr:
	pushl	%ebp			# save old base pointer
	movl	%esp, %ebp		# create new stack pointer
	movl	12(%ebp), %edx  # high part of num
	movl	8(%ebp), %eax	# low part of num
	pushl	%edx			# push high part into stack
	pushl	%eax			# push low part into stack
	call	sqrt_approx		# calls sqrt function
	movl	%eax, %ebx		# edx is the sqrt
	popl	%eax			# restores low part
	popl	%edx			# restores high part
	movl	$1, %edi		# index/divisor	

gpf_loop:
	cmpl	%ebx, %edi		# compares index with sqrt of num
	ja		gpf_exit		# leave if greater
	pushl	%edx			# save high
	pushl	%eax			# save low
	pushl	%ecx			# save gpf result
	pushl	%ebx			# save sqrt
	pushl	%edi			# save and passes index as parameter
	call	is_prime		# call is_prime function
	cmpl	$1, %eax		# check prime result
	popl	%edi			# restore index
	popl	%ebx			# restore sqrt
	popl	%ecx			# restore gpf result
	popl	%eax			# restore low
	popl	%edx			# restore high
	jne		skip			# skip next statements if it was not a prime
	pushl	%edx			# save high
	pushl	%eax			# save low
	movl	$0, %edx		# high part of dividend = 0
	movl	4(%esp), %eax	# low part of dividend = high part of number
	div		%edi			# divides number by index
	movl	(%esp), %eax	# low part of dividend = low part of number
	div		%edi			# divides number by index
	cmpl	$0, %edx		# checks if remainder is 0
	popl	%eax			# restores low part of number
	popl	%edx			# restores high part of number
	jne		skip			# skip if it's not
	movl	%edi, %ecx		# index must be the current greatest prime factor

skip:
	addl	$2, %edi		# index becomes next odd number
	jmp		gpf_loop		# do it again

gpf_exit:
	movl	%ecx, %eax		# moves result into return register
	#########################
	# Did you forget this:
	movl	%ebp, %esp		# stack pointer

	popl	%ebp			# restores base pointer
	ret
#------------------------------------------------------------------------------
.type sqrt_approx, @function

sqrt_approx:
	movl	8(%esp), %ebx	# high
	movl	4(%esp), %ecx	# low
	movl	$1, %edi		# index/sqrt candidate

sqrt_loop:
	movl	%edi, %eax		# move sqrt into required mul register
	mul		%eax			# multiply said register by itself
	cmpl	%ebx, %edx 		# compares high part of result with high part of number
	jl		continue		# skip next statements if it's less than
	cmpl	%ecx, %eax		# compares low part of result with low part of number
	jae		sqrt_exit		# if it's greater than or equal leave

continue:
	incl	%edi			# else increment sqrt
	jmp		sqrt_loop		# do it again

sqrt_exit:
	movl	%edi, %eax		# moves sqrt value into return register
	ret
#------------------------------------------------------------------------------
.type is_prime, @function

is_prime:
	pushl	%ebp			# save old base pointer
	movl	%esp, %ebp		# create new stack pointer
	movl	8(%ebp), %ebx	# arg
	cmpl	$2, %ebx		# compares argument to 2
	je		yes				# if it's equal it's prime
	jl		no				# if it's less than it's not prime
	pushl	$0				# high part of parameter
	pushl	%ebx			# low part of parameter
	call	sqrt_approx		# call sqrt function
	popl	%ebx			# restores register
	addl	$4, %esp		# restores sp
	movl	%eax, %ecx		# assigns sqrt to ecx
	movl	$3, %edi		# i = 3

prime_loop:
	cmpl	%ecx, %edi 		# i >= sqrt(arg) ?
	jge		yes				# quit if it is
	########################
	# change this
	# movl	$0, %edx		# higher part of dividend
	# to this
	xor     %edx, %edx
	#########################
	movl	%ebx, %eax		# lower part of dividend
	divl	%edi			# divides current by index
	
	#########################
	# change this
	# cmpl	$0, %edx		# checks if remainder is 0
	# je		no				# if it is it's not a prime
	# to this
	test    %edx, %edx
	jz      no
	
	addl	$2, %edi		# index+=2
	jmp		prime_loop		# do it again

yes:
    ########################
	# movl	$1, %eax		# sets result to "true"
	xor     %eax, %eax
	inc     %eax
	jmp		prime_exit		# exits

no:
    #########################
	# movl	$0, %eax		# sets result to "false"
	xor     %eax, %eax

prime_exit:
	movl	%ebp, %esp		# stack pointer
	popl	%ebp			# restores base pointer
	ret



Was This Post Helpful? 0
  • +
  • -

#10 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 384
  • Joined: 18-September 11

Re: How to use >32 bit numbers in x86

Posted 25 June 2012 - 05:34 PM

Aside from that stray "4" on line 65, it runs perfectly fine. I've never used xor or test before - I'll look into those. It obviously works so I'll take your word for it and research it.

Just one question:

# we pushed 1 param on the stack, remove it

Isn't that what addl $8, %esp does? (also, I think I pushed 2 parameters, not one?).

Thanks for your help and for putting up with the AT&T syntax. :) I don't really mind it.
Was This Post Helpful? 0
  • +
  • -

#11 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 859
  • View blog
  • Posts: 2,307
  • Joined: 28-March 11

Re: How to use >32 bit numbers in x86

Posted 25 June 2012 - 05:45 PM

No, the adding 8 to esp is to clean up the stack for the call to printf. PrintF uses the CDECL calling convention where the caller cleans up the stack since it doesn't know how many params are passed.

We pushed 1 parameter on the stack for the call to print32, so we use ret 4 to remove it from the stack. Program might work today without it, but you WILL get a bug somewhere in your code eventually because of not having it. And it is not fun tracking a random bug in Assembly.

XOR'ing a register with itself is an oldschool way of zeroing out a register, do your research for the reasons we use it.

TESTing a register with itself is a bit quicker to test for zero.

For a MIPS person that never used x86, your doing a good job.
Was This Post Helpful? 0
  • +
  • -

#12 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 384
  • Joined: 18-September 11

Re: How to use >32 bit numbers in x86

Posted 25 June 2012 - 06:30 PM

Oh, so that "4" on line 65 was on purpose? Because I get this error:

test.s:65: Error: unsupported for `ret'

And XOR does make sense now that I think about it:

1101011
1101011
-------
0000000

So is movl one of the more expensive instructions that should be avoided when possible?

And thanks. The hardest part was just getting started and picking a source to learn from.
Was This Post Helpful? 0
  • +
  • -

#13 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 859
  • View blog
  • Posts: 2,307
  • Joined: 28-March 11

Re: How to use >32 bit numbers in x86

Posted 25 June 2012 - 06:39 PM

See, I am not an AT&T syntax guy. I had to look up the format, it should be:
    ret    $4
at least I think.

From the page I was looking at, should help ya

Quote

Also, the far return instruction is lret $stack-adjust in AT&T syntax; Intel syntax is ret far stack-adjust.

Was This Post Helpful? 0
  • +
  • -

#14 carnivroar  Icon User is offline

  • D.I.C Regular

Reputation: 28
  • View blog
  • Posts: 384
  • Joined: 18-September 11

Re: How to use >32 bit numbers in x86

Posted 25 June 2012 - 07:09 PM

Oh, sorry, I should have caught that.

Another question. When a function receives a parameter from the stack, should the function save that parameter in a register locally or should it leave it on the stack? Either one works but which one is better? Compare my modified version to see what I mean (look at the arrows). Also, here I'm saving the base pointer and restoring it at the end although it wasn't necessary. Is it still good to do that?

#------------------------------------------------------------------------------
.type sqrt_approx, @function

sqrt_approx:
	pushl	%ebp			# save base pointer
	xor		%edi, %edi		# index/sqrt candidate
	incl	%edi

sqrt_loop:
	movl	%edi, %eax		# move sqrt into required mul register
	mul		%eax			# multiply said register by itself
--> cmpl	12(%esp), %edx 	# compare high part 
	jl		continue		# skip next statements if it's less than
--> cmpl	8(%esp), %eax	# compare low part 
	jae		sqrt_exit		# if it's greater than or equal leave

continue:
	incl	%edi			# else increment sqrt
	jmp		sqrt_loop		# do it again

sqrt_exit:
	movl	%edi, %eax		# move sqrt value into return register	
	popl	%ebp			# restore base pointer
	ret


This post has been edited by carnivroar: 25 June 2012 - 07:09 PM

Was This Post Helpful? 0
  • +
  • -

#15 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 859
  • View blog
  • Posts: 2,307
  • Joined: 28-March 11

Re: How to use >32 bit numbers in x86

Posted 25 June 2012 - 07:22 PM

Your not using ebp in your function so you do not have to save it. We really don't use esp directly because once you start pushing things in your proc, the offests in esp will be a pain to keep track of. In this small simple proc, there is no problem.

That is why we do:
    push    ebp
    mov     ebp, esp
    ...
    ...
    ...
    pop     ebp
    ret


So now, you can push things and not worry about keeping track of the offsets in esp.

esp is a pointer to a memory address on the stack, so your comparing the value at the pointer offset to the value in a register, memory compares are always a bit slower vs moving [esp + 12] into a register first.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1