6

I'm a little confused about how to print a character to the screen using Assembly. The architecture is x86 (linux). Is it possible to call one of the C functions or is there a simpler way? The character I want to output is stored in a register.

Thanks!

user973758
  • 687
  • 1
  • 9
  • 20

3 Answers3

9

In the interest of completeness, here is how to do it without C.

Using DOS interrupts

AH = 02h -WRITE CHARACTER TO STANDARD OUTPUT

Writes character in DL. I haven't tested this myself. in NASM syntax something like

mov ah, 02h
int 21h

x86-32 Linux write syscall

write requires the address of your string. The easiest way I know of is to push your character on the stack.

push    $0x21       # '!'
mov     $4, %eax    # sys_write call number 
mov     $1, %ebx    # write to stdout (fd=1)
mov     %esp, %ecx  # use char on stack
mov     $1, %edx    # write 1 char
int     $0x80   
add     $4, %esp    # restore sp 

Info on register order

x86-64 Linux write syscall

Similar to above, but the call number is now 1, syscall instead of int $0x80, and the calling convention registers are different.

push    $0x21       # '!'
mov     $1, %rax    # sys_write call number 
mov     $1, %rdi    # write to stdout (fd=1)
mov     %rsp, %rsi  # use char on stack
mov     $1, %rdx    # write 1 char
syscall   
add     $8, %rsp    # restore sp 
qwr
  • 8,081
  • 5
  • 52
  • 86
  • 1
    For x86-64 Linux, you can `lea -1(%rsp), %rsi` / `movb $0x21, (%rsi)` to use the red-zone, and not have to pop the stack. – Peter Cordes Jun 20 '18 at 04:41
  • and what about for X86-64 Windows? (21h calls are blocked.) – BrainSlugs83 Aug 06 '20 at 18:58
  • 1
    @BrainSlugs83 I don't know about Windows, sorry. Maybe Peter Cordes can shed some light. – qwr Aug 06 '20 at 19:21
  • @BrainSlugs83: Windows has a system-call ABI you can in theory use from user-space, but it's undocumented and not stable across versions. Still, see https://github.com/j00ru/windows-syscalls for reverse-engineering details. I don't know which one(s) you'd actually want to use. – Peter Cordes May 28 '21 at 00:55
8

Sure, you can use any normal C function. Here's a NASM example that uses printf to print some output:

;
; assemble and link with:
; nasm -f elf test.asm && gcc -m32 -o test test.o
;
section .text

extern printf   ; If you need other functions, list them in a similar way

global main

main:

    mov eax, 0x21  ; The '!' character
    push eax
    push message
    call printf
    add esp, 8     ; Restore stack - 4 bytes for eax, and 4 bytes for 'message'
    ret

message db 'The character is: %c', 10, 0

If you only want to print a single character, you could use putchar:

push eax
call putchar

If you want to print out a number, you could do it like this:

mov ebx, 8
push ebx
push message
call printf
...    
message db 'The number is: %d', 10, 0
Martin
  • 35,178
  • 15
  • 71
  • 80
  • Thanks. But what if register ebx holds the value 8, and I want to print the character '8'. If I try and push ebx as the parameter it doesn't work so is there a way around this? Cheers – user973758 Nov 20 '11 at 13:26
  • that would be equivalent to `printf("%d", 8);` I added that to the answer – Martin Nov 20 '11 at 13:31
-3

Calling putchar(3) is the simplest way. Just move the char's value into the rdi register (for x86-64, or edi for x86), and call putchar.

E.g. (for x86-64):

asm("movl $120, %rdi\n\t"
    "call putchar\n\t");

will print an x to stdout.

Bailey Parker
  • 15,111
  • 4
  • 51
  • 88
Anthony Blake
  • 5,258
  • 2
  • 24
  • 24
  • Wrong ABI! (x86-64 OS X, at a guess, given the register -- although I think you want `%rdi` not `%edi` -- and the leading `_` on the library function?) – Matthew Slattery Nov 22 '11 at 01:17
  • You changed it in the code, but your description still says "the edi register." – porglezomp Sep 08 '14 at 22:36
  • The i386 System V calling convention passes args on the stack, and EDI is a call-preserved register. Plus doing this from GNU C "basic" inline asm (with no clobbers) isn't safe: you step on the compilers registers. And for x86-64, you step on the compiler's red-zone, too. This *will* call `putchar` on x86-64 Linux, but that's all, what happens after that is unpredictable. – Peter Cordes Jun 20 '18 at 04:39
  • @MatthewSlattery: `movl` to `%edi` is a more efficient way to zero-extend a value into RDI. As it stands now, this won't even assemble (mismatch between `movl` dword operand size suffix vs. qword register). But anyway, without any clobbers this is totally broken. See my previous comment. In stand-alone asm, `mov $'x', %edi` ; `call putchar` this would be fine. – Peter Cordes Feb 16 '20 at 22:12