1.8 Some Basic Machine Instructions

The 80x86 CPU family provides from just over a hundred to many thousands of different machine instructions, depending on how you define a machine instruction. Even at the low end of the count (greater than 100), it appears as though there are far too many machine instructions to learn in a short time. Fortunately, you don't need to know all the machine instructions. In fact, most assembly language programs probably use around 30 different machine instructions.[6] Indeed, you can certainly write several meaningful programs with only a few machine instructions. The purpose of this section is to provide a small handful of machine instructions so you can start writing simple HLA assembly language programs right away.

Without question, the mov instruction is the most oft-used assembly language statement. In a typical program, anywhere from 25 percent to 40 percent of the instructions are mov instructions. As its name suggests, this instruction moves data from one location to another.[7] The HLA syntax for this instruction is:

mov( source_operand, destination_operand );

The source_operand can be a register, a memory variable, or a constant. The destination_operand may be a register or a memory variable. Technically the 80x86 instruction set does not allow both operands to be memory variables. HLA, however, will automatically translate a mov instruction with two-word or double-word memory operands into a pair of instructions that will copy the data from one location to another. In a high-level language like Pascal or C/C++, the mov instruction is roughly equivalent to the following assignment statement:

destination_operand = source_operand ;

Perhaps the major restriction on the mov instruction's operands is that they must both be the same size. That is, you can move data between a pair of byte (8-bit) objects, word (16-bit) objects, or double-word (32-bit) objects; you may not, however, mix the sizes of the operands. Table 1-1 lists all the legal combinations for the mov instruction.

You should study this table carefully because most of the general-purpose 80x86 instructions use this syntax.

Table 1-1. Legal 80x86 mov Instruction Operands

Source

Destination

Reg8[a]

Reg8

Reg8

Mem8

Mem8

Reg8

Constant[b]

Reg8

Constant

Mem8

Reg16

Reg16

Reg16

Mem16

Mem16

Reg16

Constant

Reg16

Constant

Mem16

Reg32

Reg32

Reg32

Mem32

Mem32

Reg32

Constant

Reg32

Constant

Mem32

[a] The suffix denotes the size of the register or memory location.

[b] The constant must be small enough to fit in the specified destination operand.

The 80x86 add and sub instructions let you add and subtract two operands. Their syntax is nearly identical to the mov instruction:

add( source_operand, destination_operand );
     sub( source_operand, destination_operand );

The add and sub operands take the same form as the mov instruction.[8] The add instruction does the following:

destination_operand = destination_operand + source_operand ;
     destination_operand += source_operand;  // For those who prefer C syntax.

The sub instruction does the calculation:

destination_operand = destination_operand - source_operand ;
     destination_operand -= source_operand ;  // For C fans.

With nothing more than these three instructions, plus the HLA control structures that the next section discusses, you can actually write some sophisticated programs. Example 1-3 provides a sample HLA program that demonstrates these three instructions.

Example 1-3. Demonstration of the mov, add, and sub instructions

program DemoMOVaddSUB;

#include( "stdlib.hhf" )

static
    i8:     int8    := −8;
    i16:    int16   := −16;
    i32:    int32   := −32;

begin DemoMOVaddSUB;

    // First, print the initial values
    // of our variables.

    stdout.put
    (
        nl,
        "Initialized values: i8=", i8,
        ", i16=", i16,
        ", i32=", i32,
        nl
    );

    // Compute the absolute value of the
    // three different variables and
    // print the result.
    // Note: Because all the numbers are
    // negative, we have to negate them.
    // Using only the mov, add, and sub
    // instructions, we can negate a value
    // by subtracting it from zero.

    mov( 0, al );   // Compute i8 := -i8;
    sub( i8, al );
    mov( al, i8 );

    mov( 0, ax );   // Compute i16 := -i16;
    sub( i16, ax );
    mov( ax, i16 );

    mov( 0, eax );  // Compute i32 := -i32;
    sub( i32, eax );
    mov( eax, i32 );

    // Display the absolute values:

    stdout.put
    (
        nl,
        "After negation: i8=", i8,
        ", i16=", i16,
        ", i32=", i32,
        nl
    );

    // Demonstrate add and constant-to-memory
    // operations:

    add( 32323200, i32 );
    stdout.put( nl, "After add: i32=", i32, nl );



end DemoMOVaddSUB;


[6] Different programs may use a different set of 30 instructions, but few programs use more than 30 distinct instructions.

[7] Technically, mov actually copies data from one location to another. It does not destroy the original data in the source operand. Perhaps a better name for this instruction would have been copy. Alas, it's too late to change it now.

[8] Remember, though, that add and sub do not support memory-to-memory operations.