2

I have recently written my assembly program in visual studio 2017 on a windows 10 laptop. I now wish to alter this code to place values obtained from the user into the registries eax, ebx, ecx and edx

\I have gotten the program to work with defualt hardcorded values but am struggling to find anything on the web to help me get user input. The task specifies that i must use assembly to ask the user

.586                            ;Enables assembly on non Priiliged intructions for the prntium processor
.model flat ,c                  ;model=Initialises the program memory mode, flat=Identifies the size of code and data pointers and 
                            ;c=  identifies the naming and calling coventions
.stack 100h
.data                           ; This section will contain all of the static variables for our program
foo dd 0                    ;Variable to be used to store into meory   
.code                           ; Assembly code will be placed here

multi proc                      ; Start of the doit process. Like a method in C#. Method is called 
                            ;in the visual studio form Source

mov eax, 8                  ; Moves the value 8 into the eax Registry
mov ebx, 4                  ; Moves the value 4 into the ebx Registry
mov ecx, 6                  ; Moves the value 6 into the ecx Registry
mov edx, 12                 ; Moves the value 12 into the edx Registry

add eax, ebx                ; Adds the value stored in registry ebx to the vale in eax and stores the answer in eax
add eax, edx                ; Adds the value stored in registry edx to the vale in eax and stores the answer in eax
sub eax, ecx                ; subtracts the value stored in registry ecx from the vale in eax and stores the answer in eax
mul ebx                     ; Multiply the value in registry eax with the value in eax and stores the answer in eax
mov [foo], eax              ; stores the value in registry in eax into the computer memory


ret                         ; returns the valie of the accumulator
multi endp                      ; End of the doit method

end     

and this is the code i use to call it from visual studio

#include <iostream>

extern "C" int multi();

void main()
{
printf("%d%",multi());
    std:getchar();
}

I just now please need assistance to alter my code to allow input from the user, I have a feeling to i may have to do a system cll but am not sure which one. This is litterally my first day doing assembly so any help would be appreciated

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Is `.stack 100h` the size of the stack? That's pretty small if you're going to call library functions. You should be able to leave it at the default. – Peter Cordes Feb 13 '19 at 04:40

1 Answers1

2

Yes, you will need to use a system call. In C++, you'd call std::getchar() to read a character from the standard input. If you're allowed to use the C++ standard library as long as you call it from assembly, then the code would look something like this:

multi proc
    push   esi                   ; \ preserve
    push   ebx                   ; |  callee-preserve
    push   edi                   ; / registers

    call   _getchar              ; read input; return result in EAX
    mov    esi, eax              ; ESI = EAX
    sub    esi, 48               ; ESI -= '0'

    call   _getchar              ; read input; return result in EAX
    mov    ebx, eax              ; EBX = EAX
    sub    ebx, 48               ; EBX -= '0'

    call   _getchar              ; read input; return result in EAX
    mov    edi, eax              ; EDI = EAX
    sub    edi, 48               ; EDI -= '0'

    call   _getchar              ; read input; return result in EAX
    mov    edx, eax              ; EDX = EAX
    sub    edx, 48               ; EDX -= '0'

    mov    ecx, edi              ; ECX = EDI
    mov    eax, esi              ; EAX = ESI

    add    eax, ebx              ; EAX += EBX
    add    eax, edx              ; EAX += EDX
    sub    eax, ecx              ; EAX -= ECX
    mul    ebx                   ; EDX:EAX = EAX * EBX
    mov    [foo], eax            ; *foo = EAX

    pop    edi                   ; \ restore
    pop    ebx                   ; |  callee-preserve
    pop    esi                   ; /  registers

    ret
multi endp

Calling the getchar function is very straightforward. Since it takes no parameters, you don't need to worry about passing anything. It returns its result in the EAX register, like all functions on x86.

The return value of getchar is an ASCII code for the character that the user entered. If you want a numeric value, then you need to subtract '0' from the ASCII code, exploiting the fact that the numbers 0 through 9 are sequential in the ASCII table.

However, you need to store the result somewhere across multiple calls to getchar, since the x86 calling convention specifies that the EAX, EDX, and ECX registers are subject to being clobbered (overwritten) by the function call. Since ESI, EBX, and EDI are call-preserved, I've used those as temporary registers. Another alternative would be to use the stack to temporarily store the input values. Or, optimizing the code to do the arithmetic operations as you go.

Oh, and notice that while the function's name is getchar in the C code, it's _getchar when we call it from assembly. That's because Microsoft's compiler prepends an underscore to exported symbol names.

The expert programmer would add some conditional tests to this code that check for errors. Recall that getchar returns EOF (-1) on failure. You might also want to handle the case when the user presses the Enter key without entering a number. You could use the equivalent of a while loop (cmp eax, -1 + je) to keep spinning until the getchar returns a value that you consider to be within range (from '0' to '9', say).

Consider (warning: completely untested!):

ReadInteger proc
TryAgain:
    call    _getchar          ; read input from stdin; return result in EAX
    cmp     eax, 48           ; \ if (input < '0')
    jl      TryAgain          ; /  jump to TryAgain
    cmp     eax, 57           ; \ if (input > '9')
    jg      TryAgain          ; /  jump to TryAgain
    sub     eax, 48           ; input -= '0'
    ret
ReadInteger endp

multi proc
    push   esi
    push   ebx
    push   edi

    call   ReadInteger
    mov    esi, eax

    call   ReadInteger
    mov    ebx, eax

    call   ReadInteger
    mov    edi, eax

    call   ReadInteger
    add    eax, esi
    add    eax, ebx
    sub    eax, edi
    mul    ebx
    mov    [foo], eax

    pop    edi
    pop    ebx
    pop    esi

    ret
multi endp

If you can't use the C++ standard library and are forced to use operating system calls, then this becomes much more difficult. Far more difficult, I suspect, than your instructor would ever expect you to be able to do at this stage. You need to call a Win32 function like ReadConsoleInput. There is a trick here, though: write the function C (or C++), compile it with the /Fa option, and look at the assembly listing that the compiler generated.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 1
    There's usually no reason for beginners to use `mul` unless they need 8086-compatible 16-bit code. x86's "normal" / efficient / simple multiply instruction is two-operand [`imul r32, r/m32`](https://www.felixcloutier.com/x86/IMUL.html), just like add and sub, so `imul esi, ebx` is what I would have suggested. Only use widening multiply if you need the high-half result, or if you're code-golfing and want to save a byte of code size. Instructions that have existed since 8086 aren't necessarily simpler. – Peter Cordes Feb 13 '19 at 06:29