Windows 64bit assembly programming by example: Introduction.
Introduction. | 1. Numbers and arrays. | 2. Characters and strings. | Sample programs list. |
Some important notes that you should read before starting this tutorial:
- I myself am not an assembly programmer. Thus, please, don't expect assembly code that is optimized for size or performance, nor ask yourself questions like "why did he that in this way, as it would be lots easier, lots shorter, or lots better in that way?" All that mattered to me, when writing the tutorial samples, was that the program output was what I wanted it to be...
- This is not really an assembly tutorial. It's a collection of simple assembly programs that show how you can realize a simple task. The tutorial samples are intended for assembly beginners, and for this reason are explained in a simple way, so that the novice can understand how the program works (at least I hope). The samples may also be seen as starting points, or templates for your own assembly projects.
- For beginners, yes, but not for people without some general knowledge of assembly programming. It is supposed that the reader knows what are the sections of an assembly program, what the Intel syntax of assembly instructions look like, what are the registers of a x64 CPU, what are the Flags register and conditional jumps, what is a memory address and what are the different addressing modes used in assembly instructions. And, of course, knowing about bits, bytes and word, and of the decimal, hexadecimal and binary number system.
- The tutorial is about writing 64-bit assembly programs. The code of the tutorial samples has no chance to work on a 32-bit target without important modifications.
- The samples have been developed and tested on Windows 11. They should run without problems on other 64-bit Windows releases. No effort has been made to guarantee whatever portability to Linux or macOS.
- The tutorial samples are intended to be used with FASM. There is probably a certain compatibility with NASM; they will probably not work with MASM or TASM, and certainly not with GAS (that uses the A&T syntax).
- I developed these samples using the SASM IDE (cf. my tutorial 32bit and 64bit assembly programming made easy with SASM), that automatically creates the C-bindings needed for input-output operations. If you use another IDE, you must see yourself to make that work correctly.
- There are lots of assembly books, articles, and code samples available on the Internet. When you consider to use them for study, or reference, be sure that they are about 64-bit assembly on Windows, also be sure that the assembler being used is FASM or NASM. Understanding Windows x64 Assembly is a very long and detailed tutorial, containing lots of stuff that you probably aren't interested in; on the other side, it includes lots of theoretical background about the architecture of the x64 CPU (registers, ...), the usage of memory, the x64 calling convention, the layout of an assembly program and the Intel assembly syntax.
- Staring with assembly may be extremely annoying; the programs aborting again and again, and loosing lots of time to search where you made a mistake. Just don't give up too soon! You will see that after a while, by the experience that you gain with every program that you write, you will find the correct code without having to look it up in an assembly book, find it automatically, so to say, and even if the program misbehaves or aborts, you will be able to "imagine" what could have caused the problem. Writing computer programs, using assembly or another language, can and should be fun. Try to enjoy it!
General structure of a 64-bit FASM program.
This structure corresponds to what a 64-bit FASM program has to look like, in order to be correctly built in the SASM IDE. In fact, the samples are assembly functions, executed by a C program, that does nothing else than just calling this function. This way to proceed gives automatically access to the C libraries from within the assembly code, and this way we can use C functions like scanf and printf for input-output.
Here is the general structure of the tutorial samples:
format ELF64
section '.data' writeable
...
section '.bss' writeable
...
section '.text' writeable
public main
extrn ...
main:
prolog code
...
epilog code
The reader is supposed to know what the three sections are about. It is obvious that the .bss section will only be occasionally used. The reserved word extrn is used to declare functions that are external to the assembly program; it's this way that we will declare printf and other C functions. The x64 prolog and epilog code will be introduced in the following paragraphs. They are so to say part of the x64 calling convention (for details, cf. the article x64 calling convention at learn.microsoft.com). All this is a rather complicate topic, and I would say that it's outside the scope of this beginner tutorial. I will just briefly describe the code of the prolog and the epilog; if you want to have details, please, have a look at the article x64 prolog and epilog at learn.microsoft.com. And finally, if you don't understand what this is about, don't worry: Just copy the corresponding instructions to the beginning resp. to the end of every program that you write...
The x64 prolog.
Here is the prolog code used in all tutorial examples:
push rbp
mov rbp, rsp
sub rsp, 32
and rsp, -16
First, we save the old base pointer, and we set the new base pointer. Then, we have the magic line in 64-bit assembly. Under the Microsoft x64 calling convention, there is a unique concept of what's known as a shadow space. This is a space that is reserved every time you enter a function and is equal to at least 32 bytes (which is enough space to hold 4 arguments). This space must be reserved whenever you're making use of the stack, since it's what is reserved for things leaving the register values on the stack for debuggers to inspect later on. While the calling convention does not explicitly require the callee to use the shadow space, you should allocate it. Finally, no matter how much space you allocate for the shadow space and your own function's variables, you still need to ensure that the stack pointer is aligned on a 16-byte boundary.
The x64 epilog.
Here is the epilog code used in all tutorial examples:
mov rsp, rbp
pop rbp
xor rax, rax
ret
First, we restore the stack pointer from RBP (for the case we had allocated local stack space), then pop the old value of the base pointer (pushed with the first instruction of the prolog); note that these two instructions may be replaced by the single instruction leave. The xor of RAX with itself is not part of the standard epilog; normally we would now return control to the calling program. So, what is this instruction for? The RAX register is used to return the result of a function. Here we return the value 0, typically used to indicate that the function completed successfully.
Calling internal and external procedures.
According to the x64 calling convention, with Windows 64-bit assembly, we usually use the RCX, RDX, R8 and R9 registers, in this order to pass arguments to a function. This is mandatory, if we call a C function like, for example, printf. If there is a single variable to print, the output format (more exactly its address) has to be placed in RCX, and the variable value in RDX. If there are two variables to print, the first one has to be placed in RDX, the second one in R8.
The function return value is placed in the RAX register.
Several tutorial samples include internal subroutines, that use different registers for argument passing or the return value. As the usage of the registers is documented with the function comments, I think that this is all ok (?).
Tutorial content.
The tutorial is made of several more or less independent parts, so you do not necessarily have to read them all or read them in a specific order (even though this might be recommendable). On the other hand, the sample programs of each part partially build on the explanations given in the samples before. Thus, real assembly beginners should view them in the order that they appear in the tutorial.
Tutorial content:
- Numbers and arrays:
- 14 sample programs dealing with the following: Integer and floating point arrays, arrays passed to and modified in a procedure, print-out of an array, sorting of an array, two-dimensional arrays, random numbers.
- 3 "real world" programs: "Guess the number" game, linear equations in one variable, equation of a line passing through two points.
- Characters and strings.
Further parts will (probably) be added in the future...
If you find this text helpful, please, support me and this website by signing my guestbook.