how do i simplify/condense the code (if possible)? [closed] - c

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'd like to present you my program in c and assembler code attached to his one. also, I've got some questions.
here is a piece of code in c
#include <stdio.h>
void podaj_znak(int tab[], int n);
int main()
{
int tab[7] = {4, 5, 6, 2, -80, 0, 56};
printf("Przed: ");
for (int i = 0; i < 7; i++)
printf("%d ", tab[i]);
printf("\n");
podaj_znak(tab, 7);
printf("Po: %d %d %d %d %d %d %d", tab[0], tab[1], tab[2], tab[3], tab[4], tab[5], tab[6]);
printf("\n");
return 0;
}
and asm right here
.686
.model flat
public _podaj_znak
.code
_podaj_znak PROC
push ebp
mov ebp, esp
mov edx, [ebp+8]
mov ecx, [ebp+12]
ptl:
mov eax, [edx]
cmp eax, 0
jl minus
ja plus
mov ebx, 0
mov [edx], ebx
jmp dalej
minus: mov ebx, -1
mov [edx], ebx
jmp dalej
plus: mov ebx, 1
mov [edx], ebx
jmp dalej
dalej: add edx, 4
sub ecx, 1
jnz ptl
pop ebp
ret
_podaj_znak ENDP
END
my question is, how can I simplify/condense the code?
edit: posting what the program does and what I like it to be like. it is just for me to train and to get used to assembler. the program is like you've got numbers from -inf to inf and it when the actual number is equal 0, it stays as it is, when it is something less than 0, it is replaced by -1, and when the number is more than 0, it is replaced by 1. the thing is, that I wanted to somehow optimize assembler code, but I don't know whether it is even possible to condense it.

Not really a good fit for this forum, but still:
For the C code, I'd create a PrintTab function that accepts tab and count and prints the table. Then invoke it both before and after the podaj_znak call.
For the asm code:
PLEASE add comments. I know this is probably just a class project, but still, get in the habit.
Why move [edx] to eax instead of just cmp [edx],0?
If perf matters, perhaps skip prolog/epilog and use a 'fastcall' calling convention.
Why repeat "mov [edx], ebx" for each case? Move it down to dalej.
As a 'trick' you might try checking for -1, but then handle the other 2 cases with setnz.

nasm syntax, may need subtle fixing for other asm, my solution:
; converts values in tab into [-1, 0, 1] as sgn()
; arguments: two on stack(int tab[], int n)
; modified registers: esi, edi, eax, ebx
; "no branch" version (except loop itself)
_podaj_znak:
mov esi,[esp+4] ; tab ptr
mov eax,[esp+8] ; count
xor ebx,ebx
lea edi,[esi+eax*4] ; tab.end() ptr
sgn_loop:
lodsd ; eax = [ds:esi], esi += 4
; change eax to [-1, 0, 1] by sgn(eax)
test eax,eax
setnz bl
sar eax,31
or eax,ebx
; overwrite original value with sgn() result
cmp esi,edi ; test if end of tab was reached
mov [esi-4],eax
jb sgn_loop
ret
And then for the curiosity googling Internet (just the loop part is different), 3 instructions version (my is 4):
...
; modifies also edx in this variant
sgn_loop:
lodsd ; eax = [ds:esi], esi += 4
; set edx to [-1, 0, 1] by sgn(eax)
cdq
cmp edx,eax
adc edx,ebx
; overwrite original value with sgn() result
cmp esi,edi
mov [esi-4],edx
jb sgn_loop
ret
Both variants are branch-less, so they should have superior performance to any branch variant (but I'm not going to profile it).

It is possible to optimize a little bit the assembly by calling only one time the mov [edx], ebx as follow:
ptl:
mov eax, [edx]
cmp eax, 0
jl minus
ja plus
mov ebx, 0 ; only set to 0
jmp dalej
minus: mov ebx, -1 ; only set to -1
jmp dalej
plus: mov ebx, 1 ; only set to 1
jmp dalej
dalej: mov [edx], ebx ; update the array[edx]
add edx, 4
sub ecx, 1
jnz ptl

Related

View array while debugging assembly code in visual studio

I defined a DWORD variable: arr DWORD 6 Dup(?) and tried to view this array with visual studio debugger.
I did try arr, 6 but it doesn't seem to work while debugging assembly code.
I also tried arr, arr + 1, arr + 2 separately but the result is terribly wrong.
How to view the entire array?
ReadDec_inline proc Uses eax ebx ecx esi edi, n:DWORD, pArr:DWORD
Local OldEsp:DWORD
; Type in n numbers
mov eax,10
mov ebx,n
mul ebx
add eax,n
sub eax,1; add (n-1) space characters
mov OldEsp,esp
sub esp,eax
mov esi,esp
invoke ReadString,esi,45,offset _dword
;Now we extract
mov esi,pArr
mov ecx,n
L1:
mov eax,0
W1:
.If(BYTE PTR[esp + eax] == " " || BYTE PTR[esp + eax] == 0)
mov BYTE PTR[esp+eax],0
jmp quitW1
.Else
inc eax
.EndIf
jmp W1
quitW1:
mov edi,esp
mov ebx,eax
invoke ReadDec, edi, 1
mov [esi],eax ; Fetch the number
add esi,4
add esp,ebx
inc esp ; Next number
loop L1
mov esp,OldEsp
ret
ReadDec_inline endp
proc Uses eax ebx ecx esi edi
Because of the mul ebx instruction, you should add EDX to the list. The result of this multiplication will be in EDX:EAX.
Or better, change
mov eax,10
mov ebx,n
mul ebx
add eax,n
sub eax,1; add (n-1) space characters
into
imul eax, n, 10 + 1
dec eax
invoke ReadString,esi,45,offset _dword
Shouldn't this hardcoded 45 be actually 65? Same as the calculated value?
Your L1 loop depends on the ECX register. I don't think that ECX is a call-preserved register, so probably invoke ReadDec, edi, 1 messes up the count.
mov esi, pArr
mov ecx, n
L1:
mov edi, esp
push ecx ; (1)
xor ebx, ebx
W1:
.If(BYTE PTR[edi + ebx] == " " || BYTE PTR[edi + ebx] == 0)
mov BYTE PTR[edi + ebx], 0
jmp quitW1
.Else
inc ebx
.EndIf
jmp W1
quitW1:
invoke ReadDec, edi, 1
mov [esi], eax ; Fetch the number
add esi, 4
pop ecx ; (1)
add esp, ebx
inc esp ; Next number
dec ecx
jnz L1
I defined a DWORD variable: arr DWORD 6 Dup(?) and tried to view this array with visual studio debugger.
Your invoke ReadDec_inline, 3, offset arr asks for just 3 array elements and you can see their values in the Memory 1 pane. I sincerely thought the issue here was not having specified the whole 6.
However, it fails in the Watch 1 pane because Visual Studio allows you to write expressions in the Watch windows. This means that arr, arr+1, arr+2, and arr+4 are expressions that first retrieve the value at arr (the first array element which has the value 13), and then have some constant added.
I don't use Visual Studio myself, but for MASM you could try:
arr[0] or just arr
arr[4]
arr[8]
or
[arr+0] or just arr
[arr+4]
[arr+8]

Assembly GCD Coding Infinite Loop

I am trying to make an assembly program for finding GCD, takes in two integers, and prints out the GCD. The code assembles fine, however the program gets stuck in an infinite loop after both integers have been submitted:
;Title - GCD
INCLUDE Irvine32.inc
.data
strA BYTE "Enter an integer A: ",0
strB BYTE "Enter an integer B: ",0
temp DWORD ?
finalStr BYTE "GCD of the two integers is: ",0
.code
main PROC
call Clrscr
mainLoop:
mov edx,OFFSET strA
call WriteString
call ReadInt
mov temp, eax
call Crlf
mov edx, OFFSET strB
call WriteString
call ReadInt
mov ebx, eax
mov eax, temp
call Crlf
call GCD
mov edx, OFFSET finalStr
call WriteString
call WriteInt
call WaitMsg
jmp mainLoop
main ENDP
;-----------------------------------------------
abs PROC
; Computes the absolute value of a number.
; Receives: eax = the number
; Returns: eax = absolute value of the number
;-----------------------------------------------
cmp eax, 0 ; see if we have a negative number
jge done
neg eax
done:
ret
abs ENDP
;-----------------------------------------------
gcd PROC
; Finds Greatest Common Divisor of two integers
; Recieves: eax= int A , ebx = int B
; Returns eax = GCD
;-----------------------------------------------
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov eax, temp
cmp eax, ebx ; making sure we divide the bigger number by the smaller
jz DONE ; if numbers are equal, GCD is eax either way
jc SWITCH ;swaps if ebx is larger then eax
mov edx, 0
SWITCH: ;swaps values so eax is larger then ebx
mov temp, eax
mov eax, ebx
mov ebx, temp
mov edx, 0
jmp L1
L1: ;divides until remainder is 0, then eax is GCD
div ebx
cmp edx, 0
jz DONE
mov eax, edx
jmp L1
DONE:
gcd ENDP
END main
How can I get out of this loop?
I see one problem straight up:
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov eax, temp
There's nothing there moving the abs-ed ebx value back into ebx, it should be:
call abs ;takes absolute value of both registers
mov temp, eax
mov eax, ebx
call abs
mov ebx, eax
mov eax, temp
Another issue, though not directly related to your problem, is that the x86 architecture has long had an xchg instruction that would greatly clean up your code, specifically the use of temp.
And think about this sequence:
cmp eax, ebx ; making sure we divide the bigger number by the smaller
jz DONE ; if numbers are equal, GCD is eax either way
jc SWITCH ;swaps if ebx is larger then eax
mov edx, 0
SWITCH: ;swaps values so eax is larger then ebx
You'll find that the code at SWITCH: is going to be run regardless of which value is larger.
You can fix that by changing:
jc SWITCH ; swaps if ebx is larger then eax
mov edx, 0
into:
jc SWITCH ; swaps if ebx is larger then eax
mov edx, 0
jmp L1
Beyond that, I'm going to suggest actually running that code in your head to get an understanding of how it works but, more importantly, how to think like a machine in such a way that you become a better developer.
Start with the following table:
[temp] eax ebx edx <stack>
-------- -------- -------- -------- --------
? ? ? ? ?,
Then execute each line in turn, filling in the columns as data changes.
You'll eventually figure out where the problem is coming from and you'll also understand why sometimes the older guys that only ever had this means to debug are much better at it :-)
I'll give you a clue. Keep a particular eye on edx and see how that's changing, and how it's likely to affect certain other instructions, like div, that may depend on it being zero.

I am dealing with a possible array in assembly, but I cannot figure out what the starter value is

Size contains the number 86.
var_10= dword ptr -10h
var_C= dword ptr -0Ch
size= dword ptr 8
push ebp
mov ebp, esp
sub esp, 28h
mov eax, [ebp+size]
mov [esp], eax ; size
call _malloc
mov ds:x, eax
mov [ebp+var_C], 0
jmp short loc_804889E
loc_804889E: ~~~~~~~~~~~~~~~~~~~~~
mov eax, [ebp+size]
sub eax, 1
cmp eax, [ebp+var_C]
jg short loc_8048887
loc_8048887: ~~~~~~~~~~~~~~~~~~~~~
mov edx, ds:x
mov eax, [ebp+var_C]
add edx, eax
mov eax, [ebp+var_C]
add eax, 16h
mov [edx], al
add [ebp+var_C], 1
I am having difficulties reversing this portion of a project I am working on. There's a portion of the code where ds:x is moved into edx and is added with var_c and I am unsure where to go with that.
To me the program looks like it calls malloc and then moves that into ds:x and then moves 0 to var_c.
After that it simply subtracts 1 from the size of my pointer array and compares that number to 0, then jumps to a portion where it adds ds:x into edx so it can add eax to edx.
Am I dealing with some sort of array here? What is the first value that's going to go into edx in loc_8048887? Another way this could help would be to see a C equivalent of it... But that would be what I am trying to accomplish and would rather learn the solution through a different means.
Thank you!
In x86 assembly there's no strict distinction between a variable stored in memory and an array in memory. It only depends on how you access the memory region. All you have is code and data. Anyway, I'd say that ds:x is an array as because of this code here:
mov edx, ds:x ; edx = [x]
mov eax, [ebp+var_C] ; eax = something
add edx, eax ; edx = [x] + something
mov eax, [ebp+var_C] ; eax = something
add eax, 16h ; eax = something + 0x16
mov [edx], al ; [[x] + something ] = al . Yes, ds:x is an array!
What is the value of edx in loc_8048887? To find it out you only need some very basic debugging skills. I assume you have gdb at hand, if not, get it ASAP. Then compile the code with debug symbols and link it, then run gdb with the executable, set a code breakpoint at loc_8048887, run the program with r, and finally check the value of edx.
These are the commands you need:
gdb myexecutable
(gdb) b loc_8048887
(gdb) r
(gdb) info registers edx

Intel 8086 Insertion Sort: Skipping numbers within the Array

So we're currently studying Intel 8086 Insertion Sort Code that our professor showed us. He wanted us to figure out why the code skips the 0th element within the array and the 3rd element in the array from a code that he had taken from the web.
; void isort(int *a, int n)
; sorts the first n elements of a
;
; Parameters
; a - pointer to the array
; n - number of elements to sorts
%define a [ebp + 8]
%define n [ebp + 12]
isort:
enter 0, 0
pusha
mov ecx, 1
for:
mov ebx, ecx
imul ebx, 4
add ebx, a
mov ebx, [ebx]
mov edx, ecx
dec edx
while:
cmp edx, 0
jl while_quit
mov eax, edx
imul eax, 4
add eax, a
cmp ebx, [eax]
jge while_quit
mov esi, [eax]
mov dword [eax + 4], esi
dec edx
jmp while
while_quit:
mov [eax], ebx
inc ecx
cmp ecx, n
jl for
popa
leave
ret
And the sample array was {5, 8, 12, 2, 1, 7}. This is more for understanding the 8086 language since we just started a couple days ago, and I was wondering if anyone could explain how and what might be going wrong.
Consider what the code will do when ECX is 1:
The while loop will be entered with EBX=8 and EDX=0.
The jl while_quit will not be taken, since EDX is 0.
EBX is compared to [EAX]. That is; 8 is compared to a[0], which is 5, so the jge while_quit is taken.
mov [eax], ebx stores 8 at a[0], so your array now contains {8,8,12,2,1,7}. Clearly not what you want, since you've lost one of the original elements.
Apart from the code's logical flaw, those imul instructions are completely unnecessary since you can use scaled indices on the x86. So the sorting code can be simplified to this (I've verified that it sorts the array correctly):
mov ecx, 1
for_:
; esi = &a[holePos]
lea esi,[a + ecx*4]
; valueToInsert = a[holePos]
mov ebx, [esi]
while_:
; Did we reach the beginning of the array?
cmp esi, OFFSET a
jle while_quit
; valueToInsert < a[holePos-1] ?
cmp ebx, [esi-4]
jge while_quit
; a[holePos] = a[holePos-1]
mov eax, [esi-4]
mov [esi], eax
; esi = &a[holePos-1]
sub esi,4
jmp while_
while_quit:
; a[holePos] = valueToInsert
mov [esi], ebx
inc ecx
cmp ecx, 6
jl for_
I'm using MASM syntax, but you get the idea.

Double Type Matrix [n]x[n] transfered to ASSEMBLY

I would like some help with operation on matrix in assembly language.
My code does Cholesky Decomposition in C and ASM and compare their speed.
I created nested loops already and its working fine, yest i have no idea how to adress properly matrix to access its elements. Matrix is DOUBLE in C and i managed to transer its adress (first element) to assembly.
Cholesky_double proc \
tab_addr:DWORD, \ ; begin adres of matrix
num_elem:DWORD ; element count in row/column (n of Matrix[n][n])
LOCAL i:DWORD, k:DWORD, j:DWORD, skoczek:DWORD
;skoczek is for operation count check (ex.should be 13 for 3x3 matrix)
; push register on stack
push edx
push ecx
push ebx
push esi
push edi
mov k, 0
mov skoczek, 0
for0start:
inc skoczek
mov eax, k
mov i, eax
inc i
;there should be MATRIX[k][k] = sqrt(MATRIX[k][k])
mov eax, num_elem
sub eax, i
cmp eax, 0
je for1end
for1start:
inc skoczek
;MATRIX[i][k]=MATRIX[i][k]/MATRIX[k][k]
for1koniec:
inc i
mov eax, num_elem
sub eax, i
cmp eax, 0
jne for1start
for1END:
mov eax, k
mov j, eax
inc j
mov eax, num_elem
sub eax, j
cmp eax, 0
je for2end
for2start:
inc skoczek
mov eax, j
mov i, eax
for3start:
inc skoczek
;MATRIX[i][j] = MATRIX[i][j]-MATRIX[i][k]*MATRIX[j][k]
for3koniec:
inc i
mov eax, num_elem
sub eax, i
cmp eax, 0
jne for3start
for2koniec:
inc j
mov eax, num_elem
sub eax, j
cmp eax, 0
jne for2start
for2end:
for0koniec:
inc k
mov eax, num_elem
sub eax, k
cmp eax, 0
jne for0start
koniec:
pop edi
pop esi
pop ebx
pop ecx
pop edx
mov eax, skoczek
ret
; return with operation count in eax
Cholesky_double endp
The matrix passed in C with
extern "C" int __stdcall Cholesky_double(double* tab_adr, int num_el);
I use Visual Studio 2010 and solution with project that create ASSEMBLY library and project with code in C++ that can use assembler functions.
I am not asking for filling code for me, just for a little bit help with correct adressing of matrix to properly access its elemets. If you foresee more problems coming here (like Sqrt in asm i would be pleased with some guidance.
You first have to linearize the address:
&matrix[k][i] = matrix + i*sizeof(double) + k*N*sizeof(double);
where N is the row width. (Assuming NxN matrix)
That can be loaded with
fld [%eax] // load to top of stack in FPU (assuming ia-32 system)
mov %rbx,[%rax]; // vs. load 64-bit register
movsd %xmm0, [%rax] // vs. load a double to lower 64-bits of xmm register

Resources