CIS-261 Home http://www.c-jump.com/bcc/
What are the aspects of using Assembly language with high-level programming languages?
Assembly is a powerful and flexible programming tool. Every professional programmer, regardless of the chosen programming language, has to master the Assembly language as a second tool. This is similar to a common opinion held by linguists, who say that everyone who chooses to become a professional linguist must master Latin before studying other European languages.
In flat memory model all procedure calls are NEAR, which means that they take place within one large memory segment. This makes CALL/RET coordination between multiple modules, potentially written in different programming languages, quite easy.
However, procedure name coordination is more complicated:
The MASM Assembler adds the @N suffix to names, where N is the number of parameters passed on the stack, measured in bytes. The Visual C++ compiler does the same thing.
MASM generates the the leading underscore character automatically, if the standard call calling convention , STDCALL, is specified at the beginning of the program.
Coordination of uppercase and lowercase letters is important.
C++ allows function name overloading, so that the same C++ identifier can refer to different functions. For C++ programmers these functions differ in the number of parameters, parameter types, and type of the return value. Internally, C++ compiler automatically decorates function names in such way that different functions can be distinguished by the linker.
By default, Assembly programs use STDCALL calling convention for passing parameters.
When dealing with high-level languages, programmer needs to be aware of the calling convention used by that particular language.
Types of return values:
In Assembly language, everything is simple: the value is returned in the EAX register with two possibilities:
the value is either a number, or
a pointer to some variable or structure.
If the return value has the WORD data type, it is passed in the least significant word of the EAX register, namely, AX.
(When dealing with the C programming language, type casting of return value must be also considered.)
The following sample demonstrates ASM module M13_COPYSTR.ASM ( download ) written in Assembly language.
The ASM file contains a procedure that copies one text string to another string.
This module can be linked to different programs written in the C++.
The caller program is M13_main.cpp ( download ) is written in C++.
; M13_COPYSTR.ASM .586P .MODEL FLAT, stdcall ; Flat memory model PUBLIC COPYSTR _TEXT SEGMENT ; Procedure for copying the null-terminated source string to the target string. ; ; Input: ; str_dest...Target string [ EBP + 08H ] ; str_src....Source string [ EBP + 0CH ] ; Output: ; EAX........address of the target string ; ; WARNING! target string length is unchecked ; COPYSTR PROC str_dest: DWORD, str_src: DWORD MOV ESI, str_src ; DWORD PTR [ EBP + 0CH ] MOV EDI, str_dest ; DWORD PTR [ EBP + 08H ] L1: MOV AL, BYTE PTR [ESI] MOV BYTE PTR [EDI], AL CMP AL, 0 JE L2 INC ESI INC EDI JMP L1 L2: MOV EAX, DWORD PTR [ EBP + 08H ] RET COPYSTR ENDP _TEXT ENDS END
// M13_main.cpp #include <iostream> extern "C" int __stdcall COPYSTR( char*, char const* ); int main() { char destination[ 100 ] = { 0 }; char const* source = "Hello!"; COPYSTR( destination, source ); std::cout << destination; return 0; }
The function that the program calls from the main program is declared using the extern "C" and __stdcall modifiers:
// The M13_main.cpp file
extern "C" int __stdcall COPYSTR( char*, char* );
The __stdcall calling type assumes that the stack is cleared in the called procedure.
In the Assembly module, the called procedure must be declared using the PUBLIC directive:
; The M13_COPYSTR.ASM file
.586P
.MODEL FLAT, stdcall
PUBLIC COPYSTR
The C++ compiler automatically adds the underscore and the @8 suffix to the end of the public procedure name: _COPYSTR@8
To verify this, one could run the command
dumpbin /disasm Release\M13.exe > Release\M13.txt
and examine the resulting Disassembly file M13.txt.
Please note that when using PROC directive when defining procedures such as COPYSTR, the explicit setting and releasing of the stack frame using the EBP register isn't necessary. The assembler automatically controls the stack. Here is the excerpt from the M13.txt dump:
Dump of file Release\M13.exe File Type: EXECUTABLE IMAGE _COPYSTR@8: 00401000: 55 push ebp 00401001: 8B EC mov ebp,esp 00401003: 8B 75 0C mov esi,dword ptr [ebp+0Ch] 00401006: 8B 7D 08 mov edi,dword ptr [ebp+8] 00401009: 8A 06 mov al,byte ptr [esi] 0040100B: 88 07 mov byte ptr [edi],al 0040100D: 3C 00 cmp al,0 0040100F: 74 04 je 00401015 00401011: 46 inc esi 00401012: 47 inc edi 00401013: EB F4 jmp 00401009 00401015: 8B 45 08 mov eax,dword ptr [ebp+8] 00401018: C9 leave 00401019: C2 08 00 ret 8
The LEAVE command is an equivalent of
mov esp, ebp pop ebp
C/C++ extern keyword specifies that external linkage conventions to link with other languages should used by the variable or function declarator.
extern-declared functions and data become visible to the linker accross multiple .OBJ modules.
However, the extern functions must be defined in a separately compiled translation unit(s).
The following sample demonstrates C++ program calling COPYNEW( ) function defined in the module written in Assembly. The sample contains the following files:
M13_main.cpp ( download ) the main driver program.
M13_NEWARRAY.h ( download ) C++ header file declaring extern functions.
M13_NEWARRAY.cpp ( download ) C++ implementation file defining external functions.
M13_COPYNEW.ASM ( download ) Assembly program that dynamically allocates block of memory and makes copy of the source string.
Your assignment is to build a small command-based arithmetic calculator.
The calculator understands arithmetic operators + - * / % =
followed by an integer:
+ 1 ; addition - 3 ; subtraction * 8 ; multiplication / 4 ; division % 2 ; modulo (remainder) = 5 ; assignment
After each input the calculator does the required math and displays the result. The result is a running total: it is used in the next calculation. Lastly entered arithmetic operator is "sticky": the user can enter numbers without an operator to continue using the same operator. For example, (all user inputs are highlighted):
0+ 10 ; addition is a default operation = 10 10+ 10 ; user enters a number without an operator = 20 20+ 10 = 30 30+ /3 ; the user wants to divide by 3 = 10 10/ 2 ; division becomes the default operation = 5 5/ quit ; quit exits the program
The prototype of this project contains multiple files. Create new empty C++ project named M13, create a "src" subdirectory, and download the following files:
M13_main.cpp ( download ) the main driver program.
m13_externs.h ( download ) declarations of global variables and functions.
m13_externs.cpp ( download ) definitions of global variables and implementation of global functions.
m13.h ( download ) declarations of miscellaneous functions, including user input interpreter.
m13.cpp ( download ) implementation of miscellaneous functions
m13_calculator.asm ( download ) implementation of math handler procedures
The prototype of the "Running Total Calculator" uses C++ functions to deal with the user input and with the display of the calculation result. The actual math is done inside m13_calculator.asm, written in Assembly.
All C++ code is finished. The only remaining work is the Assembly module m13_calculator.asm.
When the user enters an arithmetic operator followed by a value, C++ invokes
m13_calculate( arithmetic_operator, value )
function implemented in Assembly. The m13_calculate procedure further dispatches the call to a specific math "handler" procedure. There are six ASM handlers, one for each arithmetic operator:
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
Two procedures, binary_plus and binary_divide, are fully implemented.
The rest is your assignment. Search for "TODO" comments in m13_calculator.asm
to identify procedures requiring your attention.
Examine C++ code and the m13_calculator.asm source file.
There is a call_table in the data segment. It stores addresses of the handler procedures.
Although procedure bodies are already defined in m13_calculator.asm, the prototype stores zeros in the call_table for yet unfinished procedures. This causes display of "unimplemented" message when the user enters an operator corresponding to an unfinished procedure.
As you start working on the procedure implementation, do not forget to remove the zero from the call_table. For example, before writing code for the binary_minus procedure, remove "0 ;" from the call_table, so that the actual address is stored in the table.
call_table DWORD OFFSET binary_plus
DWORD OFFSET binary_minus
DWORD 0 ; OFFSET binary_multiply
DWORD OFFSET binary_divide
DWORD 0 ; OFFSET binary_modulo
DWORD 0 ; OFFSET binary_assign
If you forget to remove zeros, the procedures are not invoked. Examine the m13_calculate procedure for details.
NOTE: each unimplemented operator will result in 15 pts deduction from your M13 grade.
When you are done with the changes in m13_calculator.asm, upload your C/C++ and ASM source files.
Do not send any EXE, binary, or other project files.
Consider creating a ZIP archive for your source subdirectory and upload it as a single file.