disassembly error when using pop after a call - nasm

In the code below I have lines 27-37 that work well and the same code called by a label (line 58) crash when executing the pop instructions.
There is another label (line 48) which produces the same result without using pop and this works.
I'm in the context of building a x86_64 operating system.
Compile command : nasm -f elf
Linking command : ld -T link.ld -melf_i386
Iso generated using Grub 2.02 : grub-mkrescue
Run with VirtualBox version 5.2.32_Ubuntur132056
Following pop Instruction not supported in 64-bit mode using NASM? I tried with pop and/or push instructions in uppercase and got same result.
loader: ; the loader label (defined as entry point in linker script)
mov esp, kernel_stack + STACK_SIZE ; point esp to the start of the stack (end of memory area)
push dword 0x71
push dword 'C'
push dword 0x50 ; first pushes to test directly the function
pop edx
pop eax
pop ebx
mov ah, bl
mov ebx, FRAMEBUFFER
add ebx, edx
mov byte [ebx], al
mov byte [ebx + 1], ah ; should print a C with a blue foreground and a light grey background on the 40'th case of the frame buffer
push dword 0x71
push dword 'C'
push dword 0x52
call fb_write_chara
.loop:
nop
jmp .loop ; loop forever
fb_write_chara: ; void fb_write_char(unsigned int offset, char character, unsigned char color)
mov ah, [esp + 12] ; color
mov al, [esp + 8] ; character
mov edx, [esp + 4] ; offset
mov ebx, FRAMEBUFFER
add ebx, edx ; ebx is now at the right position
mov byte [ebx], al ; write char
mov byte [ebx + 1], ah ; write color
ret
fb_write_char: ; void fb_write_char(unsigned int offset, char character, unsigned char color)
pop edx
pop eax
pop ebx
mov ah, bl
mov ebx, FRAMEBUFFER
add ebx, edx ; ebx is now at the right position
mov byte [ebx], al ; write char
mov byte [ebx + 1], ah ; write color
ret
Result when executing with fb_write_chara:
eax=00007143 ebx=000b8052 ecx=00000000 edx=00000052 esi=00000000 edi=00000000
eip=00100033 esp=00101ff4 ebp=00000000 iopl=0 nv up di pl nz na pe nc
cs=0010 ds=0018 es=0018 fs=0018 gs=0018 ss=0018 eflags=00200002
0010:00100033 90 nop
State of the registers when trying with fb_write_char:
eax=00004352 ebx=001b8033 ecx=00000000 edx=00100033 esi=00000000 edi=00000000
eip=00000077 esp=00101ffc ebp=00000000 iopl=0 rf nv up di pl nz na pe nc
cs=0010 ds=0018 es=0018 fs=0018 gs=0018 ss=0018 eflags=00210002
0010:00000077 f0 c7 ef 00 f0 ed 57 Illegal opcode

CALL pushes the return address (.loop in your case) before jumping to fb_write_char. So your first POP pops the return address. When RET runs, it uses the only thing left on the stack, which is 0x71 in your case, as the return address. Of course the problem will crashes.
When you use stack to pass parameters, use [ESP + 4] to access the last pushed parameter, [ESP + 8] to access the second-last pushed parameter, and so on. To clean up the stack, use RET 12 (callee cleanup) or use RET and ADD ESP, 12 after CALL (caller cleanup).
Read more about call stack before you continue to code in assembly: https://en.wikipedia.org/wiki/Call_stack

Related

Swapping first and last characters of string results in seg fault

My goal is to swap the first character with the last character of the string some_str in x86-assembly.
Here is my attempt:
; assemble and link with:
; nasm -f elf32 -g test.asm && ld -melf_i386 test.asm.o -o test
section .text
global _start
extern printf
_start:
mov eax, some_str
_loop:
mov di, [eax + 4] ; ptr to end char
mov si, [eax] ; ptr to start char
mov dl, [di] ; DL = end char
mov al, [si] ; AL = start char
mov [si], dl ; start char = end char
mov [di], al ; end char = char 1
mov edx, len
mov ecx, eax
mov ebx, 1
mov eax, 4
int 0x80
ret
mov eax, 1
int 0x80
section .data
some_str db `abcd`, 0xa
len equ $ - some_str
For some reason I am oblivious to the lines:
mov dl, [di] ; DL = end char
mov al, [si] ; AL = start char
Causes the program to result in a segmentation fault.
The expected stdout is:
dbca
Actual stdout:
Segmentation fault (core dumped)`
Is there something I am missing? How do I correct this code to correctly swap the first and last character of some_str.
Your code seems to be doing something much more complicated than necessary. After mov eax, some_str, we have that eax points to one of the bytes that wants to be swapped, and eax+4 points to the other. So just load them into two 8-bit registers and then store them back the other way around.
mov eax, some_str
mov cl, [eax]
mov dl, [eax + 4]
mov [eax + 4], cl
mov [eax], dl
And you're done and can proceed to write out the result.
Note it isn't necessary to load the pointer into eax first; you could also do
mov cl, [some_str]
mov dl, [some_str + 4]
mov [some_str + 4], cl
mov [some_str], dl
If you really wanted to have two different registers to point to the two different bytes: first of all, they need to be 32-bit registers. Trying to address memory in 32-bit mode using 16-bit registers si, di is practically never going to work. Second, mov edi, [eax] would load edi with the contents of the memory at location eax, which is some bytes of your string, not a pointer. You'd want simply mov edi, eax. For the second one, you can use lea to do the arithmetic of an effective address calculation but keep the resulting pointer instead of doing a load. So I think the way to turn your code into something in the original (inefficient) spirit, but correct, would be
mov edi, eax
lea esi, [eax+4]
mov dl, [edi]
mov al, [esi]
mov [esi], dl
mov [edi], al

Print register value to console

I want to print the value in %RCX directly to the console, let's say an ASCII value. I've searched through some wise books and tutorials, but all use buffers to pass anything. Is it possible to print anything without creating special buffer for that purpose?
lets say i am here (all this answers are fat too complicated to me and use different syntax):
movq $5, %rax
...???(print %rax)
Output on console:
\>5
in example, to print buffer i use code:
SYSWRITE = 4
STDOUT = 1
EXIT_SUCCESS = 0
.text
buff: .ascii "Anything to print\n"
buff_len = . - buff
movq $SYSWRITE, %eax
mov $STDOUT, %ebx
mov $buff, %ecx
mov $buff_len, %edx
NO C CODE OR DIFFERENT ASS SYNTAX ALLOWED!!!
In order to print a register (in hex representation or numeric) the routine (write to stdout, stderr, etc.) expects ASCII characters. Just writing a register will cause the routine to try an display the ascii equivalent of the value in the register. You may get lucky sometimes if each of the bytes in the register happen to fall into the printable character range.
You will need to convert it vis-a-vis routines that convert to decimal or hex. Here is an example of converting a 64 bit register to the hex representation (using intel syntax w/nasm):
section .rodata
hex_xlat: db "0123456789abcdef"
section .text
; Called with RDI is the register to convert and
; RSI for the buffer to fill
;
register_to_hex:
push rsi ; Save for return
xor eax,eax
mov ecx, 16 ; looper
lea rdx, [rel hex_xlat] ; position-independent code can't index a static array directly
ALIGN 16
.loop:
rol rdi, 4 ; dil now has high bit nibble
mov al, dil ; capture low nibble
and al, 0x0f
mov al, byte [rdx+rax] ; look up the ASCII encoding for the hex digit
; rax is an 'index' with range 0x0 - 0xf.
; The upper bytes of rax are still zero from xor
mov byte [rsi], al ; store in print buffer
inc rsi ; position next pointer
dec ecx
jnz .loop
.exit:
pop rax ; Get original buffer pointer
ret
This answer is an addendum to the answer given by Frank, and utilizes the mechanism used there to do the conversion.
You mention the register %RCX in your question. This suggests you are looking at 64-bit code and that your environment is likely GCC/GAS (GNU Assembler) based since % is usually the AT&T style prefix for registers.
With that in mind I've created a quick and dirty macro that can be used inline anywhere you need to print a 64-bit register, 64-bit memory operand, or a 32-bit immediate value in GNU Assembly. This version was a proof of concept and could be amended to support 64 bit immediate values. All the registers that are used are preserved, and the code will also account for the Linux 64-bit System V ABI red zone.
The code below is commented to point out what is occurring at each step.
printmac.inc:
.macro memreg_to_hex src # Macro takes one input
# src = memory operand, register,
# or 32 bit constant to print
# Define the translation table only once for the current object
.ifndef MEMREG_TO_HEX_NOT_FIRST
.set MEMREG_TO_HEX_NOT_FIRST, 1
.PushSection .rodata
hex_xlat: .ascii "0123456789abcdef"
.PopSection
.endif
add $-128,%rsp # Avoid 128 byte red zone
push %rsi # Save all registers that will be used
push %rdi
push %rdx
push %rcx
push %rbx
push %rax
push %r11 # R11 is destroyed by SYSCALL
mov \src, %rdi # Move src value to RDI for processing
# Output buffer on stack at ESP-16 to ESP-1
lea -16(%rsp),%rsi # RSI = output buffer on stack
lea hex_xlat(%rip), %rdx # RDX = translation buffer address
xor %eax,%eax # RAX = Index into translation array
mov $16,%ecx # 16 nibbles to print
.align 16
1:
rol $4,%rdi # rotate high nibble to low nibble
mov %dil,%al # dil now has previous high nibble
and $0xf,%al # mask off all but low nibble
mov (%rdx,%rax,1),%al # Lookup in translation table
mov %al,(%rsi) # Store in output buffer
inc %rsi # Update output buffer address
dec %ecx
jne 1b # Loop until counter is 0
mov $1,%eax # Syscall 1 = sys_write
mov %eax,%edi # EDI = 1 = STDIN
mov $16,%edx # EDX = Number of chars to print
sub %rdx,%rsi # RSI = beginning of output buffer
syscall
pop %r11 # Restore all registers used
pop %rax
pop %rbx
pop %rcx
pop %rdx
pop %rdi
pop %rsi
sub $-128,%rsp # Restore stack
.endm
printtest.s
.include "printmac.inc"
.global main
.text
main:
mov $0x123456789abcdef,%rcx
memreg_to_hex %rcx # Print the 64-bit value 0x123456789abcdef
memreg_to_hex %rsp # Print address containing ret pointer
memreg_to_hex (%rsp) # Print return pointer
memreg_to_hex $0x402 # Doesn't support 64-bit immediates
# but can print anything that fits a DWORD
retq
This can be compiled and linked with:
gcc -m64 printtest.s -o printtest
The macro doesn't print an end of line character so the output of the test program looks like:
0123456789abcdef00007fff5283d74000007f5c4a080a500000000000000402
The memory addresses will be be different.
Since the macros are inlined, each time you invoke the macro the entire code will be emitted. The code is space inefficient. The bulk of the code could be moved to an object file you can include at link time. Then a stub macro could wrap a CALL to the main printing function.
The code doesn't use printf because at some point I thought I saw a comment that you couldn't use the C library. If that's not the case this can be simplified greatly by calling printf to format the output to print a 64-bit hexadecimal value.
Just for fun, here are a couple other sequences for storing a hex string from a register. Printing the buffer is not the interesting part, IMO; copy that part from Michael's excellent answer if needed.
I tested some of these. I've included a main that calls one of these functions and then uses printf("%s\n%lx\n", result, test_value); to make it easy to spot problems.
Test main():
extern printf
global main
main:
push rbx
mov rdi, 0x1230ff56dcba9911
mov rbx, rdi
sub rsp, 32
mov rsi, rsp
mov byte [rsi+16], 0
call register_to_hex_ssse3
mov rdx, rbx
mov edi, fmt
mov rsi, rsp
xor eax,eax
call printf
add rsp, 32
pop rbx
ret
section .rodata
fmt: db `%s\n%lx\n`, 0 ; YASM doesn't support `string with escapes`, so this only assembles with NASM.
; NASM needs
; %use smartalign
; ALIGNMODE p6, 32
; or similar, to stop it using braindead repeated single-byte NOPs for ALIGN
SSSE3 pshufb for the LUT
This version doesn't need a loop, but the code size is much larger than the rotate-loop versions because SSE instructions are longer.
section .rodata
ALIGN 16
hex_digits:
hex_xlat: db "0123456789abcdef"
section .text
;; rdi = val rsi = buffer
ALIGN 16
global register_to_hex_ssse3
register_to_hex_ssse3: ;;;; 0x39 bytes of code
;; use PSHUFB to do 16 nibble->ASCII LUT lookups in parallel
movaps xmm5, [rel hex_digits]
;; x86 is little-endian, but we want the hex digit for the high nibble to be the first character in the string
;; so reverse the bytes, and later unpack nibbles like [ LO HI ... LO HI ]
bswap rdi
movq xmm1, rdi
;; generate a constant on the fly, rather than loading
;; this is a bit silly: we already load the LUT, might as well load another 16B from the same cache line, a memory operand for PAND since we manage to only use it once
pcmpeqw xmm4,xmm4
psrlw xmm4, 12
packuswb xmm4,xmm4 ; [ 0x0f 0x0f 0x0f ... ] mask for low-nibble of each byte
movdqa xmm0, xmm1 ; xmm0 = low nibbles at the bottom of each byte
psrlw xmm1, 4 ; xmm1 = high nibbles at the bottom of each byte (with garbage from next byte)
punpcklbw xmm1, xmm0 ; unpacked nibbles (with garbage in the high 4b of some bytes)
pand xmm1, xmm4 ; mask off the garbage bits because pshufb reacts to the MSB of each element. Delaying until after interleaving the hi and lo nibbles means we only need one
pshufb xmm5, xmm1 ; xmm5 = the hex digit for the corresponding nibble in xmm0
movups [rsi], xmm5
ret
AVX2: you can do two integers at once, with something like
int64x2_to_hex_avx2: ; (const char buf[32], uint64_t first, uint64_t second)
bswap rsi ; We could replace the two bswaps with one 256b vpshufb, but that would require a mask
vmovq xmm1, rsi
bswap rdx
vpinsrq xmm1, xmm1, rdx, 1
vpmovzxbw ymm1, xmm1 ; upper lane = rdx, lower lane = rsi, with each byte zero-extended to a word element
vpsllw ymm1, ymm1, 12 ; shift the high nibbles out, leaving the low nibbles at the top of each word
vpor ymm0, ymm0, ymm1 ; merge while hi and lo elements both need the same shift
vpsrlw ymm1, ymm1, 4 ; low nibbles in elems 1, 3, 5, ...
; high nibbles in elems 0, 2, 4, ...
pshufb / store ymm0 / ret
Using pmovzx and shifts to avoid pand is a win compared to generating the constant on the fly, I think, but probably not otherwise. It takes 2 extra shifts and a por. It's an option for the 16B non-AVX version, but it's SSE4.1.
Optimized for code-size (fits in 32 (0x20) bytes)
(Derived from Frank's loop)
Using cmov instead of the LUT to handle 0-9 vs. a-f might take fewer than 16B of extra code size. That might be fun: edits welcome.
The ways to get a nibble from the bottom of rsi into an otherwise-zeroed rax include:
mov al, sil (3B (REX required for sil)) / and al, 0x0f (2B special encoding for and al, imm8).
mov eax, esi (2B) / and eax, 0x0f (3B): same size and doesn't require an xor beforehand to zero the upper bytes of rax.
Would be smaller if the args were reversed, so the dest buffer was already in rdi. stosb is a tiny instruction (but slower than mov [rdi], al / inc rdi), so it actually saved overall bytes to use xchg rdi, rsi to set up for it. changing the function signature could save 5 bytes: void reg_to_hex(char buf[16], uint64_t val) would save two bytes from not having to return buf in rax, and 3 bytes from dropping the xchg. The caller will probably use 16B of stack, and having the caller do a mov rdx, rsp instead of mov rdx, rax before calling another function / syscall on the buffer doesn't save anything.
The next function is probably going to ALIGN 16, though, so shrinking the function to even smaller than 32B isn't as useful as getting it inside half a cache-line.
Absolute addressing for the LUT (hex_xlat) would save a few bytes
(use mov al, byte [hex_xlat + rax] instead of needing the lea).
global register_to_hex_size
register_to_hex_size:
push rsi ; pushing/popping return value (instead of mov rax, rsi) frees up rax for stosb
xchg rdi, rsi ; allows stosb. Better: remove this and change the function signature
mov cl, 16 ; 3B shorter than mov ecx, 16
lea rdx, [rel hex_xlat]
;ALIGN 16
.loop:
rol rsi, 4
mov eax, esi ; mov al, sil to allow 2B AND AL,0xf requires a 2B xor eax,eax
and eax, 0x0f
mov al, byte [rdx+rax]
stosb
;; loop .loop ; setting up ecx instead of cl takes more bytes than loop saves
dec cl
jne .loop
pop rax ; get the return value back off the stack
ret
Using xlat costs 2B (to save/restore rbx), but saves 3B, for a net savings of 1B. It's a 3-uop instruction, with 7c latency, one per 2c throughput (Intel Skylake). The latency and throughput aren't a problem here, since each iteration is a separate dependency chain, and there's too much overhead for this to run at one clock per iteration anyway. So the main problem is that it's 3 uops, making it less uop-cache-friendly. With xlat, the loop becomes 10 uops instead of 8 (using stosb), so that sucks.
112: 89 f0 mov eax,esi
114: 24 0f and al,0xf
116: d7 xlat BYTE PTR ds:[rbx]
117: aa stos BYTE PTR es:[rdi],al
vs.
f1: 89 f0 mov eax,esi
f3: 83 e0 0f and eax,0xf
f6: 8a 04 02 mov al,BYTE PTR [rdx+rax*1]
f9: aa stos BYTE PTR es:[rdi],al
Interestingly, this still has no partial-register stalls, because we never read a wide register after writing only part of it. mov eax, esi is write-only, so it cleans up the partial-reg-ness from the load into al. So there would be no advantage to using movzx eax, byte [rdx+rax]. Even when we return to the caller, the pop rax doesn't leave the caller succeptible to partial-reg problems.
(If we don't bother returning the input pointer in rax, then the caller could have a problem. Except in that case it shouldn't be reading rax at all. Usually it only matters if you call with call-preserved registers in a partial-reg state, because the called function might push them. Or more obviously, with arg-passing / return-value registers.
Efficient version (uop-cache friendly)
Looping backwards didn't turn out to save any instructions or bytes, but I've included this version because it's more different from the version in Frank's answer.
ALIGN 16
global register_to_hex_countdown
register_to_hex_countdown:
;;; work backwards in the buffer, starting with the least-significant nibble as the last char
mov rax, rsi ; return value, and loop bound
add rsi, 15 ; last char of the buffer
lea rcx, [rel hex_xlat] ; position-independent code
ALIGN 16
.loop:
mov edx, edi
and edx, 0x0f ; isolate low nibble
mov dl, byte [rcx+rdx] ; look up the ascii encoding for the hex digit
; rdx is an 'index' with range 0x0 - 0xf
; non-PIC version: mov dl, [hex_digits + rdx]
mov byte [rsi], dl
shr rdi, 4
dec rsi
cmp rsi, rax
jae .loop ; rsi counts backwards down to its initial value
ret
The whole thing is only 12 insns (11 uops with macro-fusion, or 12 including the NOP for alignment). Some CPUs can fuse cmp/jcc but not dec/jcc (e.g. AMD, and Nehalem)
Another option for looping backwards was mov ecx, 15, and store with mov [rsi+rcx], dl, but two-register addressing modes can't micro-fuse. Still, that would only bring the loop up to 8 uops, so it would be fine.
Instead of always storing 16 digits, this version could use rdi becoming zero as the loop condition to avoid printing leading zeros. i.e.
add rsi, 16
...
.loop:
...
dec rsi
mov byte [rsi], dl
shr rdi, 4
jnz .loop
; lea rax, [rsi+1] ; correction not needed because of adjustments to how rsi is managed
mov rax, rsi
ret
printing from rax to the end of the buffer gives just the significant digits of the integer.

NASM: adding numbers of the main diagonal in a table

The program i am writing aims to take in a table from text file. The table is in the following format: The table is NxN, and the first line is the number, N. Each row of the table is then included on its own line. Therefore, the file has N + 1 lines.
The program should read in the table, and grab the numbers along the diagonal, going from top left to bottom right, and add them together, outputting the result to screen.
Currently, i am working on a procedure which takes as input the buffer which holds the row of numbers, along with which number the user wishes to retrieve. The intent is to return this in eax. However, it seems that this procedure currently causes a segfault. I have looked over my code and it seems to make sense to me. Below is both a sample table file and my source.
hw6_1.dat
5
2 45 16 22 4
17 21 67 29 65
45 67 97 35 87
68 34 90 72 7
77 15 105 3 66
hw6_1.asm
; this program demonstrates how to open files for reading
; It reads a text file line by line and displays it on the screen
extern fopen
extern fgets
extern fclose
extern printf
extern exit
global main
segment .data
readmode: db "r",0
filename: db "hw6_1.dat",0 ; filename to open
error1: db "Cannot open file",10,0
format_1: db "%d",10,0
format_2: db "%s",10,0
segment .bss
buflen: equ 256 ; buffer length
buffer: resd buflen ; input buffer
tempBuff: resd buflen
segment .text
main:
pusha
; OPENING FILE FOR READING
push readmode ; 1- push pointer to openmode
push filename ; 2- push pointer to filename
call fopen ; fopen retuns a filehandle in eax
add esp, 8 ; or 0 if it cannot open the file
cmp eax, 0
jnz .L1
push error1 ; report an error and exit
call printf
add esp, 4
jmp .L4
; READING FROM FILE
.L1:
mov ebx, eax ; save filepointer of opened file in ebx
; Get first line and pass to ecx
push ebx
push buflen
push buffer
call fgets
add esp, 12
cmp eax, 0
je .L3
;convert string -> numeric
push buffer
call parseInt
mov ecx, eax
.L2:
push ecx
push ebx ; 1- push filehandle for fgets
push dword buflen ; 2- push max number of read chars
push buffer ; 3- push pointer to text buffer
call fgets ; get a line of text
add esp, 12 ; clean up the stack
cmp eax, 0 ; eax=0 in case of error or EOF
je .L3
push buffer ; output the read string
call printf
add esp, 4
push dword 2
push buffer
call grabNum ;Get the 3rd number in the current line. Space delimited.
;do somehing with the number. For now, lets just output to screen.
push eax
push format_1
call printf
add esp, 8
pop ecx
dec ecx
cmp ecx, 0
jg .L2
;CLOSING FILE
.L3:
push ebx ; push filehandle
call fclose ; close file
add esp, 4 ; clean up stack
.L4:
popa
call exit
parseInt:
push ebp
mov ebp, esp
push ebx
push esi
mov esi, [ebp+8] ; esi points to the string
xor eax, eax ; clear the accumulator
.I1:
cmp byte [esi], 48 ; end of string?
jl .I2
mov ebx, 10
mul ebx ; eax *= 10
xor ebx, ebx
mov bl, [esi] ; bl = character
sub bl, 48 ; ASCII conversion
add eax, ebx
inc esi
jmp .I1
.I2:
pop esi
pop ebx
pop ebp
ret 4
grabNum:
;This method will grab a specified number in a sequence.
;Ex: passed in is buffer and the number 4. The 4th number will be
;returned. It is assumed to be a space delimited buffer.
mov esi, [esp + 4]
mov ecx, [esp + 8]
dec ecx
.skipNum:
;for each number in ecx, advance past a number in esi.
;this is done by decrementing ecx each time a "non-digit" is detected.
;Since the buffer is known to be space delimted, this is a valid strategy.
cmp ecx, 0
je .doneSkipping
cmp byte [esi], 48
jl .numSkipped
cmp byte [esi], 57
jg .numSkipped
inc esi
jmp .skipNum
.numSkipped:
inc esi
dec ecx
jmp .skipNum
.doneSkipping:
;now we grab the number from buffer in its ASCII form. We place it in tempBuff,
;and call parseInt. This should leave the number in integer form waiting in eax
;after the end of the grabNum call.
cmp byte [esi + 1 * ecx], 48
jl .retGrab
cmp byte [esi + 1 * ecx], 57
jg .retGrab
mov ebx, [esi + 1 * ecx]
mov [tempBuff + 1 * ecx], ebx
inc ecx
jmp .doneSkipping
.retGrab:
mov [tempBuff + 1 * ecx], byte 0
push tempBuff
call parseInt
ret 8
To be precise, the program prints out "45", the second number in the first row, as i intend at the moment, but seems to throw the segfault before the second line can be output to the screen.
Accidently used EBX register to hold some temporary data. This trashed the file handle and caused a segfault. EAX was used to hold this data instead, which fixes the issue!
To solve this problem I modified these 2 lines:
mov ebx, [esi + 1 * ecx]
mov [tempBuff + 1 * ecx], ebx
to be:
mov eax, [esi + 1 * ecx]
mov [tempBuff + 1 * ecx], eax

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.
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.
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.
Linux x86-64 with printf
main.asm
default rel ; make [rel format] the default, you always want this.
extern printf, exit ; NASM requires declarations of external symbols, unlike GAS
section .rodata
format db "%#x", 10, 0 ; C 0-terminated string: "%#x\n"
section .text
global main
main:
sub rsp, 8 ; re-align the stack to 16 before calling another function
; Call printf.
mov esi, 0x12345678 ; "%x" takes a 32-bit unsigned int
lea rdi, [rel format]
xor eax, eax ; AL=0 no FP args in XMM regs
call printf
; Return from main.
xor eax, eax
add rsp, 8
ret
GitHub upstream.
Then:
nasm -f elf64 -o main.o main.asm
gcc -no-pie -o main.out main.o
./main.out
Output:
0x12345678
Notes:
sub rsp, 8: How to write assembly language hello world program for 64 bit Mac OS X using printf?
xor eax, eax: Why is %eax zeroed before a call to printf?
-no-pie: plain call printf doesn't work in a PIE executable (-pie), the linker only automatically generates a PLT stub for old-style executables. Your options are:
call printf wrt ..plt to call through the PLT like traditional call printf
call [rel printf wrt ..got] to not use a PLT at all, like gcc -fno-plt.
Like GAS syntax call *printf#GOTPCREL(%rip).
Either of these are fine in a non-PIE executable as well, and don't cause any inefficiency unless you're statically linking libc. In which case call printf can resolve to a call rel32 directly to libc, because the offset from your code to the libc function would be known at static linking time.
See also: Can't call C standard library function on 64-bit Linux from assembly (yasm) code
If you want hex without the C library: Printing Hexadecimal Digits with Assembly
Tested on Ubuntu 18.10, NASM 2.13.03.
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.
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 ; край
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

Clearing out a string variable

I have writen this little experiement bootstrap that has a getline and print_string "functions". The boot stuff is taken from MikeOS tutorial but the rest I have writen myself. I compile this with NASM and run it in QEMU.
So the actual question: I've declared this variable curInpLn on line 6. What ever the user types is saved on that variable and then after enter is hit it is displayed to the user with some additional messages. What I'd like to do is to clear the contents of curInpLn each time the getline function is called but for some reason I can't manage to do that. I'm quite the beginner with Assmebly at the moment.
You can compile the code to bin format and then create a floppy image of it with: "dd status=noxfer conv=notrunc if=FILENAME.bin of=FILENAME.flp" and run it in qemu with: "qemu -fda FILENAME.flp"
BITS 16
jmp start
welcomeSTR: db 'Welcome!',0
promptSTR: db 'Please prompt something: ',0
responseSTR: db 'You prompted: ',0
curInpLn: times 80 db 0 ;this is a variable to hold the input 'command'
curCharCnt: dw 0
curLnNum: dw 1
start:
mov ax, 07C0h ; Set up 4K stack space after this bootloader
add ax, 288 ; (4096 + 512) / 16 bytes per paragraph
mov ss, ax
mov sp, 4096
mov ax, 07C0h ; Set data segment to where we're loaded
mov ds, ax
call clear_screen
lea bx, [welcomeSTR] ; Put string position into SI
call print_string
call new_line
.waitCMD:
lea bx, [promptSTR]
call print_string
call getLine ; Call our string-printing routine
jmp .waitCMD
getLine:
cld
mov cx, 80 ;number of loops for loopne
mov di, 0 ;offset to bx
lea bx, [curInpLn] ;the address of our string
.gtlLoop:
mov ah, 00h ;This is an bios interrupt to
int 16h ;wait for a keypress and save it to al
cmp al, 08h ;see if backspace was pressed
je .gtlRemChar ;if so, jump
mov [bx+di], al ;effective address of our curInpLn string
inc di ;is saved in bx, di is an offset where we will
;insert our char in al
cmp al, 0Dh ;see if character typed is car-return (enter)
je .gtlDone ;if so, jump
mov ah, 0Eh ;bios interrupt to show the char in al
int 10h
.gtlCont:
loopne .gtlLoop ;loopne loops until cx is zero
jmp .gtlDone
.gtlRemChar:
;mov [bx][di-1], 0 ;this needs to be solved. NASM gives error on this.
dec di
jmp .gtlCont
.gtlDone:
call new_line
lea bx, [responseSTR]
call print_string
mov [curCharCnt], di ;save the amount of chars entered to a var
lea bx, [curInpLn]
call print_string
call new_line
ret
print_string: ; Routine: output string in SI to screen
mov si, bx
mov ah, 0Eh ; int 10h 'print char' function
.repeat:
lodsb ; Get character from string
cmp al, 0
je .done ; If char is zero, end of string
int 10h ; Otherwise, print it
jmp .repeat
.done:
ret
new_line:
mov ax, [curLnNum]
inc ax
mov [curLnNum], ax
mov ah, 02h
mov dl, 0
mov dh, [curLnNum]
int 10h
ret
clear_screen:
push ax
mov ax, 3
int 10h
pop ax
ret
times 510-($-$$) db 0 ; Pad remainder of boot sector with 0s
dw 0xAA55 ; The standard PC boot signature
I haven't written code in Assembly for 20 years (!), but it looks like you need to use the 'stosw' instruction (or 'stosb'). STOSB loads the value held in AL to the byte pointed to by ES:DI, whereas STOSSW loads the value held in AX to the word pointed to by ES:DI. The instruction automatically advances the pointer. As your variable curInpLn is 80 bytes long, you can clear it with 40 iterations of STOSW. Something like
xor ax, ax ; ax = 0
mov es, ds ; point es to our data segment
mov di, offset curInpLn ; point di at the variable
mov cx, 40 ; how many repetitions
rep stosw ; zap the variable
This method is probably the quickest method of clearing the variable as it doesn't require the CPU to retrieve any instructions from the pre-fetch queue. In fact, it allows the pre-fetch queue to fill up, thus allowing any following instructions to execute as quickly as possible.

Resources