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 tested this with the DOS emulator emu2)
mov dl, 21h ; char '!'
mov ah, 02h ; char output service
int 21h ; call DOS services
x86-32 Linux write
syscall
write
requires the address of your string. For a single character you can push your character on the stack.
push '!' ; push dword
mov eax, 4 ; write call number, __NR_write from unistd_32.h
mov ebx, 1 ; write to stdout (fd=1)
mov ecx, esp ; use char on stack
mov edx, 1 ; write 1 char
int 0x80 ; call into the kernel
add esp, 4 ; 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 '!' ; push qword 0x21
mov rax, 1 ; write call number, __NR_write from unistd_64.h
mov edi, 1 ; write to stdout (int fd=1)
mov rsi, rsp ; use char on stack
mov rdx, 1 ; size_t len = 1 char to write.
syscall ; call the kernel, it looks at registers to decide what to do
add rsp, 8 ; restore stack pointer
mov edx, 1
does exactly the same thing as mov rdx, 1
, but NASM will optimize it for you. This code uses operand-sizes that match the C types, not optimizing mov of small non-negative 64-bit integers.