My assignment is to Implement a function in assembly that would do the following:
loop through a sequence of characters and swap them such that the end result is the original string in reverse ( 100 points )
Hint: collect the string from user as a C-string then pass it to the assembly function along with the number of characters entered by the user. To find out the number of characters use strlen() function.
i have written both c++ and assembly programs and it works fine for extent: for example if i input 12345 the out put is correctly shown as 54321 , but if go more than 5 characters : the out put starts to be incorrect: for example if i input 123456 the output is :653241. i will greatly appreciate anyone who can point where my mistake is:
.code
_reverse PROC
push ebp
mov ebp,esp ;stack pointer to ebp
mov ebx,[ebp+8] ; address of first array element
mov ecx,[ebp+12] ; the number of elemets in array
mov eax,ebx
mov ebp,0 ;move 0 to base pointer
mov edx,0 ; set data register to 0
mov edi,0
Setup:
mov esi , ecx
shr ecx,1
add ecx,edx
dec esi
reverse:
cmp ebp , ecx
je allDone
mov edx, eax
add eax , edi
add edx , esi
Swap:
mov bl, [edx]
mov bh, [eax]
mov [edx],bh
mov [eax],bl
inc edi
dec esi
cmp edi, esi
je allDone
inc ebp
jmp reverse
allDone:
pop ebp ; pop ebp out of stack
ret ; retunr the value of eax
_reverse ENDP
END
and here is my c++ code:
#include<iostream>
#include <string>
using namespace std;
extern"C"
char reverse(char*, int);
int main()
{
char str[64] = {NULL};
int lenght;
cout << " Please Enter the text you want to reverse:";
cin >> str;
lenght = strlen(str);
reverse(str, lenght);
cout << " the reversed of the input is: " << str << endl;
}
You didn't comment your code, so IDK what exactly you're trying to do, but it looks like you are manually doing the array indexing with MOV / ADD instead of using an addressing mode like [eax + edi].
However, it looks like you're modifying your original value and then using it in a way that would make sense if it was unmodified.
mov edx, eax ; EAX holds a pointer to the start of array, read every iter
add eax , edi ; modify the start of the array!!!
add edx , esi
Swap:
inc edi
dec esi
EAX grows by EDI every step, and EDI increases linearly. So EAX increases geometrically (integral(x * dx) = x^2).
Single-stepping this in a debugger should have found this easily.
BTW, the normal way to do this is to walk one pointer up, one pointer down, and fall out of the loop when they cross. Then you don't need a separate counter, just cmp / ja. (Don't check for JNE or JE, because they can cross each other without ever being equal.)
Overall you the right idea to start at both ends of the string and swap elements until you get to the middle. Implementation is horrible though.
mov ebp,0 ;move 0 to base pointer
This seems to be loop counter (comment is useless or even worse); I guess idea was to swap length/2 elements which is perfectly fine. HINT I'd just compare pointers/indexes and exit once they collide.
mov edx,0 ; set data register to 0
...
add ecx,edx
mov edx, eax
Useless and misleading.
mov edi,0
mov esi , ecx
dec esi
Looks like indexes to start/end of the string. OK. HINT I'd go with pointers to start/end of the string; but indexes work too
cmp ebp , ecx
je allDone
Exit if did length/2 iterations. OK.
mov edx, eax
add eax , edi
add edx , esi
eax and edx point to current symbols to be swapped. Almost OK but this clobbers eax! Each loop iteration after second will use wrong pointers! This is what caused your problem in the first place. This wouldn't have happened if you used pointers instead indexes, or if you'd used offset addressing [eax+edi]/[eax+esi]
...
Swap part is OK
cmp edi, esi
je allDone
Second exit condition, this time comparing for index collision! Generally one exit condition should be enough; several exit conditions usually either superfluous or hint at some flaw in the algorithm. Also equality comparison is not enough - indexes can go from edi<esi to edi>esi during single iteration.
Related
My assignment for school is to loop through a sequence of characters in a string and swap them such that the end result is the original string in reverse.
I have written 3 assembly functions and one cpp function but on the function below I am getting a few errors when I try to run the program and I'm not sure how to fix it. I will post both the cpp code and assembly code below with the errors pointed out, if anyone could point out what my mistake is I would appreciate it a lot!
My c++ code is below
#include<iostream>
#include <string>
using namespace std;
extern"C"
char reverse(char*, int);
int main()
{
char str[64] = {NULL};
int lenght;
cout << " Please Enter the text you want to reverse:";
cin >> str;
lenght = strlen(str);
reverse(str, lenght);
cout << " the reversed of the input is: " << str << endl;
}
Below is my assembly code
.model flat
.code
_reverse PROC ;named _test because C automatically prepends an underscode, it is needed to interoperate
push ebp
mov ebp,esp ;stack pointer to ebp
mov ebx,[ebp+8] ; address of first array element
mov ecx,[ebp+12] ; the number of elemets in array
mov eax,ebx
mov ebp,0 ;move 0 to base pointer
mov edx,0 ; set data register to 0
mov edi,0
Setup:
mov esi , ecx
shr ecx,1
add ecx,edx
dec esi
reverse:
cmp ebp , ecx
je allDone
mov edx, eax
add eax , edi
add edx , esi
LoopMe:
mov bl, [edx]
mov bh, [eax]
mov [edx],bh
mov [eax],bl
inc edi
dec esi
cmp edi, esi
je allDone
inc ebp
jmp reverse
allDone:
pop ebp ; pop ebp out of stack
ret ; retunr the value of eax
_reverse ENDP
END
On the line close to the beginning where it reads push ebp I'm getting an error that says
invalid instruction operands
and towards the end where it reads pop ebp I'm getting an error where it says the same thing.
Not sure if this is big but I'm also getting a syntax error on the very first line of code that reads .model flat.
Based on reproducing the symptoms, I diagnose the problem as: this is 32-bit x86 assembly (clearly), but it was treated as x64 assembly, and that didn't work.
the .model directive is not valid for x64, so there is a syntax error there.
pushing and popping 32-bit registers is not encodeable in x64, so there are invalid operand errors there.
If this is in a project in Visual Studio, set the "platform" for either the whole solution or this individual project to x86/win32 (it has different names in different places, but set it to 32-bit).
I am writing an assembly function callable from C++ that will read the CPU Vendor ID. Here is the function signature:
extern "C" void GetVendorID(const char* id);
Here is how I am calling it:
char vendorID[13];
GetVendorID(vendorID);
vendorID[12] = '\0';
Here is the important parts of the assembly:
global GetVendorID
GetVendorID:
push ebp
mov ebp, esp
push eax
push ebx
push ecx
push edx
mov eax, 0
cpuid ; <- this instruction moves the vendor id into ebx, edx, ecx
mov eax, [ebp + 8] ; <- move the value of the char pointer parameter into eax
; I have verified that this instruction works by returning eax and comparing it to the
; address of the vendorID array
; start with ebx
mov byte [eax], bl ; <- move a character into the char array
inc eax ; <- increment the pointer
shl ebx, 8 ; <- shift ebx to get the next character in its least significant bits
mov byte [eax], bl ; <- repeat
inc eax
shl ebx, 8
mov byte [eax], bl
inc eax
shl ebx, 8
mov byte [eax], bl
inc eax
shl ebx, 8
; above is repeated for edx and ecx
pop edx
pop ecx
pop ebx
pop eax
mov esp, ebp
pop ebp
ret
The way the string is stored in the registers is weird. The first character is stored in the least significant byte of ebx, the next is stored in the second least significant byte, and so on. That is why I am doing the left shifts.
I have verified that ebx, edx, ecx do contain the correct values by returning them from the function and printing them out. They contain "GenuineIntel". However, the char array remains unchanged. It is full of zeroes after the function returns.
I am not really sure why this isn't working. Am I accessing the parameter incorrectly?
this is the c++ file:
#include <iostream>
using namespace std;
//This is the C prototype of the assembly function, it requires extern"C" to
//show the name is going to be decorated as _test and not the C++ way of
//doing things
extern"C"
{
//char arrayReverse(char*, int);
int numChars(char *, int);
char swapChars(char *, int);
}
int main()
{
const int SIZE = 7;
char arr[SIZE] = { 'h', 'e', 'l', 'l', 'o', '1', '2' };
int val1 = numChars(arr, SIZE);
cout << "The number of elements is: " << val1 << endl;
char val2 = swapChars(arr, SIZE);
cout << "Swapped! " << endl;
return 0;
}
and my swapChars() file:
.686
.model flat
.code
_swapChars PROC ; named _test because C automatically prepends an underscode, it is needed to interoperate
push ebp
mov ebp,esp ; stack pointer to ebp
mov ebx,[ebp+8] ; address of first array element
mov ecx,[ebp+12] ; number of elements in array
mov ebp,0
mov eax,0
mov edx,ebx ;move first to edx
add edx, 7 ;last element in the array
loopMe:
cmp ebp, ecx ;comparing iterator to total elements
jge nextLoopMe
mov eax, [ebx] ;move 1st element into tmp eax
mov ebx, [edx] ;move last element into first
mov edx, eax ;move tmp into last
push ebx ;push first element onto stack
add ebx, 1 ;first + 1
sub edx, 1 ;last - 1
add ebp, 1 ;increment
jmp loopMe
nextLoopMe:
mov ebx,[ebp+8] ;find first element again USING AS FFRAME POINTER AGAIN
cmp ebx, ecx ;comparing first element to number of elements
je allDone
pop ebx
add ebx, 1
jmp nextLoopMe
allDone:
pop ebp
ret
_swapChars ENDP
END
This is supposed to take the value in arr[0] and swap that with arr[6], arr[1] with arr[5] etc. until the entire array is swapped and then display it. I don't know if any of the code I wrote does anything I want it to, but I'm looking for a way to see what is going on.
Is there a way I can make the asm code print something to the console while iterating through the loop?
Do brackets around the register ( [ebx] ) mean the value for the register?
When in loopMe:, the third line
mov eax, [ebx]
I get an exception "Exception thrown at 0x012125FC in assignment4.exe: 0xC0000005: Access violation reading location 0xCCCCCCCD."
Am I handling the swaps correctly?
Thanks for your time.
You do really need to learn to use the debugger to step thru this. That said, here are some problems I see.
add edx,7
will point edx past the end of your array. Just like arr[7] would in the C code. It should be add edx,6 to point edx to the last character.
It's error prone to change ebp in your the middle of your proc and I think you have an error there. You change it's value, but then expect [ebp+8] to reference the same data later.
You are not modifying the list correctly either. To move a char from one element to another you would do something like:
mov al, [ebx] ; copy byte from address ebx to register al
mov [edx], al ; copy byte in register al into address edx
The eax register is 32-bits and will copy 4 bytes at a time, not 1.
First of all, your code is not safe since you forget to add \0 at the end of the char array. When you use function to process your char, or char string, it will initiate a memory leak. The size should be 8 and the last one in the array should be \0.
I am working on a project where we need to pass an array of type char as a parameter and reverse the array. I feel like I am very close to getting it done, but I am stuck on the actual swapping process.
For my swapping function in my .asm, I used the same method I would in c++ (use an unused register as a temp, then swap the front and the back.) What I am not understanding is how would I go about changing the actual content at that address. I assumed performing the following would "change" the content at the destination address:
mov eax,[edx]
However, this did not work as planned. After I ran a for loop to iterate through the array again, everything stayed the same.
If anyone can point me in the right direction, it would be great. I have provided the code below with as much comments as I could provide.
Also, I am doing all this in a single .asm file; however, my professor wants me to have 3 separate .asm document for each of the following functions: swap, reverse, and getLength. I tried to include the other 2 .asm document in the reverse.asm, but it kept giving me an error.
Assembly Code Starts:
.686
.model flat
.code
_reverse PROC
push ebp
mov ebp,esp ;Have ebp point to esp
mov ebx,[ebp+8] ;Point to beginning of array
mov eax,ebx
mov edx,1
mov ecx,0
mov edi,0
jmp getLength
getLength:
cmp ebp, 0 ;Counter to iterate until needed to stop
je setup
add ecx,1
mov ebp,[ebx+edx]
add edx,1
jmp getLength
setup: ;This is to set up the numbers correctly and get array length divided by 2
mov esi,ecx
mov edx,0
mov eax,ecx
mov ecx,2
div ecx
mov ecx,eax
add ecx,edx ;Set up ecx(Length of string) correctly by adding modulo if odd length string
mov eax,ebx
dec esi
jmp reverse
reverse: ;I started the reverse function by using a counter to iterate through length / 2
cmp edi, ecx
je allDone
mov ebx,eax ;Set ebx to the beginning of array
mov edx,eax ;Set edx to the beginning of array
add ebx,edi ;Move ebx to correct index to perform swap
add edx,esi ;Move edx to the back at the correct index
jmp swap ;Invoke swap function
swap:
mov ebp,ebx ;Move value to temp
mov ebx,[edx] ;Swap the back end value to the front
mov edx,[edx] ;Move temp to back
inc edi ;Increment to move up one index to set up next swap
dec esi ;Decrement to move back one index to set up for next swap
jmp reverse ;Jump back to reverse to setup next index swapping
allDone:
pop ebp
ret
_reverse ENDP
END
C++ Code starts:
#include <iostream>
#include <string>
using namespace std;
extern "C" char reverse(char*);
int main()
{
const int SIZE = 20;
char str1[SIZE] = { NULL };
cout << "Please enter a string: ";
cin >> str1;
cout << "Your string is: ";
for (int i = 0; str1[i] != NULL; i++)
{
cout << str1[i];
}
cout << "." << endl;
reverse(str1);
cout << "Your string in reverse is: ";
for (int i = 0; str1[i] != NULL; i++)
{
cout << str1[i];
}
cout << "." << endl;
system("PAUSE");
return 0;
}
So after many more hours of tinkering and looking around, I was finally able to figure out how to properly copy over a byte. I will post my .asm code below with comments if anybody needs it for future reference.
I was actually moving the content of the current address into a 32 bit registers. After I changed it from mov ebx,[eax] to mov bl,[eax], it copied the value correctly.
I will only post the code that I was having difficulty with so I do not give away the entire project for other students.
ASM Code Below:
swap:
mov bl,[edx] ;Uses bl since we are trying to copy a 1 byte char value
mov bh,[eax] ;Uses bh since we are trying to copy a 1 byte char value
mov [edx],bh ;Passing the value to the end of the array
mov [eax],bl ;Passing the value to the beginning of the array
inc eax ;Moving the array one index forward
dec edx ;Moving the array one index backwards
dec ecx ;Decreasing the counter by one to continue loop as needed
jmp reverse ;Jump back to reverse to check if additional swap is needed
Thanks for everyone that helped.
mov eax,[edx] (assuming intel syntax) places the 32 bits found in memory at address edx into eax. I.e, this code retrieves data from a memory location. If you'd like to write to a mem location, you need to reverse this, i.e mov [edx], eax
After playing with some 16 bit code overnight for sorting, I've the following two functions that may be of use. Obviously, you can't copy/paste them - you'll have to study it. However, you'll notice that it is able to swap items of arbitrary size. Perfect for swapping elements that are structures of some type.
; copies cx bytes from ds:si to es:di
copyBytes:
shr cx, 1
jnc .swapCopy1Loop
movsb
.swapCopy1Loop:
shr cx, 1
jnc .swapCopy2Loop
movsw
.swapCopy2Loop:
rep movsd
ret
; bp+0 bp+2 bp+4
;void swap(void *ptr1, void *ptr2, int dataSizeBytes)
swapElems:
push bp
mov bp, sp
add bp, 4
push di
push si
push es
mov ax, ds
mov es, ax
sub sp, [bp+4] ; allocate dataSizeBytes on the stack, starting at bp-6 - dataSizeBytes
mov di, sp
mov si, [bp+0]
mov cx, [bp+4]
call copyBytes
mov si, [bp+2]
mov di, [bp+0]
mov cx, [bp+4]
call copyBytes
mov si, sp
mov di, [bp+2]
mov cx, [bp+4]
call copyBytes
add sp, [bp+4]
pop es
pop si
pop di
pop bp
ret 2 * 3
This is a homework. I have 3 arrays, v1={5,4,3,2,1} ,v2={1,2,3,4,5} and v3={2,3,5,1,4}, the assigment is to change the 1 to 6. Of course, any solution like v1[4]=6, in asm or c is forbidden. So this was my code:
First Code
void main(){
int myArray[5]={5,4,3,2,1};
__asm {
mov ecx,0 //using ecx as counter
myLoop:
mov eax, myArray[ecx] //moving the content on myArray in position ecx to eax
cmp eax,1 //comparing eax to 1
je is_one //if its equal jump to label is_one
inc ecx //ecx+1
cmp ecx,5 //since all vectors have size 5, comparing if ecx is equal to 5
jne myLoop //if not, repeat
jmp Done //if true, go to label Done
is_one:
mov myArray[ecx],6 //changing the content in myArray position ecx to 6
inc ecx //ecx+1
cmp ecx,5 // ecx=5?
jne myLoop //no? repeat loop
jmp Done //yes? Done
Done:
}
printArray(myArray);
}
this didn't work, tried many things like mov eax,6 or mov [eax+ecx],6 , nothing worked until I found this solution
Many tries later code
void main(){
int myArray[5]={5,4,3,2,1};
__asm {
mov ecx,0 //using ecx as counter
myLoop:
mov eax, myArray[TYPE myArray*ecx] //I don't understand how this works
cmp eax,1 //comparing eax to 1
je is_one //if its equal jump to label is_one
inc ecx //ecx+1
cmp ecx,5 //since all vectors have size 5, comparing if ecx is equal to 5
jne myLoop //if not, repeat
jmp Done //if true, go to label Done
is_one:
mov myArray[TYPE myArray*ecx],6 //Uhh...
inc ecx //ecx+1
cmp ecx,5 // ecx=5?
jne myLoop //no? repeat loop
jmp Done //yes? Done
Done:
}
printArray(myArray);
}
And that works like a charm. But I don't understand how or why the MOV array[TYPE array * index], value works(besides TYPE returning the size as explained in link), and why not the others.
Also, since I have to do this for 3 arrays, I tried to copy and paste all the code to changingArray(int myArray[]), declared the 3 arrays in the main, and passed them to changingArray, but now is not changing them. Im pretty sure that with vector you dont have to pass with &, I could be wrong. Still, I can't see why it doesn't change them. So...
Final Code
void changingArray(int myArray[]){
__asm {
mov ecx,0 //using ecx as counter
myLoop:
mov eax, myArray[TYPE myArray*ecx] //I don't understand how this works
cmp eax,1 //comparing eax to 1
je is_one //if its equal jump to label is_one
inc ecx //ecx+1
cmp ecx,5 //since all vectors have size 5, comparing if ecx is equal to 5
jne myLoop //if not, repeat
jmp Done //if true, go to label Done
is_one:
mov myArray[TYPE myArray*ecx],6 //Uhh...
inc ecx //ecx+1
cmp ecx,5 // ecx=5?
jne myLoop //no? repeat loop
jmp Done //yes? Done
Done:
}
printArray(myArray);
}
void main(){
//for some odd reason, they arent changing
int v1[5]={5,4,3,2,1};
int v2[5]={1,2,3,4,5};
int v3[5]={2,3,5,1,4};
changingArray(v1);
changingArray(v2);
changingArray(v3);
}
TL:DR section:
Homework of changing the number 1 to 6 in 3 arrays v1={5,4,3,2,1} ,v2={1,2,3,4,5} and v3={2,3,5,1,4}
1-I don't get why the first code doesn't work, but many tries later code works (the MOV array[TYPE array * index], value instruction).
2- Since I need to do this with 3 arrays, I put all the code in changingArray(int myArray[]), and in the main I declared my 3 arrays in main as shown in final code. While many tries code did change the array, this doesnt. Probably I just made a mistake in c and not asm, but I don't see it.
And sorry for bad english, is not my first language.
mov eax, myArray[TYPE myArray*ecx]
Here the address referred to is (base address of myArray) + sizeof(the type of elements of myArray) * ecx. In assembly language the indexing should be done in bytes.