; m13_calculator.asm ; CIS-261 final poject: "running total" calculator .586P .MODEL FLAT ; Flat memory model option casemap:none ; Treat labels as case-sensitive ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Add C run-time libraries ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; see https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=vs-2019 ; printf() needs the following library: includelib legacy_stdio_definitions.lib PUBLIC m13_calculate ; make this procedure externally visible to the linker m13_calculate PROTO NEAR32 stdcall, arithmetic_command:DWORD, right_operand:DWORD binary_plus PROTO NEAR32 stdcall, right_operand:DWORD binary_minus PROTO NEAR32 stdcall, right_operand:DWORD binary_multiply PROTO NEAR32 stdcall, right_operand:DWORD binary_divide PROTO NEAR32 stdcall, right_operand:DWORD binary_modulo PROTO NEAR32 stdcall, right_operand:DWORD binary_assign PROTO NEAR32 stdcall, right_operand:DWORD ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; procedures defined in other modules ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; EXTERN _m13_left_operand:DWORD ; global variable defined in m13_externs.cpp EXTERN _print_error@4:NEAR ; procedure is defined in m13_externs.cpp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; procedures of the C standard library ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; EXTERN _printf:NEAR .const ASCII_TAB EQU 9 ERROR_division_by_zero BYTE ASCII_TAB, "attempt to divide by zero ignored...", 0 ERROR_unexpected_command BYTE ASCII_TAB, "unexpected command code [%d]", 0 ERROR_unimplemented_command BYTE ASCII_TAB, "unimplemented command [%d]", 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; command codes for arithmetic operators ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; COMMAND_ADD EQU 0 COMMAND_SUBTRACT EQU 1 COMMAND_MULTIPLY EQU 2 COMMAND_DIVIDE EQU 3 COMMAND_MODULO EQU 4 COMMAND_ASSIGN EQU 5 COMMAND_UNKNOWN EQU 6 .data ; The data segment ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Table of addresses of procedures that are invoked indirectly ; Zero entry indicates that procedure is still unimplemented ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; call_table DWORD OFFSET binary_plus DWORD 0 ; OFFSET binary_minus DWORD 0 ; OFFSET binary_multiply DWORD OFFSET binary_divide DWORD 0 ; OFFSET binary_modulo DWORD 0 ; OFFSET binary_assign ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .code ; Code segment begins ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; entry point routine to dispatch operations to the appropriate handlers ; inputs: ; right_operand -- param on the stack ; arithmetic_command -- code of operation. acceptable codes are: ; --------------- ; operation code ; --------- ----- ; '+' 0 ; '-' 1 ; '*' 2 ; '/' 3 ; '%' 4 ; '=' 5 ; --------------- ; returns ; zero if success, error code otherwise (the returned value is in EAX) ; m13_calculate PROC NEAR32 stdcall, arithmetic_command:DWORD, right_operand:DWORD push ebx ; preserve EBX mov ebx, [arithmetic_command] ; load command code into EBX ;;------------------------------------------------------------------------- ;; validate range of command code ;;------------------------------------------------------------------------- cmp ebx, COMMAND_UNKNOWN ; if ( arithmetic_command < COMMAND_UNKNOWN ) jb @F ; command is in good range ;;------------------------------------------------------------------------- ;; command code is out of range ;;------------------------------------------------------------------------- ; NOTE: printf() follows C convention: arguments passed right to left push ebx ; command code to display push OFFSET ERROR_unexpected_command ; format string to use call _printf ; print string ; NOTE: printf is a varargs function. It does not ; pop its own args off the stack. ; The caller must remove the arguments: add esp, 8 pop ebx ; restore EBX mov eax, @Line ; use line number in this file as error code ret ; RETURN @@: cmp call_table[ ebx * 4 ], 0 ; if ( arithmetic_command != 0 ) jne @F ; command is implemented ;;------------------------------------------------------------------------- ;; command code is not implemented ;;------------------------------------------------------------------------- push ebx ; command code to display push OFFSET ERROR_unimplemented_command ; format string to use call _printf ; print string ; NOTE: printf is a varargs function. It does not ; pop its own args off the stack. ; The caller must remove the arguments: add esp, 8 pop ebx ; restore EBX mov eax, @Line ; use line number in this file as error code ret ; RETURN @@: ;;------------------------------------------------------------------------- ;; all validations passed, make calculations ;;------------------------------------------------------------------------- mov eax, [right_operand] push eax ; prepare right operand on the stack call call_table[ ebx * 4 ] ; invoke command handler pop ebx ; restore ebx ret m13_calculate ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; inputs: ; m14_left_operand -- global variable ; right_operand -- param on the stack ; returns: ; EAX: zero if success, error code otherwise ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; binary_plus PROC NEAR32 stdcall, right_operand:DWORD mov eax, [right_operand] ; add [_m13_left_operand], eax ; [_m13_left_operand] += [right_operand] xor eax, eax ; EAX = 0 indicating success ret binary_plus ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; inputs: ; m14_left_operand -- global variable ; right_operand -- param on the stack ; returns: ; EAX: zero if success, error code otherwise ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; binary_minus PROC NEAR32 stdcall, right_operand:DWORD ; TODO ret binary_minus ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; inputs: ; m14_left_operand -- global variable ; right_operand -- param on the stack ; returns: ; EAX: zero if success, error code otherwise ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; binary_multiply PROC NEAR32 stdcall, right_operand:DWORD ; TODO ret binary_multiply ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; inputs: ; m14_left_operand -- global variable ; right_operand -- param on the stack ; returns: ; EAX: zero if success, error code otherwise ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; binary_divide PROC NEAR32 stdcall, right_operand:DWORD push ecx ; preserve ECX mov eax, [_m13_left_operand] ; EAX = dividend cdq ; must extend the sign bit of EAX into the EDX register mov ecx, [right_operand] ; ECX = divisor test ecx, ecx ; if ( ECX == 0 ) jz @F ; then error idiv ecx ; ( quotient EAX : remainder EDX ) = EAX / ECX mov [_m13_left_operand], eax ; store quotient xor eax, eax ; EAX = 0 indicating success pop ecx ; restore ret @@: push OFFSET ERROR_division_by_zero ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Example using printf() for output ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; call _printf ; print string ; NOTE: printf is a varargs function. It does not ; pop its own args off the stack. ; The caller must remove the arguments: add esp, 4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Example calling our own C function for output ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;call _print_error@4 pop ecx ; restore ECX mov eax, @Line ; use line number in this file as error code ret binary_divide ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; inputs: ; m14_left_operand -- global variable ; right_operand -- param on the stack ; returns: ; EAX: zero if success, error code otherwise ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; binary_modulo PROC NEAR32 stdcall, right_operand:DWORD ; TODO ret binary_modulo ENDP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; inputs: ; m14_left_operand -- global variable ; right_operand -- param on the stack ; returns: ; EAX: zero if success, error code otherwise ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; binary_assign PROC NEAR32 stdcall, right_operand:DWORD ; TODO ret binary_assign ENDP END