diff --git a/content/asm_2.md b/content/asm_2.md index 38ee08d..bac2d2f 100644 --- a/content/asm_2.md +++ b/content/asm_2.md @@ -25,6 +25,8 @@ Now that we've successfully written and run our first assembly program, it's tim One of the first concepts we met in the previous post was a **register**. We agreed that we can consider a register as a small memory slot. Following the definition on [Wikipedia](https://en.wikipedia.org/wiki/Processor_register), we can see that it's not so far from truth: > A processor register is a quickly accessible location available to a computer's processor. +> +> -- Wikipedia, "Processor register" The main goal of a processor is data processing. To process data, a processor must access this data somewhere. Of course, a processor can get data from [main memory](https://en.wikipedia.org/wiki/Random-access_memory), but it is a very slow operation. If we take a look at the [Latency Numbers Every Programmer Should Know](https://samwho.dev/numbers), we can see the following picture: @@ -215,6 +217,8 @@ The stack grows downwards, from higher memory addresses to lower ones. So, when In the [system call](#system-calls) section, we saw that the first six arguments of a system call are passed in the general purpose registers. According to the calling conventions document: > System-calls are limited to six arguments, no argument is passed directly on the stack. +> +> -- *System V Application Binary Interface, AMD64 Architecture Processor Supplement*, section A.2.1, "Calling Conventions" So the available number of general purpose registers should be enough to execute any system call. But what about other functions? What if one has more than six arguments? In this case, the first six parameters are also passed in general purpose registers and all the next parameters are passed on the stack. The set of general purpose registers to call a library function is slightly different from the set of registers used for a system call: diff --git a/content/asm_6.md b/content/asm_6.md index f689a87..df068a2 100644 --- a/content/asm_6.md +++ b/content/asm_6.md @@ -13,6 +13,8 @@ First of all, let's take a look at how floating-point numbers are represented in The Intel [Software Developer Manual](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html) says: > The data formats for these data types correspond directly to formats specified in the IEEE Standard 754 for Binary Floating-Point Arithmetic. +> +> -- *IntelĀ® 64 and IA-32 Architectures Software Developer's Manual*, vol. 1, section 4.2.2, "Floating-Point Data Types" To get all the possible details about the representation of the floating-point numbers in computer memory, you should take a look at this standard. @@ -377,6 +379,8 @@ After executing the code, the buffer specified by the `buffer_1` name will conta The user input contains the `newline` symbol at the end. We don't need it in our input, as we can't convert it to a floating-point number. To get rid of this symbol, we replace it with the `0` byte. To do that, we need to know the length of the user input. The good news is that we already know it. Take a look at the documentation of the `sys_read` system call: > On success, the number of bytes read is returned +> +> -- *read(2)*, Linux man-pages, "RETURN VALUE" As you may remember, the return value of a system call is stored in the `rax` register. To write the zero byte into the buffer right after the user input, we just need to take the pointer to the beginning of this buffer, add an offset to it (which is equal to the length of the user input), and add the `0` byte to this address. All of these you can see in the last four lines of the code above. diff --git a/content/asm_7.md b/content/asm_7.md index 8b90620..ea57716 100644 --- a/content/asm_7.md +++ b/content/asm_7.md @@ -211,6 +211,8 @@ asm [volatile] ("assembly code" The `asm` keyword introduces the inline assembly block. Adding the `volatile` qualifier tells the compiler not to optimize or reorder this code, which is important if the code has side effects that the compiler cannot see. As the [GCC documentation](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) explains: > The typical use of Extended asm statements is to manipulate input values to produce output values. However, your asm statements may also produce side effects. If so, you may need to use the volatile qualifier to disable certain optimizations. +> +> -- *Using the GNU Compiler Collection*, "Extended Asm", the `volatile` qualifier After defining the assembly code, we can specify input and output operands that describe how C variables should be mapped to registers or memory. Each operand consists of a constraint string followed by the C expression in parentheses. The constraint tells the compiler what kind of location can hold the operand, and ensures that values are moved in and out properly.