assembly - Issue with permutations generation - c

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

Related

NASM: Convert multi character input to decimal

I am trying to write a program that gets a number with one or two digits and write Hello! as many times as that number.
I used this posts to write my code:
NASM: The loop turns into an infinite loop
Check null character in assembly language
Multi-Digit Input from NASM I did not understand this :)
But my code only works for two digit numbers and the result for single digit numbers are wrong.
My code:
section .data
msg db 'Hello!',0xA
len equ $-msg
section .bss
n resb 2
section .text
global _start
_start:
;get n
mov edx, 2
mov ecx, n
mov ebx, 0
mov eax, 3
int 0x80
lea esi,[n] ;address of our text
call toint
;loop for print 'Hello!\n'
print_loop:
push ecx
mov edx, len
mov ecx, msg
mov ebx, 1
mov eax, 4
int 0x80
pop ecx
loop print_loop
mov eax, 1
int 0x80
toint:
push eax
push ebx
xor ebx, ebx
next_digit:
movzx eax, byte[esi]
sub al , '0'
imul ebx, 10
add ebx, eax
inc esi
cmp byte [esi] , 0x0 ;check next character is null or not
jne next_digit
; end_loop:
mov ecx, ebx
pop eax
pop ebx
ret
The sys_read call returns in EAX the count of characters that were sent to your inputbuffer. Because you allowed for an input of max. 2 characters, this count will be either 0, 1, or 2. You could use this info in your toint routine.
; IN (eax,esi) OUT (ecx)
toint:
mov ecx, eax ; Number of digits
jecxz done
push eax ; (1)
push ebx ; (2)
push esi ; (3)
xor ebx, ebx
next:
movzx eax, byte [esi]
sub al , '0'
imul ebx, 10
add ebx, eax
inc esi
dec ecx
jnz next
mov ecx, ebx
pop esi ; (3)
pop ebx ; (2)
pop eax ; (1)
done:
ret
Please notice that there's a reversed ordering to be observed when preserving/restoring registers on the stack! (Your code missed this...)
4 tips
a. Prefer the MOV variant to load an address. It's always a shorter instruction.
b. Guard yourself against an input of zero.
c. Don't use LOOP. It's a slow instruction.
d. Provide the exit code to terminate the program.
mov esi, n ; (a)
call toint ; -> ECX
jecxz Exit ; (b)
print_loop:
...
dec ecx ; (c)
jnz print_loop
Exit:
xor ebx, ebx ; (d)
mov eax, 1
int 0x80

Traversing a 2-d array

I'm having trouble understanding how to traverse a 2-d array in x86 assembly language. I am missing a bit of understanding. This is what I have so far.
The issue is the lines with //offset and //moving through array
For the //offset line the error I am getting is "non constant expression in second operand"
and also
"ebx: illegal register in second operand"
For the next line I get the error
"edx: illegal register in second operand"
mov esi, dim
mul esi
mov eax, 0 // row index
mov ebx, 0 // column index
mov ecx, dim
StartFor:
cmp ecx, esi
jge EndFor
lea edi, image;
mov edx, [eax *dim + ebx] // offset
mov dl, byte ptr [edi + esi*edx] // moving through array
mov edx, edi
and edx, 0x80
cmp edx, 0x00
jne OverThreshold
mov edx, 0xFF
OverThreshold:
mov edx, 0x0
See x86 tag wiki, including the list of addressing modes.
You can scale an index register by a constant, but you can't multiply two registers in an addressing mode. You'll have to do that yourself (e.g. with imul edx, esi, if the number of columns isn't a compile time constant. If it's a power-of-2 constant, you can shift, or even use a scaled addressing mode like [reg + reg*8]).
re: edit: *dim should work if dim is defined with something like dim equ 8. If it's a memory location holding a value, then of course it's not going to work. The scale factor can be 1, 2, 4, or 8. (The machine code format has room for a 2-bit shift count, which is why the options are limited.)
I'd also recommend loading with movzx to zero-extend a byte into edx, instead of only writing dl (the low byte). Actually nvm, your code doesn't need that. In fact, you overwrite the value you loaded with edi. I assume that's a bug.
You can replace
imul edx, esi
mov dl, byte ptr [edi + edx] ; note the different addressing mode
mov edx, edi ; bug? throw away the value you just loaded
and edx, 0x80 ; AND already sets flags according to the result
cmp edx, 0x00 ; so this is redundant
jne OverThreshold
with
imul edx, esi
test 0x80, byte ptr [edi + edx] ; AND, discard the result and set flags.
jnz
Of course, instead of multiplying inside the inner loop, you can just add the columns in the outer loop. This is called Strength Reduction. So you do p+=1 along each row, and p+=cols to go from row to row. Or if you don't need to care about rows and columns, you can just iterate over the flat memory of the 2D array.
A 2-dimensional array is just an interpretation of a sequence of bytes. You'll have to choose in which order to store the items. For example, you might choose "row-major order".
I've written a demo where a buffer is filled with a sequence of numbers. Then the sequence is interpreted as single- and two-dimensional array.
tx86.s
%define ITEM_SIZE 4
extern printf
section .bss
cols: equ 3
rows: equ 4
buf: resd cols * rows
c: resd 1
r: resd 1
section .data
fmt: db "%-4d", 0 ; fmt string, '0'
fmt_nl: db 10, 0 ; "\n", '0'
section .text ; Code section.
global main
main:
push ebp
mov ebp, esp
; fill buf
mov ecx, cols * rows - 1
mov [buf + ITEM_SIZE], ecx
.fill_buf:
mov [buf + ecx * ITEM_SIZE], ecx
dec ecx
jnz .fill_buf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; buf as 1-dimensional array
; buf[c] = [buf + c * ITEM_SIZE]
xor ecx, ecx
mov [c], ecx
.lp1d:
mov ecx, [c]
push dword [buf + ecx * ITEM_SIZE]
push dword fmt
call printf
mov ecx, [c]
inc ecx
mov [c], ecx
cmp ecx, cols * rows
jl .lp1d
; print new line
push dword fmt_nl
call printf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; buf as 2-dimensional array
; buf[r][c] = [buf + (r * cols + c) * ITEM_SIZE]
xor ecx, ecx
mov [r], ecx
.lp1:
xor ecx, ecx
mov [c], ecx
.lp2:
; calculate address
mov eax, [r]
mov edx, cols
mul edx ; eax = r * cols
add eax, [c] ; eax = r * cols + c
; print buf[r][c]
push dword [buf + eax * ITEM_SIZE]
push dword fmt
call printf
; next column
mov ecx, [c]
inc ecx
mov [c], ecx
cmp ecx, cols
jl .lp2
; print new line
push dword fmt_nl
call printf
; next row
mov ecx, [r]
inc ecx
mov [r], ecx
cmp ecx, rows
jl .lp1
mov esp, ebp
pop ebp ; restore stack
xor eax, eax ; normal exit code
ret
Buidling(on Linux)
nasm -f elf32 -l tx86.lst tx86.s
gcc -Wall -g -O0 -m32 -o tx86 tx86.o
Running
./tx86
0 1 2 3 4 5 6 7 8 9 10 11
0 1 2
3 4 5
6 7 8
9 10 11

Indexing array elements in Assembly

I'm having some trouble with arrays in NASM. I am trying to implement this pseudo code but am obtaining incorrect results.
For instance, when I send in 'abcdefgh' as byte_arr I should get 8 7 6 5 4 3 2 1. However, I actually receive: 5 4 3 2 1 1 1 1. Here's the code:
maxLyn(Z[0 . . n − 1], n, k) : returns integer
if k = n − 1
return 1
p ← 1
for i ← k+1 to n − 1
{
if Z[i−p] = Z[i]
if Z[i−p] > Z[i]
return p
else
p ← i+1−k
}
return p.
Rather than returning p, I wish to store it as a global variable which I can access in my other subroutines. Here is my attempt at coding it:
enter 0, 0 ; creates stack
pusha ; pushes current register values onto stack
mov eax, [ebp+16] ; kth index
mov ebx, [ebp+12] ; length
mov edi, [ebp+8] ; byte_arr
mov [len], ebx ; len = length
sub [len], dword 1 ; len = len - 1
mov ebx, [len] ; ebx contains len - 1
mov [k], eax ; k = epb+16
mov [p], dword 1 ; p = 1
mov [i], eax ; i = k
inc dword [i] ; i = k+1
mov ecx, [i] ; ecx = i
cmp [k], ebx ; checks if kth index is last element
je ENDFOR
FOR: cmp dword [i], ebx ; goes from ith index to len
ja ENDFOR
mov esi, [edi + ecx*1] ; esi = array[i]
mov [i_temp], esi ; store array[i] in variable
sub ecx, [p] ; ecx = i-p
mov edx, [edi + ecx*1] ; edx = array [i-p]
cmp edx, [i_temp]
je REPEAT ; array[i-p] = array[i]
ja ENDFOR ; array[i-p] > array[i]
mov eax, dword [i]
mov [p], eax ;p = i
inc dword [p] ;p = i + 1
mov eax, dword [p]
sub eax, [k] ;eax = p - k
mov [p], eax ;p = i+1-k
REPEAT:
inc dword [i] ; i = i + 1
mov ecx, [i] ; ecx = i + 1
jmp FOR
ENDFOR:
popa ; saves register values and pops them off
leave ; cleans up
ret ; returns to caller
mov esi, [edi + ecx*1] ; esi = array[i]
mov [i_temp], esi ; store array[i] in variable
sub ecx, [p] ; ecx = i-p
mov edx, [edi + ecx*1] ; edx = array [i-p]
cmp edx, [i_temp]
je REPEAT ; array[i-p] = array[i]
ja ENDFOR ; array[i-p] > array[i]
In these lines you read your array elements as dwords but you actually are using an array of bytes!
That's why the comparing fails. Change this cmp into:
cmp dl, byte ptr [i_temp]
Alternatively rework your code to use byte sized variables/registers where appropriate.

Traversing and averaging elements in an array MASM

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

Calculating average of floats rounded to an integer NASM

Basically the program is suppose to input floating point numbers from the user, then get the sum of them, the average of them (from 0.0 to 100.0), anything above is not counted and anything below isn't counted as well. A negative number will not just not be counted but also exit the loop/program. The sum is outputted, number of valid numbers inputted is also outputted, and finally the average of the floating point numbers ROUNDED to nearest integer is outputted. I think I am close right now, my program thus far is below. Please, I could use some help finding the bug/(error in reasoning, if it exists) in my code below. Note I am using NASM.
Update: When it runs and input just the simple 1,2,3,4 it should output 3(rounded to nearest integer) is the average, but it outputs 100, which means it is running all the way through that loop or something similar is going wrong.
Update: I found the problem(s) there should have been a couple more comparisons and should have had more ffree st0 to free up st0, that kept creating a problem. Anyways got it working.
; Declare needed external functions
;
extern printf ; the C function, to be called
extern scanf ; the C function, to be called
SECTION .data ; Data section, initialized variables
three: dq 3.0
erroro: db "Did not work as intended...", 10, 0
zero: dq 0.0
half: dq 0.5
max_val: dq 100.0 ; max value is 100
min_val: dq 0.0 ; min value is 0
input: db "%lf", 0 ; input format
intro: db "Program title is Average and programmer is Joe", 10, 0 ; introduction format
iname: db "Enter your full name", 10, 0 ; get name instructions from user format
gname: db "%s %s", 0 ; get first and lats name
inst: db "Enter a number between 0.0 and 100.0, negative number to exit", 10, 0 ; instruction format
countout: db "The number of valid numbers entered: %d", 10, 0 ; counter output format
sumoutput: db "The sum is: %f", 10, 0 ; sum output format
avgoutput: db "The average is: %d", 10, 0 ; average output format
specialbye: db "You didn't enter any numbers, something wrong %s %s?", 10, 0 ; special goodbye format (if user did not input any valid numbers)
bye: db "Thanks for using this program, %s %s", 10, 0 ; goodbye output format
SECTION .bss ; BSS, uninitialized variables
x: resq 1 ; number entered by user, set to 0
sum: resq 1 ; sum variable is set to 0
avgc: resq 1 ; average used to compare (not what is outputted)
avg: resd 101 ; an array that is used for lookup value of average (it is how it outputs an integer)
count: resd 1 ; counter set to 0
avga: resq 201 ; an array of values from .5 to 100 (by increments of .5) used for comparing avg
fn: resw 10 ; first name set to 0, with a size of 10 words
ln: resw 10 ; last name set to 0, with a size of 10 words
SECTION .text ; Code section.
global main ; the standard gcc entry point
main: ; the program label for the entry point
push ebp ; set up stack frame
mov ebp,esp
mov eax, 0
mov ebx, 0
mov ecx, 101
.setavg: ;average array set up (used to output)
mov [avg+ebx], eax
push eax
mov eax, ebx
add eax, 4
mov ebx, eax
pop eax
add eax, 1
loop .setavg
finit
; get the first one taken care of...
fld qword [zero]
fstp qword [avga+0]
mov ebx, 0
mov ecx, 200
.setavga: ; average array set up to compare average values and then round to nearest integer (sort of..., actually it uses the average array to set which integer to "round to")
fld qword [avga+ebx]
fld qword [half]
fadd
mov eax, ebx
add eax, 8
mov ebx, eax
fstp qword [avga+ebx]
loop .setavga
jmp .start ; skip to .start label, used for testing purposes to reduce user input during testing
; output introduction
push dword intro
call printf
add esp, 4
; output asking for name
push dword iname
call printf
add esp, 4
; get first and last name
push dword ln
push dword fn
push dword gname
call scanf
add esp, 12
; start loop
.start:
; output number input instructions to user
push dword inst
call printf
add esp, 4
;get number
push dword x
push dword input
call scanf
add esp, 8
;compare x and minimum value
;mov eax, [x] ; put x in eax
;cmp eax, min_val ; compare x and minimum value
;jl .post_while ; if x is less than minimum value (which means it is a negative), jump to post while
; compare value to minimum value, if minimum is greater than user input, jump to post_while
fld qword [x]
fld qword [min_val]
fcomip st0, st1
ja .post_while
; free up st0
ffree st0
;compare x and max value
;mov eax, [x] ; put x in eax
;cmp eax, max_val ; compare x and max value
;jg .start ; if x is greater than max value, jump up to start (loop to start)
; compare against max value, if max is less than input jump back to .start label without counting it
fld qword [x]
fld qword [max_val]
fcomip st0, st1
jb .start
;free up st0
ffree st0
; else calculate sum
;mov eax, [sum]
;add eax, [x]
;mov [sum], eax
; calculate sum
fld qword [sum]
fld qword [x]
fadd
fstp qword [sum]
; update counter
mov eax, [count]
add eax, 1
mov [count], eax
jmp .start ; loop back to start
; after loop
.post_while:
; special check: if count = 0 that means no valid numbers have been received by user, then jump down to .special label
;mov eax, [count]
;mov ebx, 0
;cmp eax, ebx
;je .special
; calculate the average (sum/count)
;mov eax, [sum]
;mov ebx, [count]
;cdq
;div ebx
;mov [avg], eax
; calculate average
fld qword [sum]
fild dword [count]
fdiv
fstp qword [avgc]
; calculate rounding (closer to below or above) i.e. 1.3 is closer to 1 so avg is 1
;mov eax, edx
;mov ebx, 2
;mul ebx
;cmp eax, [count]
;jnge .dontadd ; if not greater i.e. 1.3, then jump down to .dontadd label
; else add one to the average
;mov eax, [avg]
;add eax, 1
;mov [avg], eax
; setup counter and index counters
mov ecx, 201
mov esi, 0
mov edi, 0
.roundloop: ; loop that rounds the average to nearest integer
; caluclate if going to increase edi (which is used for avg array *integer array*), if ecx is divisible by 2, then increase it.
mov eax, ecx
mov ebx, 2
cdq
div ebx
mov ebx, 0
cmp edx, ebx
jne .dontinc
inc edi
.dontinc:
; calculate if avga at index esi is above avgc (average calculate value), if so found where it is and end loop
fld qword [avgc]
fld qword [avga+esi]
;fld qword [three]
fcomip st0, st1
ja .endrloop
; increment esi by 8
mov eax,esi
mov ebx, 8
add eax, ebx
mov esi, eax
loop .roundloop
.endrloop:
mov ebx, edi ; save edi index of avg (integer array)
; means it is not closer to next integer
.dontadd:
push ebx
; output count
push dword [count]
push dword countout
call printf
add esp, 8
pop ebx
push ebx
; output sum
push dword [sum+4]
push dword [sum]
push dword sumoutput
call printf
add esp, 12
; output average
pop ebx
mov eax, ebx
mov ebx, 4
mul ebx
mov ebx, eax
push dword [avg+ebx]
push dword avgoutput
call printf
add esp, 8
jmp .regular ; output should be normal, since we got to here.
; special case where count == 0, meaning no valid numbers have been inputted
.special: ; not used in testing purposes at the moment
; output special goodbye message
push dword ln
push dword fn
push dword specialbye
call printf
add esp, 12
jmp .small ; now small jump to skip rest of output
.regular:
; output regular bye message
push dword ln
push dword fn
push dword bye
call printf
add esp, 12
; small jump used only in special case
.small:
mov esp, ebp ; takedown stack frame
pop ebp ; same as "leave" op
mov eax,0 ; normal, no error, return value
ret ; return
Glad you found a solution!
Could you tell us why you did the following the hard way?
.setavg: ;average array set up (used to output)
mov [avg+ebx], eax
push eax
mov eax, ebx
add eax, 4
mov ebx, eax
pop eax
add eax, 1
loop .setavg
Usual version
.setavg: ;average array set up (used to output)
mov [avg+ebx], eax
ADD EBX, 4
add eax, 1
loop .setavg

Resources