How to print a number in assembly NASM?
Suppose that I have an integer number in a register, how can I print it? Can you show a simple example code?
I already know how to print a string such as "hello, world".
I'm developing on Linux.
assembly nasm
add a comment |
Suppose that I have an integer number in a register, how can I print it? Can you show a simple example code?
I already know how to print a string such as "hello, world".
I'm developing on Linux.
assembly nasm
Please specify the OS where the program will run.
– Alexey Frunze
Nov 19 '11 at 12:56
1
Related: convert an integer to an ASCII decimal string in a buffer on the stack and print it with Linuxwrite
system call, not usingprintf
or any other functions. With comments and explanation.
– Peter Cordes
Nov 18 '17 at 18:14
add a comment |
Suppose that I have an integer number in a register, how can I print it? Can you show a simple example code?
I already know how to print a string such as "hello, world".
I'm developing on Linux.
assembly nasm
Suppose that I have an integer number in a register, how can I print it? Can you show a simple example code?
I already know how to print a string such as "hello, world".
I'm developing on Linux.
assembly nasm
assembly nasm
edited Mar 24 '14 at 2:42
alex
341k170768914
341k170768914
asked Nov 19 '11 at 12:46
AR89AR89
1,16652037
1,16652037
Please specify the OS where the program will run.
– Alexey Frunze
Nov 19 '11 at 12:56
1
Related: convert an integer to an ASCII decimal string in a buffer on the stack and print it with Linuxwrite
system call, not usingprintf
or any other functions. With comments and explanation.
– Peter Cordes
Nov 18 '17 at 18:14
add a comment |
Please specify the OS where the program will run.
– Alexey Frunze
Nov 19 '11 at 12:56
1
Related: convert an integer to an ASCII decimal string in a buffer on the stack and print it with Linuxwrite
system call, not usingprintf
or any other functions. With comments and explanation.
– Peter Cordes
Nov 18 '17 at 18:14
Please specify the OS where the program will run.
– Alexey Frunze
Nov 19 '11 at 12:56
Please specify the OS where the program will run.
– Alexey Frunze
Nov 19 '11 at 12:56
1
1
Related: convert an integer to an ASCII decimal string in a buffer on the stack and print it with Linux
write
system call, not using printf
or any other functions. With comments and explanation.– Peter Cordes
Nov 18 '17 at 18:14
Related: convert an integer to an ASCII decimal string in a buffer on the stack and print it with Linux
write
system call, not using printf
or any other functions. With comments and explanation.– Peter Cordes
Nov 18 '17 at 18:14
add a comment |
6 Answers
6
active
oldest
votes
If you're already on Linux, there's no need to do the conversion yourself. Just use printf instead:
;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf
main:
mov eax, 0xDEADBEEF
push eax
push message
call printf
add esp, 8
ret
message db "Register = %08X", 10, 0
Note that printf
uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.
Thanks, it seems to be the what I was looking for. Do you know if it works also on Mac os X?
– AR89
Nov 19 '11 at 15:17
How to compile it on 64-bit?
– Figen Güngör
Dec 19 '12 at 9:43
@FigenGüngör stackoverflow.com/a/32853546/895245
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Sep 29 '15 at 20:52
add a comment |
You have to convert it in a string; if you're talking about hex numbers it's pretty easy. Any number can be represented this way:
0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3
So when you have this number you have to split it like I've shown then convert every "section" to its ASCII equivalent.
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we're interested in in the first four bits then AND the result with 0xf to isolate it from the rest. Here's what I mean (soppose we want to take the 3):
0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003
Now that we have a single number we have to convert it into its ASCII value. If the number is smaller or equal than 9 we can just add 0's ASCII value (0x30), if it's greater than 9 we have to use a's ASCII value (0x61).
Here it is, now we just have to code it:
mov si, ??? ; si points to the target buffer
mov ax, 0a31fh ; ax contains the number we want to convert
mov bx, ax ; store a copy in bx
xor dx, dx ; dx will contain the result
mov cx, 3 ; cx's our counter
convert_loop:
mov ax, bx ; load the number into ax
and ax, 0fh ; we want the first 4 bits
cmp ax, 9h ; check what we should add
ja greater_than_9
add ax, 30h ; 0x30 ('0')
jmp converted
greater_than_9:
add ax, 61h ; or 0x61 ('a')
converted:
xchg al, ah ; put a null terminator after it
mov [si], ax ; (will be overwritten unless this
inc si ; is the last one)
shr bx, 4 ; get the next part
dec cx ; one less to do
jnz convert_loop
sub di, 4 ; di still points to the target buffer
PS: I know this is 16 bit code but I still use the old TASM :P
PPS: this is Intel syntax, converting to AT&T syntax isn't difficult though, look here.
@downvoter: reason?
– BlackBear
Nov 19 '11 at 14:12
You don't need AT&T syntax to run this on linux.
– Andrei Bârsan
Feb 5 '13 at 18:52
@AndreiBârsan: You're right, fixed that.. It's such an old answer :)
– BlackBear
Feb 5 '13 at 21:13
1
IMHO, this answer is better since you don't need the C runtime (which a call toprintf(...)
requires.
– Andrei Bârsan
Feb 6 '13 at 12:41
1
@AndreiBârsan yes, and it's kind of pointless using the C runtime in assembly
– BlackBear
Feb 6 '13 at 18:25
add a comment |
Linux x86-64 with printf
extern printf, exit
section .data
format db "%x", 10, 0
section .text
global main
main:
sub rsp, 8
mov rsi, 0x12345678
mov rdi, format
xor rax, rax
call printf
mov rdi, 0
call exit
Then:
nasm -f elf64 main.asm
gcc main.o
./a.out
The "hard points" of the System V AMD64 ABI calling convention:
sub rsp, 8
: How to write assembly language hello world program for 64 bit Mac OS X using printf?
mov rax, 1
: Why is %eax zeroed before a call to printf?
If you want hex without the C library: https://stackoverflow.com/a/32756303/895245
add a comment |
It depends on the architecture/environment you are using.
For instance, if I want to display a number on linux, the ASM code will be different from the one I would use on windows.
Edit:
You can refer to THIS for an example of conversion.
A Linux example would be fine.
– AR89
Nov 19 '11 at 13:04
@AR89 it's a bad job.. You have to convert the number to ASCII first. Take a look at the edited question.
– AlQafir
Nov 19 '11 at 13:19
add a comment |
I'm relatively new to assembly, and this obviously is not the best solution,
but it's working. The main function is _iprint, it first checks whether the
number in eax is negative, and prints a minus sign if so, than it proceeds
by printing the individual numbers by calling the function _dprint for
every digit. The idea is the following, if we have 512 than it is equal to: 512 = (5 * 10 + 1) * 10 + 2 = Q * 10 + R, so we can found the last digit of a number by dividing it by 10, and
getting the reminder R, but if we do it in a loop than digits will be in a
reverse order, so we use the stack for pushing them, and after that when
writing them to stdout they are popped out in right order.
; Build : nasm -f elf -o baz.o baz.asm
; ld -m elf_i386 -o baz baz.o
section .bss
c: resb 1 ; character buffer
section .data
section .text
; writes an ascii character from eax to stdout
_cprint:
pushad ; push registers
mov [c], eax ; store ascii value at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; copy c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; writes a digit stored in eax to stdout
_dprint:
pushad ; push registers
add eax, '0' ; get digit's ascii code
mov [c], eax ; store it at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; pass the address of c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; now lets try to write a function which will write an integer
; number stored in eax in decimal at stdout
_iprint:
pushad ; push registers
cmp eax, 0 ; check if eax is negative
jge Pos ; if not proceed in the usual manner
push eax ; store eax
mov eax, '-' ; print minus sign
call _cprint ; call character printing function
pop eax ; restore eax
neg eax ; make eax positive
Pos:
mov ebx, 10 ; base
mov ecx, 1 ; number of digits counter
Cycle1:
mov edx, 0 ; set edx to zero before dividing otherwise the
; program gives an error: SIGFPE arithmetic exception
div ebx ; divide eax with ebx now eax holds the
; quotent and edx the reminder
push edx ; digits we have to write are in reverse order
cmp eax, 0 ; exit loop condition
jz EndLoop1 ; we are done
inc ecx ; increment number of digits counter
jmp Cycle1 ; loop back
EndLoop1:
; write the integer digits by poping them out from the stack
Cycle2:
pop eax ; pop up the digits we have stored
call _dprint ; and print them to stdout
dec ecx ; decrement number of digits counter
jz EndLoop2 ; if it's zero we are done
jmp Cycle2 ; loop back
EndLoop2:
popad ; pop registers
ret ; bye
global _start
_start:
nop ; gdb break point
mov eax, -345 ;
call _iprint ;
mov eax, 0x01 ; sys_exit
mov ebx, 0 ; error code
int 0x80 ; край
You canadd '0'
and store your digits in a buffer as you produce them. Usedec
do move the pointer downwards. When you're done, you have a pointer to the last digit you stored, so you can pass that tosys_write()
(along with the digit count). This is much more efficient than making a separate system call for every byte, and doesn't really take more code. It's easy to allocate a buffer long enough to hold the longest possible string of digits, and start at the end, because you know how many decimal digits 2^32 has.
– Peter Cordes
Jul 29 '17 at 7:59
related: I wrote an integer->string loop as part of this extended-precision Fibonacci code-golf answer. See the.toascii_digit:
loop. Of course, that's optimized for size, so it uses a slowdiv
instead of a multiply trick.
– Peter Cordes
Jul 29 '17 at 8:01
1
Thank you this is definitely preferable than calling sys_write for every digit:)
– baz
Aug 15 '17 at 15:44
I posted my int->string +sys_write
code as a stand-alone function on another question, with comments.
– Peter Cordes
Nov 18 '17 at 18:17
add a comment |
Because you didn't say about number representation I wrote the following code for unsigned number with any base(of course not too big), so you could use it:
BITS 32
global _start
section .text
_start:
mov eax, 762002099 ; unsigned number to print
mov ebx, 36 ; base to represent the number, do not set it too big
call print
;exit
mov eax, 1
xor ebx, ebx
int 0x80
print:
mov ecx, esp
sub esp, 36 ; reserve space for the number string, for base-2 it takes 33 bytes with new line, aligned by 4 bytes it takes 36 bytes.
mov edi, 1
dec ecx
mov [ecx], byte 10
print_loop:
xor edx, edx
div ebx
cmp dl, 9 ; if reminder>9 go to use_letter
jg use_letter
add dl, '0'
jmp after_use_letter
use_letter:
add dl, 'W' ; letters from 'a' to ... in ascii code
after_use_letter:
dec ecx
inc edi
mov [ecx],dl
test eax, eax
jnz print_loop
; system call to print, ecx is a pointer on the string
mov eax, 4 ; system call number (sys_write)
mov ebx, 1 ; file descriptor (stdout)
mov edx, edi ; length of the string
int 0x80
add esp, 36 ; release space for the number string
ret
It's not optimised for numbers with base of power of two and doesn't use printf
from libc
.
The function print
outputs the number with a new line. The number string is formed on stack. Compile by nasm.
Output:
clockz
https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm
You're using space below ESP. That's only safe in cases like this where you know there are no signal handlers installed, and shouldn't be used in functions that could be called in other contexts. 32-bit Linux doesn't guarantee a red-zone. Also, usexor edx,edx
/div
orcdq
/idiv
so the zero or sign-extension of the dividend matches the signedness of the division. In this case you wantxor
/div
so you always have a positive remainder. If you want to treat your input as signed, you'll want to test/js and print the unsigned absolute value (with a leading-
if needed).
– Peter Cordes
Dec 30 '18 at 7:18
@PeterCordes, Hi, Peter! You're right about safety. It's a partial solution and I didn't think about signed numbers.
– TigerTV.ru
Dec 30 '18 at 17:09
You should still changeidiv
todiv
so it works for the full range of unsigned numbers. Hmm, actually this might be safe anyway, because 2^32-1 / 10 doesn't overflow EAX. zero-extending into edx:eax gives you a signed non-negative dividend from 0..2^32-1.
– Peter Cordes
Dec 30 '18 at 20:57
@PeterCordes, Theidiv
has been replaced. Also I added base for the number. What do you think about it? And else I reserved a buffer on stack for number string with size 32.
– TigerTV.ru
Dec 31 '18 at 1:13
add esp, 32
should besub
to reserve space. You're stepping on the caller's stack space.mov byte [ecx], 10
would be more efficient than setting a register first. Or evenpush 10
/mov ecx, esp
/sub esp, 32
. (For your current version, a large number with base=2 will use 32 digits, but you use up one of your 32 with a newline.)
– Peter Cordes
Dec 31 '18 at 1:25
|
show 1 more comment
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f8194141%2fhow-to-print-a-number-in-assembly-nasm%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
If you're already on Linux, there's no need to do the conversion yourself. Just use printf instead:
;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf
main:
mov eax, 0xDEADBEEF
push eax
push message
call printf
add esp, 8
ret
message db "Register = %08X", 10, 0
Note that printf
uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.
Thanks, it seems to be the what I was looking for. Do you know if it works also on Mac os X?
– AR89
Nov 19 '11 at 15:17
How to compile it on 64-bit?
– Figen Güngör
Dec 19 '12 at 9:43
@FigenGüngör stackoverflow.com/a/32853546/895245
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Sep 29 '15 at 20:52
add a comment |
If you're already on Linux, there's no need to do the conversion yourself. Just use printf instead:
;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf
main:
mov eax, 0xDEADBEEF
push eax
push message
call printf
add esp, 8
ret
message db "Register = %08X", 10, 0
Note that printf
uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.
Thanks, it seems to be the what I was looking for. Do you know if it works also on Mac os X?
– AR89
Nov 19 '11 at 15:17
How to compile it on 64-bit?
– Figen Güngör
Dec 19 '12 at 9:43
@FigenGüngör stackoverflow.com/a/32853546/895245
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Sep 29 '15 at 20:52
add a comment |
If you're already on Linux, there's no need to do the conversion yourself. Just use printf instead:
;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf
main:
mov eax, 0xDEADBEEF
push eax
push message
call printf
add esp, 8
ret
message db "Register = %08X", 10, 0
Note that printf
uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.
If you're already on Linux, there's no need to do the conversion yourself. Just use printf instead:
;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf
main:
mov eax, 0xDEADBEEF
push eax
push message
call printf
add esp, 8
ret
message db "Register = %08X", 10, 0
Note that printf
uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.
answered Nov 19 '11 at 13:36
MartinMartin
27k136373
27k136373
Thanks, it seems to be the what I was looking for. Do you know if it works also on Mac os X?
– AR89
Nov 19 '11 at 15:17
How to compile it on 64-bit?
– Figen Güngör
Dec 19 '12 at 9:43
@FigenGüngör stackoverflow.com/a/32853546/895245
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Sep 29 '15 at 20:52
add a comment |
Thanks, it seems to be the what I was looking for. Do you know if it works also on Mac os X?
– AR89
Nov 19 '11 at 15:17
How to compile it on 64-bit?
– Figen Güngör
Dec 19 '12 at 9:43
@FigenGüngör stackoverflow.com/a/32853546/895245
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Sep 29 '15 at 20:52
Thanks, it seems to be the what I was looking for. Do you know if it works also on Mac os X?
– AR89
Nov 19 '11 at 15:17
Thanks, it seems to be the what I was looking for. Do you know if it works also on Mac os X?
– AR89
Nov 19 '11 at 15:17
How to compile it on 64-bit?
– Figen Güngör
Dec 19 '12 at 9:43
How to compile it on 64-bit?
– Figen Güngör
Dec 19 '12 at 9:43
@FigenGüngör stackoverflow.com/a/32853546/895245
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Sep 29 '15 at 20:52
@FigenGüngör stackoverflow.com/a/32853546/895245
– Ciro Santilli 新疆改造中心 六四事件 法轮功
Sep 29 '15 at 20:52
add a comment |
You have to convert it in a string; if you're talking about hex numbers it's pretty easy. Any number can be represented this way:
0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3
So when you have this number you have to split it like I've shown then convert every "section" to its ASCII equivalent.
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we're interested in in the first four bits then AND the result with 0xf to isolate it from the rest. Here's what I mean (soppose we want to take the 3):
0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003
Now that we have a single number we have to convert it into its ASCII value. If the number is smaller or equal than 9 we can just add 0's ASCII value (0x30), if it's greater than 9 we have to use a's ASCII value (0x61).
Here it is, now we just have to code it:
mov si, ??? ; si points to the target buffer
mov ax, 0a31fh ; ax contains the number we want to convert
mov bx, ax ; store a copy in bx
xor dx, dx ; dx will contain the result
mov cx, 3 ; cx's our counter
convert_loop:
mov ax, bx ; load the number into ax
and ax, 0fh ; we want the first 4 bits
cmp ax, 9h ; check what we should add
ja greater_than_9
add ax, 30h ; 0x30 ('0')
jmp converted
greater_than_9:
add ax, 61h ; or 0x61 ('a')
converted:
xchg al, ah ; put a null terminator after it
mov [si], ax ; (will be overwritten unless this
inc si ; is the last one)
shr bx, 4 ; get the next part
dec cx ; one less to do
jnz convert_loop
sub di, 4 ; di still points to the target buffer
PS: I know this is 16 bit code but I still use the old TASM :P
PPS: this is Intel syntax, converting to AT&T syntax isn't difficult though, look here.
@downvoter: reason?
– BlackBear
Nov 19 '11 at 14:12
You don't need AT&T syntax to run this on linux.
– Andrei Bârsan
Feb 5 '13 at 18:52
@AndreiBârsan: You're right, fixed that.. It's such an old answer :)
– BlackBear
Feb 5 '13 at 21:13
1
IMHO, this answer is better since you don't need the C runtime (which a call toprintf(...)
requires.
– Andrei Bârsan
Feb 6 '13 at 12:41
1
@AndreiBârsan yes, and it's kind of pointless using the C runtime in assembly
– BlackBear
Feb 6 '13 at 18:25
add a comment |
You have to convert it in a string; if you're talking about hex numbers it's pretty easy. Any number can be represented this way:
0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3
So when you have this number you have to split it like I've shown then convert every "section" to its ASCII equivalent.
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we're interested in in the first four bits then AND the result with 0xf to isolate it from the rest. Here's what I mean (soppose we want to take the 3):
0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003
Now that we have a single number we have to convert it into its ASCII value. If the number is smaller or equal than 9 we can just add 0's ASCII value (0x30), if it's greater than 9 we have to use a's ASCII value (0x61).
Here it is, now we just have to code it:
mov si, ??? ; si points to the target buffer
mov ax, 0a31fh ; ax contains the number we want to convert
mov bx, ax ; store a copy in bx
xor dx, dx ; dx will contain the result
mov cx, 3 ; cx's our counter
convert_loop:
mov ax, bx ; load the number into ax
and ax, 0fh ; we want the first 4 bits
cmp ax, 9h ; check what we should add
ja greater_than_9
add ax, 30h ; 0x30 ('0')
jmp converted
greater_than_9:
add ax, 61h ; or 0x61 ('a')
converted:
xchg al, ah ; put a null terminator after it
mov [si], ax ; (will be overwritten unless this
inc si ; is the last one)
shr bx, 4 ; get the next part
dec cx ; one less to do
jnz convert_loop
sub di, 4 ; di still points to the target buffer
PS: I know this is 16 bit code but I still use the old TASM :P
PPS: this is Intel syntax, converting to AT&T syntax isn't difficult though, look here.
@downvoter: reason?
– BlackBear
Nov 19 '11 at 14:12
You don't need AT&T syntax to run this on linux.
– Andrei Bârsan
Feb 5 '13 at 18:52
@AndreiBârsan: You're right, fixed that.. It's such an old answer :)
– BlackBear
Feb 5 '13 at 21:13
1
IMHO, this answer is better since you don't need the C runtime (which a call toprintf(...)
requires.
– Andrei Bârsan
Feb 6 '13 at 12:41
1
@AndreiBârsan yes, and it's kind of pointless using the C runtime in assembly
– BlackBear
Feb 6 '13 at 18:25
add a comment |
You have to convert it in a string; if you're talking about hex numbers it's pretty easy. Any number can be represented this way:
0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3
So when you have this number you have to split it like I've shown then convert every "section" to its ASCII equivalent.
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we're interested in in the first four bits then AND the result with 0xf to isolate it from the rest. Here's what I mean (soppose we want to take the 3):
0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003
Now that we have a single number we have to convert it into its ASCII value. If the number is smaller or equal than 9 we can just add 0's ASCII value (0x30), if it's greater than 9 we have to use a's ASCII value (0x61).
Here it is, now we just have to code it:
mov si, ??? ; si points to the target buffer
mov ax, 0a31fh ; ax contains the number we want to convert
mov bx, ax ; store a copy in bx
xor dx, dx ; dx will contain the result
mov cx, 3 ; cx's our counter
convert_loop:
mov ax, bx ; load the number into ax
and ax, 0fh ; we want the first 4 bits
cmp ax, 9h ; check what we should add
ja greater_than_9
add ax, 30h ; 0x30 ('0')
jmp converted
greater_than_9:
add ax, 61h ; or 0x61 ('a')
converted:
xchg al, ah ; put a null terminator after it
mov [si], ax ; (will be overwritten unless this
inc si ; is the last one)
shr bx, 4 ; get the next part
dec cx ; one less to do
jnz convert_loop
sub di, 4 ; di still points to the target buffer
PS: I know this is 16 bit code but I still use the old TASM :P
PPS: this is Intel syntax, converting to AT&T syntax isn't difficult though, look here.
You have to convert it in a string; if you're talking about hex numbers it's pretty easy. Any number can be represented this way:
0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3
So when you have this number you have to split it like I've shown then convert every "section" to its ASCII equivalent.
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we're interested in in the first four bits then AND the result with 0xf to isolate it from the rest. Here's what I mean (soppose we want to take the 3):
0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003
Now that we have a single number we have to convert it into its ASCII value. If the number is smaller or equal than 9 we can just add 0's ASCII value (0x30), if it's greater than 9 we have to use a's ASCII value (0x61).
Here it is, now we just have to code it:
mov si, ??? ; si points to the target buffer
mov ax, 0a31fh ; ax contains the number we want to convert
mov bx, ax ; store a copy in bx
xor dx, dx ; dx will contain the result
mov cx, 3 ; cx's our counter
convert_loop:
mov ax, bx ; load the number into ax
and ax, 0fh ; we want the first 4 bits
cmp ax, 9h ; check what we should add
ja greater_than_9
add ax, 30h ; 0x30 ('0')
jmp converted
greater_than_9:
add ax, 61h ; or 0x61 ('a')
converted:
xchg al, ah ; put a null terminator after it
mov [si], ax ; (will be overwritten unless this
inc si ; is the last one)
shr bx, 4 ; get the next part
dec cx ; one less to do
jnz convert_loop
sub di, 4 ; di still points to the target buffer
PS: I know this is 16 bit code but I still use the old TASM :P
PPS: this is Intel syntax, converting to AT&T syntax isn't difficult though, look here.
edited Feb 5 '13 at 21:12
answered Nov 19 '11 at 13:31
BlackBearBlackBear
15.3k83367
15.3k83367
@downvoter: reason?
– BlackBear
Nov 19 '11 at 14:12
You don't need AT&T syntax to run this on linux.
– Andrei Bârsan
Feb 5 '13 at 18:52
@AndreiBârsan: You're right, fixed that.. It's such an old answer :)
– BlackBear
Feb 5 '13 at 21:13
1
IMHO, this answer is better since you don't need the C runtime (which a call toprintf(...)
requires.
– Andrei Bârsan
Feb 6 '13 at 12:41
1
@AndreiBârsan yes, and it's kind of pointless using the C runtime in assembly
– BlackBear
Feb 6 '13 at 18:25
add a comment |
@downvoter: reason?
– BlackBear
Nov 19 '11 at 14:12
You don't need AT&T syntax to run this on linux.
– Andrei Bârsan
Feb 5 '13 at 18:52
@AndreiBârsan: You're right, fixed that.. It's such an old answer :)
– BlackBear
Feb 5 '13 at 21:13
1
IMHO, this answer is better since you don't need the C runtime (which a call toprintf(...)
requires.
– Andrei Bârsan
Feb 6 '13 at 12:41
1
@AndreiBârsan yes, and it's kind of pointless using the C runtime in assembly
– BlackBear
Feb 6 '13 at 18:25
@downvoter: reason?
– BlackBear
Nov 19 '11 at 14:12
@downvoter: reason?
– BlackBear
Nov 19 '11 at 14:12
You don't need AT&T syntax to run this on linux.
– Andrei Bârsan
Feb 5 '13 at 18:52
You don't need AT&T syntax to run this on linux.
– Andrei Bârsan
Feb 5 '13 at 18:52
@AndreiBârsan: You're right, fixed that.. It's such an old answer :)
– BlackBear
Feb 5 '13 at 21:13
@AndreiBârsan: You're right, fixed that.. It's such an old answer :)
– BlackBear
Feb 5 '13 at 21:13
1
1
IMHO, this answer is better since you don't need the C runtime (which a call to
printf(...)
requires.– Andrei Bârsan
Feb 6 '13 at 12:41
IMHO, this answer is better since you don't need the C runtime (which a call to
printf(...)
requires.– Andrei Bârsan
Feb 6 '13 at 12:41
1
1
@AndreiBârsan yes, and it's kind of pointless using the C runtime in assembly
– BlackBear
Feb 6 '13 at 18:25
@AndreiBârsan yes, and it's kind of pointless using the C runtime in assembly
– BlackBear
Feb 6 '13 at 18:25
add a comment |
Linux x86-64 with printf
extern printf, exit
section .data
format db "%x", 10, 0
section .text
global main
main:
sub rsp, 8
mov rsi, 0x12345678
mov rdi, format
xor rax, rax
call printf
mov rdi, 0
call exit
Then:
nasm -f elf64 main.asm
gcc main.o
./a.out
The "hard points" of the System V AMD64 ABI calling convention:
sub rsp, 8
: How to write assembly language hello world program for 64 bit Mac OS X using printf?
mov rax, 1
: Why is %eax zeroed before a call to printf?
If you want hex without the C library: https://stackoverflow.com/a/32756303/895245
add a comment |
Linux x86-64 with printf
extern printf, exit
section .data
format db "%x", 10, 0
section .text
global main
main:
sub rsp, 8
mov rsi, 0x12345678
mov rdi, format
xor rax, rax
call printf
mov rdi, 0
call exit
Then:
nasm -f elf64 main.asm
gcc main.o
./a.out
The "hard points" of the System V AMD64 ABI calling convention:
sub rsp, 8
: How to write assembly language hello world program for 64 bit Mac OS X using printf?
mov rax, 1
: Why is %eax zeroed before a call to printf?
If you want hex without the C library: https://stackoverflow.com/a/32756303/895245
add a comment |
Linux x86-64 with printf
extern printf, exit
section .data
format db "%x", 10, 0
section .text
global main
main:
sub rsp, 8
mov rsi, 0x12345678
mov rdi, format
xor rax, rax
call printf
mov rdi, 0
call exit
Then:
nasm -f elf64 main.asm
gcc main.o
./a.out
The "hard points" of the System V AMD64 ABI calling convention:
sub rsp, 8
: How to write assembly language hello world program for 64 bit Mac OS X using printf?
mov rax, 1
: Why is %eax zeroed before a call to printf?
If you want hex without the C library: https://stackoverflow.com/a/32756303/895245
Linux x86-64 with printf
extern printf, exit
section .data
format db "%x", 10, 0
section .text
global main
main:
sub rsp, 8
mov rsi, 0x12345678
mov rdi, format
xor rax, rax
call printf
mov rdi, 0
call exit
Then:
nasm -f elf64 main.asm
gcc main.o
./a.out
The "hard points" of the System V AMD64 ABI calling convention:
sub rsp, 8
: How to write assembly language hello world program for 64 bit Mac OS X using printf?
mov rax, 1
: Why is %eax zeroed before a call to printf?
If you want hex without the C library: https://stackoverflow.com/a/32756303/895245
edited May 23 '17 at 11:47
Community♦
11
11
answered Sep 29 '15 at 20:52
Ciro Santilli 新疆改造中心 六四事件 法轮功Ciro Santilli 新疆改造中心 六四事件 法轮功
141k33542458
141k33542458
add a comment |
add a comment |
It depends on the architecture/environment you are using.
For instance, if I want to display a number on linux, the ASM code will be different from the one I would use on windows.
Edit:
You can refer to THIS for an example of conversion.
A Linux example would be fine.
– AR89
Nov 19 '11 at 13:04
@AR89 it's a bad job.. You have to convert the number to ASCII first. Take a look at the edited question.
– AlQafir
Nov 19 '11 at 13:19
add a comment |
It depends on the architecture/environment you are using.
For instance, if I want to display a number on linux, the ASM code will be different from the one I would use on windows.
Edit:
You can refer to THIS for an example of conversion.
A Linux example would be fine.
– AR89
Nov 19 '11 at 13:04
@AR89 it's a bad job.. You have to convert the number to ASCII first. Take a look at the edited question.
– AlQafir
Nov 19 '11 at 13:19
add a comment |
It depends on the architecture/environment you are using.
For instance, if I want to display a number on linux, the ASM code will be different from the one I would use on windows.
Edit:
You can refer to THIS for an example of conversion.
It depends on the architecture/environment you are using.
For instance, if I want to display a number on linux, the ASM code will be different from the one I would use on windows.
Edit:
You can refer to THIS for an example of conversion.
edited Nov 19 '11 at 13:18
answered Nov 19 '11 at 12:49
AlQafirAlQafir
2,2531322
2,2531322
A Linux example would be fine.
– AR89
Nov 19 '11 at 13:04
@AR89 it's a bad job.. You have to convert the number to ASCII first. Take a look at the edited question.
– AlQafir
Nov 19 '11 at 13:19
add a comment |
A Linux example would be fine.
– AR89
Nov 19 '11 at 13:04
@AR89 it's a bad job.. You have to convert the number to ASCII first. Take a look at the edited question.
– AlQafir
Nov 19 '11 at 13:19
A Linux example would be fine.
– AR89
Nov 19 '11 at 13:04
A Linux example would be fine.
– AR89
Nov 19 '11 at 13:04
@AR89 it's a bad job.. You have to convert the number to ASCII first. Take a look at the edited question.
– AlQafir
Nov 19 '11 at 13:19
@AR89 it's a bad job.. You have to convert the number to ASCII first. Take a look at the edited question.
– AlQafir
Nov 19 '11 at 13:19
add a comment |
I'm relatively new to assembly, and this obviously is not the best solution,
but it's working. The main function is _iprint, it first checks whether the
number in eax is negative, and prints a minus sign if so, than it proceeds
by printing the individual numbers by calling the function _dprint for
every digit. The idea is the following, if we have 512 than it is equal to: 512 = (5 * 10 + 1) * 10 + 2 = Q * 10 + R, so we can found the last digit of a number by dividing it by 10, and
getting the reminder R, but if we do it in a loop than digits will be in a
reverse order, so we use the stack for pushing them, and after that when
writing them to stdout they are popped out in right order.
; Build : nasm -f elf -o baz.o baz.asm
; ld -m elf_i386 -o baz baz.o
section .bss
c: resb 1 ; character buffer
section .data
section .text
; writes an ascii character from eax to stdout
_cprint:
pushad ; push registers
mov [c], eax ; store ascii value at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; copy c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; writes a digit stored in eax to stdout
_dprint:
pushad ; push registers
add eax, '0' ; get digit's ascii code
mov [c], eax ; store it at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; pass the address of c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; now lets try to write a function which will write an integer
; number stored in eax in decimal at stdout
_iprint:
pushad ; push registers
cmp eax, 0 ; check if eax is negative
jge Pos ; if not proceed in the usual manner
push eax ; store eax
mov eax, '-' ; print minus sign
call _cprint ; call character printing function
pop eax ; restore eax
neg eax ; make eax positive
Pos:
mov ebx, 10 ; base
mov ecx, 1 ; number of digits counter
Cycle1:
mov edx, 0 ; set edx to zero before dividing otherwise the
; program gives an error: SIGFPE arithmetic exception
div ebx ; divide eax with ebx now eax holds the
; quotent and edx the reminder
push edx ; digits we have to write are in reverse order
cmp eax, 0 ; exit loop condition
jz EndLoop1 ; we are done
inc ecx ; increment number of digits counter
jmp Cycle1 ; loop back
EndLoop1:
; write the integer digits by poping them out from the stack
Cycle2:
pop eax ; pop up the digits we have stored
call _dprint ; and print them to stdout
dec ecx ; decrement number of digits counter
jz EndLoop2 ; if it's zero we are done
jmp Cycle2 ; loop back
EndLoop2:
popad ; pop registers
ret ; bye
global _start
_start:
nop ; gdb break point
mov eax, -345 ;
call _iprint ;
mov eax, 0x01 ; sys_exit
mov ebx, 0 ; error code
int 0x80 ; край
You canadd '0'
and store your digits in a buffer as you produce them. Usedec
do move the pointer downwards. When you're done, you have a pointer to the last digit you stored, so you can pass that tosys_write()
(along with the digit count). This is much more efficient than making a separate system call for every byte, and doesn't really take more code. It's easy to allocate a buffer long enough to hold the longest possible string of digits, and start at the end, because you know how many decimal digits 2^32 has.
– Peter Cordes
Jul 29 '17 at 7:59
related: I wrote an integer->string loop as part of this extended-precision Fibonacci code-golf answer. See the.toascii_digit:
loop. Of course, that's optimized for size, so it uses a slowdiv
instead of a multiply trick.
– Peter Cordes
Jul 29 '17 at 8:01
1
Thank you this is definitely preferable than calling sys_write for every digit:)
– baz
Aug 15 '17 at 15:44
I posted my int->string +sys_write
code as a stand-alone function on another question, with comments.
– Peter Cordes
Nov 18 '17 at 18:17
add a comment |
I'm relatively new to assembly, and this obviously is not the best solution,
but it's working. The main function is _iprint, it first checks whether the
number in eax is negative, and prints a minus sign if so, than it proceeds
by printing the individual numbers by calling the function _dprint for
every digit. The idea is the following, if we have 512 than it is equal to: 512 = (5 * 10 + 1) * 10 + 2 = Q * 10 + R, so we can found the last digit of a number by dividing it by 10, and
getting the reminder R, but if we do it in a loop than digits will be in a
reverse order, so we use the stack for pushing them, and after that when
writing them to stdout they are popped out in right order.
; Build : nasm -f elf -o baz.o baz.asm
; ld -m elf_i386 -o baz baz.o
section .bss
c: resb 1 ; character buffer
section .data
section .text
; writes an ascii character from eax to stdout
_cprint:
pushad ; push registers
mov [c], eax ; store ascii value at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; copy c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; writes a digit stored in eax to stdout
_dprint:
pushad ; push registers
add eax, '0' ; get digit's ascii code
mov [c], eax ; store it at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; pass the address of c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; now lets try to write a function which will write an integer
; number stored in eax in decimal at stdout
_iprint:
pushad ; push registers
cmp eax, 0 ; check if eax is negative
jge Pos ; if not proceed in the usual manner
push eax ; store eax
mov eax, '-' ; print minus sign
call _cprint ; call character printing function
pop eax ; restore eax
neg eax ; make eax positive
Pos:
mov ebx, 10 ; base
mov ecx, 1 ; number of digits counter
Cycle1:
mov edx, 0 ; set edx to zero before dividing otherwise the
; program gives an error: SIGFPE arithmetic exception
div ebx ; divide eax with ebx now eax holds the
; quotent and edx the reminder
push edx ; digits we have to write are in reverse order
cmp eax, 0 ; exit loop condition
jz EndLoop1 ; we are done
inc ecx ; increment number of digits counter
jmp Cycle1 ; loop back
EndLoop1:
; write the integer digits by poping them out from the stack
Cycle2:
pop eax ; pop up the digits we have stored
call _dprint ; and print them to stdout
dec ecx ; decrement number of digits counter
jz EndLoop2 ; if it's zero we are done
jmp Cycle2 ; loop back
EndLoop2:
popad ; pop registers
ret ; bye
global _start
_start:
nop ; gdb break point
mov eax, -345 ;
call _iprint ;
mov eax, 0x01 ; sys_exit
mov ebx, 0 ; error code
int 0x80 ; край
You canadd '0'
and store your digits in a buffer as you produce them. Usedec
do move the pointer downwards. When you're done, you have a pointer to the last digit you stored, so you can pass that tosys_write()
(along with the digit count). This is much more efficient than making a separate system call for every byte, and doesn't really take more code. It's easy to allocate a buffer long enough to hold the longest possible string of digits, and start at the end, because you know how many decimal digits 2^32 has.
– Peter Cordes
Jul 29 '17 at 7:59
related: I wrote an integer->string loop as part of this extended-precision Fibonacci code-golf answer. See the.toascii_digit:
loop. Of course, that's optimized for size, so it uses a slowdiv
instead of a multiply trick.
– Peter Cordes
Jul 29 '17 at 8:01
1
Thank you this is definitely preferable than calling sys_write for every digit:)
– baz
Aug 15 '17 at 15:44
I posted my int->string +sys_write
code as a stand-alone function on another question, with comments.
– Peter Cordes
Nov 18 '17 at 18:17
add a comment |
I'm relatively new to assembly, and this obviously is not the best solution,
but it's working. The main function is _iprint, it first checks whether the
number in eax is negative, and prints a minus sign if so, than it proceeds
by printing the individual numbers by calling the function _dprint for
every digit. The idea is the following, if we have 512 than it is equal to: 512 = (5 * 10 + 1) * 10 + 2 = Q * 10 + R, so we can found the last digit of a number by dividing it by 10, and
getting the reminder R, but if we do it in a loop than digits will be in a
reverse order, so we use the stack for pushing them, and after that when
writing them to stdout they are popped out in right order.
; Build : nasm -f elf -o baz.o baz.asm
; ld -m elf_i386 -o baz baz.o
section .bss
c: resb 1 ; character buffer
section .data
section .text
; writes an ascii character from eax to stdout
_cprint:
pushad ; push registers
mov [c], eax ; store ascii value at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; copy c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; writes a digit stored in eax to stdout
_dprint:
pushad ; push registers
add eax, '0' ; get digit's ascii code
mov [c], eax ; store it at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; pass the address of c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; now lets try to write a function which will write an integer
; number stored in eax in decimal at stdout
_iprint:
pushad ; push registers
cmp eax, 0 ; check if eax is negative
jge Pos ; if not proceed in the usual manner
push eax ; store eax
mov eax, '-' ; print minus sign
call _cprint ; call character printing function
pop eax ; restore eax
neg eax ; make eax positive
Pos:
mov ebx, 10 ; base
mov ecx, 1 ; number of digits counter
Cycle1:
mov edx, 0 ; set edx to zero before dividing otherwise the
; program gives an error: SIGFPE arithmetic exception
div ebx ; divide eax with ebx now eax holds the
; quotent and edx the reminder
push edx ; digits we have to write are in reverse order
cmp eax, 0 ; exit loop condition
jz EndLoop1 ; we are done
inc ecx ; increment number of digits counter
jmp Cycle1 ; loop back
EndLoop1:
; write the integer digits by poping them out from the stack
Cycle2:
pop eax ; pop up the digits we have stored
call _dprint ; and print them to stdout
dec ecx ; decrement number of digits counter
jz EndLoop2 ; if it's zero we are done
jmp Cycle2 ; loop back
EndLoop2:
popad ; pop registers
ret ; bye
global _start
_start:
nop ; gdb break point
mov eax, -345 ;
call _iprint ;
mov eax, 0x01 ; sys_exit
mov ebx, 0 ; error code
int 0x80 ; край
I'm relatively new to assembly, and this obviously is not the best solution,
but it's working. The main function is _iprint, it first checks whether the
number in eax is negative, and prints a minus sign if so, than it proceeds
by printing the individual numbers by calling the function _dprint for
every digit. The idea is the following, if we have 512 than it is equal to: 512 = (5 * 10 + 1) * 10 + 2 = Q * 10 + R, so we can found the last digit of a number by dividing it by 10, and
getting the reminder R, but if we do it in a loop than digits will be in a
reverse order, so we use the stack for pushing them, and after that when
writing them to stdout they are popped out in right order.
; Build : nasm -f elf -o baz.o baz.asm
; ld -m elf_i386 -o baz baz.o
section .bss
c: resb 1 ; character buffer
section .data
section .text
; writes an ascii character from eax to stdout
_cprint:
pushad ; push registers
mov [c], eax ; store ascii value at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; copy c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; writes a digit stored in eax to stdout
_dprint:
pushad ; push registers
add eax, '0' ; get digit's ascii code
mov [c], eax ; store it at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; pass the address of c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; now lets try to write a function which will write an integer
; number stored in eax in decimal at stdout
_iprint:
pushad ; push registers
cmp eax, 0 ; check if eax is negative
jge Pos ; if not proceed in the usual manner
push eax ; store eax
mov eax, '-' ; print minus sign
call _cprint ; call character printing function
pop eax ; restore eax
neg eax ; make eax positive
Pos:
mov ebx, 10 ; base
mov ecx, 1 ; number of digits counter
Cycle1:
mov edx, 0 ; set edx to zero before dividing otherwise the
; program gives an error: SIGFPE arithmetic exception
div ebx ; divide eax with ebx now eax holds the
; quotent and edx the reminder
push edx ; digits we have to write are in reverse order
cmp eax, 0 ; exit loop condition
jz EndLoop1 ; we are done
inc ecx ; increment number of digits counter
jmp Cycle1 ; loop back
EndLoop1:
; write the integer digits by poping them out from the stack
Cycle2:
pop eax ; pop up the digits we have stored
call _dprint ; and print them to stdout
dec ecx ; decrement number of digits counter
jz EndLoop2 ; if it's zero we are done
jmp Cycle2 ; loop back
EndLoop2:
popad ; pop registers
ret ; bye
global _start
_start:
nop ; gdb break point
mov eax, -345 ;
call _iprint ;
mov eax, 0x01 ; sys_exit
mov ebx, 0 ; error code
int 0x80 ; край
answered May 23 '17 at 6:57
bazbaz
33735
33735
You canadd '0'
and store your digits in a buffer as you produce them. Usedec
do move the pointer downwards. When you're done, you have a pointer to the last digit you stored, so you can pass that tosys_write()
(along with the digit count). This is much more efficient than making a separate system call for every byte, and doesn't really take more code. It's easy to allocate a buffer long enough to hold the longest possible string of digits, and start at the end, because you know how many decimal digits 2^32 has.
– Peter Cordes
Jul 29 '17 at 7:59
related: I wrote an integer->string loop as part of this extended-precision Fibonacci code-golf answer. See the.toascii_digit:
loop. Of course, that's optimized for size, so it uses a slowdiv
instead of a multiply trick.
– Peter Cordes
Jul 29 '17 at 8:01
1
Thank you this is definitely preferable than calling sys_write for every digit:)
– baz
Aug 15 '17 at 15:44
I posted my int->string +sys_write
code as a stand-alone function on another question, with comments.
– Peter Cordes
Nov 18 '17 at 18:17
add a comment |
You canadd '0'
and store your digits in a buffer as you produce them. Usedec
do move the pointer downwards. When you're done, you have a pointer to the last digit you stored, so you can pass that tosys_write()
(along with the digit count). This is much more efficient than making a separate system call for every byte, and doesn't really take more code. It's easy to allocate a buffer long enough to hold the longest possible string of digits, and start at the end, because you know how many decimal digits 2^32 has.
– Peter Cordes
Jul 29 '17 at 7:59
related: I wrote an integer->string loop as part of this extended-precision Fibonacci code-golf answer. See the.toascii_digit:
loop. Of course, that's optimized for size, so it uses a slowdiv
instead of a multiply trick.
– Peter Cordes
Jul 29 '17 at 8:01
1
Thank you this is definitely preferable than calling sys_write for every digit:)
– baz
Aug 15 '17 at 15:44
I posted my int->string +sys_write
code as a stand-alone function on another question, with comments.
– Peter Cordes
Nov 18 '17 at 18:17
You can
add '0'
and store your digits in a buffer as you produce them. Use dec
do move the pointer downwards. When you're done, you have a pointer to the last digit you stored, so you can pass that to sys_write()
(along with the digit count). This is much more efficient than making a separate system call for every byte, and doesn't really take more code. It's easy to allocate a buffer long enough to hold the longest possible string of digits, and start at the end, because you know how many decimal digits 2^32 has.– Peter Cordes
Jul 29 '17 at 7:59
You can
add '0'
and store your digits in a buffer as you produce them. Use dec
do move the pointer downwards. When you're done, you have a pointer to the last digit you stored, so you can pass that to sys_write()
(along with the digit count). This is much more efficient than making a separate system call for every byte, and doesn't really take more code. It's easy to allocate a buffer long enough to hold the longest possible string of digits, and start at the end, because you know how many decimal digits 2^32 has.– Peter Cordes
Jul 29 '17 at 7:59
related: I wrote an integer->string loop as part of this extended-precision Fibonacci code-golf answer. See the
.toascii_digit:
loop. Of course, that's optimized for size, so it uses a slow div
instead of a multiply trick.– Peter Cordes
Jul 29 '17 at 8:01
related: I wrote an integer->string loop as part of this extended-precision Fibonacci code-golf answer. See the
.toascii_digit:
loop. Of course, that's optimized for size, so it uses a slow div
instead of a multiply trick.– Peter Cordes
Jul 29 '17 at 8:01
1
1
Thank you this is definitely preferable than calling sys_write for every digit:)
– baz
Aug 15 '17 at 15:44
Thank you this is definitely preferable than calling sys_write for every digit:)
– baz
Aug 15 '17 at 15:44
I posted my int->string +
sys_write
code as a stand-alone function on another question, with comments.– Peter Cordes
Nov 18 '17 at 18:17
I posted my int->string +
sys_write
code as a stand-alone function on another question, with comments.– Peter Cordes
Nov 18 '17 at 18:17
add a comment |
Because you didn't say about number representation I wrote the following code for unsigned number with any base(of course not too big), so you could use it:
BITS 32
global _start
section .text
_start:
mov eax, 762002099 ; unsigned number to print
mov ebx, 36 ; base to represent the number, do not set it too big
call print
;exit
mov eax, 1
xor ebx, ebx
int 0x80
print:
mov ecx, esp
sub esp, 36 ; reserve space for the number string, for base-2 it takes 33 bytes with new line, aligned by 4 bytes it takes 36 bytes.
mov edi, 1
dec ecx
mov [ecx], byte 10
print_loop:
xor edx, edx
div ebx
cmp dl, 9 ; if reminder>9 go to use_letter
jg use_letter
add dl, '0'
jmp after_use_letter
use_letter:
add dl, 'W' ; letters from 'a' to ... in ascii code
after_use_letter:
dec ecx
inc edi
mov [ecx],dl
test eax, eax
jnz print_loop
; system call to print, ecx is a pointer on the string
mov eax, 4 ; system call number (sys_write)
mov ebx, 1 ; file descriptor (stdout)
mov edx, edi ; length of the string
int 0x80
add esp, 36 ; release space for the number string
ret
It's not optimised for numbers with base of power of two and doesn't use printf
from libc
.
The function print
outputs the number with a new line. The number string is formed on stack. Compile by nasm.
Output:
clockz
https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm
You're using space below ESP. That's only safe in cases like this where you know there are no signal handlers installed, and shouldn't be used in functions that could be called in other contexts. 32-bit Linux doesn't guarantee a red-zone. Also, usexor edx,edx
/div
orcdq
/idiv
so the zero or sign-extension of the dividend matches the signedness of the division. In this case you wantxor
/div
so you always have a positive remainder. If you want to treat your input as signed, you'll want to test/js and print the unsigned absolute value (with a leading-
if needed).
– Peter Cordes
Dec 30 '18 at 7:18
@PeterCordes, Hi, Peter! You're right about safety. It's a partial solution and I didn't think about signed numbers.
– TigerTV.ru
Dec 30 '18 at 17:09
You should still changeidiv
todiv
so it works for the full range of unsigned numbers. Hmm, actually this might be safe anyway, because 2^32-1 / 10 doesn't overflow EAX. zero-extending into edx:eax gives you a signed non-negative dividend from 0..2^32-1.
– Peter Cordes
Dec 30 '18 at 20:57
@PeterCordes, Theidiv
has been replaced. Also I added base for the number. What do you think about it? And else I reserved a buffer on stack for number string with size 32.
– TigerTV.ru
Dec 31 '18 at 1:13
add esp, 32
should besub
to reserve space. You're stepping on the caller's stack space.mov byte [ecx], 10
would be more efficient than setting a register first. Or evenpush 10
/mov ecx, esp
/sub esp, 32
. (For your current version, a large number with base=2 will use 32 digits, but you use up one of your 32 with a newline.)
– Peter Cordes
Dec 31 '18 at 1:25
|
show 1 more comment
Because you didn't say about number representation I wrote the following code for unsigned number with any base(of course not too big), so you could use it:
BITS 32
global _start
section .text
_start:
mov eax, 762002099 ; unsigned number to print
mov ebx, 36 ; base to represent the number, do not set it too big
call print
;exit
mov eax, 1
xor ebx, ebx
int 0x80
print:
mov ecx, esp
sub esp, 36 ; reserve space for the number string, for base-2 it takes 33 bytes with new line, aligned by 4 bytes it takes 36 bytes.
mov edi, 1
dec ecx
mov [ecx], byte 10
print_loop:
xor edx, edx
div ebx
cmp dl, 9 ; if reminder>9 go to use_letter
jg use_letter
add dl, '0'
jmp after_use_letter
use_letter:
add dl, 'W' ; letters from 'a' to ... in ascii code
after_use_letter:
dec ecx
inc edi
mov [ecx],dl
test eax, eax
jnz print_loop
; system call to print, ecx is a pointer on the string
mov eax, 4 ; system call number (sys_write)
mov ebx, 1 ; file descriptor (stdout)
mov edx, edi ; length of the string
int 0x80
add esp, 36 ; release space for the number string
ret
It's not optimised for numbers with base of power of two and doesn't use printf
from libc
.
The function print
outputs the number with a new line. The number string is formed on stack. Compile by nasm.
Output:
clockz
https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm
You're using space below ESP. That's only safe in cases like this where you know there are no signal handlers installed, and shouldn't be used in functions that could be called in other contexts. 32-bit Linux doesn't guarantee a red-zone. Also, usexor edx,edx
/div
orcdq
/idiv
so the zero or sign-extension of the dividend matches the signedness of the division. In this case you wantxor
/div
so you always have a positive remainder. If you want to treat your input as signed, you'll want to test/js and print the unsigned absolute value (with a leading-
if needed).
– Peter Cordes
Dec 30 '18 at 7:18
@PeterCordes, Hi, Peter! You're right about safety. It's a partial solution and I didn't think about signed numbers.
– TigerTV.ru
Dec 30 '18 at 17:09
You should still changeidiv
todiv
so it works for the full range of unsigned numbers. Hmm, actually this might be safe anyway, because 2^32-1 / 10 doesn't overflow EAX. zero-extending into edx:eax gives you a signed non-negative dividend from 0..2^32-1.
– Peter Cordes
Dec 30 '18 at 20:57
@PeterCordes, Theidiv
has been replaced. Also I added base for the number. What do you think about it? And else I reserved a buffer on stack for number string with size 32.
– TigerTV.ru
Dec 31 '18 at 1:13
add esp, 32
should besub
to reserve space. You're stepping on the caller's stack space.mov byte [ecx], 10
would be more efficient than setting a register first. Or evenpush 10
/mov ecx, esp
/sub esp, 32
. (For your current version, a large number with base=2 will use 32 digits, but you use up one of your 32 with a newline.)
– Peter Cordes
Dec 31 '18 at 1:25
|
show 1 more comment
Because you didn't say about number representation I wrote the following code for unsigned number with any base(of course not too big), so you could use it:
BITS 32
global _start
section .text
_start:
mov eax, 762002099 ; unsigned number to print
mov ebx, 36 ; base to represent the number, do not set it too big
call print
;exit
mov eax, 1
xor ebx, ebx
int 0x80
print:
mov ecx, esp
sub esp, 36 ; reserve space for the number string, for base-2 it takes 33 bytes with new line, aligned by 4 bytes it takes 36 bytes.
mov edi, 1
dec ecx
mov [ecx], byte 10
print_loop:
xor edx, edx
div ebx
cmp dl, 9 ; if reminder>9 go to use_letter
jg use_letter
add dl, '0'
jmp after_use_letter
use_letter:
add dl, 'W' ; letters from 'a' to ... in ascii code
after_use_letter:
dec ecx
inc edi
mov [ecx],dl
test eax, eax
jnz print_loop
; system call to print, ecx is a pointer on the string
mov eax, 4 ; system call number (sys_write)
mov ebx, 1 ; file descriptor (stdout)
mov edx, edi ; length of the string
int 0x80
add esp, 36 ; release space for the number string
ret
It's not optimised for numbers with base of power of two and doesn't use printf
from libc
.
The function print
outputs the number with a new line. The number string is formed on stack. Compile by nasm.
Output:
clockz
https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm
Because you didn't say about number representation I wrote the following code for unsigned number with any base(of course not too big), so you could use it:
BITS 32
global _start
section .text
_start:
mov eax, 762002099 ; unsigned number to print
mov ebx, 36 ; base to represent the number, do not set it too big
call print
;exit
mov eax, 1
xor ebx, ebx
int 0x80
print:
mov ecx, esp
sub esp, 36 ; reserve space for the number string, for base-2 it takes 33 bytes with new line, aligned by 4 bytes it takes 36 bytes.
mov edi, 1
dec ecx
mov [ecx], byte 10
print_loop:
xor edx, edx
div ebx
cmp dl, 9 ; if reminder>9 go to use_letter
jg use_letter
add dl, '0'
jmp after_use_letter
use_letter:
add dl, 'W' ; letters from 'a' to ... in ascii code
after_use_letter:
dec ecx
inc edi
mov [ecx],dl
test eax, eax
jnz print_loop
; system call to print, ecx is a pointer on the string
mov eax, 4 ; system call number (sys_write)
mov ebx, 1 ; file descriptor (stdout)
mov edx, edi ; length of the string
int 0x80
add esp, 36 ; release space for the number string
ret
It's not optimised for numbers with base of power of two and doesn't use printf
from libc
.
The function print
outputs the number with a new line. The number string is formed on stack. Compile by nasm.
Output:
clockz
https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm
edited Dec 31 '18 at 13:52
answered Dec 29 '18 at 2:09
TigerTV.ruTigerTV.ru
7801621
7801621
You're using space below ESP. That's only safe in cases like this where you know there are no signal handlers installed, and shouldn't be used in functions that could be called in other contexts. 32-bit Linux doesn't guarantee a red-zone. Also, usexor edx,edx
/div
orcdq
/idiv
so the zero or sign-extension of the dividend matches the signedness of the division. In this case you wantxor
/div
so you always have a positive remainder. If you want to treat your input as signed, you'll want to test/js and print the unsigned absolute value (with a leading-
if needed).
– Peter Cordes
Dec 30 '18 at 7:18
@PeterCordes, Hi, Peter! You're right about safety. It's a partial solution and I didn't think about signed numbers.
– TigerTV.ru
Dec 30 '18 at 17:09
You should still changeidiv
todiv
so it works for the full range of unsigned numbers. Hmm, actually this might be safe anyway, because 2^32-1 / 10 doesn't overflow EAX. zero-extending into edx:eax gives you a signed non-negative dividend from 0..2^32-1.
– Peter Cordes
Dec 30 '18 at 20:57
@PeterCordes, Theidiv
has been replaced. Also I added base for the number. What do you think about it? And else I reserved a buffer on stack for number string with size 32.
– TigerTV.ru
Dec 31 '18 at 1:13
add esp, 32
should besub
to reserve space. You're stepping on the caller's stack space.mov byte [ecx], 10
would be more efficient than setting a register first. Or evenpush 10
/mov ecx, esp
/sub esp, 32
. (For your current version, a large number with base=2 will use 32 digits, but you use up one of your 32 with a newline.)
– Peter Cordes
Dec 31 '18 at 1:25
|
show 1 more comment
You're using space below ESP. That's only safe in cases like this where you know there are no signal handlers installed, and shouldn't be used in functions that could be called in other contexts. 32-bit Linux doesn't guarantee a red-zone. Also, usexor edx,edx
/div
orcdq
/idiv
so the zero or sign-extension of the dividend matches the signedness of the division. In this case you wantxor
/div
so you always have a positive remainder. If you want to treat your input as signed, you'll want to test/js and print the unsigned absolute value (with a leading-
if needed).
– Peter Cordes
Dec 30 '18 at 7:18
@PeterCordes, Hi, Peter! You're right about safety. It's a partial solution and I didn't think about signed numbers.
– TigerTV.ru
Dec 30 '18 at 17:09
You should still changeidiv
todiv
so it works for the full range of unsigned numbers. Hmm, actually this might be safe anyway, because 2^32-1 / 10 doesn't overflow EAX. zero-extending into edx:eax gives you a signed non-negative dividend from 0..2^32-1.
– Peter Cordes
Dec 30 '18 at 20:57
@PeterCordes, Theidiv
has been replaced. Also I added base for the number. What do you think about it? And else I reserved a buffer on stack for number string with size 32.
– TigerTV.ru
Dec 31 '18 at 1:13
add esp, 32
should besub
to reserve space. You're stepping on the caller's stack space.mov byte [ecx], 10
would be more efficient than setting a register first. Or evenpush 10
/mov ecx, esp
/sub esp, 32
. (For your current version, a large number with base=2 will use 32 digits, but you use up one of your 32 with a newline.)
– Peter Cordes
Dec 31 '18 at 1:25
You're using space below ESP. That's only safe in cases like this where you know there are no signal handlers installed, and shouldn't be used in functions that could be called in other contexts. 32-bit Linux doesn't guarantee a red-zone. Also, use
xor edx,edx
/ div
or cdq
/ idiv
so the zero or sign-extension of the dividend matches the signedness of the division. In this case you want xor
/div
so you always have a positive remainder. If you want to treat your input as signed, you'll want to test/js and print the unsigned absolute value (with a leading -
if needed).– Peter Cordes
Dec 30 '18 at 7:18
You're using space below ESP. That's only safe in cases like this where you know there are no signal handlers installed, and shouldn't be used in functions that could be called in other contexts. 32-bit Linux doesn't guarantee a red-zone. Also, use
xor edx,edx
/ div
or cdq
/ idiv
so the zero or sign-extension of the dividend matches the signedness of the division. In this case you want xor
/div
so you always have a positive remainder. If you want to treat your input as signed, you'll want to test/js and print the unsigned absolute value (with a leading -
if needed).– Peter Cordes
Dec 30 '18 at 7:18
@PeterCordes, Hi, Peter! You're right about safety. It's a partial solution and I didn't think about signed numbers.
– TigerTV.ru
Dec 30 '18 at 17:09
@PeterCordes, Hi, Peter! You're right about safety. It's a partial solution and I didn't think about signed numbers.
– TigerTV.ru
Dec 30 '18 at 17:09
You should still change
idiv
to div
so it works for the full range of unsigned numbers. Hmm, actually this might be safe anyway, because 2^32-1 / 10 doesn't overflow EAX. zero-extending into edx:eax gives you a signed non-negative dividend from 0..2^32-1.– Peter Cordes
Dec 30 '18 at 20:57
You should still change
idiv
to div
so it works for the full range of unsigned numbers. Hmm, actually this might be safe anyway, because 2^32-1 / 10 doesn't overflow EAX. zero-extending into edx:eax gives you a signed non-negative dividend from 0..2^32-1.– Peter Cordes
Dec 30 '18 at 20:57
@PeterCordes, The
idiv
has been replaced. Also I added base for the number. What do you think about it? And else I reserved a buffer on stack for number string with size 32.– TigerTV.ru
Dec 31 '18 at 1:13
@PeterCordes, The
idiv
has been replaced. Also I added base for the number. What do you think about it? And else I reserved a buffer on stack for number string with size 32.– TigerTV.ru
Dec 31 '18 at 1:13
add esp, 32
should be sub
to reserve space. You're stepping on the caller's stack space. mov byte [ecx], 10
would be more efficient than setting a register first. Or even push 10
/ mov ecx, esp
/ sub esp, 32
. (For your current version, a large number with base=2 will use 32 digits, but you use up one of your 32 with a newline.)– Peter Cordes
Dec 31 '18 at 1:25
add esp, 32
should be sub
to reserve space. You're stepping on the caller's stack space. mov byte [ecx], 10
would be more efficient than setting a register first. Or even push 10
/ mov ecx, esp
/ sub esp, 32
. (For your current version, a large number with base=2 will use 32 digits, but you use up one of your 32 with a newline.)– Peter Cordes
Dec 31 '18 at 1:25
|
show 1 more comment
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f8194141%2fhow-to-print-a-number-in-assembly-nasm%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Please specify the OS where the program will run.
– Alexey Frunze
Nov 19 '11 at 12:56
1
Related: convert an integer to an ASCII decimal string in a buffer on the stack and print it with Linux
write
system call, not usingprintf
or any other functions. With comments and explanation.– Peter Cordes
Nov 18 '17 at 18:14