My program accepts 4 integers and is suppose to display them back to the user. When printing the values I don't get the expected results. I'm using MASM with Kip's Irvine32 library
My code is:
include irvine32.inc
.data
msg byte "Enter a number",0
arr dword 4 dup(?)
len=($-arr)/4
.code
main PROC
mov edx,offset msg
call writestring
call crlf
mov eax,0
mov ecx,0
.while(ecx<len)
call readdec
mov arr[ecx],eax
inc ecx
.endw
mov ebx,0
mov ecx,0
.while(ecx<len)
mov ebx,arr[ecx]
call writedec
call crlf
inc ecx
.endw
exit
main ENDP
END main
A sample run of my program:
Enter a number
1
2
3
4
4
4
4
4
After entering the numbers 1,2,3, and 4 the program should have displayed those numbers back to the user. The output I expected is:
Enter a number
1
2
3
4
1
2
3
4
If I modify the loop that prints the numbers so that I place the value to print in EAX instead of EBX with this code:
mov eax,arr[ecx]
call writedec
I end up with nonsensical output values like this:
Enter a number
1
2
3
4
67305985
262914
1027
4
Why is my program behaving this way, and how can I modify it to get the expected results?
There is only one real issue in the code that appears in different places. When you do something like:
.while(ecx<len)
call readdec
mov arr[ecx],eax
inc ecx
.endw
You must realize that arr[ecx] is the same as arr+ecx. ECX is the offset in bytes added to arr . The issue is that each element of arr is 32-bit (4 bytes) since you declared the array as:
arr dword 4 dup(?)
What you need to do is multiply ECX by the length of each element (in this case 4 bytes). You can do that by multiplying ECX by the size of the elements in the array arr[ecx*4] = arr+(ecx*4) . This form of scaled addressing only supports multiplying by the value of 1,2,4, and 8 in 32-bit code. Your code should have looked like this:
.while(ecx<len)
call readdec
mov arr[ecx*4],eax
inc ecx
.endw
There is a similar issue with the code that calls writedec. As well writedec takes the number to print in EAX, not EBX. This code:
.while(ecx<len)
mov ebx,arr[ecx]
call writedec
call crlf
inc ecx
.endw
Should be something like:
.while(ecx<len)
mov eax,arr[ecx*4]
call writedec
call crlf
inc ecx
.endw
One other trick to getting the length of an array in MASM is to use the lengthof pseudo-opcode. Where you wrote:
len=($-arr)/4
You could have used:
len=lengthof arr
lengthof will return the number of elements in arr . MASM takes into account the size of each element size (in this case 4 since you declared arr with DWORD elements). There was nothing wrong with the way you did it, I am offering up another mechanism.
I highly recommend learning to use a debugger to step through your code. A debugger can be an invaluable tool that can allow you to see how things are progressing in the code and see what is in the registers and memory at any given time.
Related
Edit: Thank you so much for all your help! I really appreciate it because for some reason I am having some trouble conceptualizing assembly but I'm piecing it together.
1) I am using the debugger to step through line by line. The problem, Unhandled exception at 0x0033366B in Project2.exe: 0xC0000005: Access violation writing location 0x00335FF8 occurs at this line:
mov [arrayfib + ebp*4], edx
Is think maybe this because the return statement from the other loop is not able to be accessed by the main procedure, but not sure - having a hard time understanding what is happening here.
2) I have added additional comments, hopefully making this somewhat clearer. For context: I've linked the model I've used to access the Fibonacci numbers, and my goal is to fill this array with the values, looping from last to first.
.386
.model flat, stdcall
.stack 4096
INCLUDE Irvine32.inc
ExitProcess PROTO, dwExitCode: DWORD
.data
arrayfib DWORD 35 DUP (99h)
COMMENT !
eax = used to calculate fibonacci numbers
edi = also used to calculate fibonacci numbers
ebp = number of fibonacci sequence being calculated
edx = return value of fibonacci number requested
!
.code
main PROC
;stores n'th value to calculate fibonacci sequence to
mov ebp, 30
mov edx, 0
;recursive call to fibonacci sequence procedure
call FibSequence
mov [arrayfib + ebp*4], edx
dec ebp
jnz FibSequence
mov esi, OFFSET arrayfib
mov ecx, LENGTHOF arrayfib
mov ebx, TYPE arrayfib
call DumpMem
INVOKE ExitProcess, 0
main ENDP
;initializes 0 and 1 as first two fibonacci numbers
FibSequence PROC
mov eax, 0
mov edi, 1
;subrracts 2 from fibonacci number to be calculated. if less than 0, jumps to finish,
;else adds two previous fibonacci numbers together
L1:
sub ebp, 2
cmp ebp, 0
js FINISH
add eax, edi
add edi, eax
LOOP L1
;stores odd fibonacci numbers
FINISH:
test eax, 1
jz FINISHEVEN
mov edx, eax
ret
;stores even fibonacci numbers
FINISHEVEN:
mov edx, edi
ret
FibSequence ENDP
END main
Your Fibonacci function destroys EBP, returning with it less than zero.
If your array is at the start of a page, then arrafib + ebp*4] will try to access the previous page and fault. Note the fault address of 0x00335FF8 - the last 3 hex digits are the offset within a 4k virtual page, an 0x...3FF8 = 0x...4000 + 4*-2.
So this is exactly what happened: EBP = -2 when your mov store executed.
(It's normal for function calls to destroy their register args in typical calling conventions, although using EBP for arg-passing is unusual. Normally on Windows you'd pass args in ECX and/or EDX, and return in EAX. Or on the stack, but that sucks for performance.)
(There's a lot of other stuff that doesn't make sense about your Fibonacci function too, e.g. I think you want jmp L1 not loop L1 which is like dec ecx / jnz L1 without setting flags).
In assembly language, every instruction has a specific effect on the architectural state (register values and memory contents). You can look up this effect in an instruction-set reference manual like https://www.felixcloutier.com/x86/index.html.
There is no "magic" that preserves registers for you. (Well, MASM will push/pop for you if you write stuff like proc foo uses EBP, but until you understand what that's doing for you it's better not to have the assembler adding instructions to you code.)
If you want a function to preserve its caller's EBP value, you need to write the function that way. (e.g. mov to copy the value to another register.) See What are callee and caller saved registers? for more about this idea.
maybe this because the return statement from the other loop is not able to be accessed by the main procedure
That doesn't make any sense.
ret is just how we write pop eip. There are no non-local effects, you just have to make sure that ESP is pointing to the address (pushed by call) that you want to jump to. Aka the return address.
I'm learning x86 assembly and I'm trying to write a program that reads a number n (2 digits) from user input and iterate n times.
I've tried many ways but I get an infinite loop or segment fault.
input:
push msgInputQty
call printf
add esp, 4
push quantity
call gets
add esp, 4
mov ecx, 2
mov eax, 0
mov ebx, 0
mov edi, 0
mov dl, 10
transform:
mul dl
mov ebx, 0
mov bl, byte[quantity+edi]
sub bl, 30h
add eax, ebx
inc edi
loop transform
mov ecx, eax
printNTimes:
push msgDig
call printf
add esp, 4
loop printNTimes
I'd like to save in ecx and iterate n times this number
Your ecx register is being blown away by the call to printf.
ecx is a volatile register in some calling conventions and its likely that your loop is being corrupted by what printf is leaving in there.
To begin with, I would follow Raymond's advice in the comment attached to your original question and attach a debugger to witness this behaviour for yourself.
As for a solution, you can try preserving ecx and restoring it after the call to see the difference:
; for example
mov edi,ecx
call printf
mov ecx,edi
There may be more issues here (hard to know for sure since your code is incomplete ... but things like your stack allocations that don't appear to be for any reason are interesting) - but that is a good place to start.
Peter has left a comment under my answer to point out that you could remove the issue and optimize my solution by just not using ecx for your loop at all and instead do it manually, making your code change:
mov edi, eax
printNTimes:
push msgDig
call printf
add esp, 4
dec edi
jnz printNTimes
The output for the given program should be 1--2--3--4--5--6--7--8.
However, mine keeps giving me 1--2--3--4--5--6--7-8--.I need to get rid of "--" after 8, but I couldn't figure out how to do it. Could someone help me, please?
INCLUDE Irvine32.inc
.data
arrayb byte 1,2,3,4,5,6,7,8
space byte "--",0
.code
main PROC
mov eax,0
mov edx,offset space
mov esi,offset arrayb
mov ecx, 8
printb:
mov al, [esi]
call writedec
call writestring
inc esi
mov eax, 8
loop printb
exit
main ENDP
end main
Your code (and loop specifically) currently does:
<init state>
counter = 8
printb:
output number
output "--"
loop to printb
<exit>
If you run through it in your head, it should be obvious why "--" is printed after last number.
There are many ways how to adjust that code, in real world code doing formatting I often either use some kind of join function which takes list and separator, and produces the formatted string, or if doing it manually, I would probably hardcode output of "1" ahead of loop, initialize the state to start loop as if starting from "2", and output "--" as first thing in the loop, i.e.:
<init state>
output number
advance state as if it was printed inside loop
counter = 8-1 ; one value is already out
printb:
output "--" ; the loop body starts with "--" output
output number
loop to printb
<exit>
I.e. in your code (with some modifications "improving" some things I didn't like :
...
mov edx,offset space
mov esi,offset arrayb
movzx eax, BYTE PTR [esi]
inc esi
call writedec
mov ecx, 8-1
printb:
call writestring
movzx eax, BYTE PTR [esi]
inc esi
call writedec
loop printb
exit
...
edit
The Peter Cordes idea from comments to make last item special case would maybe lead to better code in generic cases, where the amount of items is variable (with "8" fixed you know you can display first element and then 7 more will be looped).
Let's imagine such function in assembly, taking arguments in registers:
; esi = byte array to output
; ecx = amount of bytes
; will modify eax, ecx, edx, esi
OutputByteArray PROC
jecxz NoItemsToOutput ; if ecx=0
dec ecx
jz OutputLastItem ; if ecx=1, display only last item
; 1 < count, so "0 < ecx" here (ecx = count-1) => do the loop
mov edx,offset space
printb: ; output number and "--" "count-1" times
movzx eax, BYTE PTR [esi]
inc esi
call writedec
call writestring
loop printb
OutputLastItem:
movzx eax, BYTE PTR [esi]
call writedec
NoItemsToOutput:
ret
ENDP
i'm struggling with learning assembly code so please don't hate me if this is a stupid question..
I'm trying to understand how to do loops, I have learned that to loop you use the following structure:
mov ECX, 3
l1:
<loop body?
loop l1
however, instead of 3 im trying to load the value of a variable (called 'first') however when I do this, it just seems to loop infinitely. This is the current code i've got:
lea eax, get1; ask for the first number
push eax
call printf
add esp, 4
lea eax, first; read it in
push eax
lea eax, format
push eax
call scanf
add esp, 8
mov ECX, first
l1:
/* test loop body */
lea eax, get1; ask for the first number
push eax
call printf
add esp, 4
loop l1
does anyone know where i'm going wrong?
I'm currently presented with implementing a program that takes an input of a grade value (ex. 75) and then outputs a letter grade corresponding to it. I've implemented the following requested scale via an array / table:
.data
table BYTE 89d, 'A'
BYTE 79d, 'B'
BYTE 69d, 'C'
BYTE 59d, 'D'
BYTE 0d, 'F'
NumCols = 2
NumRows = 5
user_ip BYTE ?
message1 BYTE "Enter a grade value: ", 0h
message2 BYTE "The Grade of ", 0h
message3 BYTE " Yields A Letter Grade of ", 0h
I'm using the following code to sort through this array / table and output the letter grade.
mov edx, OFFSET message1
call WriteString
call readDec
mov user_ip, al
mov esi, OFFSET user_ip
mov edi, OFFSET table
mov ecx, NumRows
L1:
CMPSB
jae L2
add edi, NumCols
Loop L1
L2:
mov edx, OFFSET message2
call WriteString
mov al, user_ip
call WriteDec
mov edx, OFFSET message3
call WriteString
mov edx, edi
call WriteString
call Crlf
With an input of 75, I'm being presented with: "The Grade of 75 Yields A Letter Grade of EC;D". The Program also temporarily stops working.
I'm confident it has something to do with pointers and data sizes. My ideal goal is to store the value of the letter grade in a variable, but I can't seem to find a way to do it given the data size needed to use pointers. Any ideas of how to do this?
You are calling WriteString but the values in your table are characters not strings. The difference in this case is that they are not zero terminated. Either use WriteChar if you have that, or put a zero in your table but then don't forget to adjust NumCols too.
Also note that CMPSB increments both pointers which means your comparisons will be wrong. You should probably just use the non-string CMP especially since the user_ip is already in register AL.
PS: Finally somebody who uses a table :)