Simple Print to Console Algorithm using NASM Assembly Code on Linux*

When searching for a good, clean working example other than perhaps writing out a "Hello World" string to the console in Netwide Assembler (NASM) for standard x86 architecture... I've found only a little.  There are only a small number of places one can go for examples.  I've found the downloadable pdf isn't the quickest or easiest way to begin learning Netwide Assembly.  Stackoverflow does offer some enigmatic propositions to ponder, and my print-to-console algorithm that I'm sharing with you today is loosely based upon a mishmash of some things you may find there.  It's obvious that when assembly language is required or preferred in certain scenarios you won't discover much out there that's quick or easy.  For example, here, on the blog there isn't a set available for syntax highlighting of .asm code.  I think I'll try wrapping it in a .cpp bulletin board set of markup brackets and be okay with it.  Please note that this code is for NASM and ought to work on Ubuntu and other Linux platforms that support Netwide Assembler.  When using NASM you'll want to use "elf" and "stabs" in the command line to assemble your code, then use "ld" to link everything together.  I've commented a proper command line example at the top:


	;nasm -f elf -g -F stabs print2console_lanceregala.asm

	;ld -o p2c print2console_lanceregala.o -melf_i386



;initialized data

	    section .data

	          nln: dd 0xA

	          nln_len: equ $-nln

	          val: dd 48 ;remember that dd is a 32 bit double word which is the size of the register

	          val_len: equ $-val ;if you use db or dw, keep in mind what can be done with the extended bits

	          spacer: dd " "

;uninitialized data

	    section .bss

	          valueToPrint: resd 1 ;likewise, 1 resd = 4 resb (Dword = 4 Bytes)


	    section .text

	          global _start


	    nop                             ;necessary for ld linking 

	 call _newline

	    call _print_string         ;print this value in ASCII

	 call _newline

	 call _print_decimal ;if it's really a decimal, this is the actual, readable value converted to ASCII



 call _newline

    jmp _norm_exit           ;not expecting to return




_print_string:                       ;when you want to print the ASCII [val] it's easy

    mov eax, 4                       ;syswrite

	    mov ebx, 1                       ;stdout

	    mov ecx, val                     ;whatever value 'val' points to in [ecx] will print

	    mov edx, val_len               ;...

	    int 0x80                           ;invoke kernel to perform instruction

	    jmp _return


	_print_decimal:                       ;converting that pesky ASCII to it's decimal form

	    push '$'                             ;throw a $ sign on my stack for no reason other than to designate a stack base

	    mov eax, [val]                    ;[val] to decimal


	    mov [val], eax

	    xor edx, edx                       ;zero out edx (...can be accomplished in many different ways)

	    mov ecx, 10                       ;mod out a base 10 place value 

	    idiv ecx                             ;signed divide (you can do unsigned division too) edx:eax by 10 

	                                                        ;...result in eax, remainder in edx

	    add edx, 0x30                               ;convert the remainder to ascii, ('0' = 0x30)

	                                                        ;(extra steps, commented out for hex conversion to print A-F)

    ;cmp edx, 0x39                             ;'9' comparison because numerical digit range is 0-9

    ;jng .hexrange  ;

    ;add edx, 0x07                              ;adding 7 hex to create an ascii


	    push edx ;push REMAINDER on stack (in e.g., it's 0)

	    mov [val], eax ;copy the quotient into [val]

	    cmp eax, 0

	    jnz .conversion ;break from loop when eax == 0 


	    pop eax

	    mov [valueToPrint], eax ;store contents of 'eax' in [valueToPrint] 

	    cmp eax, '$'

	    je _return

	    mov eax, 4 ;syswrite

	    mov ebx, 1 ;stdout

	    mov ecx, valueToPrint ;whatever value exists in [ecx] will print

	    mov edx, 1 ;print only a single byte's worth of data

	    int 0x80 ;invoke kernel to perform instruction

	    jmp .printpostfix





	    mov eax, 4

	    mov ebx, 1

	    mov ecx, spacer

	    mov edx, 1

	    int 80h

	    jmp _return


	    mov eax, 4

	    mov ebx, 1

	    mov ecx, nln

	    mov edx, 1

	    int 80h

	    jmp _return




	    mov eax, 1 ;initiate 'exit' syscall

	    mov ebx, 0 ;exit with error code 0

	    int 0x80 ;invoke kernel





	; Lance

	; November 5th, 2013

By modifying this example, you could covert to any base and print the ASCII equivalent to the console.  In essence, to write good assembly you just have to know "what data type" the computer is processing at any given moment as you step through the program and also keep in mind "the size of the data" with which you're working -- i.e., the number of place values that you are modifying at any given moment, whether it's a byte or 2 bytes, etc. My structuring of the abstract problem space while coding in assembly usually involves placing my declarations at the top, followed by my most abstracted list of calls similar to a main() style procedural method next, and then finishing up with a defined implementation of those calls in a series of _labels and nested .labels for NASM to identify in its first of always only two passes through the assembly code. I use the stackframe to produce a kind of "postfix" readable format from where the little-endian nature of the register created "prefix" style backwards output. I also have found that it works well in my mind to jump to a '_return' label at the end for the sake of pattern and because I can simply call the '_return' label, and similarly so with the '_spacer' and '_newline' labels.

I hope someone finds this blog as helpful.  Although I don't expect to be blogging to any great extent on assembly code in the future, my study of the x86 assembly language has been very enlightening.  And if you're looking for a good NASM example, there have you.

My name's Lance Regala, and I'm a student programmer and developer currently residing in Sacramento, CA.  Thanks to Paul Steinberg, Student Community manager of the Intel® Developer Zone,  for the opportunity to document my learning through blogging here on the official Intel website.



Для получения подробной информации о возможностях оптимизации компилятора обратитесь к нашему Уведомлению об оптимизации.
Возможность комментирования русскоязычного контента была отключена. Узнать подробнее.