I have to write a program that
takes a string from the key board buffer and puts it into a character array
asks for a character to remove
removes the character from the character array while shifting everything else over
I want to use a stack to accomplish this. So here is my logic.
Starting from the end of the string compare that character with the character that is to be removed. If it isn't the character push it on to the stack. If it is ignore it and move on through the string. Then starting from the beginning pop everything into place.
I'm supposed to use a procedure to accomplish this. When I'm stepping through everything seems to be working ok until I attempt to leave the procedure and return to main. I'm fairly sure my logic in my procedure is the problem. Right now when I attempt to work with the string "The" and remove the e I get "he".
TITLE String Manipulation
INCLUDE Irvine32.inc
.data
prompt byte "Please enter a string to manipulate : ",0
prompt2 byte "Please enter a character to remove: ",0
answerMSG byte "The new string is: ",0
string BYTE 51 DUP (0)
char BYTE ?
byteCount dword ?
.code
main PROC
call clrscr
push eax ;perserve the registers
push ecx
push edx
mov edx, OFFSET prompt ;prints the prompt
call writeString
mov edx, OFFSET string ;moves the register to the first location for the string
mov ecx, SIZEOF string ;Sets the max characters
call readString
mov byteCount,eax ;places actual count into a counting register
call crlf
mov edx, OFFSET prompt2 ;prints the prompt
call writeString
mov edx, OFFSET char
mov ecx, 1
call readString
call clrscr
mov ecx, byteCount
mov edx, OFFSET string
call stringMan
mov edx, OFFSET string
call writeString
pop edx
pop ecx
pop eax
main ENDP
;
stringMan PROC USES eax ecx edx
mov eax,0
L1:
movzx edx , string[ecx]
cmp dl, char
jz L2
push edx
inc eax
L2:
mov string[ecx],0
LOOP L1
mov ecx,eax
mov eax,0
L3:
pop edx
mov byte ptr string[eax],dl
inc eax
loop L3
ret
stringMan ENDP
END main
Figured it out.
Answer:
I was not dealing with getting a character from the console correctly. I also was not dealing with the case when ecx = 0. This is the first position of the character array. So I was not comparing the correct character, and not pushing the first character onto the array when necessary. I have fixed it by removing
mov edx, OFFSET char
mov ecx, 1
call readString
and replacing it with
call readChar
mov char,al
then adding this after the L1 loop.
movzx edx , string[ecx]
cmp dl,char
jz L4
push edx
inc eax
L4:
It now works as designed. I just have some formatting issues to clear up.
Answer:
I was not dealing with getting a character from the console correctly. I also was not dealing with the case when ecx = 0. This is the first position of the character array. So I was not comparing the correct character, and not pushing the first character onto the array when necessary. I have fixed it by removing
mov edx, OFFSET char
mov ecx, 1
call readString
and replacing it with
call readChar
mov char,al
then adding this after the L1 loop.
movzx edx , string[ecx]
cmp dl,char
jz L4
push edx
inc eax
L4:
It now works as designed. I just have some formatting issues to clear up.
Related
I am studying Assembly language using this nasm tutorial. Here is the code that prints a string:
SECTION .data
msg db 'Hello!', 0Ah
SECTION .text
global _start
_start:
mov ebx, msg
mov eax, ebx
; calculate number of bytes in string
nextchar:
cmp byte [eax], 0
jz finished
inc eax
jmp nextchar
finished:
sub eax, ebx ; number of bytes in eax now
mov edx, eax ; number of bytes to write - one for each letter plus 0Ah (line feed character)
mov ecx, ebx ; move the memory address of our message string into ecx
mov ebx, 1 ; write to the STDOUT file
mov eax, 4 ; invoke sys_write (kernel opcode 4)
int 80h
mov ebx, 0 ; no errors
mov eax, 1 ; invoke sys_exit (kernel opcode 1)
int 80h
It works and successfully prints "Hello!\n" to STDOUT. One thing I don't understand: it searches for \0 byte in msg, but we didn't define it. Ideally, the correct message definition should be
msg db 'Hello!', 0Ah, 0h
How does it successfully get the zero byte at the end of the string?
The similar case is in exercise 7:
; String printing with line feed function
sprintLF:
call sprint
push eax ; push eax onto the stack to preserve it while we use the eax register in this function
mov eax, 0Ah ; move 0Ah into eax - 0Ah is the ascii character for a linefeed
push eax ; push the linefeed onto the stack so we can get the address
mov eax, esp ; move the address of the current stack pointer into eax for sprint
call sprint ; call our sprint function
pop eax ; remove our linefeed character from the stack
pop eax ; restore the original value of eax before our function was called
ret ; return to our program
It puts just 1 byte: 0Ah into eax without terminating 0h, but the string length is calculated correctly inside sprint. What is the cause?
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
My code is required to have a string that will be printed to the console, alongside a string length counting program that will count it instead of manually putting length of string in edx register. But i am getting strange characters printed right after the string is printed.
global _start
section .text
_start:
mov edi, message
call _strlen
mov edx, eax
mov eax, 4
mov ebx, 1
mov ecx, message
int 80h
mov eax, 1
mov ebx, 5
int 80h
section .data
message: db "My name is Stanley Hudson", 0Ah
_strlen:
push ebx
push ecx
mov ebx, edi
xor al, al
mov ecx, 0xffffffff
repne scasb ; REPeat while Not Equal [edi] != al
sub edi, ebx ; length = offset of (edi – ebx)
mov eax, edi
pop ebx
pop ecx
ret
Here is the output
strlen searches for a 0 byte terminating the string, but your string doesn't have one, so it goes until it does find a zero byte and returns a value that's too large.
You want to write
message: db "My name is Stanley Hudson", 0Ah, 0
; ^^^
Another bug is that your _strlen function is apparently in the .data section, because you didn't go back to section .text after your string. x86-32 doesn't have the NX bit so the .data section is executable and everything still works, but it's surely not what you intend.
To get rid of the special characters write the strlen function before the start process and create a new register for the newline character
;This program reverses a string.
INCLUDE Irvine32.inc
.data
aName BYTE "Abraham Lincoln",0
nameSize = ($ - aName) - 1
.code
main PROC
; Push the name on the stack.
mov ecx,nameSize
mov esi,0
L1: movzx eax,aName[esi] ; get character
push eax ; push on stack
inc esi
Loop L1
; Pop the name from the stack, in reverse,
; and store in the aName array.
mov ecx,nameSize
mov esi,0
L2: pop eax ; get character
mov aName[esi],al ; store in string
inc esi
Loop L2
; Display the name.
mov edx,OFFSET aName
call Writestring
call Crlf
exit
main ENDP
END main
I finished the first part of the problem which was making a Reverse String program but now I need to modify the program so the user can input a string containing between 1 and 50 characters. Im not to sure how to do this and was wondering if someone could help out. This is in assembly language btw
You can use ReadString from irvine32.lib. Therefor you have to change nameSize to a variable and to increase the size of aName.
I did it for you ;-) :
;This program reverses a string.
INCLUDE Irvine32.inc
.data
aName BYTE 51 DUP (?)
nameSize dd ?
.code
main PROC
mov edx, OFFSET aName
mov ecx, 50 ;buffer size - 1
call ReadString
mov nameSize, eax
; Push the name on the stack.
mov ecx,nameSize
mov esi,0
L1: movzx eax,aName[esi] ; get character
push eax ; push on stack
inc esi
Loop L1
; Pop the name from the stack, in reverse,
; and store in the aName array.
mov ecx,nameSize
mov esi,0
L2: pop eax ; get character
mov aName[esi],al ; store in string
inc esi
Loop L2
; Display the name.
mov edx,OFFSET aName
call Writestring
call Crlf
exit
main ENDP
END main
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