Menu English Ukrainian russian Home

Free technical library for hobbyists and professionals Free technical library


Lecture notes, cheat sheets
Free library / Directory / Lecture notes, cheat sheets

Computer science and information technology. Control transfer commands (lecture notes)

Lecture notes, cheat sheets

Directory / Lecture notes, cheat sheets

Comments on the article Comments on the article

Table of contents (expand)

LECTURE No. 18. Teams

1. Data transfer commands

For the convenience of practical application and reflection of their specifics, it is more convenient to consider the commands of this group in accordance with their functional purpose, according to which they can be divided into the following groups of commands:

1) general purpose data transfers;

2) input-output to the port;

3) work with addresses and pointers;

4) data transformations;

5) work with the stack.

General Data Transfer Commands

This group includes the following commands:

1) mov is the basic data transfer command. It implements a wide variety of shipping options. Note the specifics of this command:

a) the mov command cannot transfer from one memory area to another. If such a need arises, then any currently available general-purpose register should be used as an intermediate buffer;

b) it is impossible to load a value directly from memory into a segment register. Therefore, to perform such a load, you need to use an intermediate object. This may be a general purpose register or a stack;

c) you cannot transfer the contents of one segment register to another segment register. This is because there is no corresponding opcode in the command system. But the need for such action often arises. You can perform such a transfer using the same general-purpose registers as intermediate ones;

d) you cannot use the segment register CS as a destination operand. The reason is simple. The fact is that in the architecture of the microprocessor, the cs: ip pair always contains the address of the command that should be executed next. Changing the contents of the CS register with the mov command would actually mean a jump operation, not a transfer, which is unacceptable. 2) xchg - used for bidirectional data transfer. For this operation, of course, you can use a sequence of several mov instructions, but due to the fact that the exchange operation is used quite often, the developers of the microprocessor instruction system considered it necessary to introduce a separate xchg exchange instruction. Naturally, the operands must be of the same type. It is not allowed (as for all assembler instructions) to exchange the contents of two memory cells with each other.

Port I/O Commands

Look at Figure 22. It shows a highly simplified, conceptual diagram of computer hardware control.

Rice. 22. Conceptual diagram of computer hardware control

As you can see from Figure 22, the lowest level is the BIOS level, where the hardware is handled directly through the ports. This implements the concept of equipment independence. When replacing hardware, it will only be necessary to correct the corresponding BIOS functions, reorienting them to new addresses and the logic of the ports.

Fundamentally, managing devices directly through ports is easy. Information about port numbers, their bit depth, control information format is given in the technical description of the device. You only need to know the ultimate goal of your actions, the algorithm in accordance with which a particular device works, and the order of programming its ports, i.e., in fact, you need to know what and in what sequence you need to send to the port (when writing to it) or read from it (when reading) and how this information should be interpreted. To do this, just two commands that are present in the microprocessor command system are enough:

1) in accumulator, port_number - input to the accumulator from the port with number port_number;

2) out port, accumulator - output the contents of the accumulator to the port with the number port_number.

Commands for working with addresses and memory pointers

When writing programs in assembler, intensive work is done with the addresses of operands that are in memory. To support this kind of operations, there is a special group of commands, which includes the following commands:

1) lea destination, source - effective address loading;

2) Ids destination, source - loading the pointer into the data segment register ds;

3) les destination, source - loading the pointer into the register of the additional data segment es;

4) lgs destination, source - loading the pointer into the register of the additional data segment gs;

5) lfs destination, source - loading the pointer into the register of the additional data segment fs;

6) lss destination, source - load pointer into stack segment register ss.

The lea command is similar to the mov command in that it also performs a move. However, the lea instruction does not transfer data, but the effective address of the data (that is, the offset of the data from the beginning of the data segment) to the register indicated by the destination operand.

Often, to perform some action in a program, it is not enough to know the value of the effective data address alone, but it is necessary to have a full pointer to the data. A complete data pointer consists of a segment component and an offset. All other commands of this group allow you to get such a full pointer to an operand in memory in a pair of registers. In this case, the name of the segment register, in which the segment component of the address is placed, is determined by the operation code. Accordingly, the offset is placed in the general register indicated by the destination operand.

But not everything is so simple with the source operand. In fact, in a command, as a source, you cannot directly specify the name of the operand in memory, to which we would like to receive a pointer. First, you need to get the value of the full pointer in some memory area and specify the full address of the name of this area in the get command. To perform this action, you need to remember the directives for reserving and initializing memory.

When applying these directives, a special case is possible when the name of another data definition directive (in fact, the name of a variable) is specified in the operand field. In this case, the address of this variable is formed in memory. Which address will be generated (effective or complete) depends on the applied directive. If it is dw, then only the 16-bit value of the effective address is formed in memory; if it is dd, the full address is written to memory. The location of this address in memory is as follows: the low word contains the offset, the high word contains the 16-bit segment component of the address.

For example, when organizing work with a chain of characters, it is convenient to place its starting address in a certain register and then modify this value in a loop for sequential access to the elements of the chain.

The need to use commands to obtain a full data pointer in memory, i.e., the address of the segment and the offset value within the segment, arises, in particular, when working with chains.

Data conversion commands

Many microprocessor instructions can be attributed to this group, but most of them have certain features that require them to be attributed to other functional groups. Therefore, out of the entire set of microprocessor commands, only one command can be directly attributed to data conversion commands: xlat [address_of_transcoding_table]

This is a very interesting and useful team. Its effect is that it replaces the value in the al register with another byte from the memory table located at the address specified by the remap_table_address operand.

The word "table" is very conditional, in fact, it's just a string of bytes. The address of the byte in the string that will replace the contents of the al register is determined by the sum (bx) + (al), i.e. the contents of al acts as an index in the byte array.

When working with the xlat command, pay attention to the following subtle point. Even though the command specifies the address of the byte string from which the new value is to be retrieved, this address must be preloaded (for example, using the lea command) into the bx register. Thus, the lookup_table_address operand is not really needed (the operand's optionality is shown by enclosing it in square brackets). As for the byte string (transcoding table), it is a memory area from 1 to 255 bytes in size (the range of an unsigned number in an 8-bit register).

Stack Commands

This group is a set of specialized commands focused on organizing flexible and efficient work with the stack.

The stack is an area of ​​memory specially allocated for temporary storage of program data. The importance of the stack is determined by the fact that a separate segment is provided for it in the program structure. In case the programmer forgot to declare a stack segment in his program, the tlink linker will issue a warning message.

The stack has three registers:

1) ss - stack segment register;

2) sp/esp - stack pointer register;

3) bp/ebp - stack frame base pointer register.

The stack size depends on the operating mode of the microprocessor and is limited to 64 KB (or 4 GB in protected mode).

Only one stack is available at a time, the segment address of which is contained in the SS register. This stack is called the current stack. In order to refer to another stack ("switch the stack"), it is necessary to load another address into the ss register. The SS register is automatically used by the processor to execute all instructions that work on the stack.

We list some more features of working with the stack:

1) writing and reading data on the stack is carried out in accordance with the LIFO principle,

2) as data is written to the stack, the latter grows towards lower addresses. This feature is embedded in the algorithm of commands for working with the stack;

3) when using the esp/sp and ebp/bp registers for memory addressing, the assembler automatically considers that the values ​​contained in it are offsets relative to the ss segment register.

In general, the stack is organized as shown in Figure 23.

Rice. 23. Conceptual diagram of stack organization

The SS, ESP/SP and EUR/BP registers are designed to work with the stack. These registers are used in a complex way, and each of them has its own functional purpose.

The ESP/SP register always points to the top of the stack, that is, it contains the offset at which the last element was pushed onto the stack. The stack instructions implicitly change this register so that it always points to the last element pushed onto the stack. If the stack is empty, then the value of esp is equal to the address of the last byte of the segment allocated for the stack. When an element is pushed onto the stack, the processor decrements the value of the esp register, and then writes the element to the address of the new vertex. When popping data from the stack, the processor copies the element located at the vertex address, and then increments the value of the stack pointer register esp. Thus, it turns out that the stack grows down, in the direction of decreasing addresses.

What if we need to access elements not at the top, but inside the stack? To do this, use the EBP register. The EBP register is the stack frame base pointer register.

For example, a typical trick when entering a subroutine is to pass the desired parameters by pushing them onto the stack. If the subroutine is also actively working with the stack, then access to these parameters becomes problematic. The way out is to save the address of the top of the stack in the frame (base) pointer of the stack after writing the necessary data to the stack - the EUR register. The value in EUR can later be used to access the passed parameters.

The beginning of the stack is located at higher memory addresses. In Figure 23, this address is denoted by the pair ss: fffF. The shift of wT is given here conditionally. In reality, this value is determined by the value that the programmer specifies when describing the stack segment in his program.

To organize work with the stack, there are special commands for writing and reading.

1. push source - writing the value of the source to the top of the stack.

Of interest is the algorithm of this command, which includes the following actions (Fig. 24):

1) (sp) = (sp) - 2; the value of sp is reduced by 2;

2) the value from the source is written to the address specified by the ss: sp pair.

Rice. 24. How the push command works

2. pop assignment - writing the value from the top of the stack to the location specified by the destination operand. The value is "popped" from the top of the stack. The algorithm of the pop command is the reverse of the algorithm of the push command (Fig. 25):

1) writing the contents of the top of the stack at the location indicated by the destination operand;

2) (sp) = (sp) + 2; increasing the value of sp.

Rice. 25. How the pop command works

3. pusha - a group write command to the stack. By this command, the registers ax, cx, dx, bx, sp, bp, si, di are sequentially written to the stack. Note that the original contents of sp are written, that is, the content that was before the pusha command was issued (Fig. 26).

Rice. 26. How the pusha command works

4. pushaw is almost synonymous with the pusha command. What's the difference? The bitness attribute can be either use16 or use32. Let's look at how the pusha and pushaw commands work with each of these attributes:

1) use16 - the pushaw algorithm is similar to the pusha algorithm;

2) use32 - pushaw does not change (i.e. it is insensitive to segment width and always works with word-sized registers - ax, cx, dx, bx, sp, bp, si, di). The pusha command is sensitive to the set segment width and when a 32-bit segment is specified, it works with the corresponding 32-bit registers, i.e. eax, esx, edx, ebx, esp, ebp, esi, edi.

5. pushad - performed similarly to the pusha command, but there are some peculiarities.

The following three commands perform the reverse of the above commands:

1) rora;

2) popaw;

3) pop.

The group of instructions described below allows you to save the flag register on the stack and write a word or double word to the stack. Note that the instructions listed below are the only ones in the microprocessor instruction set that allow (and require) access to the entire contents of the flag register.

1. pushf - saves the register of flags on the stack.

The operation of this command depends on the segment size attribute:

1) use 16 - the flags register of 2 bytes in size is written to the stack;

2) use32 - the eflags register of 4 bytes is written to the stack.

2. pushfw - saving a word-sized register of flags on the stack. Always works like pushf with the use16 attribute.

3. pushfd - saving the flags or eflags flags register on the stack depending on the bit width attribute of the segment (ie, the same as pushf).

Similarly, the following three commands perform the reverse of the operations discussed above:

1) popf;

2) popftv;

3) popfd.

And in conclusion, we note the main types of operations when the use of the stack is almost inevitable:

1) calling subroutines;

2) temporary storage of register values;

3) definition of local variables.

2. Arithmetic commands

The microprocessor can perform integer and floating point operations. To do this, its architecture has two separate blocks:

1) a device for performing integer operations;

2) a device for performing floating point operations.

Each of these devices has its own command system. In principle, an integer device can take over many of the functions of a floating point device, but this will be computationally expensive. For most problems using assembly language, integer arithmetic is sufficient.

Overview of a group of arithmetic instructions and data

An integer computing device supports a little more than a dozen arithmetic instructions. Figure 27 shows the classification of commands in this group.

Rice. 27. Classification of arithmetic commands

The group of integer arithmetic instructions works with two types of numbers:

1) integer binary numbers. Numbers may or may not have a signed digit, i.e., be signed or unsigned numbers;

2) integer decimal numbers.

Consider the machine formats in which these data types are stored.

Integer binary numbers

A fixed-point binary integer is a number encoded in the binary number system.

The dimension of a binary integer can be 8, 16 or 32 bits. The sign of a binary number is determined by how the most significant bit in the representation of the number is interpreted. This is 7,15 or 31st bits for numbers of the corresponding dimension. At the same time, it is interesting that among the arithmetic commands there are only two commands that really take into account this most significant bit as a sign one - these are the integer multiplication and division commands imul and idiv. In other cases, the responsibility for actions with signed numbers and, accordingly, with a sign bit lies with the programmer. The range of values ​​of a binary number depends on its size and interpretation of the most significant bit either as the most significant bit of the number or as the sign bit of the number (Table 9).

Table 9. Range of binary numbers Decimal numbers

Decimal numbers are a special type of representation of numerical information, which is based on the principle of encoding each decimal digit of a number by a group of four bits. In this case, each byte of the number contains one or two decimal digits in the so-called binary-coded decimal code (BCD - Binary-Coded Decimal). The microprocessor stores BCD numbers in two formats (Fig. 28):

1) packed format. In this format, each byte contains two decimal digits. A decimal digit is a 0-bit binary value between 9 and 4. In this case, the code of the highest digit of the number occupies the highest 4 bits. Therefore, the range of representation of a decimal packed number in 1 byte is from 00 to 99;

2) unpackaged format. In this format, each byte contains one decimal digit in the four least significant bits. The upper 4 bits are set to zero. This is the so-called zone. Therefore, the range of representing a decimal unpacked number in 1 byte is from 0 to 9.

Rice. 28. Representation of BCD numbers

How to describe binary decimal numbers in a program? To do this, you can use only two data description and initialization directives - db and dt. The possibility of using only these directives to describe BCD numbers is due to the fact that the principle of "low byte at low address" is also applicable to such numbers, which is very convenient for their processing. And in general, when using such a data type as BCD numbers, the order in which these numbers are described in the program and the algorithm for processing them is a matter of taste and personal preferences of the programmer. This will become clear after we look at the basics of working with BCD numbers below.

Arithmetic operations on binary integers

Adding unsigned binary numbers

The microprocessor performs the addition of operands according to the rules for adding binary numbers. There are no problems as long as the value of the result does not exceed the dimensions of the operand field. For example, when adding byte-sized operands, the result must not exceed the number 255. If this happens, then the result is incorrect. Let's consider why this happens.

For example, let's do the addition: 254 + 5 = 259 in binary. 11111110 + 0000101 = 1 00000011. The result went beyond 8 bits and its correct value fits into 9 bits, and the value 8 remained in the 3-bit field of the operand, which, of course, is not true. In the microprocessor, this outcome of the addition is predicted and special means are provided for fixing such situations and processing them. So, to fix the situation of going beyond the bit grid of the result, as in this case, the carry flag cf is intended. It is located in bit 0 of the EFLAGS/FLAGS flag register. It is the setting of this flag that fixes the fact of the transfer of one from the high order of the operand. Naturally, the programmer must take into account the possibility of such an outcome of the addition operation and provide means for correction. This involves including sections of code after the addition operation in which the cf flag is parsed. This flag can be parsed in various ways.

The easiest and most accessible is to use the jcc conditional branch command. This instruction has as its operand the name of the label in the current code segment. The transition to this label is carried out if, as a result of the operation of the previous command, the cf flag is set to 1. There are three binary addition commands in the microprocessor command system:

1) inc operand - increment operation, i.e. increase the value of the operand by 1;

2) add operand_1, operand_2 - addition instruction with the principle of operation: operand_1 = operand_1 + operand_2;

3) adc operand_1, operand_2 - addition instruction taking into account the carry flag cf. Command operation principle: operand_1 = operand_1 + operand_2 + value_sG.

Pay attention to the last command - this is the addition command, which takes into account the transfer of one from the high order. We have already considered the mechanism for the appearance of such a unit. Thus, the adc instruction is a microprocessor tool for adding long binary numbers, the dimensions of which exceed the lengths of standard fields supported by the microprocessor.

Signed Binary Addition

In fact, the microprocessor is "unaware" of the difference between signed and unsigned numbers. Instead, he has the means of fixing the occurrence of characteristic situations that develop in the process of calculations. We covered some of them when discussing unsigned addition:

1) the cf carry flag, setting it to 1 indicates that the operands were out of range;

2) the adc command, which takes into account the possibility of such an exit (carry from the least significant bit).

Another means is to register the state of the most significant (sign) bit of the operand, which is done using the overflow flag of in the EFLAGS register (bit 11).

Of course, you remember how numbers are represented in a computer: positive - in binary, negative - in two's complement. Consider various options for adding numbers. The examples are intended to show the behavior of the two most significant bits of the operands and the correctness of the result of the addition operation.

Example

= 30566 0111011101100110

+

00687 = 00000010

=

31253 = 01111010

We monitor the transfers from the 14th and 15th digits and the correctness of the result: there are no transfers, the result is correct.

Example

= 30566 0111011101100110

+

= 30566 0111011101100110

=

1132 = 11101110

There was a transfer from the 14th category; there is no transfer from the 15th category. The result is incorrect, because there is an overflow - the value of the number turned out to be greater than what a 16-bit signed number (+32 767) can have.

Example

-30566 = 10001000 10011010

+

-04875 = 11101100 11110101

=

-35441 = 01110101 10001111

There was a transfer from the 15th digit, there is no transfer from the 14th digit. The result is incorrect, because instead of a negative number, it turned out to be positive (the most significant bit is 0).

Example

-4875 = 11101100 11110101

+

-4875 = 11101100 11110101

=

09750 = 11011001

There are transfers from the 14th and 15th bits. The result is correct.

Thus, we examined all cases and found out that the overflow situation (setting the OF flag to 1) occurs during the transfer:

1) from the 14th digit (for signed positive numbers);

2) from the 15th digit (for negative numbers).

Conversely, no overflow occurs (ie, the OF flag is reset to 0) if there is a carry from both bits, or if there is no carry in both bits.

So overflow is registered with the overflow flag of. In addition to the of flag, when transferring from the high order, the transfer flag CF is set to 1. Since the microprocessor does not know about the existence of signed and unsigned numbers, the programmer is solely responsible for the correct actions with the resulting numbers. You can parse the CF and OF flags with the conditional jump instructions JC\JNC and JO\JNO, respectively.

As for the commands for adding numbers with a sign, they are the same as for numbers without a sign.

Subtraction of unsigned binary numbers

As in the analysis of the operation of addition, we will discuss the essence of the processes that occur when performing the operation of subtraction. If the minuend is greater than the subtrahend, then there is no problem - the difference is positive, the result is correct. If the minuend is less than the subtracted, there is a problem: the result is less than 0, and this is already a signed number. In this case, the result must be wrapped. What does this mean? With the usual subtraction (in a column), they make a loan of 1 from the highest order. The microprocessor does the same, i.e., it takes 1 from the digit following the highest one in the bit grid of the operand. Let's explain with an example.

Example

05 = 00000000

-10 = 00000000 00001010

To do the subtraction, let's do

senior imaginary loan:

100000000 00000101

-

00000000 00001010

=

11111111 11111011

Thus, in essence, the action

(65 + 536) - 5 = 10

0 here is, as it were, equivalent to the number 65536. The result, of course, is incorrect, but the microprocessor considers that everything is fine, although it fixes the fact of borrowing a unit by setting the carry flag cf. But look again carefully at the result of the subtraction operation. It's -5 in two's complement! Let's conduct an experiment: represent the difference as a sum of 5 + (-10).

Example

5 = 00000000

+

(-10)= 11111111 11110110

=

11111111 11111011

i.e. we got the same result as in the previous example.

Thus, after the command to subtract unsigned numbers, it is necessary to analyze the state of the CE flag. If it is set to 1, then this indicates that a borrow has occurred from the high order and the result is in the additional code.

Like the addition instructions, the group of subtraction instructions consists of the smallest possible set. These commands perform subtraction according to the algorithms that we are now considering, and the exceptions must be taken into account by the programmer himself. Subtraction commands include:

1) dec operand - decrement operation, i.e. decrease the value of the operand by 1;

2) sub operand_1, operand_2 - subtraction command; its operating principle: operand_1 = operand_1 - operand_2;

3) sbb operand_1, operand_2 - subtraction command taking into account the loan (ci flag): operand_1 = operand_1 - operand_2 - value_sG.

As you can see, among the subtraction commands there is an sbb command that takes into account the carry flag cf. This command is similar to adc, but now the cf flag acts as an indicator of borrowing 1 from the most significant digit when subtracting numbers.

Signed binary subtraction

Here everything is somewhat more complicated. The microprocessor does not need to have two devices - addition and subtraction. It is enough to have only one - the addition device. But for subtraction by the way of adding numbers with a sign in an additional code, it is necessary to represent both operands - both the reduced and the subtracted. The result should also be treated as a two's complement value. But here difficulties arise. First of all, they are related to the fact that the most significant bit of the operand is considered as a sign bit. Consider the example of subtracting 45 - (-127).

Example

Subtraction of signed numbers 1

45 = 0010

-

-127 = 1000 0001

=

-44 = 1010 1100

Judging by the sign bit, the result turned out to be negative, which, in turn, suggests that the number should be considered as a complement equal to -44. The correct result should be 172. Here, as in the case of signed addition, we met with a mantissa overflow, when the significant bit of the number changed the sign bit of the operand. You can track this situation by the contents of the overflow flag of. Setting it to 1 indicates that the result is out of range of signed numbers (i.e., the most significant bit has changed) for an operand of this size, and the programmer must take action to correct the result.

Example

Subtraction of signed numbers 2

-45-45 = -45 + (-45) = -90.

-45 = 11010011

+

-45 = 11010011

=

-90 = 1010 0110

Everything is fine here, the overflow flag of is reset to 0, and 1 in the sign bit indicates that the result value is a two's complement number.

Subtraction and addition of large operands

If you notice, the addition and subtraction instructions work with operands of a fixed dimension: 8, 16, 32 bits. But what if you need to add numbers of a larger dimension, for example 48 bits, using 16-bit operands? For example, let's add two 48-bit numbers:

Rice. 29. Adding large operands

Figure 29 shows the technology for adding long numbers step by step. It can be seen that the process of adding multi-byte numbers occurs in the same way as when adding two numbers "in a column" - with the implementation, if necessary, of transferring 1 to the highest bit. If we manage to program this process, then we will significantly expand the range of binary numbers on which we can perform addition and subtraction operations.

The principle of subtracting numbers with a representation range exceeding the standard operand bit grids is the same as for addition, i.e., the cf carry flag is used. You just need to imagine the process of subtracting in a column and correctly combine the microprocessor instructions with the sbb instruction.

To conclude our discussion of the addition and subtraction instructions, in addition to the cf and of flags, there are several other flags in the eflags register that can be used with binary arithmetic instructions. These are the following flags:

1) zf - zero flag, which is set to 1 if the result of the operation is 0, and to 1 if the result is not equal to 0;

2) sf - sign flag, the value of which after arithmetic operations (and not only) coincides with the value of the most significant bit of the result, i.e. with bit 7, 15 or 31. Thus, this flag can be used for operations on signed numbers.

Multiplication of unsigned numbers

The command for multiplying unsigned numbers is

mul factor_1

As you can see, only one multiplier operand is specified in the command. The second operand factor_2 is implicitly specified. Its location is fixed and depends on the size of the factors. Since, in general, the result of a multiplication is greater than any of its factors, its size and location must also be uniquely determined. Options for the sizes of the factors and the placement of the second operand and the result are shown in Table 10.

Table 10. Arrangement of operands and result in multiplication

It can be seen from the table that the product consists of two parts and, depending on the size of the operands, is placed in two places - in place factor_2 (lower part) and in the additional register ah, dx, edx (higher part). How, then, dynamically (i.e., during program execution) to know that the result is small enough to fit in one register, or that it exceeded the dimension of the register and the highest part ended up in another register? To do this, we use the cf and overflow flags already known to us from the previous discussion:

1) if the leading part of the result is zero, then after the product operation the flags cf = 0 and of = 0;

2) if these flags are non-zero, then this means that the result has gone beyond the smallest part of the product and consists of two parts, which should be taken into account in further work.

Multiply signed numbers

The command for multiplying numbers with a sign is

[imul operand_1, operand_2, operand_3]

This command is executed in the same way as the mul command. A distinctive feature of the imul command is only the formation of the sign.

If the result is small and fits in one register (i.e. if cf = of = 0), then the contents of the other register (the high part) is sign extension - all its bits are equal to the high bit (sign bit) of the low part of the result. Otherwise (if cf = of = 1), the sign of the result is the sign bit of the high part of the result, and the sign bit of the low part is the significant bit of the binary result code.

Division of unsigned numbers

The command for dividing unsigned numbers is

div divider

The divisor can be in memory or in a register and be 8, 16, or 32 bits in size. The location of the dividend is fixed and, like in the multiplication instruction, depends on the size of the operands. The result of the division command is the quotient and remainder values.

Options for the location and size of the operands of the division operation are shown in Table 11.

Table 11. Arrangement of operands and result in division

After the divide instruction is executed, the contents of the flags are undefined, but interrupt number 0, called "divide by zero", may occur. This type of interruption belongs to the so-called exceptions. This kind of interrupt occurs inside the microprocessor due to some anomalies during the computing process. Interrupt O, "divide by zero", while executing the div command can occur for one of the following reasons:

1) the divisor is zero;

2) the quotient is not included in the bit grid allocated for it, which can happen in the following cases:

a) when dividing a dividend with a value of a word by a divisor with a value of bytes, and the value of the dividend is more than 256 times greater than the value of the divisor;

b) when dividing a dividend with a value of a double word by a divisor with a value of a word, and the value of the dividend is more than 65 times greater than the value of the divisor;

c) when dividing the dividend with a quadruple word value by a divisor with a double word value, and the value of the dividend is more than 4 times greater than the value of the divisor.

Division with a sign

The command for dividing numbers with a sign is

idiv divider

For this command, all the considered provisions regarding commands and signed numbers are valid. We only note the features of the occurrence of the exception 0, "division by zero", in the case of numbers with a sign. It occurs when executing the idiv command for one of the following reasons:

1) the divisor is zero;

2) the quotient is not included in the bit grid allocated for it.

The latter, in turn, can happen:

1) when dividing a dividend with a signed word value by a divisor with a signed byte value, and the value of the dividend is more than 128 times the value of the divisor (thus, the quotient should not be outside the range from -128 to + 127);

2) when dividing the dividend by a signed double word value by the divisor by a signed word value, and the value of the dividend is more than 32 times the value of the divisor (thus, the quotient must not be outside the range from -768 to +32) ;

3) when dividing the dividend by a signed quadword value by a signed double word divisor, and the value of the dividend is more than 2 times the value of the divisor (thus, the quotient must not be outside the range of -147 to +483 648 2 147).

Auxiliary Instructions for Integer Operations

There are several instructions in the microprocessor instruction set that can make it easier to program algorithms that perform arithmetic calculations. Various problems may arise in them, for the resolution of which the microprocessor developers have provided several commands.

Type conversion commands

What if the sizes of the operands involved in arithmetic operations are different? For example, suppose in an addition operation, one operand is a word and the other is a double word. It was said above that operands of the same format must participate in the addition operation. If the numbers are unsigned, then the output is easy to find. In this case, on the basis of the original operand, a new one (double word format) can be formed, the high bits of which can simply be filled with zeros. The situation is more complicated for signed numbers: how to take into account the sign of the operand dynamically, during program execution? To solve such problems, the microprocessor instruction set has so-called type conversion instructions. These instructions expand bytes into words, words into double words, and double words into quad words (64-bit values). Type conversion instructions are especially useful when converting signed integers, as they automatically fill in the high-order bits of the newly constructed operand with the values ​​of the old object's sign bit. This operation results in integer values ​​of the same sign and the same magnitude as the original, but in a longer format. Such a transformation is called a sign propagation operation.

There are two kinds of type conversion commands.

1. Instructions without operands. These commands work with fixed registers:

1) cbw (Convert Byte to Word) - a command to convert a byte (in the al register) into a word (in the ah register) by spreading the value of the high bit al to all bits of the ah register;

2) cwd (Convert Word to Double) - a command to convert a word (in register ax) into a double word (in registers dx: ax) by spreading the value of the high bit ax to all bits of register dx;

3) cwde (Convert Word to Double) - a command to convert a word (in the register ax) into a double word (in the register eax) by spreading the value of the most significant bit of ax to all bits of the upper half of the eax register;

4) cdq (Convert Double Word to Quarter Word) - a command to convert a double word (in the eax register) into a quadruple word (in the edx: eax registers) by spreading the value of the most significant bit of eax to all bits of the edx register.

2. Commands movsx and movzx related to string processing commands. These commands have a useful property in the context of our problem:

1) movsx operand_1, operand_2 - send with sign propagation. Extends the 8 or 16-bit value of operand_2, which can be a register or a memory operand, to a 16 or 32-bit value in one of the registers, using the value of the sign bit to fill the higher positions of operand_1. This instruction is useful for preparing signed operands for arithmetic operations;

2) movzx operand_1, operand_2 - send with zero extension. Extends the 8-bit or 16-bit value of operand_2 to 16-bit or 32-bit, clearing (filling) the high positions of operand_2 with zeros. This instruction is useful for preparing unsigned operands for arithmetic.

Other useful commands

1. xadd destination, source - exchange and addition.

The command allows you to perform two actions in sequence:

1) exchange destination and source values;

2) place the destination operand in place of the sum: destination = destination + source.

2. neg operand - negation with two's complement.

The instruction inverts the value of the operand. Physically, the command performs one action:

operand = 0 - operand, i.e. subtracts the operand from zero.

The neg operand command can be used:

1) to change the sign;

2) to perform subtraction from a constant.

Arithmetic operations on binary-decimal numbers

In this section, we will look at the specifics of each of the four basic arithmetic operations for packed and unpacked BCD numbers.

The question may rightly arise: why do we need BCD numbers? The answer might be: BCD numbers are needed in business applications, i.e. where the numbers need to be large and precise. As we have already seen on the example of binary numbers, operations with such numbers are quite problematic for assembly language. The disadvantages of using binary numbers include the following:

1) Values ​​in word and double word format have a limited range. If the program is designed to work in the field of finance, then limiting the amount in rubles to 65 (for a word) or even 536 (for a double word) will significantly narrow the scope of its application;

2) the presence of rounding errors. Can you imagine a program running somewhere in a bank that does not take into account the value of the balance when operating with binary integers and operates with billions? I would not like to be the author of such a program. The use of floating point numbers will not save - the same rounding problem exists there;

3) presentation of a large amount of results in symbolic form (ASCII code). Business programs don't just do calculations; one of the purposes of their use is the prompt delivery of information to the user. To do this, of course, the information must be presented in symbolic form. Converting numbers from binary to ASCII requires some computational effort. A floating point number is even more difficult to translate into a symbolic form. But if you look at the hexadecimal representation of an unpacked decimal digit and its corresponding character in the ASCII table, you can see that they differ by 30h. Thus, the conversion to symbolic form and vice versa is much easier and faster.

You have probably already seen the importance of mastering at least the basics of actions with decimal numbers. Next, consider the features of performing basic arithmetic operations with decimal numbers. We immediately note the fact that there are no separate commands for addition, subtraction, multiplication and division of BCD numbers. This was done for quite understandable reasons: the dimension of such numbers can be arbitrarily large. BCD numbers can be added and subtracted, both packed and unpacked, but only unpacked BCD numbers can divide and multiply. Why this is so will be seen from further discussion.

Arithmetic on unpacked BCD numbers

Add unpacked BCD numbers

Let's consider two cases of addition.

Example

The result of addition is not more than 9

6 = 0000

+

3 = 0000

=

9 = 0000

There is no transfer from the junior to the senior tetrad. The result is correct.

Example

The result of addition is greater than 9:

06 = 0000

+

07 = 0000

=

13 = 0000

We have received no longer a BCD number. The result is wrong. The correct result in unpacked BCD format should be 0000 0001 0000 0011 in binary (or 13 in decimal).

After analyzing this problem when adding BCD numbers (and similar problems when performing other arithmetic operations) and possible ways to solve it, the developers of the microprocessor command system decided not to introduce special commands for working with BCD numbers, but to introduce several corrective commands.

The purpose of these instructions is to correct the result of the operation of ordinary arithmetic instructions for cases where the operands in them are BCD numbers.

In the case of subtraction in example 10, it can be seen that the result obtained needs to be corrected. To correct the operation of adding two single-digit unpacked BCD numbers in the microprocessor command system, there is a special command - aaa (ASCII Adjust for Addition) - correction of the result of addition for representation in symbolic form.

This instruction has no operands. It works implicitly only with the al register and parses the value of its lower tetrad:

1) if this value is less than 9, then the cf flag is reset to XNUMX and the transition to the next instruction is carried out;

2) if this value is greater than 9, then the following actions are performed:

a) 6 is added to the contents of the lower tetrad al (but not to the contents of the entire register!) Thus, the value of the decimal result is corrected in the correct direction;

b) the flag cf is set to 1, thereby fixing the transfer to the most significant bit so that it can be taken into account in subsequent actions.

So, in example 10, assuming that the sum value 0000 1101 is in al, after the aaa instruction, the register will have 1101 + 0110 = 0011, i.e. binary 0000 0011 or decimal 3, and the cf flag will be set to 1, i.e. The transfer has been stored in the microprocessor. Next, the programmer will need to use the adc addition instruction, which will take into account the carry from the previous bit.

Subtraction of unpacked BCD numbers

The situation here is quite similar to addition. Let's consider the same cases.

Example

The result of subtraction is not greater than 9:

6 = 0000

-

3 = 0000

=

3 = 0000

As you can see, there is no loan from the senior notebook. The result is correct and does not require correction.

Example

The result of subtraction is greater than 9:

6 = 0000

-

7 = 0000

=

-1 = 1111 1111

Subtraction is carried out according to the rules of binary arithmetic. Therefore, the result is not a BCD number.

The correct result in unpacked BCD format should be 9 (0000 1001 in binary). In this case, a borrow from the most significant digit is assumed, as with a normal subtraction command, i.e. in the case of BCD numbers, subtraction 16 - 7 should actually be performed. Thus, it is clear that, as in the case of addition, the subtraction result must be corrected. For this, there is a special command - aas (ASCII Adjust for Substraction) - correction of the result of subtraction for representation in symbolic form.

The aas instruction also has no operands and operates on the al register, parsing its least-order tetrad as follows:

1) if its value is less than 9, then the cf flag is reset to 0 and control is transferred to the next command;

2) if the value of the tetrad in al is greater than 9, then the aas command performs the following actions:

a) subtracts 6 from the contents of the lower tetrad of register al (note - not from the contents of the entire register);

b) resets the upper tetrad of register al;

c) sets the cf flag to 1, thereby fixing the imaginary high-order borrow.

It is clear that the aas command is used in conjunction with the basic sub and sbb subtraction commands. In this case, it makes sense to use the sub command only once, when subtracting the lowest digits of the operands, then the sbb command should be used, which will take into account a possible loan from the highest order.

Multiplication of unpacked BCD numbers

Using the example of adding and subtracting unpacked numbers, it became clear that there are no standard algorithms for performing these operations on BCD numbers, and the programmer must himself, based on the requirements for his program, implement these operations.

The implementation of the two remaining operations - multiplication and division - is even more complicated. In the microprocessor instruction set, there are only means for the production of multiplication and division of single-digit unpacked BCD numbers.

In order to multiply numbers of arbitrary dimension, you need to implement the multiplication process yourself, based on some multiplication algorithm, for example, "in a column".

In order to multiply two one-digit BCD numbers, you must:

1) place one of the factors in the AL register (as required by the mul instruction);

2) place the second operand in a register or memory, allocating a byte;

3) multiply the factors with the mul command (the result, as expected, will be in ah);

4) the result, of course, will be in binary code, so it needs to be corrected.

To correct the result after multiplication, a special command is used - aam (ASCII Adjust for Multiplication) - correction of the result of multiplication for representation in symbolic form.

It has no operands and operates on the AX register as follows:

1) divides al by 10;

2) the result of division is written as follows: quotient in al, remainder in ah. As a result, after executing the aam instruction, the AL and ah registers contain the correct BCD digits of the product of two digits.

Before we end our discussion of the aam command, we need to note one more use for it. This command can be used to convert a binary number in the AL register into an unpacked BCD number, which will be placed in the ah register: the most significant digit of the result is in ah, the least significant digit is in al. It is clear that the binary number must be in the range 0... 99.

Division of unpacked BCD numbers

The process of dividing two unpacked BCD numbers is somewhat different from the other previously considered operations with them. Correction actions are also required here, but they must be carried out before the main operation that directly divides one BCD number by another BCD number. First, in the register ah, you need to get two unpacked BCD digits of the dividend. This makes the programmer comfortable for him in a way. Next, you need to issue the command aad - aad (ASCII Adjust for Division) - division correction for symbolic representation.

The instruction has no operands and converts the two-digit unpacked BCD number in the ax register to a binary number. This binary number will subsequently play the role of the dividend in the division operation. In addition to the conversion, the aad command places the resulting binary number in the AL register. The dividend will naturally be a binary number from the range 0... 99.

The algorithm by which the aad command performs this conversion is as follows:

1) multiply the highest digit of the original BCD number in ah (the content of AH) by 10;

2) perform the addition AH + AL, the result of which (binary number) is entered in AL;

3) reset the contents of AH.

Next, the programmer needs to issue the usual div division instruction to perform division of the contents of ax by a single BCD digit located in a byte register or byte memory location.

Similar to aash, the aad command can also be used to convert unpacked BCD numbers from the range 0... 99 to their binary equivalent.

To divide numbers of greater capacity, as well as in the case of multiplication, you need to implement your own algorithm, for example, "in a column", or find a more optimal way.

Arithmetic on Packed BCD Numbers

As noted above, packed BCD numbers can only be added and subtracted. To perform other actions on them, they must be additionally converted either to an unpacked format or to a binary representation. Due to the fact that packed BCD numbers are not of great interest, we will consider them briefly.

Adding Packed BCD Numbers

First, let's get to the heart of the problem and try to add two two-digit packed BCD numbers. Example Adding Packed BCD Numbers:

= 67 01100111

+

= 75 01110101

=

142 = 1101 1100 = 220

As you can see, in binary the result is 1101 1100 (or 220 in decimal), which is incorrect. This is because the microprocessor is unaware of the existence of BCD numbers and adds them according to the rules for adding binary numbers. Actually, the result in BCD should be 0001 0100 0010 (or 142 in decimal).

It can be seen that, as for unpacked BCD numbers, for packed BCD numbers there is a need to somehow correct the results of arithmetic operations.

The microprocessor provides for this command daa - daa (Decimal Adjust for Addition) - correction of the result of addition for presentation in decimal form.

The daa command converts the contents of the al register into two packed decimal digits according to the algorithm given in the description of the daa command. The resulting unit (if the result of the addition is greater than 99) is stored in the cf flag, thereby taking into account the transfer to the most significant bit.

Subtraction of packed BCD numbers

Similar to addition, the microprocessor treats the packed BCD numbers as binary and subtracts the BCD numbers as binary accordingly.

Example

Subtraction of packed BCD numbers.

Let's subtract 67-75. Since the microprocessor performs subtraction in the way of addition, we will follow this:

= 67 01100111

+

-75 = 10110101

=

-8 = 0001 1100 = 28

As you can see, the result is 28 in decimal, which is absurd. In BCD, the result should be 0000 1000 (or 8 in decimal).

When programming the subtraction of packed BCD numbers, the programmer, as well as when subtracting unpacked BCD numbers, must control the sign himself. This is done using the CF flag, which fixes the high-order borrow.

The subtraction of BCD numbers itself is done by a simple sub or sbb subtraction command. Correction of the result is carried out by the command das - das (Decimal Adjust for Substraction) - correction of the result of subtraction for representation in decimal form.

The das command converts the contents of the AL register to two packed decimal digits according to the algorithm given in the description of the das command.

LECTURE No. 19. Control transfer commands

1. Logic commands

Along with the means of arithmetic calculations, the microprocessor command system also has means of logical data conversion. By logical means such data transformations, which are based on the rules of formal logic.

Formal logic operates at the level of true and false statements. For a microprocessor, this usually means 1 and 0, respectively. For a computer, the language of zeros and ones is native, but the minimum unit of data with which machine instructions work is a byte. However, at the system level, it is often necessary to be able to operate at the lowest possible level, the bit level.

Rice. 29. Means of logical data processing

The means of logical data transformation include logical commands and logical operations. The operand of an assembler instruction can generally be an expression, which, in turn, is a combination of operators and operands. Among these operators there may be operators that implement logical operations on expression objects.

Before a detailed consideration of these tools, let's consider what the logical data themselves are and what operations are performed on them.

Boolean data

The theoretical basis for logical data processing is formal logic. There are several systems of logic. One of the most famous is the propositional calculus. A proposition is any statement that can be said to be either true or false.

The propositional calculus is a set of rules used to determine the truth or falsity of some combination of propositions.

The propositional calculus is very harmoniously combined with the principles of the computer and the basic methods of its programming. All hardware components of a computer are built on logic chips. The system for representing information in a computer at the lowest level is based on the concept of a bit. A bit, having only two states (0 (false) and 1 (true)), naturally fits into the propositional calculus.

According to the theory, the following logical operations can be performed on statements (on bits).

1. Negation (logical NOT) - a logical operation on one operand, the result of which is the reciprocal of the value of the original operand.

This operation is uniquely characterized by the following truth table (Table 12).

Table 12. Truth table for logical negation

2. Logical addition (logical inclusive OR) - a logical operation on two operands, the result of which is "true" (1) if one or both operands are true (1), and "false" (0) if both operand is false (0).

This operation is described using the following truth table (Table 13).

Table 13. Truth table for logical inclusive OR

3. Logical multiplication (logical AND) - a logical operation on two operands, the result of which is true (1) only if both operands are true (1). In all other cases, the value of the operation is "false" (0).

This operation is described using the following truth table (Table 14).

Table 14. Logic AND truth table

4. Logical exclusive addition (logical exclusive OR) - a logical operation on two operands, the result of which is "true" (1), if only one of the two operands is true (1), and false (0), if both operand is either false (0) or true (1). This operation is described using the following truth table (Table 15).

Table 15. Truth table for logical XOR

The microprocessor instruction set contains five instructions that support these operations. These instructions perform logical operations on the bits of the operands. The dimensions of the operands, of course, must be the same. For example, if the dimension of the operands is equal to a word (16 bits), then the logical operation is performed first on zero bits of the operands, and its result is written to the place of bit 0 of the result. Next, the command sequentially repeats these actions on all bits from the first to the fifteenth.

Logic commands

The microprocessor command system has the following set of commands that support working with logical data:

1) and operand_1, operand_2 - logical multiplication operation. The command performs a bitwise logical AND operation (conjunction) on the bits of the operands operand_1 and operand_2. The result is written in place of operand_1;

2) og operand_1, operand_2 - logical addition operation. The command performs a bitwise logical OR operation (disjunction) on the bits of the operands operand_1 and operand_2. The result is written in place of operand_1;

3) xor operand_1, operand_2 - operation of logical exclusive addition. The command performs a bitwise logical XOR operation on the bits of the operands operand_1 and operand_2. The result is written in place of the operand;

4) test operand_1, operand_2 - "test" operation (using the logical multiplication method). The command performs a bitwise logical AND operation on the bits of the operands operand_1 and operand_2. The state of the operands remains the same, only the flags zf, sf, and pf are changed, which makes it possible to analyze the state of individual bits of the operand without changing their state;

5) not operand - operation of logical negation. The command performs a bitwise inversion (replacing the value with the opposite) of each bit of the operand. The result is written in place of the operand.

In order to understand the role of logical commands in the microprocessor command system, it is very important to understand the areas of their application and typical methods of their use in programming.

With the help of logical commands, it is possible to select individual bits in the operand for the purpose of setting them, resetting them, inverting them, or simply checking for a certain value.

To organize such work with bits, operand_2 usually plays the role of a mask. With the help of the bits of this mask set in bit 1, the operand_1 bits necessary for a particular operation are determined. Let's show what logical commands can be used for this purpose:

1) to set certain digits (bits) to 1, the command og operand_1, operand_2 is used.

In this instruction, operand_2, which acts as a mask, must contain 1 bits in place of those bits that must be set to 1 in operand_XNUMX;

2) to reset certain digits (bits) to 0, the command and operand_1, operand_2 is used.

In this instruction, operand_2, which acts as a mask, must contain zero bits in place of those bits that must be set to 0 in operand_1;

3) command xor operand_1, operand_2 is applied:

a) to find out which bits in operand_1 and operand differ;

b) to invert the state of the specified bits in operand_1.

The mask bits of interest to us (operand_2) when executing the xor command must be single, the rest must be zero;

The command test operand_1, operand_2 (check operand_1) is used to check the status of the specified bits.

The tested bits of operand_1 in the mask (operand_2) must be set to one. The algorithm of the test command is similar to the algorithm of the and command, but it does not change the value of operand_1. The result of the command is to set the value of the zero flag zf:

1) if zf = 0, then as a result of logical multiplication, a zero result was obtained, i.e., one unit bit of the mask, which did not match the corresponding unit bit of the operand;

2) if zf = 1, then as a result of logical multiplication, a non-zero result is obtained, i.e. at least one unit bit of the mask coincides with the corresponding unit bit of operand_1.

To react to the result of the test command, it is advisable to use the jump command jnz label (Jump if Not Zero) - jump if the zero flag zf is non-zero, or the command with the opposite action - jz label (Jump if Zero) - jump if the zero flag zf = 0.

The following two commands search for the first operand bit set to 1. The search can be performed both from the beginning and from the end of the operand:

1) bsf operand_1, operand_2 (Bit Scanning Forward) - scanning bits forward. The instruction searches (scans) the bits of operand_2 from the least significant to the most significant (from bit 0 to the most significant bit) in search of the first bit set to 1. If one is found, operand_1 is filled with the number of this bit as an integer value. If all bits of operand_2 are 0, then the zero flag zf is set to 1, otherwise the zf flag is reset to 0;

2) bsr operand_1, operand_2 (Bit Scanning Reset) - scan bits in reverse order. The instruction searches (scans) the bits of operand_2 from high to low (from most significant bit to bit 0) in search of the first bit set to 1. If one is found, operand_1 is filled with the number of this bit as an integer value. It is important that the position of the first unit bit on the left is still counted relative to bit 0. If all bits of operand_2 are 0, then the zero flag zf is set to 1, otherwise the flag zf is reset to 0.

In the latest models of Intel microprocessors, several more instructions have appeared in the group of logical instructions that allow you to access one specific bit of the operand. The operand can be either in memory or in a general register. The bit position is given by the bit offset relative to the least significant bit of the operand. The offset value can be specified either as a direct value or contained in a general purpose register. You can use the results of the bsr and bsf commands as the offset value. All instructions assign the value of the selected bit to the CE flag.

1) bt operand, bit_offset (Bit Test) - bit test. The instruction transfers the bit value to the cf flag;

2) bts operand, offset_bit (Bit Test and Set) - checking and setting a bit. The instruction transfers the bit value to the CF flag and then sets the bit to be checked to 1;

3) btr operand, bit_offset (Bit Test and Reset) - checking and resetting a bit. The instruction transfers the bit value to the CF flag and then sets this bit to 0;

4) btc operand, offset_bit (Bit Test and Convert) - checking and inverting a bit. The instruction wraps the value of a bit in the cf flag and then inverts the value of that bit.

Shift Commands

The instructions in this group also provide manipulation of individual bits of the operands, but in a different way than the logical instructions discussed above.

All shift instructions move bits in the operand field left or right depending on the opcode. All shift instructions have the same structure - copy operand, shift_count.

The number of shifted bits - shift_counter - is located at the place of the second operand and can be set in two ways:

1) statically, which involves setting a fixed value using a direct operand;

2) dynamically, which means entering the value of the shift counter into the cl register before executing the shift instruction.

Based on the dimension of the cl register, it is clear that the value of the shift counter can range from 0 to 255. But in fact, this is not entirely true. For optimization purposes, the microprocessor accepts only the value of the five least significant bits of the counter, i.e. the value lies in the range from 0 to 31.

All shift instructions set the carry flag cf.

As bits shift out of the operand, they first hit the carry flag, setting it equal to the value of the next bit outside the operand. Where this bit goes next depends on the type of shift instruction and the program algorithm.

Shift commands can be divided into two types according to the principle of operation:

1) linear shift commands;

2) cyclic shift commands.

Linear shift commands

Commands of this type include commands that shift according to the following algorithm:

1) the next bit that is pushed sets the CF flag;

2) the bit entered into the operand from the other end has the value 0;

3) when the next bit is shifted, it goes into the CF flag, while the value of the previous shifted bit is lost! Linear shift commands are divided into two subtypes:

1) logical linear shift commands;

2) arithmetic linear shift instructions.

The logical linear shift commands include the following:

1) shl operand, counter_shifts (Shift Logical Left) - logical shift to the left. The content of the operand is shifted to the left by the number of bits specified by shift_count. On the right (in the position of the least significant bit) zeros are entered;

2) shr operand, shift_count (Shift Logical Right) - logical shift to the right. The content of the operand is shifted to the right by the number of bits specified by shift_count. On the left (in the position of the most significant, sign bit), zeros are entered.

Figure 30 shows how these commands work.

Rice. 30. Scheme of work of commands of linear logical shift

Arithmetic linear shift instructions differ from logical shift instructions in that they operate on the sign bit of the operand in a special way.

1) sal operand, shift_counter (Shift Arithmetic Left) - arithmetic shift to the left. The content of the operand is shifted to the left by the number of bits specified by shift_count. On the right (in the position of the least significant bit), zeros are entered. The sal instruction does not preserve the sign, but sets the flag with / in case of a sign change by the next bit advanced. Otherwise, the sal command is exactly the same as the shl command;

2) sar operand, shift_count (Shift Arithmetic Right) - arithmetic right shift. The content of the operand is shifted to the right by the number of bits specified by shift_count. Zeros are inserted into the operand on the left. The sar command preserves the sign, restoring it after each bit shift.

Figure 31 shows how linear arithmetic shift instructions work.

Rice. 31. Scheme of operation of linear arithmetic shift commands

Rotate Commands

The cyclic shift instructions include instructions that store the values ​​of the shifted bits. There are two types of cyclic shift instructions:

1) simple cyclic shift commands;

2) cyclic shift commands via the carry flag cf.

Simple cyclic shift commands include:

1) rol operand, shift_counter (Rotate Left) - cyclic shift to the left. The content of the operand is shifted to the left by the number of bits specified by the shift_count operand. Left-shifted bits are written to the same operand from the right;

2) gog operand, counter_shifts (Rotate Right) - cyclic shift to the right. The contents of the operand are shifted to the right by the number of bits specified by the shift_count operand. Right-shifted bits are written to the same operand on the left.

Rice. 32. Scheme of operation of commands of a simple cyclic shift

As can be seen from Figure 32, the instructions of a simple cyclic shift in the course of their work perform one useful action, namely: the cyclically shifted bit is not only inserted into the operand from the other end, but at the same time its value becomes the value of the CE flag.

The cyclic shift commands through the carry flag CF differ from the simple cyclic shift commands in that the shifted bit does not immediately enter the operand from its other end, but is first written to the carry flag CE Only the next execution of this shift command (provided that it is executed in loop) causes the previously advanced bit to be placed at the other end of the operand (Fig. 33).

The following are related to the cyclic shift commands via the carry flag:

1) rcl operand, shift_count (Rotate through Carry Left) - cyclic left shift through carry.

The content of the operand is shifted to the left by the number of bits specified by the shift_count operand. The shifted bits in turn become the value of the carry flag cf.

2) rsg operand, shift_count (Rotate through Carry Right) - cyclic shift to the right through a carry.

The contents of the operand are shifted to the right by the number of bits specified by the shift_count operand. The shifted bits in turn become the value of the carry flag CF.

Rice. 33. Rotate Instructions via Carry Flag CF

Figure 33 shows that when shifting through the carry flag, an intermediate element appears, with the help of which, in particular, it is possible to replace cyclically shifted bits, in particular, the mismatch of bit sequences.

Hereinafter, mismatch of a bit sequence means an action that allows in some way to localize and extract the necessary sections of this sequence and write them to another place.

Additional shift commands

The command system of the latest Intel microprocessor models, starting with the i80386, contains additional shift commands that expand the capabilities we discussed earlier. These are the double precision shift commands:

1) shld operand_1, operand_2, shift_counter - double precision left shift. The shld command performs a replacement by shifting the bits of operand_1 to the left, filling its bits on the right with the values ​​of the bits displaced from operand_2 according to the diagram in Fig. 34. The number of bits to be shifted is determined by the shift_counter value, which can be in the range 0... 31. This value can be specified as an immediate operand or contained in the cl register. The value of operand_2 is not changed.

Rice. 34. The scheme of the shld command

2) shrd operand_1, operand_2, shift_counter - double precision right shift. The instruction performs the replacement by shifting the bits of the operand_1 operand to the right, filling its bits on the left with the values ​​of the bits displaced from operand_2 according to the diagram in Figure 35. The number of bits to be shifted is determined by the value of the shift_counter, which can lie in the range 0... 31. This value can be specified by the immediate operand or contained in the cl register. The value of operand_2 is not changed.

Rice. 35. The scheme of the shrd command

As we noted, the shld and shrd commands perform shifts up to 32 bits, but due to the peculiarities of specifying operands and the operation algorithm, these commands can be used to work with fields up to 64 bits long.

2. Control Transfer Commands

We got acquainted with some commands from which the linear sections of the program are formed. Each of them generally performs some action to convert or transfer data, after which the microprocessor transfers control to the next instruction. But very few programs work in such a consistent way. There are usually points in a program where a decision must be made about which instruction will be executed next. This solution could be:

1) unconditional - at this point, it is necessary to transfer control not to the command that comes next, but to another, which is at some distance from the current command;

2) conditional - the decision about which command will be executed next is made on the basis of an analysis of some conditions or data.

A program is a sequence of commands and data that occupy a certain amount of RAM space. This memory space can either be contiguous or consist of multiple fragments.

Which program instruction should be executed next, the microprocessor learns from the contents of the cs: (e) ip register pair:

1) cs - code segment register, which contains the physical (base) address of the current code segment;

2) eip/ip - instruction pointer register, which contains a value representing the offset in memory of the next instruction to be executed relative to the beginning of the current code segment.

Which particular register will be used depends on the set addressing mode use16 or use32. If use 16 is specified, then ip is used, if use32, then eip is used.

Thus, control transfer instructions change the contents of the cs and eip / ip registers, as a result of which the microprocessor selects for execution not the next program instruction in order, but an instruction in some other section of the program. The pipeline inside the microprocessor is reset.

According to the principle of operation, the microprocessor commands that provide the organization of transitions in the program can be divided into 3 groups:

1. Unconditional transfer of control commands:

1) an unconditional branch command;

2) a command to call a procedure and return from a procedure;

3) command to call software interrupts and return from software interrupts.

2. Commands for conditional transfer of control:

1) jump commands by the result of the comparison command p;

2) transition commands according to the state of a certain flag;

3) instructions for jumping through the contents of the esx/cx register.

3. Cycle control commands:

1) a command for organizing a cycle with a counter ехх/сх;

2) a command for organizing a cycle with a counter ех/сх with the possibility of early exit from the cycle by an additional condition.

Unconditional jumps

The previous discussion has revealed some details of the transition mechanism. Jump instructions modify the eip/ip instruction pointer register and possibly the cs code segment register. What exactly needs to be modified depends on:

1) on the type of operand in the unconditional branch instruction (near or far);

2) from specifying a modifier before the jump address (in the jump instruction); in this case, the jump address itself can be located either directly in the instruction (direct jump), or in a register or memory cell (indirect jump).

The modifier can take the following values:

1) near ptr - direct transition to a label inside the current code segment. Only the eip/ip register is modified (depending on the specified use16 or use32 code segment type) based on the address (label) specified in the command or an expression using the value extraction symbol - $;

2) far ptr - direct transition to a label in another code segment. The jump address is specified as an immediate operand or address (label) and consists of a 16-bit selector and a 16/32-bit offset, which are loaded into the cs and ip/eip registers, respectively;

3) word ptr - indirect transition to a label inside the current code segment. Only eip/ip is modified (by the offset value from memory at the address specified in the command, or from a register). Offset size 16 or 32 bits;

4) dword ptr - indirect transition to a label in another code segment. Both registers - cs and eip / ip - are modified (by a value from memory - and only from memory, from a register). The first word/dword of this address represents the offset and is loaded into ip/eip; the second/third word is loaded into cs. jmp unconditional jump instruction

The command syntax for an unconditional jump is jmp [modifier] jump_address - an unconditional jump without saving information about the return point.

Jump_address is the address in the form of a label or the address of the memory area in which the jump pointer is located.

In total, in the microprocessor instruction system there are several codes of machine instructions for the unconditional jump jmp.

Their differences are determined by the transition distance and the way the target address is specified. The jump distance is determined by the location of the jump_address operand. This address may be in the current code segment or in some other segment. In the first case, the transition is called intra-segment, or close, in the second - inter-segment, or distant. An intra-segment jump assumes that only the contents of the eip/ip register are changed.

There are three options for intra-segment use of the jmp command:

1) straight short;

2) straight;

3) indirect.

Процедуры

Assembly language has several tools that solve the problem of duplicating sections of code. These include:

1) mechanism of procedures;

2) macro assembler;

3) interrupt mechanism.

A procedure, often also called a subroutine, is the basic functional unit for decomposing (dividing into several parts) a task. A procedure is a group of commands for solving a specific subtask and has the means of receiving control from the point where the task of a higher level is called and returning control to this point.

In the simplest case, the program may consist of a single procedure. In other words, a procedure can be defined as a well-formed set of commands, which, being described once, can be called anywhere in the program if necessary.

To describe a sequence of commands as a procedure in assembly language, two directives are used: PROC and ENDP.

The procedure description syntax is as follows (Fig. 36).

Rice. 36. Syntax of the description of the procedure in the program

Figure 36 shows that in the procedure header (PROC directive) only the procedure name is mandatory. Among the large number of operands of the PROC directive, [distance] should be highlighted. This attribute can take the values ​​near or far and characterizes the possibility of calling the procedure from another code segment. By default, the [distance] attribute is set to near.

The procedure can be placed anywhere in the program, but in such a way that it does not randomly get control. If the procedure is simply inserted into the general instruction stream, then the microprocessor will perceive the instructions of the procedure as part of this stream and, accordingly, will execute the instructions of the procedure.

Conditional Jumps

The microprocessor has 18 conditional jump instructions. These commands allow you to check:

1) the relationship between operands with a sign ("greater - less");

2) the relation between operands without a sign ("higher - lower");

3) states of arithmetic flags ZF, SF, CF, OF, PF (but not AF).

Conditional jump commands have the same syntax:

jcc jump_label

As you can see, the mnemonic code of all commands begins with "j" - from the word jump (jump), it - determines the specific condition analyzed by the command.

As for the jump_label operand, this label can only be located within the current code segment, inter-segment transfer of control in conditional jumps is not allowed. In this regard, there is no question about the modifier, which was present in the syntax of the unconditional jump commands. In early models of the microprocessor (i8086, i80186 and i80286), conditional branch instructions could only make short jumps - from -128 to +127 bytes from the instruction following the conditional branch instruction. Starting with microprocessor model 80386, this restriction is removed, but, as you can see, only within the current code segment.

In order to make a decision about where control will be transferred to the conditional jump command, a condition must first be formed, on the basis of which the decision to transfer control will be made.

Sources of such a condition can be:

1) any command that changes the state of arithmetic flags;

2) the comparison instruction p, which compares the values ​​of two operands;

3) the state of the esx/cx register.

cmp comparison command

The page compare command has an interesting way of working. It is exactly the same as the subtraction command - sub operand, operand_2.

The p instruction, like the sub instruction, subtracts operands and sets flags. The only thing it doesn't do is write the result of the subtraction in place of the first operand.

The command syntax str - str operand_1, operand_2 (compare) - compares two operands and sets flags based on the results of the comparison.

The flags set by the p command can be analyzed by special conditional branch instructions. Before we look at them, let's pay a little attention to the mnemonics of these conditional jump instructions (Table 16). Understanding the notation when forming the name of the conditional jump commands (the element in the name of the jcc command, we designated it) will facilitate their memorization and further practical use.

Table 16. Meaning of abbreviations in the jcc command name Table 17. List of conditional jump commands for the command p operand_1, operand_2

Do not be surprised by the fact that several different mnemonic codes of conditional branch commands correspond to the same flag values ​​(they are separated from each other by a slash in Table 17). The difference in name is due to the desire of microprocessor developers to make it easier to use conditional jump instructions in combination with certain groups of instructions. Therefore, different names reflect rather a different functional orientation. However, the fact that these commands respond to the same flags makes them absolutely equivalent and equal in the program. Therefore, in Table 17 they are grouped not by name, but by the values ​​of the flags (conditions) to which they respond.

Conditional Branch Instructions and Flags

The mnemonic designation of some conditional jump instructions reflects the name of the flag with which they work, and has the following structure: the first character is "j" (Jump, jump), the second is either the flag designation or the negation character "n", followed by the name of the flag . This team structure reflects its purpose. If there is no character "n", then the state of the flag is checked, if it is equal to 1, a transition to the jump label is made. If the character "n" is present, then the flag state is checked for equality to 0, and if successful, a jump to the jump label is made.

Command mnemonics, flag names, and jump conditions are shown in Table 18. These commands can be used after any commands that modify the specified flags.

Table 18. Conditional Jump Instructions and Flags

If you look closely at tables 17 and 18, you can see that many of the conditional jump instructions in them are equivalent, since both of them are based on the analysis of the same flags.

Conditional Jump Instructions and the esx/cx Register

The architecture of the microprocessor involves the specific use of many registers. For example, the EAX / AX / AL register is used as an accumulator, and the BP, SP registers are used to work with the stack. The ECX / CX register also has a certain functional purpose: it acts as a counter in loop control commands and when working with character strings. It is possible that functionally the conditional branch instruction associated with the esx/cx register would be more correctly attributed to this group of instructions.

The syntax for this conditional branch instruction is:

1) jcxz jump_label (Jump if ex is Zero) - jump if cx is zero;

2) jecxz jump_label (Jump Equal ех Zero) - jump if ех is zero.

These commands are very useful when looping and when working with character strings.

It should be noted that there is a limitation inherent in the jcxz/jecxz command. Unlike other conditional transfer instructions, the jcxz/jecxz instruction can only address short jumps -128 bytes or +127 bytes from the instruction following it.

Cycle organization

The cycle, as you know, is an important algorithmic structure, without the use of which, probably, no program can do. You can organize the cyclic execution of a certain section of the program, for example, using the conditional transfer of control commands or the unconditional jump command jmp. With this organization of the cycle, all operations for its organization are performed manually. But, given the importance of such an algorithmic element as a cycle, the developers of the microprocessor introduced a group of three commands into the instruction system, which facilitates the programming of cycles. These instructions also use the esx/cx register as a loop counter.

Let's give a brief description of these commands:

1) loop transition_label (Loop) - repeat the loop. The command allows you to organize loops similar to for loops in high-level languages ​​with automatic decrement of the loop counter. The team's job is to do the following:

a) decrement of the ECX/CX register;

b) comparing the ECX/CX register with zero: if (ECX/CX) = 0, then control is transferred to the next command after loop;

2) loope/loopz jump_label

The loope and loopz commands are absolute synonyms. The work of commands is to perform the following actions:

a) decrement of the ECX/CX register;

b) comparing the ECX/CX register with zero;

c) analysis of the status of the zero flag ZF if (ECX/CX) = 0 or XF = 0, control is transferred to the next command after loop.

3) loopne/loopnz jump_label

The commands loopne and loopnz are also absolute synonyms. The work of commands is to perform the following actions:

a) decrement of the ECX/CX register;

b) comparing the ECX/CX register with zero;

c) analysis of the state of the zero flag ZF: if (ECX/CX) = 0 or ZF = 1, control is transferred to the next command after loop.

The loope/loopz and loopne/loopnz commands are reciprocal in their operation. They extend the action of the loop command by additionally parsing the zf flag, which makes it possible to organize an early exit from the loop, using this flag as an indicator.

The drawback of the loop, loope/loopz, and loopne/loopnz commands is that they only implement short jumps (-128 to +127 bytes). To work with long loops, you will need to use conditional jumps and the jmp instruction, so try to master both ways of organizing loops.

Author: Tsvetkova A.V.

<< Back: commands (Data transfer commands. Arithmetic commands)

We recommend interesting articles Section Lecture notes, cheat sheets:

Notaries. Crib

Roman law. Lecture notes

Faculty Therapy. Crib

See other articles Section Lecture notes, cheat sheets.

Read and write useful comments on this article.

<< Back

Latest news of science and technology, new electronics:

The existence of an entropy rule for quantum entanglement has been proven 09.05.2024

Quantum mechanics continues to amaze us with its mysterious phenomena and unexpected discoveries. Recently, Bartosz Regula from the RIKEN Center for Quantum Computing and Ludovico Lamy from the University of Amsterdam presented a new discovery that concerns quantum entanglement and its relation to entropy. Quantum entanglement plays an important role in modern quantum information science and technology. However, the complexity of its structure makes understanding and managing it challenging. Regulus and Lamy's discovery shows that quantum entanglement follows an entropy rule similar to that for classical systems. This discovery opens new perspectives in the field of quantum information science and technology, deepening our understanding of quantum entanglement and its connection to thermodynamics. The results of the study indicate the possibility of reversibility of entanglement transformations, which could greatly simplify their use in various quantum technologies. Opening a new rule ... >>

Mini air conditioner Sony Reon Pocket 5 09.05.2024

Summer is a time for relaxation and travel, but often the heat can turn this time into an unbearable torment. Meet a new product from Sony - the Reon Pocket 5 mini-air conditioner, which promises to make summer more comfortable for its users. Sony has introduced a unique device - the Reon Pocket 5 mini-conditioner, which provides body cooling on hot days. With it, users can enjoy coolness anytime, anywhere by simply wearing it around their neck. This mini air conditioner is equipped with automatic adjustment of operating modes, as well as temperature and humidity sensors. Thanks to innovative technologies, Reon Pocket 5 adjusts its operation depending on the user's activity and environmental conditions. Users can easily adjust the temperature using a dedicated mobile app connected via Bluetooth. Additionally, specially designed T-shirts and shorts are available for convenience, to which a mini air conditioner can be attached. The device can oh ... >>

Energy from space for Starship 08.05.2024

Producing solar energy in space is becoming more feasible with the advent of new technologies and the development of space programs. The head of the startup Virtus Solis shared his vision of using SpaceX's Starship to create orbital power plants capable of powering the Earth. Startup Virtus Solis has unveiled an ambitious project to create orbital power plants using SpaceX's Starship. This idea could significantly change the field of solar energy production, making it more accessible and cheaper. The core of the startup's plan is to reduce the cost of launching satellites into space using Starship. This technological breakthrough is expected to make solar energy production in space more competitive with traditional energy sources. Virtual Solis plans to build large photovoltaic panels in orbit, using Starship to deliver the necessary equipment. However, one of the key challenges ... >>

Random news from the Archive

Mini Electric 11.07.2019

The German automaker BMW has introduced a serial electric car Mini Electric (Mini Cooper SE). The novelty is based on the ICE version of the model, so the design, dimensions, interior design and other characteristics are almost identical (with a few exceptions). For example, the height of an electric car is only 18 mm higher, and the weight is only 145 kg more.

The three-door, front-wheel-drive Mini Electric received a BMW-designed electric motor with 135 kW (184 hp) and 270 Nm of torque. Acceleration from 0 to 60 km / h takes 3,9 seconds, from 0 to 100 km / h - 7,3 seconds, top speed is limited to 150 km / h.

The driver has four efficiency modes (Standard, Sport, GREEN and GREEN+) and two levels of recuperation settings that allow you to choose a driving mode in which just one accelerator pedal is enough to control the dynamics.

The T-shaped battery is integrated into the floor (it is located between the front and under the rear seats, which made it possible to save legroom), it is assembled from 12 modules with a total capacity of 32,6 kWh.

A full charge of the battery is enough to cover from 235 to 270 km (WLTP) depending on driving style and road conditions. The battery supports high-speed charging up to 50 kW, in this mode, 80% of the capacity is gained in 35 minutes. The charging connector is located above the rear right wheel.

Standard equipment includes LED headlights, 5,5-inch digital instrument cluster, dual-zone air conditioning, heat pump-based interior heating, electric parking brake, and 6,5-inch Connected Navigation (Connected Navigation package available as an option). Plus with 8,8-inch screen).

The Mini Electric will be assembled in November this year in the UK, at the same plant in Oxford where the ICE version is assembled. The cost of an electric car, taking into account benefits, starts at $30400, the first deliveries to customers will take place in early 2020.

Other interesting news:

▪ New processor Intel's new dual-core Pentium E6300 processor

▪ Vortex thermosyphon cooler - a new source of energy

▪ Drones to rescue drowning people

▪ Plastc Cards

▪ Sleeping before bed improves memory

News feed of science and technology, new electronics

 

Interesting materials of the Free Technical Library:

▪ section of the site Interesting facts. Selection of articles

▪ article Brain drain, brain drain. Popular expression

▪ article How are responsibilities distributed in an ant family? Detailed answer

▪ article Battery chargers. Directory

▪ article Antenna Isotron 80/40 Combo. Encyclopedia of radio electronics and electrical engineering

▪ article Always 100. The secret of the focus

Leave your comment on this article:

Name:


Email (optional):


A comment:





All languages ​​of this page

Home page | Library | Articles | Website map | Site Reviews

www.diagram.com.ua

www.diagram.com.ua
2000-2024