I am working on a program in Assembly Language right now where the user can input any word of their preference and then the program will reverse it. So, if the user inputs HELLO and the program will output OLLEH.
The algorithm I decided to implement was by first getting the length of the string and storing it in length. I will then use the length to traverse backward in the given string and store each character in a new memory location which is REVERSE. I tested my program several times but it only outputs one character. I tried several words and noticed that the character is always output is the second character of the string so I am unsure if it is able to reverse the word or not.
Did I miss anything or I implemented a code block incorrectly?
Note: The GET_STRING[end] 5 is used when I run the code in .exe format.
Below is the code I am currently working on:
%include "io.inc"
section .data
string dd 0x00
end dd 0x00
REVERSE dd 0x00
length db 0
section .text
global CMAIN
CMAIN:
;write your code here
PRINT_STRING "Please enter a string: "
GET_STRING [string], 10
lea esi, [string]
lea edi, [REVERSE]
lea ecx, [length]
L1:
mov al, [esi]
cmp al, 0
JE FINISH
JNE INCREMENT
inc esi
jmp L1
INCREMENT:
inc byte[length]
inc esi
jmp L1
FINISH:
L2:
;points to the current index of the string (i.e. if Hello,it will first point at 'o' which is 5
mov al, [ecx]
cmp cl, 0 ;checks if length == 0
JE DONE
JNE DECREMENT
dec ecx
jmp L2
DECREMENT:
mov byte[edi], al ;adds a character from [string]
dec ecx
jmp L2
DONE:
PRINT_STRING REVERSE
GET_STRING[end], 5
xor eax, eax
ret
If GET_STRING gives you a zero-terminated string then your L1 part will find the length of that string.
This is what remains after removing the redundant code:
lea esi, [string]
L1:
mov al, [esi]
cmp al, 0
je FINISH
inc byte [length]
inc esi
jmp L1
FINISH:
lea ecx, [length]
In the L2 part however
you are using the address of the length variable as if it were an address into the string itself!
you are checking for length == 0 from a register that you did not load with the length
At the start of part L2, the ESI register still points at the terminating zero. So you'll have to decrement first and then fetch and store the character.
Of course you can't copy any characters if the length variable contains 0. So check this condition first.
lea edi, [REVERSE]
mov cl, [length]
test cl, cl
jz DONE
L2:
dec esi ; Decrement
mov al, [esi] ; Fetch
mov [edi], al ; Store
inc edi
dec cl ; 1 more character done
jnz L2
DONE:
mov [edi], cl ; CL=0 at this point, now zero-terminating the result
If you keep your current data definitions, then don't input more than 3 characters. A dword dd allows for just 3 characters and 1 terminating zero.
Alternatively widen your buffers:
string db 11 dup (0) ; GET_STRING [string], 10
end db 6 dup (0) ; GET_STRING [end], 5
REVERSE db 11 dup (0)
length db 0
Below is a version that doesn't use a memory-based length variable. The length of the string is stored in ECX.
lea edi, [REVERSE]
lea esi, [string]
L1:
mov al, [esi]
inc esi
cmp al, 0
jne L1
lea ecx, [esi - 1 - String]
jecxz DONE
; ESI points behind the terminating zero, the fetch uses a -2 offset (avoiding address stall)
L2:
mov al, [esi - 2] ; Fetch
mov [edi], al ; Store
dec esi
inc edi
dec ecx ; 1 more character done
jnz L2
DONE:
mov [edi], cl ; CL=0 at this point, now zero-terminating the result
Related
I have recently started learning assembly. I am trying to concate two 32 byte strings into a final one that is preallocated as 64 bytes.
section .data
string1 db "static string",0
section .bss
user_inputed_string resb 32
concated_string resb 64
I am trying to achieve the strings concated in a way the user inputted one goes first and the static one second: concated_string = user_inputed_string + string1
I have looked the internet for solutions and none even seems to be valid syntax for NASM.
First copy the user inputted string to the output buffer. I assume it is a zero-terminated string.
mov edi, concated_string ; Address of output buffer
mov esi, user_inputed_string ; Address of input buffer
more1:
mov al, [esi]
inc esi
mov [edi], al
inc edi
cmp al, 0
jne more1
Then copy the static string but do overwrite the terminating zero from the first copy:
dec edi ; Removes the zero
mov esi, string1
more2:
mov al, [esi]
inc esi
mov [edi], al
inc edi
cmp al, 0
jne more2 ; This zero needs to stay
You can replace mov al, [esi] inc esi by lodsb, and you can replace mov [edi], al inc edi by stosb.
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
Input is random number of integers(does not exceed 1 byte), separated with spaces like this : -3 4 5 7 8 -1
There is ReadInt function in Irvine32 library, as far as I know, one integer as its input function. But since there is no clear information about the number of inputs, I had to declare the space in BYTE string form like this :
keyListInString BYTE 31 DUP(0)
and get integer string like this :
mov edx,OFFSET keyListInString
mov ecx,SIZEOF keyListInString
call ReadString
and I tried to separate each integers by recognizing spaces,'-' symbol and manually inserting them to new space I have created.
keyListInInteger BYTE 31 DUP(0) ;new space for integers
And here is the code I used.
mov esi,OFFSET keyListInString
mov edi,OFFSET keyListInInteger
mov ecx,eax ;eax = length of string = number of cycles
L1:
mov al,[esi]
cmp al,45 ;if current component of keyListInString is '-'
je ifMinus
mov al,[esi]
cmp al,0 ;if this is null(space)
je ifNull
mov al,[esi] ;if its none of above(normal number)
sub al,48 ;sub 48 for ascii --> integer
mov [edi],al ;put integer to where edi points(keyListInInteger)
inc edi
jmp final
ifMinus:
inc esi ;points next index on keyListInString(number part)
mov al,[esi]
sub al,48
neg al ;have to reverse the sign, because it is originally a minus
mov [edi],al
inc edi ;manually increase edi
jmp final
ifNull:
inc esi ;it is space anyway, so point next.
jmp L1 ;and repeat from first.
final:
inc esi
loop L1
I tried to check if this is the right code in this way :
mov edx,OFFSET keyListInInteger
mov al,[edx] ;see [edx+2],[edx+4],...
movzx eax,al
call DumpRegs
and see al's value.
I think I somehow approached to the result I wanted, but not exactly.
I wanted to make each integers like elements in an array, but I have no idea how to do so.
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
I am taking an assembly course and I have gotten most of the program written out, I am just having trouble replacing the word and displaying the new string. The problem asks for a sentence, a word to find, and a word to replace it with. The program scans the string, replaces any instances of the word, and shows you the new string.
Example: "The sky is blue."
Word to find: "sky"
Word to replace it: "ocean"
New String: "The ocean is blue."
Here is what I have so far:
.586
.MODEL FLAT
INCLUDE io.h ; header file for input/output
.STACK 4096
.DATA
prompt1 BYTE "String to Search: ", 0
prompt2 BYTE "Word to Search For: ", 0
prompt3 BYTE "Word to replace with: ", 0
target BYTE 80 DUP (?)
key BYTE 80 DUP (?)
strSub BYTE 80 DUP (?)
trgtLength DWORD ?
keyLength DWORD ?
lastPosn DWORD ?
strSubLen DWORD ?
resultLbl BYTE "The new sentence is: ", 0
.CODE
_MainProc PROC
input prompt1, target, 80 ;input target string
lea eax, target ;address of target
push eax ;parameter
call strlen ;strlen(target)
add esp, 4 ;remove parameter
mov trgtLength, eax ;save length of target
input prompt2, key, 80 ;input key string
lea eax, key ;address of key
push eax ;parameter
call strlen ;strlen(key)
add esp, 4 ;remove parameter
mov keyLength, eax ;save length of key
input prompt3, strSub, 80 ;input word to search for
lea eax, strSub ;address of key
push eax ;parameter
call strlen ;strlen(strSub)
add esp, 4 ;remove parameter
mov strSubLen, eax ;save length of key
mov eax, trgtLength
sub eax, keyLength
inc eax ;trgtLength - keyLength +1
mov lastPosn, eax
cld ;Left to Right comparison
mov eax, 1 ;starting position
whilePosn:
cmp eax, lastPosn ;position <= last_posn?
jnle endWhilePosn ;exit if past last position
lea esi, target ;address of target string
add esi, eax ;add position
dec esi ;address of position to check
lea edi, key ;address of key
mov ecx, keyLength ;number of position to check
repe cmpsb ;check
jz found ;exit of success
inc eax ;increment position
jmp whilePosn ;repeat
endWhilePosn:
output resultLbl, [esi] ;display new sentence
jmp quit
found:
sub edi, keyLength
mov ecx, strSubLen
lea esi, strSub
cld
rep movsb
inc eax
jmp whilePosn
quit:
mov eax, 0 ; exit with return code 0
ret
_MainProc ENDP
strlen PROC
push ebp ;establish stack frame
mov ebp, esp
push ebx ;save EBX
sub eax, eax ;length := 0
mov ebx, [ebp+8] ;address of string
whileChar:
cmp BYTE PTR [ebx], 0 ;null byte?
je endWhileChar ;exit if so
inc eax ;increment length
inc ebx ;point at next character
jmp whileChar ;repeat
endWhileChar:
pop ebx ;restore registers
pop ebp
ret
strlen ENDP
END
The code works as far as finding the word that I want to switch, but actually switching the words is tricking me up. The book says that the destination string should be in EDI and the word to replace should be in ESI, but the code they give as an example has the destination string in ESI, and word to replace in EDI (like I have here).
The book also does a pretty horrible job of explaining the "rep" and "movs" instructions, so I am 90% sure that my "found" code block is going to be where the problem is. Any help is much appreciated.
When dealing with strings in x86 assembly, one has to quickly master the rep instruction. If not, you will probably pass away before your first program has been completed.
A very good introduction on string manipulation in assembly can be found here (or here), but what you really need here are the following ones:
rep stosb: Store string
rep scas: Scan string
rep cmpsb: Compare string