Copy Link
Add to Bookmark
Report
NULL mag Issue 07 30 Assembly tutorial 7
Linefeeds are essential to console programs like our 'hello world' program.
They become even more important once we start building programs that
require user input. But linefeeds can be a pain to maintain. Sometimes you
will want to include them in your strings and sometimes you will want to
remove them. If we continue to hard code them in our variables by adding
0Ah after our declared message text, it will become a problem. If there's
a place in the code that we don't want to print out the linefeed for that
variable we will need to write some extra logic remove it from the string
at runtime.
It would be better for the maintainability of our program if we write
a subroutine that will print out our message and then print a linefeed
afterwards. That way we can just call this subroutine when we need the
linefeed and call our current sprint subroutine when we don't.
A call to sys_write requires we pass a pointer to an address in memory of
the string we want to print so we can't just pass a linefeed character
(0Ah) to our print function. We also don't want to create another variable
just to hold a linefeed character so we will instead use the stack.
The way it works is by moving a linefeed character into EAX. We then push
EAX onto the stack and get the address pointed to by the Extended Stack
Pointer. ESP is another register. When you push items onto the stack, ESP
is decremented to point to the address in memory of the last item and so it
can be used to access that item directly from the stack. Since ESP points
to an address in memory of a character, sys_write will be able to use it to
print.
functions.asm
;------------------------------------------
; int slen(String message)
; String length calculation function
slen:
push ebx
mov ebx, eax
nextchar:
cmp byte [eax], 0
jz finished
inc eax
jmp nextchar
finished:
sub eax, ebx
pop ebx
ret
;------------------------------------------
; void sprint(String message)
; String printing function
sprint:
push edx
push ecx
push ebx
push eax
call slen
mov edx, eax
pop eax
mov ecx, eax
mov ebx, 1
mov eax, 4
int 80h
pop ebx
pop ecx
pop edx
ret
;------------------------------------------
; void sprintLF(String message)
; String printing with line feed function
sprintLF:
call sprint
push eax
; push eax onto the stack to preserve it while we use the eax register
; in this function
mov eax, 0Ah
; move 0Ah into eax - 0Ah is the ascii character for a linefeed
push eax
; push the linefeed onto the stack so we can get the address
mov eax, esp
; move the address of the current stack pointer into eax for sprint
call sprint
; call our sprint function
pop eax
; remove our linefeed character from the stack
pop eax
; restore the original value of eax before our function was called
ret
; return to our program
;------------------------------------------
; void exit()
; Exit program and restore resources
quit:
mov ebx, 0
mov eax, 1
int 80h
ret
helloworld-lf.asm
; Hello World Program (Print with line feed)
; Compile with: nasm -f elf helloworld-lf.asm
; Link with (64 bit systems require elf_i386 option):
; ld -m elf_i386 helloworld-lf.o -o helloworld-lf
; Run with: ./helloworld-lf
%include 'functions.asm'
SECTION .data
msg1 db 'Hello, brave new world!', 0h
; NOTE we have removed the line feed character 0Ah
msg2 db 'This is how we recycle in NASM.', 0h
; NOTE we have removed the line feed character 0Ah
SECTION .text
global _start
_start:
mov eax, msg1
call sprintLF
; NOTE we are calling our new print with linefeed function
mov eax, msg2
call sprintLF
; NOTE we are calling our new print with linefeed function
call quit
~$ nasm -f elf helloworld-lf.asm
~$ ld -m elf_i386 helloworld-lf.o -o helloworld-lf
~$ ./helloworld-lf
Hello, brave new world!
This is how we recycle in NASM.