0

I am working on an assignment for class and I have one last part of the assignment before I am done. I am given a C++ file that I am not allowed to edit, and I am writing the .asm for it. My last snag in the project is that: 1. My _Divide function when given 2 regular values should access the enum, but I do not know how to call to it and not hit _PrintResult by accident. 2. When dividing by 0, I can get it to display the "Display of Division by zero" but not the "Attempted division by zero."

Here is the .cpp: include

using namespace std;

enum ResultCode { ShowSquare, ShowMultiply, ShowDivide, ShowRemainder,
ShowDivideFailure };
enum SuccessCode { Failure, Success };

extern "C" SuccessCode Divide(long, long, long &, long &);
extern "C" long Multiply(long, long);
extern "C" void PrintResult(ResultCode, long);
extern "C" long Square(long);

void main()
{
long Num1;
long Num2;
long Result;
long Remainder;
do
{
    cout << "Enter Number to Square" << endl;
    cin >> Num1;
    Result = Square(Num1);
    cout << "Square is: " << Result << endl;
    cout << "Enter two numbers to multiply" << endl;
    cin >> Num1 >> Num2;
    Result = Multiply(Num1, Num2);
    cout << "Result of multiply is: " << Result << endl;
    cout << "Enter mumber to divide into then number to divide by" << endl;
    cin >> Num1 >> Num2;
    if (Divide(Num1, Num2, Result, Remainder) == Success)
        cout << "Result is " << Result << " and remainder is " << Remainder 
<< endl;
    else
        cout << "Attempted division by zero, ";
} while (Result > 0);
}

void PrintResult(ResultCode PrintCode, long Value)
{
switch (PrintCode)
{
case ShowSquare:
    cout << "Display of square is: " << Value << endl;
    break;
case ShowMultiply:
    cout << "Display of multiply is: " << Value << endl;
    break;
case ShowDivide:
    cout << "Display of divide is " << Value << endl;
    break;
case ShowRemainder:
    cout << "Display of remainder is " << Value << endl;
    break;
case ShowDivideFailure:
    cout << "Display of Division by zero" << endl;
    break;
default:
    cout << "Error in assembly routines" << endl;
}
}

And here is what I have so far:

.386

.model flat

.code

public    _Square
public    _Multiply
public    _Divide
extern    _PrintResult:proc


ShowSquare = 0
ShowMultiply = 1
ShowDivideFailure = 4

Failure = 0
Success = 1

_Square        proc
            mov     eax, [esp + 4]
            imul    eax, eax
        push    eax

        push    eax
        push    0
        call    _PrintResult
        add     esp, 8

        pop     eax
        ret
_Square        endp

_Multiply    proc
        mov         eax, [esp + 8]
        mov         ebx, [esp + 4]
        imul        eax, ebx
        push        eax

        push        eax
        push        1
        call        _PrintResult

        add        esp, 12
        mov        eax, [esp + 8]
        mov        ebx, [esp + 4]
        imul    eax, ebx

        ret
_Multiply    endp


_Divide        proc

Start1:
mov            eax, [esp+4]
mov            ebx, [esp+8]

cmp            ebx,0
je            Failure1

cdq
idiv        ebx
mov            ebx, edx
jmp            Success1

Success1:

push        eax
push        eax
push        2
call        _PrintResult
add            esp, 8
pop            eax

push        ebx
push        ebx
push        3
call        _PrintResult
add            esp, 8
pop            ebx

push        eax
push        eax
push        0
call        _SuccessCode

Failure1:

        ret
_Divide        endp


end
Michael Petch
  • 42,023
  • 8
  • 87
  • 158
Arcadiaen
  • 33
  • 5
  • `enum SuccessCode { Failure, Success };` just equates 0 with Failure and 1 with success. So when you detect division by 0, set the return value to 0 for Failure in `_Divide`. The return value of the function is in EAX. `_Divide` should return 1 for success by setting EAX to 1, and set _EAX_ to 0 if division by zero was detected. – Michael Petch Oct 18 '17 at 05:29
  • 1
    Another observation. The CDECL calling convention makes _EAX_, _ECX_ and _EDX_ volatile registers. Your functions can clobber those registers and you are fine. However _EBX_, _ESI_, _EDI_, _EBP_ are non-volatile. If you change their values you must ensure you save them at the top of your function and then restore them at the bottom. Usually done by pushing the value to save and poping them to restore. Your program may seem to work but it is possible it may fail unexpectedly. I say this because I see a couple of functions you clobber _EBX_ but you do nothing to save/restore its value. – Michael Petch Oct 18 '17 at 05:41
  • Check the Windows ABI to find out what size an `enum` has in the calling convention you're using. In the i386 and x86-64 System V ABIs, an `enum` is a 32-bit integer type, IIRC. So the entire width of `eax` matters as a return value, not just `al` for example. – Peter Cordes Oct 18 '17 at 05:52
  • 1
    @PeterCordes Microsoft Windows compilers by default assume `enum` is 32-bits although it is possible to change that through compiler options which in effect change the ABI from default. – Michael Petch Oct 18 '17 at 06:03
  • So what changes should I make? @MichaelPetch – Arcadiaen Oct 18 '17 at 06:30
  • 1
    I've already told you. Return 0 or 1 in _EAX_ before you return from `_Divide` depending on whether you detect division by zero. What you return in _EAX_ will be used by the C++ code to determine whether to print the result of the division or an error about detecting division by zero. – Michael Petch Oct 18 '17 at 06:32

0 Answers0