This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| en:multiasm:papc:chapter_6_9 [2025/12/25 23:03] – [Merging of the High-Level Languages and Assembler Code] pczekalski | en:multiasm:papc:chapter_6_9 [2026/02/27 02:50] (current) – [Merging of the High-Level Languages and Assembler Code] jtokarz | ||
|---|---|---|---|
| Line 3: | Line 3: | ||
| The integration of assembler code with applications written in high-level languages brings benefits in particular scenarios, such as implementing complex mathematical algorithms and real-time tasks that require efficient, compact code. No one uses an assembler to implement a graphical user interface (GUI) anymore, as there is no reason to do so. Modern desktop operating systems are designed to provide a rich user experience, supporting languages such as C#, C++, and Python for implementing user interfaces (UIs) through libraries. While those UI generation functions can be executed from the assembler level, there is virtually no reason to do it. A more effective approach is to have the main application is written in a high-level language and execute assembly code as needed to perform backend operations efficiently. | The integration of assembler code with applications written in high-level languages brings benefits in particular scenarios, such as implementing complex mathematical algorithms and real-time tasks that require efficient, compact code. No one uses an assembler to implement a graphical user interface (GUI) anymore, as there is no reason to do so. Modern desktop operating systems are designed to provide a rich user experience, supporting languages such as C#, C++, and Python for implementing user interfaces (UIs) through libraries. While those UI generation functions can be executed from the assembler level, there is virtually no reason to do it. A more effective approach is to have the main application is written in a high-level language and execute assembly code as needed to perform backend operations efficiently. | ||
| - | In the case of multi-tier web applications, | + | In the case of multi-tier web applications, |
| + | |||
| + | It is possible to merge assembler code with high-level languages either as: | ||
| + | * static, where assembler code is compiled as a library object file and merged with the code during linking (figure {{ref> | ||
| + | * dynamic, where the assembler code library is loaded during runtime (figure {{ref> | ||
| + | |||
| + | <figure staticlinking> | ||
| + | {{ : | ||
| + | < | ||
| + | </ | ||
| + | <figure dynamiclinking> | ||
| + | {{ : | ||
| + | < | ||
| + | </ | ||
| + | Dynamic code loading is considered an advantage because the original application does not contain the assembler binary executable; it is kept in a separate file and loaded on demand, allowing it to be compiled and exchanged independently. On the other hand, it raises several challenges, such as versioning, compatibility, | ||
| ===== Programming in Assembler for Windows ===== | ===== Programming in Assembler for Windows ===== | ||
| Windows OS has historically supported unmanaged code written primarily in C++. This kind of code runs directly on the CPU, but divergence in hardware platforms, such as the introduction of ARM-core-based platforms running Windows, causes incompatibility issues. Since the introduction of the .NET framework, Windows has provided developers with a safer way to execute their code, called " | Windows OS has historically supported unmanaged code written primarily in C++. This kind of code runs directly on the CPU, but divergence in hardware platforms, such as the introduction of ARM-core-based platforms running Windows, causes incompatibility issues. Since the introduction of the .NET framework, Windows has provided developers with a safer way to execute their code, called " | ||
| Line 17: | Line 31: | ||
| ==== Dynamic memory management considerations ==== | ==== Dynamic memory management considerations ==== | ||
| - | Using dynamic memory management at the level of the assembler code is troublesome: | + | Using dynamic memory management at the assembler |
| <note tip> | <note tip> | ||
| + | <figure dynamicmemory> | ||
| + | {{ : | ||
| + | < | ||
| + | </ | ||
| ==== Pure Assembler Applications for Windows CMD ==== | ==== Pure Assembler Applications for Windows CMD ==== | ||
| It is possible to write an application for Windows solely in assembler. While the reason to do it is doubtful, some hints presented below, such as calling system functions, may be helpful. | It is possible to write an application for Windows solely in assembler. While the reason to do it is doubtful, some hints presented below, such as calling system functions, may be helpful. | ||
| Line 86: | Line 104: | ||
| **Programming for applications written in unmanaged code** | **Programming for applications written in unmanaged code** | ||
| - | <todo pczekalski>Continue here</todo> | + | In the case of the unmanaged code, integration is straightforward. Assembler code is usually encapsulated in the DLL library (or multiple libraries). |
| + | Below is a sample dummy assembler function that returns an integer (no parameters), | ||
| + | |||
| + | Assembler code (source for DLL): | ||
| + | <code cpp AssemblerDll.asm> | ||
| + | .code | ||
| + | |||
| + | MyAsmProc proc | ||
| + | mov RAX, 2026 | ||
| + | ret | ||
| + | MyAsmProc endp | ||
| + | end | ||
| + | </code> | ||
| + | |||
| + | Relevant definition file: | ||
| + | <code cpp AssemblerDll.def> | ||
| + | LIBRARY AssemblerDll | ||
| + | EXPORTS MyAsmProc | ||
| + | </ | ||
| + | C++ application, | ||
| + | <code cpp WindowsCmdX64.cpp> | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | typedef int(_stdcall* MyProc)(); | ||
| + | HINSTANCE dllHandle = NULL; | ||
| + | int main() | ||
| + | { | ||
| + | dllHandle = LoadLibrary(TEXT(" | ||
| + | if (!dllHandle) | ||
| + | { | ||
| + | std::cerr << " | ||
| + | return 1; | ||
| + | } | ||
| + | MyProc myAsmProcedure = (MyProc)GetProcAddress(dllHandle, | ||
| + | if (!myAsmProcedure) | ||
| + | { | ||
| + | std::cerr << " | ||
| + | FreeLibrary(dllHandle); | ||
| + | return 2; | ||
| + | } | ||
| + | std::cout << myAsmProcedure(); | ||
| + | FreeLibrary(dllHandle); | ||
| + | return 0; | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | <note tip> | ||
| + | <note tip> | ||
| **Programming for applications written in managed code** | **Programming for applications written in managed code** | ||
| + | |||
| In the case of managed code, things get more complex. The .NET framework features automated memory management that releases unused memory (e.g., objects for which there are no more references) and optimises variable locations to improve performance. It is known as a .NET Garbage Collector (GC). GC instantly traces references and, in the event of an object relocation in memory, updates all references accordingly. It also releases memory (objects) that are no longer referenced. This automated mechanism, however, applies only across managed code apps. The problem arises when developers integrate a front-end application written in managed code with assembler libraries written in unmanaged code. All pointers and references passed to the assembler code are not automatically traced by the GC. Using dynamically allocated variables on the .NET side and accessing them from the assembler code is a very common scenario. GC cannot " | In the case of managed code, things get more complex. The .NET framework features automated memory management that releases unused memory (e.g., objects for which there are no more references) and optimises variable locations to improve performance. It is known as a .NET Garbage Collector (GC). GC instantly traces references and, in the event of an object relocation in memory, updates all references accordingly. It also releases memory (objects) that are no longer referenced. This automated mechanism, however, applies only across managed code apps. The problem arises when developers integrate a front-end application written in managed code with assembler libraries written in unmanaged code. All pointers and references passed to the assembler code are not automatically traced by the GC. Using dynamically allocated variables on the .NET side and accessing them from the assembler code is a very common scenario. GC cannot " | ||
| * figure {{ref> | * figure {{ref> | ||
| Line 148: | Line 215: | ||
| ===== Programming in Assembler for Linux ===== | ===== Programming in Assembler for Linux ===== | ||
| + | Principles for composing assembler code and high-level language into a single application on Linux OSes are similar to those on Windows; dynamic loading is more complex. Thus, we consider only static linking of the code. The most common use of C++ is as a high-level application. Still other options are possible, such as Python. | ||
| + | |||
| + | Linux provides more parameters passed via registers in its x64 standard calls (up to 6) than Windows (only up to 4). Refer to the chapter [[en: | ||
| + | |||
| + | A common scenario is to use the [[https:// | ||
| + | |||
| + | The sample project is composed of the '' | ||
| + | |||
| + | The '' | ||
| + | <code ini Makefile> | ||
| + | all: main | ||
| + | |||
| + | main: main.o asmfunc.o | ||
| + | g++ -o main main.o asmfunc.o | ||
| + | |||
| + | main.o: main.cpp | ||
| + | g++ -c -g -F dwarf main.cpp | ||
| + | |||
| + | asmfunc.o: asmfunc.asm | ||
| + | nasm -g -f elf64 -F dwarf asmfunc.asm -l asmfunc.lst | ||
| + | |||
| + | clean: | ||
| + | rm -f ./main || true | ||
| + | rm -f ./main.o || true | ||
| + | rm -f ./asmfunc.o || true | ||
| + | rm -f ./ | ||
| + | </ | ||
| + | |||
| + | <note important> | ||
| + | |||
| + | Assembler code exposes functions to the linker using the '' | ||
| + | |||
| + | <code assembler asmfunc.asm> | ||
| + | section .data | ||
| + | section .bss | ||
| + | section .text | ||
| + | |||
| + | global addInAsm | ||
| + | |||
| + | addInAsm: | ||
| + | nop | ||
| + | mov rax, rsi | ||
| + | add rax, rdi | ||
| + | ret | ||
| + | </ | ||
| + | |||
| + | Finally, the calling side (C++ application) uses the '' | ||
| + | <code cpp main.cpp> | ||
| + | #include < | ||
| + | |||
| + | extern " | ||
| + | |||
| + | long long a=10; | ||
| + | long long b=7; | ||
| + | long long returnValue; | ||
| + | |||
| + | int main() { | ||
| + | std::cout << " | ||
| + | returnValue = addInAsm(a, | ||
| + | std::cout << "Sum of " << a << " and " << b << " is " << returnValue | ||
| + | << std::endl; | ||
| + | return 0; | ||
| + | } | ||
| + | </ | ||