CAPÍTULO III: ANÁLISIS DE RESULTADOS
3.2. Caracterización de la Curiosidad discriminada por participante en cada Fase del
3.2.1. Estudios de Caso Fase I - Observación Natural
3.2.1.3. Participante No. 3
the second volume covers instruction set completely and has a very useful table of contents. please, always use it to get information about instruction set: it is not only a very good practice, but also a quite reliable source.
Note, that many educational resources devoted to assembly language in the internet are often heavily outdated (as few people program in assembly these days) and do not cover the 64-bit mode at all. the instructions present in older modes often have their updated counterparts in long mode, and those are working in a different way. this is a reason we strongly discourage using search engines to find instruction descriptions, as tempting as it might be.
1.3 Registers
The data exchange between CPU and memory is a crucial part of computations in a von Neumann computer. Instructions have to be fetched from memory, operands have to be fetched from memory; some instructions store results also in memory. It creates a bottleneck and leads to wasted CPU time when it waits for the data response from the memory chip. To avoid constant wait, a processor was equipped with its own memory cells, called registers. These are few but fast. Programs are usually written in such a way that most of the time the working set of memory cells is small enough. This fact suggests that programs can be written so that most of the time the CPU will be working with registers.
Table 1-1. von Neumann Architecture: Modern Extensions
Problem Solution
Nothing is possible without querying slow memory Registers, caches
Lack of interactivity Interrupts
No support for code isolation in procedures, or for context saving Hardware stack Multitasking: any program can execute any instruction Protection rings Multitasking: programs are not isolated from one another Virtual memory
Registers are based on transistors, while main memory uses condensers. We could have implemented main memory on transistors and gotten a much faster circuit. There are several reasons engineers prefer other ways of speeding up computations.
• Registers are more expensive.
• Instructions encode the register’s number as part of their codes. To address more registers the instructions have to grow in size.
• Registers add complexity to the circuits to address them. More complex circuits are harder to speed up. It is not easy to set up a large register file to work on 5 GHz.
Naturally, register usage slows down computers in the worst case. If everything has to be fetched into registers before the computations are made and flushed into memory after, where’s the profit?
The programs are usually written in such a way, that they have one particular property. It is a result of using common programming patterns such as loops, function, and data reusage, not some law of nature.
This property is called locality of reference and there are two main types of it: temporal and spatial.
Temporal locality means that accesses to one address are likely to be close in time.
Spatial locality means that after accessing an address X the next memory access will likely to be close to X, (like X − 16 or X + 28).
These properties are not binary: you can write a program exhibiting stronger or weaker locality.
Typical programs are using the following pattern: the data working set is small and can be kept inside registers. After fetching the data into registers once we will work with them for quite some time, and then the results will be flushed into memory. The data stored in memory will rarely be used by the program. In case we need to work with this data we will lose performance because
• We need to fetch data into the registers.
• If all registers are occupied with data we still need later on, we will have to spill some of them, which means save their contents into temporally allocated memory cells.
■
Note a widespread situation for an engineer: decreasing performance in the worst case to improve it in average case. it does work quite often, but it is prohibited when building real-time systems, which impose constraints on the worst system reaction time. such systems are required to issue a reaction to events in no more than a certain amount of time, so decreasing performance in the worst case to improve it in other cases is not an option.
1.3.1 General Purpose Registers
Most of the time, programmer works with general purpose registers. They are interchangeable and can be used in many different commands.
These are 64-bit registers with the names r0, r1, …, r15. The first eight of them can be named
alternatively; these names represent the meaning they bear for some special instructions. For example, r1 is alternatively named rcx, where c stands for “cycle.” There is an instruction loop, which uses rcx as a cycle counter but accepts no operands explicitly. Of course, such kind of special register meaning is reflected in documentation for corresponding commands (e.g., as a counter for loop instruction). Table 1-2 lists all of them; see also Figure 1-3.
■
Note unlike the hardware stack, which is implemented on top of the main memory, registers are a completely different kind of memory. thus they do not have addresses, as the main memory’s cells do!
The alternate names are in fact more common for historical reasons. We will provide both for reference and give a tip for each one. These semantic descriptions are given for a reference; you don’t have to memorize them right now.
You usually do not want to use rsp and rbp registers because of their very special meaning (later we will see how they corrupt stack and stack frame). However, you can perform arithmetic operations on them directly, which makes them general purpose.
Table 1-3 shows registers sorted by their names following an indexing convention.
Addressing a part of a register is possible. For each register you can address its lowest 32 bits, lowest 16 bits, or lowest 8 bits.
When using the names r0,...,r15 it is done by adding an appropriate suffix to a register’s name:
• d for double word—lower 32 bits;
• w for word—lower 16 bits;
• b for byte—lower 8 bits.
Table 1-2. 64-bit General Purpose Registers
Name Alias Description
r0 rax Kind of an “accumulator,” used in arithmetic instructions. For example, an instruction div is used to divide two integers. It accepts one operand and uses rax implicitly as the second one. After executing div rcx a big 128-bit wide number, stored in parts in two registers rdx and rax is divided by rcx and the result is stored again in rax.
r3 rbx Base register. Was used for base addressing in early processor models.
r1 rcx Used for cycles (e.g., in loop).
r2 rdx Stores data during input/output operations.
r4 rsp Stores the address of the topmost element in the hardware stack. See section 1.5
“Hardware stack”.
r5 rbp Stack frame’s base. See section 14.1.2 “Calling convention”.
r6 rsi Source index in string manipulation commands (such as movsd) r7 rdi Destination index in string manipulation commands (such as movsd) r8
r9 … r15 no Appeared later. Used mostly to store temporal variables (but sometimes used implicitly, like r10, which saves the CPU flags when syscall instruction is executed. See Chapter 6 “Interrupts and system calls”).
Table 1-3. 64-Bit General Purpose Registers—Different Naming Conventions
r0 r1 r2 r3 r4 r5 r6 r7
rax rcx rdx rbx rsp rbp rsi rdi
For example,
• r7b is the lowest byte of register r7;
• r3w consists of the lowest two bytes of r3; and
• r0d consists of the lowest four bytes of r0.
The alternate names also allow addressing the smaller parts.
Figure 1-4 shows decomposition of wide general purpose registers into smaller ones.
The naming convention for accessing parts of rax, rbx, rcx, and rdx follows the same pattern; only the middle letter (a for rax) is changing. The other four registers do not allow an access to their second lowest bytes (like rax does by the name of ah). The lowest byte naming differs slightly for rsi, rdi, rsp, and rbp.
• The smallest parts of rsi and rdi are sil and dil (see Figure 1-5).
• The smallest parts pf rsp and rbp are spl and bpl (see Figure 1-6).
In practice, the names r0-r7 are rarely seen. Usually programmers stick with alternate names for the first eight general purpose registers. It is done for both legacy and semantic reasons: rsp relates a lot more information, than r4. The other eight (r8-r15) can only be named using an indexed convention.
■
Inconsistency in writes all reads from smaller registers act in an obvious way. the writes into 32-bit
parts, however, fill the upper 32 bits of the full register with sign bits. For example, zeroing
eaxwill zero the
entire
rax, storing -1 into
eaxwill fill the upper 32 bits with ones. other writes (e.g., in 16-bit parts) act as
intended: they leave all other bits unaffected. see section 3.4.2 “CisC and risC” for the explanation.
1.3.2 Other Registers
The other registers have special meaning. Some registers have system-wide importance and thus cannot be modified except by the OS.
Figure 1-3. Approximation of Intel 64: general purpose registers
A programmer has access to rip register. It is a 64-bit register, which always stores an address of the next instruction to be executed. Branching instructions (e.g., jmp) are in fact modifying it. So, every time any instruction is being executed, rip stores the address of the next instruction to be executed.