Disregard a strings' space characters in ASM - string

Trying to find the number of characters in a string and disregard all the " " space characters
I have a C++ portion that passes the strings to asm and here is my asm
works fine, only thing is that the space characters are being counted as well.
stringLength PROC PUBLIC
PUSH ebp ; save caller base pointer
MOV ebp, esp ; set our base pointer
SUB esp, (1 * 4) ; allocate uint32_t local vars
PUSH edi
PUSH esi
; end prologue
MOV esi, [ebp+8] ;gets the string
xor ebx, ebx
COMPARE:
MOV al, [esi + ebx]
CMP al, 0 ;compare character of string with 0
JE FINALE ;if = to 0 go to end
INC ebx ;counter
CMP al, ' ' ;compare with sapce
JE SPACE ;go get rid of the space and keep going
INC al ;otherwise inc al to next character and repeat
JMP COMPARE
SPACE:
DEC ebx ;get rid of the extra space
INC al
JMP COMPARE ;goes back to compare
FINALE:
MOV eax,ebx ; bring back the counter
ADD esp, (2 * 4) ; clear the stack
POP esi
POP edi
MOV esp, ebp ; deallocate locals
POP ebp ; restore caller base pointer
RET
stringLength ENDP ; end the procedure
END stringLength

You are doing a lot of useless stuff and not doing anything to count ignoring the spaces.
You don't really need to setup a new stack frame, for such a simple routine you can do everything in clobbered registers, or at most save a few registers on the stack;
That inc al is pointless - you are incrementing the character value, just to discard it at the next loop iteration.
push fmt and then you clean the stack immediately? What sense does it make?
mov ebx, 0- nobody does that, the idiomatic way to zero a register is xor ebx,ebx (the instruction encoding is more compact);
cmp al, 0 given that you are only interested in equality, you can just do test al, al (more compact);
you read [ebp+12] but never actually use it - is that supposed to be an unused parameter?
As for the algorithm itself, you'll just have to keep a separate counter to count non-space characters; actually, you can just keep ebx for that, and increment directly esi to iterate over characters. For example:
xor ebx, ebx
COMPARE:
mov al, [esi]
cmp al, ' '
jne nonspace
inc ebx
nonspace:
test al, al
jz FINALE
inc esi
jmp COMPARE
FINALE:
Now, this can be streamlined further exploiting the fact that the eax is going to be the return value, and that you can clobber freely ecx and edx, so:
stringLength PROC PUBLIC
mov ecx,[esp+4] ; get the string
xor eax,eax ; zero the counter
compare:
mov dl,[ecx] ; read character
cmp dl,' '
jne nospace
inc eax ; increase counter if it's a space
nospace:
test dl,dl
jz end ; go to end if we reached the NUL
inc ecx ; next character
jmp compare
end:
ret ; straight return, nothing else to do
stringLength ENDP ; end the procedure
edit: about the updated version
COMPARE:
MOV al, [esi + ebx]
CMP al, 0
JE FINALE
INC ebx
CMP al, " " ; I don't know what assembler you are using,
; but typically character values are in single quotes
JE SPACE
INC al ; this makes no sense! you are incrementing
; the _character value_, not the position!
; it's going to be overwritten at the next iteration
JMP COMPARE
SPACE:
INC eax ; you cannot use eax as a counter, you are already
; using it (al) as temporary store for the current
; character!
JMP COMPARE

I think we need to use whole the eax register to compare values. In such manner:
; inlet data:
; eax - pointer to first byte of string
; edx - count of bytes in string
; ecx - result (number of non-space chars)
push esi
mov ecx, 0
mov esi, eax
##compare: cmp edx, 4
jl ##finalpass
mov eax, [esi]
xor eax, 20202020h ; 20h - space char
cmp al, 0
jz ##nextbyte0
inc ecx
##nextbyte0: cmp ah, 0
jz ##nextbyte1
inc ecx
##nextbyte1: shr eax, 16
cmp al, 0
jz ##nextbyte2
inc ecx
##nextbyte2: cmp ah, 0
jz ##nextbyte3
inc ecx
##nextbyte3: add esi, 4
sub edx, 4
jmp ##compare
##finalpass: and edx, edx
jz ##fine
mov al, [esi]
cmp al, 20h
jz ##nextbyte4
inc ecx
##nextbyte4: inc esi
dec edx
jmp ##finalpass
##fine: pop esi
; save the result data and restore stack

Related

String Reverse in FASM x86 architecture

I am making a program that reverses a given string from the user.
The problem that has appeared is that the program works well if the string is 5 bytes long but if the string is lower then the result doesn't appear when I execute it. The other problem is that if the string is more than 5 bytes long it reverses only the first five bytes.
Please keep in mind that I am new to assembly and this question may be basic but I would be grateful is someone tells me where the problem is.
Thank you to everyone, have a great day :)
P.S The file "training. inc" is a file that has "print_str, read_line" methods implemented.
entry start
include "win32a.inc"
MAX_USER_STR = 5h
section '.data' data readable writeable
enter_string db "Enter a string : ", 0
newline db 13,10,0
user_str db MAX_USER_STR dup(?), 0
section ".text" code readable executable
start:
mov esi, enter_string
call print_str
mov edi, user_str
call read_line
call str_len
mov edx, MAX_USER_STR
mov ebx, 0
mov ecx, 0
mov esi, user_str
call print_str
mov esi, newline
call print_str
mov esi, user_str
for_loop :
push eax
mov al, byte[esi]
inc esi
inc ebx
call print_eax
cmp edx, ebx
jb clear_register
jmp for_loop
for_loop2 :
call print_eax
mov byte[esi], al
inc esi
inc ecx
pop eax
cmp ecx, edx
ja break_loop
jmp for_loop2
break_loop:
;mov edi, 0
mov esi, user_str
call print_str
push 0
call [ExitProcess]
clear_register :
mov esi, user_str
jmp for_loop2
str_len :
push ecx
sub ecx, ecx
mov ecx, -1
sub al, al
cld
repne scasb
neg ecx
sub ecx, 1
mov eax, ecx
pop ecx
ret
include 'training.inc'
MAX_USER_STR = 5h
The name MAX_ already says it, but a buffer is to be defined according to the worst case scenario. If you want to be able to deal with strings that could be longer than 5 characters, then raise this value.
MAX_USER_STR = 256 ; A decent buffer
... if the string is lower then the result doesn't appear when I execute it.
The other problem is that if the string is more than 5 bytes long it reverses only the first five bytes.
That's because your code does not actually use the length of the string but rather the size of the smaller buffer. I hope you see that this should never happen, overflowing the buffer. Your code didn't complain too much since this buffer was the last item in the data section.
Your loops could use the true length if you write:
call str_len ; -> EAX
mov edx, eax
for_loop :
push eax
mov al, byte[esi]
If it's characters that you want to push, then I would expect the push eax to follow the load from the string!
Note that in a string-reversal, you never want to move the string terminator(s) to the front of the string.
This is your basic string reversal via the stack:
mov ecx, edx ; EDX has StrLen
mov esi, user_str
loop1:
movzx eax, byte [esi]
inc esi
push eax
dec ecx
jnz loop1
mov esi, user_str
loop2:
pop eax
mov [esi], al
inc esi
dec edx
jnz loop2

Storing a character in a memory location in Assembly Language

I am trying to write a program in Assembly Language where it will replace all the letter 'T' in the string STRVAR with 'U' and place the new string in OUTPUT. I figured that I should store each character one by one in OUTPUT throughout the loop process although after doing several trial and error with mov, I already ran out of ideas on how to store a character in a new memory location.
STRVAR db "ACGTACGTCCCTTT",0
OUTPUT times 21 db 0
section .text
global CMAIN
CMAIN:
;write your code here
lea esi, [STRVAR]
L1:
mov al, [esi]
cmp al, 0
JE FINISH
cmp al, 'T'
JE REPLACE
JNE CONTINUE
inc esi
jmp L1
REPLACE:
;store character here
inc esi
jmp L1
CONTINUE:
;store character here
inc esi
jmp L1
FINISH:
xor eax, eax
ret
I followed the information shared by Jester and I finally get to have the program work based on what is stated in the specification. I realized I need to add section .data and introduced another point which in this case, lea edi, [OUTPUT] to store each character and use it to print a new string.
%include "io.inc"
section .data
STRVAR db "ACGTACGTCCCTTT",0
OUTPUT times 21 db 0
section .text
global CMAIN
CMAIN:
;write your code here
lea esi, [STRVAR]
lea edi, [OUTPUT]
L1:
mov al, [esi]
cmp al, 0
JE FINISH
cmp al, 'T'
JE REPLACE
JNE CONTINUE
inc esi
inc edi
jmp L1
REPLACE:
mov byte[edi], 'U'
inc esi
inc edi
jmp L1
CONTINUE:
mov byte[edi], al
inc esi
inc edi
jmp L1
FINISH:
mov byte [edi], 0
PRINT_STRING OUTPUT
PRINT_DEC 1, [edi] ;check if the terminating 0 is also included
xor eax, eax
ret

String reverse function x86 NASM assembly

I'm trying to write a function that reverses order of characters in a string using x86 NASM assembly language. I tried doing it using registers (I know it's more efficient to do it using stack) but I keep getting a segmentation fault, the c declaration looks as following
extern char* reverse(char*);
The assembly segment:
section .text
global reverse
reverse:
push ebp ; prologue
mov ebp, esp
mov eax, [ebp+8] ; eax <- points to string
mov edx, eax
look_for_last:
mov ch, [edx] ; put char from edx in ch
inc edx
test ch, ch
jnz look_for_last ; if char != 0 loop
sub edx, 2 ; found last
swap: ; eax = first, edx = last (characters in string)
test eax, edx
jg end ; if eax > edx reverse is done
mov cl, [eax] ; put char from eax in cl
mov ch, [edx] ; put char from edx in ch
mov [edx], cl ; put cl in edx
mov [eax], ch ; put ch in eax
inc eax
dec edx
jmp swap
end:
mov eax, [ebp+8] ; move char pointer to eax (func return)
pop ebp ; epilogue
ret
It seems like the line causing the segmentation fault is
mov cl, [eax]
Why is that happening? In my understanding eax never goes beyond the bounds of the string so there always is something in [eax]. How come I get a segmentation fault?
Ok I figured it out, I mistakenly used test eax, edx instead of which I should have used cmp eax, edx. It works now.

operation size not specified

I have a problem with 32bit Assembly, assembling it with NASM on linux.
Here is my implementation of insertion sort
myInsertionSort:
push ebp
mov ebp, esp
push ebx
push esi
push edi
mov ecx, [ebp+12] ;put len in ecx, our loop variable
mov eax, 1 ; size of one spot in array, one byte
mov ebx, 0
mov esi, [ebp+8] ; the array
loop loop_1
loop_1:
cmp eax, ecx ; if we're done
jge done_1 ; then done with loop
push ecx ; we save len, because loop command decrements ecx
mov ecx, [esi+eax] ; ecx now array[i]
mov ebx, eax
dec ebx ; ebx is now eax-1, number of times we should go through inner loop
loop_2:
cmp ebx, 0 ; we don't use loop to not affect ecx so we use ebx and compare it manually with 0
jl done_2
cmp [esi+ebx], ecx ;we see if array[ebx] os ecx so we can exit the loop
jle done_2
mov edx, esi
add edx, ebx
push [edx] ; pushing our array[ebx] *****************************
add edx, eax
pop [edx] ; poping the last one *********************************
dec ebx ; decrementing the loop iterator
jmp loop_2 ; looping again
done_2:
mov [esi+ebx+1], ecx
inc eax ; incrementing iterator
pop ecx ; len of array to compare now to eax and see if we're done
jmp loop_1
done_1:
pop edi
pop esi
pop ebx
pop ebp ; we pop them in opposite to how we pushed (opposite order, it's the stack, LIFO)
ret
Now... When I try to compile my code with nasm, I get errors of "operation size not specified" on the lines containing asterisks in the comments :P
It's basic insertion sort and I'm not sure what could have gone wrong.
Enlighten me, please.
The data at [edx] could be anything, so its size is unknown to the assembler. You'll have to specify the size of the data you want to push/pop. For example, if you want to push/pop a dword (32 bits) you'd write:
push dword [edx]
pop dword [edx]
By the way, you can combine these lines:
mov edx, esi
add edx, ebx
into:
lea edx,[esi + ebx]

write number to file using NASM

How do I write a variable to a file using NASM?
For example, if I execute some mathematical operation - how do I write the result of the operation to write a file?
My file results have remained empty.
My code:
%include "io.inc"
section .bss
result db 2
section .data
filename db "Downloads/output.txt", 0
section .text
global CMAIN
CMAIN:
mov eax,5
add eax,17
mov [result],eax
PRINT_DEC 2,[result]
jmp write
write:
mov EAX, 8
mov EBX, filename
mov ECX, 0700
int 0x80
mov EBX, EAX
mov EAX, 4
mov ECX, [result]
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
You have to implement ito (integer to ascii) subsequently len for this manner. This code tested and works properly in Ubuntu.
section .bss
answer resb 64
section .data
filename db "./output.txt", 0
section .text
global main
main:
mov eax,5
add eax,44412
push eax ; Push the new calculated number onto the stack
call itoa
mov EAX, 8
mov EBX, filename
mov ECX, 0x0700
int 0x80
push answer
call len
mov EBX, EAX
mov EAX, 4
mov ECX, answer
movzx EDX, di ; move with extended zero edi. length of the string
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
itoa:
; Recursive function. This is going to convert the integer to the character.
push ebp ; Setup a new stack frame
mov ebp, esp
push eax ; Save the registers
push ebx
push ecx
push edx
mov eax, [ebp + 8] ; eax is going to contain the integer
mov ebx, dword 10 ; This is our "stop" value as well as our value to divide with
mov ecx, answer ; Put a pointer to answer into ecx
push ebx ; Push ebx on the field for our "stop" value
itoa_loop:
cmp eax, ebx ; Compare eax, and ebx
jl itoa_unroll ; Jump if eax is less than ebx (which is 10)
xor edx, edx ; Clear edx
div ebx ; Divide by ebx (10)
push edx ; Push the remainder onto the stack
jmp itoa_loop ; Jump back to the top of the loop
itoa_unroll:
add al, 0x30 ; Add 0x30 to the bottom part of eax to make it an ASCII char
mov [ecx], byte al ; Move the ASCII char into the memory references by ecx
inc ecx ; Increment ecx
pop eax ; Pop the next variable from the stack
cmp eax, ebx ; Compare if eax is ebx
jne itoa_unroll ; If they are not equal, we jump back to the unroll loop
; else we are done, and we execute the next few commands
mov [ecx], byte 0xa ; Add a newline character to the end of the character array
inc ecx ; Increment ecx
mov [ecx], byte 0 ; Add a null byte to ecx, so that when we pass it to our
; len function it will properly give us a length
pop edx ; Restore registers
pop ecx
pop ebx
pop eax
mov esp, ebp
pop ebp
ret
len:
; Returns the length of a string. The string has to be null terminated. Otherwise this function
; will fail miserably.
; Upon return. edi will contain the length of the string.
push ebp ; Save the previous stack pointer. We restore it on return
mov ebp, esp ; We setup a new stack frame
push eax ; Save registers we are going to use. edi returns the length of the string
push ecx
mov ecx, [ebp + 8] ; Move the pointer to eax; we want an offset of one, to jump over the return address
mov edi, 0 ; Set the counter to 0. We are going to increment this each loop
len_loop: ; Just a quick label to jump to
movzx eax, byte [ecx + edi] ; Move the character to eax.
movsx eax, al ; Move al to eax. al is part of eax.
inc di ; Increase di.
cmp eax, 0 ; Compare eax to 0.
jnz len_loop ; If it is not zero, we jump back to len_loop and repeat.
dec di ; Remove one from the count
pop ecx ; Restore registers
pop eax
mov esp, ebp ; Set esp back to what ebp used to be.
pop ebp ; Restore the stack frame
ret ; Return to caller

Resources