Author Topic: Example: Simple loop with (jnz)  (Read 20507 times)

Offline Shikatsu

  • Jr. Member
  • *
  • Posts: 10
Example: Simple loop with (jnz)
« on: February 06, 2012, 03:40:02 AM »
Greetings EveryOne :D

I just learned how to make a simple loop in nasm the hard way of course :D

Let me share my code with you:

Code: [Select]
; To compile nasm code type:
; nasm myprog.asm -fbin -o myprog.com (or myprog.exe)

org 100h

segment .text
mov     ECX, 5
mov     byte [var], 30h

label1:
dec     ECX
jnz     label2

mov     AX, 0;4C00h
int     21h

label2:
call    print
jmp     label1


segment .bss
var     RESB    1

segment .data
print:
add     byte [var], 1h
mov     DL, [var]
mov     ah, 2h
int     21h
ret

I would love to hear any feedback on this, Thank You.

Offline avcaballero

  • Full Member
  • **
  • Posts: 132
  • Country: es
    • Abre los Ojos al Ensamblador
Re: Example: Simple loop with (jnz)
« Reply #1 on: February 06, 2012, 08:42:33 AM »
That's fine, but may be too many jumps?

Code: [Select]
; nasmw.exe -f bin Print.asm -o Print.com

[org 100h]
[section .text]
  MOV   CX, 5
  @Loop:
    INC   byte [var]
    MOV   DL, byte [var]
    OR    DL, 30h
    MOV   AH, 2
    INT   21h
    DEC   CX
  JNZ   @Loop
  RET

[section .data]
  var db  0

; >print
; 12345

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Example: Simple loop with (jnz)
« Reply #2 on: February 06, 2012, 10:52:28 AM »
Yeah! That jumps around a bit less (generally a good thing).

Shikatsu used ecx - not really necessary here, but it illustrates that we can use 32-bit registers, even 32-bit addressing modes, in 16-bit code. There's a slight "penalty" for using 32-bit registers in 16-bit code (or using 16-bit registers in 32-bit code): Nasm inserts a "operation size override prefix", the byte 66h, (67h is the "address size override prefix") into your code. Doesn't gain you much here, but in some cases it is well worth the "cost" to do it. Especially the 32-bit addressing modes!

The "trick" is that "mov cx" and "mov ecx" are the same opcode. You can see this in the disassembly...
Code: [Select]
00000000  B90500            mov cx,0x5
00000003  66B905000000      mov ecx,0x5
Note that, besides the 66h override, the integer 5 now takes up 4 bytes instead of just two. This is why you can't run 16-bit code when the CPU is in 32-bit mode (and vise-versa).

Anyway, either will work. I just noticed that you've got your "print" subroutine in "segment .data". That won't do any harm, but all your code should be in "segment .text". Doing the printing inline, as Alfonso shows, is simpler if you're only calling if from one place (even if in a loop), but it's good to learn to write a subroutine, so that it can be called from many places. The problem with this "print" subroutine, for that purpose, is that it always prints the same thing. A more "reusable" subroutine would print something passed as a parameter. Since we want the thing to print in dl, the very simplest "calling convention" (if you can even call it that) would be to pass the parameter in dl...
Code: [Select]
; To compile nasm code type:
; nasm myprog.asm -fbin -o myprog.com (or myprog.exe)

org 100h

segment .text
mov     ECX, 5
mov     byte [var], 30h

label1:
dec     ECX
jz     label2

add     byte [var], 1h
mov     DL, [var]
call     print
jmp     label1

label2:
mov dl, 'h'
call print

exit:
mov     AX, 0;4C00h
int     21h

segment .bss
var     RESB    1

segment .text

; print - prints a character
; expects - character to print in dl
; returns - nothing useful
; clobbered - ax
print:
mov     ah, 2h
int     21h
ret

Of course... it doesn't use "jnz" anymore...

I notice you've commented out 4C00h and replaced it with 0 in the exit. Trouble with 4C00h, or were you just experimenting? Another way to exit is just "ret". This works (in a .com file!) because dos has pushed a zero to the stack when loading us. When the "ret" gets its return address from the stack, it jump to location zero (in our one-and-only segment), where dos has thoughtfully put "int 20h" - CD 20 - still another way to exit. Make sure that you DO exit cleanly, one way or another, or the CPU will keep executing (or trying to) whatever bytes it finds next. This probably won't be a command to format your hard drive, but don't take chances. :)

Best,
Frank


Offline Shikatsu

  • Jr. Member
  • *
  • Posts: 10
Re: Example: Simple loop with (jnz)
« Reply #3 on: February 06, 2012, 03:56:34 PM »
Thank you very very much for your replies, I really appreciate it.

Mr. avcaballero code is less spaghetti than mine indeed, the first thing I went searching on the net after being done with my late code, for tips to avoid spaghetti code in assembly because it seemed to me as not well structured.

I see that Mr. avcaballero replaced:
Code: [Select]
add     byte [var], 1h
;with:
OR    DL, 30h

Can "OR" replace "ADD" like this: OR DL, 30h <=> DL = [DL] + 30h ? (I never encountered this in my readings).

Thank you Mr. Frank Kotler for taking time and explaining those things, I am really learning with you guys, again I really appreciate it.

I never knew that we can split "segment .text" (code segment) into more than one part, that's gonna be useful ;)

About commenting 4C00h, it's working fine for me and yes I was experimenting and I forget to take that comment out before posting.

THANKS.
« Last Edit: February 06, 2012, 03:59:07 PM by Shikatsu »

Offline avcaballero

  • Full Member
  • **
  • Posts: 132
  • Country: es
    • Abre los Ojos al Ensamblador
Re: Example: Simple loop with (jnz)
« Reply #4 on: February 07, 2012, 08:35:24 AM »
Do you mean?

OR DL, 30h <=> ADD DL, 30h

for DL= 0, 1, ..., 9.

Yes. But the first one is a logical operation, so it works with binary digits. Usually it is done as follows:

  ADD DL, '0'
 
Because it is more clear to understand what we are trying to do: convert binary numers into ascii numbers, that are what we can print on the screen.

I usually use OR instead ADD because it is more quickly and more flexible, but less clear to understand.

30h = 110000b = '0'
31h = 110001b = '1'
...
38h = 111000b = '8'
39h = 111001b = '9'

Regards