4.2 Assembly Language

Specification

  • Show understanding of the relationship between assembly language and machine code

  • Describe the different stages of the assembly process for a two-pass assembler

    • Apply the two-pass assembler process to a given simple assembly language program

  • Trace a given simple assembly language program

  • Show understanding that a set of instructions are grouped, including the following groups

    • Data movement

    • Input and output of data

    • Arithmetic operations

    • Unconditional and conditional instructions

    • Compare instructions

  • Modes of addressing

    • Including immediate, direct, indirect, indexed, relative

(No particular instruction set will be expected but candidates should be familiar with the type of instructions given in the table below or in the specification.)

Memory Addressing

Note that there is no generally accepted way of naming the various addressing modes. In particular, different authors and computer manufacturers may give different names to the same addressing mode, or the same names to different addressing modes. Furthermore, an addressing mode which, in one given architecture, is treated as a single addressing mode may represent functionality that, in another architecture, is covered by two or more addressing modes.

The following video gives an example of a number of the addressing modes. Not all of these are required this year, or at all in the specification. The text below clarifies this.

There are various ways to address memory. The specification requires you know the following modes:

Immediate

This value is the number to be used. There is no link to another memory address to get the required value.

Direct (aka absolute)

The value represents the memory location where the required value can be found.

Indirect

This value points to the memory address, which itself contains an address where the value can be sought.

Direct addressing is fine as long as you know what memory locations contain the data you need. But suppose you need to use a library program that's in memory. How can you access that program, if you don't know where in RAM the loader has put it? Indirect addressing is one way to solve this kind of problem. To see how indirect addressing works, follow this example. Imagine the instruction: MOV A, @11000 (The @ symbol is being used simply to show we are using indirect addressing).

Also, assume that RAM location 11000 contains the value 23000, and RAM location 23000 contains the value 5000. Memory location 5000 holds 32000.

When the CPU carries out this instruction, it:

  • Goes to memory location 11000.

  • Gets the value contained in that location, i.e. 23000.

  • Goes to memory location 23000.

  • Gets the value contained in that memory location, i.e. 5000.

  • Goes to memory location 5000.

  • In memory location 5000, it finds the value 32000. This is loaded into the accumulator.

Why indirect addressing is very important

Consider this problem. A programmer wants to use a pre-written library procedure. They want to refer to it in their own program. The problem is, they don’t know where in memory the loader will actually put the procedure. For example, a programmer is writing a program that needs to call a library routine called PrintDocument. When the library program is compiled, however, the loader, which is responsible for putting the library program in RAM, could put it anywhere! And if our programmer doesn't know where it is, how can she jump to it in her program? What she does know, however, is that there is a special area of RAM that holds memory locations (called vectors) that point to where library routines will be. She has the Specification of these vectors and on this Specification will be the vector for the PrintDocument program. So in her program, she can now use indirect addressing to get access to the PrintDocument program simply by using the vector address. When the library program is compiled, it will be the job of the 'loader' to keep the addresses held in the vectors up-to-date, so that programs that jump to the vectors can be re-directed to the correct place in RAM.

Indexed

This value points to an address, which must also be added to the value in the index register (IX) to retrieve the value required.

Indexed addressing is very often used to access numbers held in arrays. The programmer needs to set up an 'index'. This is simply a variable that they can manipulate. To use this addressing method, you state a base address. You then add to it the value held in the index. This gives you a new address. You then go to this address to get the data! For example, suppose you have a simple array that starts at address 1001. You can set the base address to 1000. Then if you want the data from the sixth location, for example, you add 6 (the index) to the base address to get 1006 and then jump to that location to get the data. If you want the data from the ninth location in the array, then you add 9 to the base address, and jump to location 1009. What is the point of this addressing system? It is a relatively quick and easy job to alter the index register. You can therefore access large chunks of data easily. For example, if you had to get all the values held in an array, you would get the base address, set up an index and then get the data from the address pointed to by (base address + index). Of course, you would need to set up a loop and increment the index in each loop until the end of the file is reached, like this:

GET BASE address

MAKE INDEX=1

WHILE the index does not point to the end of the array

BEGIN

ADD BASE to INDEX

READ data

INCREMENT INDEX

END.

This method is also very convenient if you want to store your array in different places in RAM. The only thing that you would actually need to change is the base address. For example, if you now want to run the program with the data starting in 2001 and not 1001, you just need to change the base address in the program. All the other addresses in the array are worked out relative to the base address. The advantage of this is that you don't have to make lots of changes to your program - only the base address needs to be changed.

Relative

This value represents an offset to the current memory address in the program counter register. E.g. if the PC was pointing to address 70, a relative address of 5 would retrieve from address 75.

There are times in a program where you need to jump to an address that is a certain number of memory addresses away from the current position, as given by the address held in the Program Counter or some other value. If you took the address held in the Program Counter, for example, and added a value to it, called an 'offset', this would take you to the address where you can find the instruction that needs to be done next. This is known as 'relative addressing', because you are jumping to an address that is relative to another address.

For example, you might do a test on some data. If the result of the test was TRUE, then you had to jump to a subroutine and run that code. The way that you could jump to the subroutine would be to add an offset to whatever was held in the Program Counter. When the current Fetch - Decode - Execute cycle was completed, the address held in the Program Counter (which is now the original value plus the offset) would be used to find the next instruction to do.

To speed up this process, assembler languages have relative addressing instructions. These allow you to jump to whatever is held in the Program Counter plus an offset, rather than having to load values into the Program Counter itself.

Typical Instructions

The following table is taken directly from the 2021-23 CS 9618 specification

Little Man Computer Video