Author Topic: Looping through a string  (Read 24099 times)

Offline Mixolydian

  • Jr. Member
  • *
  • Posts: 21
Looping through a string
« on: May 31, 2014, 09:48:55 AM »
I decided that yesterday would be the day that I would begin to learn assembly! Several hours later, after choosing NASM, reading some tutorials, and playing around, I have written my first program:

Code: [Select]
; External functions
extern _printf

; Global functions
global getMessageLength
global alterMessage
global printMessage
global _main

; Global variables
section .data
message:
db "abcd", 0

; Code
section .text

getMessageLength:
push ebp
mov ebp, esp ; setup a stack frame

mov eax, message ; store address of message into eax (caller saved, so we are allowed to modify it)
jmp getMessageLength_loop2
getMessageLength_loop:
add eax, 1 ; next character
getMessageLength_loop2:
cmp [eax], byte 0 ; is dereferenced character at eax the string's NULL truncator?
jnz getMessageLength_loop

sub eax, message ; subtract message from eax to get the difference from the byte that was 0 and the start (string length)
leave ; restore the stack back to how it was
ret ; returns with message length in eax

alterMessage:
push ebp
mov ebp, esp ; setup a stack frame

mov ecx, message ; store address of message into ecx
call getMessageLength
add eax, ecx ; eax holds the end of the message

alterMessage_loop:
mov bl, byte [ecx] ; bl (lowest byte of ebx) will hold the dereferenced byte at ecx
add bl, 1 ; add one to this
mov [ecx], byte bl ; put the modified character back into memory at location ecx

add ecx, 1 ; go to the next character
cmp ecx, eax
jnz alterMessage_loop ; if we have not reached the final character, repeat

leave ; restore the stack back to how it was
ret

printMessage:
push ebp
mov ebp, esp ; save the stack before calling a function with arguments

mov eax, message ; put address of message into eax (because we can't directly put it into the stack)
;lea eax, [message] ; equivalent to the above opcode
mov [esp], eax ; put eax into dereferenced stack pointer (first parameter for an int or pointer)
call _printf

;add esp, 4 ; equivalent to below opcode (in this situation)
leave ; restore the stack back to how it was
ret

_main:
call alterMessage
call printMessage

mov eax, 0
ret ; return 0

It takes the NULL truncated string "abcd" from memory, gets its length, loops through it and adds 1 to all characters. The output is:

Quote
bcde

I have programmed in C a lot (~5 years) but never dealt with assembly before so I am asking for some more experienced people to look over the above code and tell me if I am doing anything blatantly wrong, slow, or even bad stylistic layout so that I can catch these bad habits before I get more involved.

For example, I was told that using "add eax, 1" is faster than "inc eax" and so have been using that in my code but I have my doubts.

I have also seen a "loop" instruction, however it looks limited and I am not sure whether I should try to use it, or avoid it like HTML's "<center>" tag.

Thanks in advance; any suggestions are welcome.

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Looping through a string
« Reply #1 on: May 31, 2014, 10:35:52 AM »
Hi!

Your code looks great, I didn't tried to compile.

Some suggestions:

If you want to clear, zero-out or init register you could use xor instruction.

So, "mov eax, 0" could be replaced by "xor eax,eax".

I think XOR is faster or in the same time it occupies less memory, than "mov eax,0".

Loop instruction, It works this way: You have to set ecx register to a number of how many times the loop should loop,
during the loop you should not modify ecx OR you can modify it but make sure to save and restore.

Code: [Select]

        ; This will loop ten times or nine - It will loop while ecx is not zero, each "loop" instruction decrements ecx.

        mov ecx,10

.cycle:

       ; Save ecx
       push ecx
 
       ; Do other things with ecx, ... other registers


       ; Restore ecx
       pop ecx
       loop .cycle

Encryptor256's Investigation \ Research Department.

Offline Mixolydian

  • Jr. Member
  • *
  • Posts: 21
Re: Looping through a string
« Reply #2 on: May 31, 2014, 11:08:37 AM »
The xor trick is neat, I'll keep that in mind.

Regarding the loop though, I don't understand why it is ".cycle:" rather than just "cycle:". I've compiled with both names and the code appears to do the same whether it is has the dot or not.

So is a dot in front of a label just a way of saying that this isn't a function, it's just part of a function?

So in my example should I change "getMessageLength_loop:" and "getMessageLength_loop2:" to have dots before them?

EDIT: Nevermind, I was able to write to a file using this code:

Code: [Select]
writeFile:
push ebp
mov ebp, esp

push writeBinary
push filename
call _fopen

push eax
push message
push eax
call _fprintf
pop eax

push eax
call _fclose

leave
ret
« Last Edit: May 31, 2014, 12:00:59 PM by Mixolydian »

Offline Rob Neff

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 429
  • Country: us
Re: Looping through a string
« Reply #3 on: May 31, 2014, 01:48:22 PM »
The xor trick is neat, I'll keep that in mind.

Yes, that's a rather standard way of setting the register to zero.

Regarding the loop though, I don't understand why it is ".cycle:" rather than just "cycle:". I've compiled with both names and the code appears to do the same whether it is has the dot or not.

So is a dot in front of a label just a way of saying that this isn't a function, it's just part of a function?

For Nasm the dot in front of the label ties it a previously defined label.  It enables you to "reuse" the same label name in the same source file but within another defined function.  For a simplified example:
Code: [Select]
alterMessage:
    push ebp
    mov  ebp, esp
    mov  ecx, 10

.loop            ; <- this label will become alterMessage.loop
    .
    ; do stuff
    .
    jmp .loop    ; <- will jump to alterMessage.loop

    mov esp, ebp
    pop  ebp
    ret


alterAnotherMessage:
    push ebp
    mov  ebp, esp
    mov  ecx, 10

.loop            ; <- this label will become alterAnotherMessage.loop
    .
    ; do stuff
    .
    jmp .loop    ; <- will jump to alterAnotherMessage.loop

    mov esp, ebp
    pop  ebp
    ret

This is a very powerful construct when building macros.  Just don't define the same label twice within the same function.



Offline Mixolydian

  • Jr. Member
  • *
  • Posts: 21
Re: Looping through a string
« Reply #4 on: May 31, 2014, 04:06:50 PM »
In the .bss section, is there a difference between:

Code: [Select]
key resd 1
And:

Code: [Select]
key: resd 1
To reserve 1 doubleword named key?

Offline encryptor256

  • Full Member
  • **
  • Posts: 250
  • Country: lv
  • Win64 .
    • On Youtube: encryptor256
Re: Looping through a string
« Reply #5 on: May 31, 2014, 04:23:33 PM »
In the .bss section, is there a difference between:

Code: [Select]
key resd 1
And:

Code: [Select]
key: resd 1
To reserve 1 doubleword named key?

No!
Both works the same, are equal.
It's just a variation, you can choose the right way for you.

I use this variation with ":", "key: resd 1".
Encryptor256's Investigation \ Research Department.

Offline Mixolydian

  • Jr. Member
  • *
  • Posts: 21
Re: Looping through a string
« Reply #6 on: June 01, 2014, 12:57:53 PM »
OK, thanks.

Is there a way to block comment out code? Like C's:

Code: [Select]
/* ignored */
I found macros %comment and %endcomment but it appears like they only get ignored by the preprocessor.

Must I go through every line and write ";" at the beginning?

EDIT: Nevermind:

Code: [Select]
%if 0 ignored %endif
is working.
« Last Edit: June 01, 2014, 01:05:02 PM by Mixolydian »