How to set EAX to inputted value before calling a procedure

113 Views Asked by At
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
INCLUDE Irvine32.inc

.data
prompt BYTE "Please enter the lower bound: ", 0
prompt2 BYTE "Please enter the upper bound: ", 0
lowerbound SDWORD ?
count DWORD ?


.code
main PROC

    mov ecx, 3

L1:
    mov count, ecx  
    mov ecx, 30 
    mov edx, OFFSET prompt
    call WriteString
    call ReadInt
    mov lowerbound, eax

    mov edx, OFFSET prompt2
    call WriteString
    call ReadInt
    mov ebx, lowerbound

L2:
    call BetterRandomRange
    call WriteInt
    call crlf
    loop L2
    
    mov ecx, count
    loop L1
    exit
    INVOKE ExitProcess,0
main ENDP

BetterRandomRange PROC ;loop 3 times

sub eax, ebx
call RandomRange
add eax, ebx            this keeps in within range but causes a division by zero error
;sub eax, ebx        no error but slightly out of range for the 30 iterations
ret

BetterRandomRange ENDP

END main

I'm trying to complete the problem of generating an integer between M and N-1 based on user input, where M is in EBX (lower bound) and N is in EAX (upper bound) and the number is returned in EAX.

I've somewhat located the error because the upper bound is not stored in EAX before calling BetterRandomRange. However, I'm unsure on how to fix this because EAX and EBX have the correct bound values in the first iteration. I'm new to assembly and still learning it so it's a bit confusing for me. Any help would be appreciated!

2

There are 2 best solutions below

1
Chris Dodd On

When you call BetterRandomRange the first time, it will change the value in eax. In addition WriteInt and crlf will likely change it too as eax is not preserved across functions. So in the second iteration, eax contains whatever crlf left there rather than the upperbound.

What you need to do is to store the upperbound (perhaps a static var as you do with lowerbound) and reload it before calling `BetterRandomRange each time in the loop.

2
Sep Roland On

First input your upperbound and put that in a variable:

mov  edx, OFFSET prompt2
call WriteString
call ReadInt            ; -> EAX
mov  upperbound, eax

Then input your lowerbound and put that in EBX:

mov  edx, OFFSET prompt
call WriteString
call ReadInt            ; -> EAX
mov  ebx, eax           ; lowerbound

Code in the L2 loop will not clobber the EBX register, but the EAX register gets modified by BetterRandomRange. So reload the upperbound from its variable:

L2:
    mov  eax, upperbound                    <<<< new
    call BetterRandomRange  ; -> EAX
    call WriteInt
    call crlf
    loop L2
BetterRandomRange PROC ;loop 3 times

Doesn't the L2 loop run 30 times? It's the L1 loop that runs 3 times.


The quickest fix for your present code would preserve the value from EAX on the stack:

L2:
    push eax                                <<<< new
    call BetterRandomRange  ; -> EAX
    call WriteInt
    call crlf
    pop  eax                                <<<< new
    loop L2
L2:

This is not an instruction. L2 just corresponds to the address in memory where the push eax instruction (that follows) is located. This is the top of the loop. The first time ECX=30

   push eax

The stackpointer ESP gets subtracted by 4, and the EAX register gets written at [esp]. (ESP points at the preserved EAX).

   call BetterRandomRange  ; -> EAX

The stackpointer ESP gets subtracted by 4, and the return address gets written at [esp]. Next the instruction pointer EIP moves to the BetterRandomRange procedure where the code calculates a new value for EAX and then returns here. In order to return, the instruction pointer EIP is loaded from [esp] and then the stackpointer gets raised by 4. (ESP again points at the preserved EAX).

   call WriteInt

The stackpointer ESP gets subtracted by 4, and the return address gets written at [esp]. Next the instruction pointer EIP moves to the WriteInt procedure where the code writes the value in EAX to the screen and then returns here. It is fine for WriteInt to clobber the EAX register in the process. In order to return, the instruction pointer EIP is loaded from [esp] and then the stackpointer gets raised by 4. (ESP again points at the preserved EAX).

   call crlf

The stackpointer ESP gets subtracted by 4, and the return address gets written at [esp]. Next the instruction pointer EIP moves to the crlf procedure where the code writes a newline to the screen and then returns here. It is fine for crlf to clobber the EAX register in the process. In order to return, the instruction pointer EIP is loaded from [esp] and then the stackpointer gets raised by 4. (ESP again points at the preserved EAX).

   pop  eax

The EAX register is loaded from [esp] and then the stackpointer gets raised by 4. EAX is now restored to the upperbound value.

   loop L2

The ECX register gets decremented and if it is non-zero the branch is taken. The execution then continues at the top of the loop. If ECX was decremented to zero, the execution would fall through in the following lines, thereby ending the loop.

Don't feel compelled to use the loop instruction for your loops

Your L1 loop currently is:

    mov ecx, 3
L1:
    mov count, ecx
    mov  ecx, 30

    ...

    mov ecx, count
    loop L1

You can avoid the deprecated (because it is slow) loop instruction in several ways:

  mov  count, 3
L1:
  mov  ecx, 30    
  ...

  dec  count
  jnz  L1

Another way is:

  mov  ecx, 3
L1:
  push ecx
  mov  ecx, 30

  ...

  pop  ecx
  dec  ecx
  jnz  L1

You can avoid loop in the L2 loop in a similar manner.