Traversing and averaging elements in an array MASM - c

I am in a x86 masm class, and we have a project coming up that I do not understand. The TA and Professor are pretty much useless, I've been trying to get into contact with them for a week and no answer and no office hours. Anyways, I am supposed to calculate the average number of days per month over the span of 15 years. (2000-2015) We are also supposed to factor in leap year.
I wrote the program in C beforehand, and here is my code:
main()
{
double sum=0;
int dpm[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
int i=0;
int j=0;
int months=0;
while(j<16)
{
while(i<12)
{
if(j==0 || j==4 || j==8 || j==12)
{
dpm[1]=29;
}
sum+=dpm[i];
dpm[1]=28;
months++;
i++;
}
j++;
}
printf("%.2f",sum/months);
}
(I realize using a for-loop would be best here, however while loops are easier to do in asm, so I used a while loop)
I don't understand assembly too well, but here is what I have so far:
.DATA
sum DWORD 0
months DWORD 0
dpm DWORD 31,28,31,30,31,30,31,31,30,31,30,31
elm DWORD 12
i WORD 0
j WORD 0
.CODE
main
mov eax, sum ; move sum to eax
lea esi, dpm ; move array to ebx
mov ecx, elm ; move no of elements to ecx
mov ax, j ; move j to ax
mov bx, i ; move i to bx
outterLoop:
cmp 15, ax ; compare 15 to i
jle innerloop ; jump to inner loop if it is less
innerLoop:
cmp 11, bx ; compare 11 to j
jle checkLeap ; jump to checkleap if it is less
inc ax ; incremement j
checkLeap:
cmp 0, bx ; if j is 0
je leap ; go to leap year function
cmp 4, bx
je leap
cmp 8, bx
je leap
cmp 12, bx
je leap
jne updates ; if not, go to updates fn
leap:
add esi, 4 ; go to second element in array "february"
mov 29, esi ; make feb 29 instead of 28
updates:
add eax, [esi] ; add element to sum
add esi, 4 ; increment array
; inc months
mov ; here I would make second element of array 28
inc bx ; increment i
I do not know if I am on the right track, and would like guidance, advice. Thank you

When comparing with an immediate value you need to put the number last. (Correct it 7 times)
cmp ax, 15 ; compare 15 to i
To change the length of february in the list use
mov dword [esi], 29 ; make feb 29 instead of 28
Same thing to reset it
mov dword [esi], 28 ; here I would make second element of array 28
Since you have put your j index in the AX register you need to place the sum in a register different from EAX. Remember that AX is just the low 16 bits of EAX.
The next code needs an additional jump. Now it ALWAYS performs innerLoop
cmp ax,15 ; compare 15 to i
jle innerloop ; jump to inner loop if it is less
innerLoop:

this code is working and giving correct output in edx register.
comment below if you want me to add comments to this code.
i implemented your logic with necessary changes, the thing is you are implementing that incorrectly
Include irvine32.inc
.data
; (31==1Fh)(30==1Eh)(28==1Ch)(29==1Dh)
dpm dword 1Fh,1Ch,1Fh,1Eh,1Fh,1Eh,1Fh,1Fh,1Eh,1Fh,1Eh,1Fh
countj dword ?
temp dword ?
temp1 dword ?
.code
Main Proc
; [instr] [destor opp1] [addtio instr] [source orOpp2] ;comment
mov ecx, 15 ;intializing to max value of j
mov esi, offset dpm ;storing offset of dpm to esi
mov edx, 0 ;intializing sum to 0
mov ebx, 0 ;intializing counter to 0
mov eax, 0 ;intializing counter to 0
loopj: ;loop of j
mov countj, ecx ;storing count of j
mov ecx, 12 ;intializing to max value of i
loopi: ;loop of i
inc ebx
mov eax, countj
;start of if statement:
cmp eax, 1 ;jump if j is 1
je IfJConTrueStart
cmp eax, 5 ;jump if j is 5
je IfJConTrueStart
cmp eax, 9 ;jump if j is 9
je IfJConTrueStart
cmp eax, 13 ;jump if j is 13
je IfJConTrueStart
;29
JConMid:
call dumpregs
mov temp1, edx
mov eax, ecx
mov temp, type dpm
mul temp
add esi, eax
sub esi, temp
call dumpregs
mov edx, temp1
add edx, [esi]
call dumpregs
sub esi, eax
add esi, temp
call dumpregs
JConEnd:
loop loopi
mov ecx, countj
loop loopj
jmp skipIfJConTrue
IfJConTrueStart:
cmp ecx, 2
je IfJConTrueMid
jmp JConMid
IfJConTrueMid:
add edx, 1Dh
jmp JConEnd
skipIfJConTrue:
call dumpregs
Exit
Main endp
End main

Related

assembly - Issue with permutations generation

I'm trying to list all the permutations of the first N natural numbers (N <= 6) with MSVC Assembly 8086 inline. I can't change anything of the C program, I can only manage the assembly code.
So this is what I've done (and works with N <= 3), using the lexicographic algorithm:
#include <stdio.h>
int main() {
// Variabili
int N=4; // N <= 6
int Perm[4326]; // permutations array: the dimension is sufficient for N <= 6
int Num; // At the end it must contains the number of generated permutations
// Assembler block
__asm
{
XOR EAX, EAX ; Reset EAX (permutations counter)
MOV ECX, N ; Put N into ECX
CMP ECX, 1 ; Compare ECX and 1
JBE Piccolo ; If ECX <= 1 go to Piccolo (particular cases 1 and 0)
DEC ECX ; Decrement ECX (so ECX = N - 1)
MOV EAX, N ; Put N into EAX
MaxPerm:
MUL ECX ; Do EDX:EAX = EAX * ECX
LOOP MaxPerm ; Loop to find permutations number
MOV Num, EAX ; Move EAX (permutations number) into Num
MOV EAX, 1 ; Set EAX (permutations number) to 1
MOV EBX, 0 ; Reset EBX (EBX = 0)
MOV ECX, 1 ; Reset ECX
CicloIniziale:
DEC ECX ; Decrement ECX (to compare it with N)
CMP ECX, N ; Compare ECX and N
JE Permutazione ; If ECX == N the first permutation has been written, skip to the generation of the next ones
INC ECX ; Else increment ECX
MOV Perm[4*EBX], ECX ; Insert ECX into the Perm array
INC ECX ; Increment ECX
INC EBX ; Increment EBX
JMP CicloIniziale ; Continue the loop
Permutazione:
XOR EAX, EAX ; Local index
XOR EBX, EBX ; Previous permutation index
MOV ECX, N ; Current permutation index
MOV EDI, 1 ; Value of k
MOV ESI, 1 ; New position of k
CicloPermutazione:
CMP EAX, N ; Compare ECX and EAX
JE ResetPerm ; If the end of the permutation has been reached, go to ResetPerm
MOV EDX, Perm[4*EBX] ; Otherwise save the element of the previous permutation in position EBX in EDX
CMP EDX, EDI ; Compare EDX and EDI
JE ValoreUguale ; If they are equal, then go to ValoreUguale
CMP EAX, ESI ; Otherwise compare EBX and ESI
JE InserisciElSpostato ; If it's time to move the item, go to InserisciElSpostato
MOV Perm[4*ECX], EDX ; Returns the value of the previous permutation to the new permutation
ContinuaCiclo:
INC EBX ; Increment the previous permutation index
INC EAX ; Increment the local index
INC ECX ; Increment the new permutation index
JMP CicloPermutazione ; Continue the loop
InserisciElSpostato:
MOV Perm[4*ECX], EDI ; Put EDI in the new permutation
MOV EDX, N ; EDX = N
DEC EDX ; EDX -= 1
CMP ESI, EDX ; Compare ESI and EDX
JE ResettaK ; If ESI == EDX go to ResettaK
JMP ContinuaCiclo ; Continue the loop
ValoreUguale:
CMP EAX, 0 ; Confronta EAX e 0
JZ PrimaPos ; If EAX == 0 (first position) go to PrimaPos
MOV EDX, Perm[4*EBX+4] ; Else save the element into position EBX - 1
MOV Perm[4*ECX], EDX ; Insert EDX in the new permutation
JMP ContinuaCiclo ; Continue the loop
PrimaPos:
MOV EDX, Perm[4*EBX+4] ; Save into EDX the element in position ECX + 1
MOV Perm[4*ECX], EDX ; Insert EDX into the new permutation
JMP ContinuaCiclo ; Continue the loop
ResettaK:
XOR ESI, ESI ; Reset ESI
MOV EDI, Perm[4*EDI] ; EDI = Perm[EDI]
JMP ContinuaCiclo ; Continue the loop
ResettaKN:
CMP EDI, N ; Compare EDI and N
JNE ContinuaCiclo ; if EDI != N then continue the loop
JMP ResettaK ; Else go to ResettaK
ResetPerm:
MOV EAX, Num ; EAX = Num
MUL DWORD PTR N ; EAX *= N (Elements of number of all the permutations)
CMP EAX, ECX ; Compare EAX and ECX
JE Fine ; If EAX == ECX (permutations done) then finish the program
XOR EAX, EAX ; Otherwise reset EAX
INC ESI ; Increment k index
JMP CicloPermutazione ; Continue the loop
Piccolo: ; If N = 0 or N = 1, the only permutation that can exists is made by only 1 element
MOV Num, 1
MOV Perm[0], ECX
Fine:
}
// Print
{
int i,j,k;
printf("%d permutations of the first %d natural numbers\n", Num, N);
for (i=k=0;i<Num;i++)
{
for (j=0;j<N;j++)
{
printf("%3d",Perm[k++]);
}
printf("\n");
}
}
return 0;
}
The issue is that this program works only for N <= 3 and I didn't found the issue!
These are the outputs I get:
N = 3 and below works fine:
6 permutations of the first 3 natural numbers
1 2 3
2 1 3
2 3 1
3 2 1
3 1 2
1 3 2
N = 4 (the same with 5 and 6):
24 permutations of the first 4 natural numbers
1 2 3 4
2 1 3 4
2 3 1 4
2 3 4 1
3 2 4 1
3 4 2 1
3 4 1 2
4 3 1 2
4 1 3 2
4 1 2 3
1 4 2 3
1 2 4 3
1 2 3 4
1 3 3 4
1 3 2 4
1 3 4 2
1 4 4 2
1 4 3 2
1 4 2 3
1 2 2 3
1 2 4 3
1 2 3 4
1 3 3 4
1 3 2 4
As you can see after the last permutation of the first cycle the next ones are messed up...
Can you help me?
Thanks
You have written several lines where the code does not match the accompanying comment. If I were you I would suspect to find possible errors there.
CMP EAX, N ; Compare ECX and EAX
CMP EAX, ESI ; Otherwise compare EBX and ESI
MOV EDX, Perm[4*EBX+4] ; Else save the element into position EBX - 1
MOV EDX, Perm[4*EBX+4] ; Save into EDX the element in position ECX + 1
This would be the code that matches the comments:
cmp ecx, eax
cmp ebx, esi
mov edx, Perm[4*ebx - 4] ; 'save' == 'save into EDX' (*)
mov edx, Perm[4*ecx + 4]
I'm not saying that these will solve the problem because maybe your comments are wrong and your code is correct.
(*) Instead of the very confusing phrase "save into EDX", better write it as "load EDX with/from".
You have managed to write lots of redundant comments.
Below is a small portion of these:
MOV EAX, N ; Put N into EAX
MOV EBX, 0 ; Reset EBX (EBX = 0)
CMP ECX, 1 ; Compare ECX and 1
INC ECX ; Increment ECX
INC EBX ; Increment EBX
MOV EDX, N ; EDX = N
DEC EDX ; EDX -= 1
CMP ESI, EDX ; Compare ESI and EDX
JE ResettaK ; If ESI == EDX go to ResettaK
Do yourself a favor and stop writing these redundant comments. They make it much harder for anyone (including yourself) to (try to) understand the code.
A recent blog post deals with the subject of writing code comments: https://stackoverflow.blog/2021/07/05/best-practices-for-writing-code-comments/
In the end, I've managed to write a C program that does the job using the algorithm fo the next_permutation function of the C++ STD lib. Then, I've translated the C code into assembly with the help of Godbolt (online compiler).
So:
Write the C program using the next_permutation function: https://godbolt.org/z/7avGaWfha
Flatten all the functions in the main: https://godbolt.org/z/7z9MGWefK
Finally, with the great help of Godbolt, translate it to assembly

Sorting through an array after filling in x86 MASM assembly language

can anyone help me with this problem, I'm new to assembly language and somewhat stuck on what to do next. Here's the code:
.data
evenStart dword 11h
oddStart dword 20h
darray dword 15 dup (?)
.code
main PROC
mov esi, OFFSET darray
xor ecx, ecx
L1:
mov ebx, OFFSET evenStart
test cl,1
jz iseven
mov ebx, OFFSET oddStart
iseven:
mov eax, [ebx]
inc dword ptr [ebx]
mov dword ptr [esi + 4*ecx],eax
inc ecx
cmp ecx,15
jb L1
exit
main ENDP
END main
So the project requires me to fill the uninitilized array, which I did. But then it also ask me to sort through this array in descending order and then put the middle elements of the array into the eax register and call DumpRegs. This is the part where I got stuck in. Any help on how to proceed would be great. Thank you!
Next Bubble Sort uses nested loops. Because your array has 15 elements, the outer loop can do 14 comparisons during its 1st iteration. With each iteration of the outer loop it has to do 1 comparison less because the smallest element has bubbled towards the end of the array.
mov ebx, 15-1 ; Outer loop iteration count
OuterLoop:
mov esi, offset darray
mov ecx, ebx ; Inner loop iteration count
InnerLoop:
lodsd
mov edx, [esi]
cmp eax, edx
jge Skip
mov [esi-4], edx ; Swap these 2 elements
mov [esi], eax
Skip:
dec ecx
jnz InnerLoop
dec ebx
jnz OuterLoop
The unsorted array:
11h, 20h, 12h, 21h, 13h, 22h, 14h, 23h, 15h, 24h, 16h, 25h, 17h, 26h, 18h
The sorted array:
26h, 25h, 24h, 23h, 22h, 21h, 20h, 18h, 17h, 16h, 15h, 14h, 13h, 12h, 11h
In this array with an odd number of elements (15), the element indexes range from 0 to 14. There is a true middle element at index 7.

Shellsort: Write NASM code from C

I am trying to convert a C function to NASM in 32-bit.
Here is the C function
void shellsort (int *A, int n)
{
int gap, i, j, temp;
for (gap = n/2; gap > 0; gap /=2)
{
for (i= gap; i<n; i++)
{
for (j=i-gap; j>=0 && A[j] > A[j+gap]; j-=gap)
{
temp = A[j];
A[j] = A[j+gap]
A[j+gap] = temp;
}
}
}
}
Here is what I have done so far
%include "asm_io.inc"
SECTION .data
SECTION .bss
SECTION .text
extern printf
global sort
sort:
push ebp ;set up stack frame
mov ebp, esp
sub esp, 0x10 ;assign 16byte space for local variables
firstloop:
mov eax, [ebp+8] ;moves value of n into eax
mov edx, 0 ;prepares for division, remainder init
mov ecx, 2 ;divisor
div ecx ;division, store gap in eax
cmp eax, 0 ;compare if gap with zero
jle firstloopDone ;firstloopDone
firstloopDone:
div ecx
jmp done
secondloop:
mov ecx, eax ;copy value of gap into ecx, i=gap
cmp ecx, [ebp+8] ;compare i with 1st parameter n
jge secondloopDone ;jump to secondloop if greater-equal
inc ecx ;increment i
jmp thirdloop ;jump to thirdloop
secondloopDone:
inc ecx ;increment i
jmp firstloop ;jump to firstloop
thirdloop:
mov edx, ecx ;save i value
sub ecx, eax ;subtract gap from i and store in ecx
cmp ecx, 0 ;compare j with zero
jl thirdloopDone ;if not j>=0, then skip to end of loop
cmp [ebp+12+ecx], [ebp+12+ecx+eax] ;compare A[j] and A[j+gap]
jle thirdloopDone
sub ecx, eax ;subtract gap from j and store in ecx
jmp swap
thirdloopDone:
sub ecx, eax ;j-gap
jmp secondloop ;jump to second loop
swap:
mov edx, [ebp+12+ecx] ;copy A[j] to temp
mov [ebp+12+ecx], [ebp+12+ecx+eax] ;A[j]=A[j+gap]
mov [edp+12+ecx+eax], edx ;A[j+gap]= temp
jmp thirdloop
done:
leave ;destroy stack frame
ret
Needless to say, it doesn't work.
The error message says:
"error: beroset-p-603-invalid effective address"
at line 58 (thirdloop)
cmp [ebp+12+ecx], [ebp+12+ecx+eax]
and at line 71 (swap)
mov edx, [ebp+12+ecx]
I understand that this may be the wrong method. But from the nested loop in C code, I have too many variables to keep so I cant spare any of the registers.
Also, I suspect I may not have a proper understanding of the stack frame, which may show in my code.
Any help, or bug-discovery, would be greatly appreciated. Thanks.
You say you don't have enough registers to play with but you don't even use EBX, ESI, nor EDI. You made provisions for local variables yet you don't use any.
firstloop:
mov eax, [ebp+8] ;moves value of n into eax
mov edx, 0 ;prepares for division, remainder init
mov ecx, 2 ;divisor
div ecx ;division, store gap in eax
The previous code does not need the division. Simplify it with a shift operation. shr eax,1
The next code will always terminate. Use jle done
cmp eax, 0 ;compare if gap with zero
jle firstloopDone ;firstloopDone
firstloopDone:
div ecx
jmp done
Aren't parameters pushed from right to left?
mov ebx, [ebp+8] ;pointer to array A
firstloop:
mov eax, [ebp+12] ;number of elemnets n
You would typically have to code
swap:
mov edx, [ebx+ecx*4] ;TEMP=A[j]
add ecx,eax
xchg edx, [ebx+ecx*4] ;Swap A[j+gap] with TEMP
sub ecx, eax
mov [ebx+ecx*4], edx ;A[j]=TEMP
jmp thirdloop

Finding Smallest Number in NASM 2D Array

So I have the following code that worked for finding the LARGEST number in any given n by m matrix.
segment .bss
num: resw 1 ;For storing a number, to be read of printed....
nod: resb 1 ;For storing the number of digits....
temp: resb 2
matrix1: resw 200
m: resw 1
n: resw 1
i: resw 1
j: resw 1
buff resb 4
segment .data
msg1: db "Enter the number of rows in the matrix : "
msg_size1: equ $-msg1
msg2: db "Enter the elements one by one(row by row) : "
msg_size2: equ $-msg2
msg3: db "Enter the number of columns in the matrix : "
msg_size3: equ $-msg3
msg4: db "The smallest Number is... : "
msg_size4: equ $-msg4
tab: db 9 ;ASCII for vertical tab
new_line: db 10 ;ASCII for new line
segment .text
global _start
_start:
mov eax, 4
mov ebx, 1
mov ecx, msg1
mov edx, msg_size1
int 80h
mov ecx, 0
call read_num
mov cx, word[num]
mov word[m], cx
mov eax, 4
mov ebx, 1
mov ecx, msg3
mov edx, msg_size3
int 80h
mov ecx, 0
call read_num
mov cx, word[num]
mov word[n], cx
mov eax, 4
mov ebx, 1
mov ecx, msg2
mov edx, msg_size2
int 80h
;Reading each element of the matrix........
mov eax, 0
mov ebx, matrix1
mov word[i], 0
mov word[j], 0
i_loop:
mov word[j], 0
j_loop:
call read_num
mov dx , word[num]
;eax will contain the array index and each element is 2 bytes(1 word) long
mov word[ebx + 2 * eax], dx
inc eax ;Incrementing array index by one....
inc word[j]
mov cx, word[j]
cmp cx, word[n]
jb j_loop
inc word[i]
mov cx, word[i]
cmp cx, word[m]
jb i_loop
; read out matrix code
xor esp, [matrix1]
; esp initialized to first element in array, & is first smallest value
;Loop through the matrix, check each number if its
;larger than the first number in the array. AT the end print said number.
;Reading each element of the matrix.(Storing the elements in row major order).......
mov ebp, 0
mov edi, matrix1
mov esp, 0
mov word[i], 0
mov word[j], 0
i_loop2:
mov word[j], 0
j_loop2:
;eax will contain the array index and each element is 2 bytes(1 word) long
mov dx, word[edi+2*ebp] ;
mov word[num] , dx
cmp esp, [num] ; compares current smallest number with current element iterated.
jle skip
mov esp, [num] ; stores new smallest number
mov esi, ebp ; Stores pointer to the smallest element (never used..?)
skip:
inc ebp
inc word[j]
mov cx, word[j]
cmp cx, word[n]
jb j_loop2
inc word[i]
mov cx, word[i]
cmp cx, word[m]
jb i_loop2
;outut
mov eax, 4
mov ebx, 1
mov ecx, msg4
mov edx, msg_size4
int 80h
mov ecx, 0
mov eax, 4 ; system_write
mov ebx, 1 ; stdout
mov ecx, esp ; move smallest element to accumulator
add ecx, 48 ; convert to ascii representation
mov [buff], ecx ; move to memory
mov ecx, buff
mov edx, 4 ; size, 4 bytes
int 80h
exit:
mov eax, 1
mov ebx, 0
int 80h
;Function to read a number from console and to store that in num
read_num:
pusha
mov word[num], 0
loop_read:
mov eax, 3
mov ebx, 0
mov ecx, temp
mov edx, 1
int 80h
cmp byte[temp], 10
je end_read
mov ax, word[num]
mov bx, 10
mul bx
mov bl, byte[temp]
sub bl, 30h
mov bh, 0
add ax, bx
mov word[num], ax
jmp loop_read
end_read:
popa
ret
I've identified the problem is likely mov esp, 0 but without that line the results are even worse, with it the answer is always '0'. Without it it result in either nothing or gibberish.
Again, it original works for finding the LARGEST number but now I want to find the smallest, logically this should've meant just changing jge to jle but either it keeps zero (likely because of mov esp, 0 ) or gibberish if I try to make it something else?
How can I fix this so I do find the smallest number with the least amount of changes? The code did originally work albeit only after significant trial and error almost at random with my fiddling.
Additionally the gibberish outputted is this: â½µÿ
If I remove mov esp, 0 but keep it as jge, it still works for the LARGEST number, but if I switch it to jle, it outputs â½µÿ. This makes zero sense.

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.

Resources