# Improved countdown program in x86 NASM

A while back, I posted a question involving code of the same function. This post contains improved code.

From the last post, I have improved size, my work with conventions, and my comments. I was also able to minimalism memory interaction to speed things up/make things more efficient.

%define KERNEL_CALL int 80h

%define STDOUT 1
%define SYS_WRITE 4

%define EXIT 1

%define NEWLINE 0xA
%define ASCII_NUM 30h

; @params: AX - a number that is converted to ASCII, then printed
; @return: no return
%macro put_num 0
push ax;        push ax onto the stack(for restoring value)
add ax, ASCII_NUM;  numerical to ascii
push ax;        push ax onto the stack(for reference by ESP)

mov edx, 1;     message length
mov ecx, esp;   the number from the stack pointer
mov ebx, STDOUT
mov eax, SYS_WRITE
KERNEL_CALL;    logs the number to the console

pop ax;         getting it out of the way
pop ax;         restoring the original value of ax
%endmacro

section .text
_start:
push '10';      push '10' to the stack for reference by ESP

mov edx, 2;     length
mov ecx, esp;   message(number)
mov ebx, STDOUT
mov eax, SYS_WRITE
KERNEL_CALL;    logs '10' to the console

mov ax, 9;      initialize ax
countdown:
put_num;        log the current number to stdout
.subtract:
sub ax, 1;      subtract 1

cmp ax, 0;      compare ax with 0...
jmp countdown;  if not equal, go back to countdown
.blast_off:
mov edx, len;   message length
mov ecx, msg;   message
mov ebx, STDOUT
mov eax, SYS_WRITE
KERNEL_CALL;    logs 'Blast off!' to the console

xor ebx, ebx
mov eax, EXIT
KERNEL_CALL;    exit
section .data
msg db "Blast off!", NEWLINE
len equ \$ - msg


Output (I know - the formatting still needs work):

10987654321Blast off!

Assembled: nasm -f elf bo7_working.s

Linked:: ld -m elf_i386 -s -o bo bo7_working.o

Some concerns:

• After I learned about macros, I started to use them a lot. Am I using them in place of headers (and is this bad), or is how I am using them fine?
• Am I over-using/misusing %defines (more specifically, KERNEL_CALL)?
• Do I have too many or too few comments?
• Are there any unnecessary lines? I'm a little worried about the popping at the end of put_num...
• Am I following convention, or are there more things I should do?
• Is my code understandable?

•After I learned about macros, I started to use them a lot. Am I using them in place of headers (and is this bad), or is how I am using them fine?

Looks OK to me.

•Am I over-using/misusing %defines (more specifically, KERNEL_CALL)?

I wouln't use KERNAL_CALL in stead of int 80h. The latter is shorter and the value involved will not change any soon in the future.

•Do I have too many or too few comments?

I find commenting on .subtract: a bit stating the obvious.

•Are there any unnecessary lines? I'm a little worried about the popping at the end of put_num...

The popping at the end of put_num is correct (pushing/popping 2 words in a row nicely keeps the stack aligned at dwords) but a few lines seem unnecessary to me. You do not need to compare AX with 0 since subtracting already defines the flags. You could also shave off an extra jump.

sub ax, 1;      subtract 1
cmp ax, 0;      compare ax with 0...
jmp countdown;  if not equal, go back to countdown
.blast_off:


Better code:

sub ax,1
jnz countdown
.blast_off:


•Am I following convention, or are there more things I should do?

The ordering of the sections of the program is a matter of taste. But I see most people put the .data section before the .text section.

•Is my code understandable?

Very understandable.