4. Addressing Modes

The 68000 has a large number of addressing modes and these can often become overwhelming to a new machine code programmer - I know. It takes some time to understand each and every mode, what it does and why it is used. Having said that, you do not need to remember all of their names, just what they look like in source code and of course, what they do.

From here on, you need to be aware that numbers may be in decimal format or hexadecimal. All hexadecimal numbers are prefixed with the dollar sign ($) and wherever this is seen in front of a number (or in some cases, what appears to be a word) will be a hexadecimal number. (I will assume that you are familiar with hex.) A couple of examples of hexadecimal numbers are :

$100
$C0FFEE

which are equivalent to 256 and 12,648,430 respectively.

Without any further hesitation, lets dive right in with the addressing modes ...

4.1. Register Direct

This is an easy one to start off with. Register direct addressing mode simply means that both the source and the destination in the instruction are registers either data, address or a mixture of both.

Simple examples are :

MOVE.L    A2,D1
MOVE.W    D0,D1
MOVE.L    A1,A3 

These simply move (actually, they copy) data between various registers. The full meaning of the actual instructions will be described later on.

4.2. Absolute

In this mode, the operand of the instruction is simply a memory address. This is also quite simple. For example to 'zeroise' the contents of the first byte of screen memory (assuming a standard QL and this is the last time that I will assume anything !) :

CLR.B    $20000  

There are two variations to this mode, absolute short and absolute long. If the address given is a 16 bit word (ie 0 to 7FFF hex or 32767 decimal) then it refers to addresses in the first 32K of memory. If the address given is 8000 hex or 32768 decimal and upwards it refers to address FFFF8000 and upwards due to sign extension of the address word. This is absolute short, best used for addresses of 0 to 7FFFF hex only - to avoid confusion.

MOVE.L    $1000,D1    gets the long word at address $1000
MOVE.L    $9000,D1    gets the long word at address $FFFF9000  

The other variation is absolute long, in this case, the address given is a full 32 bits long and refers to the actual address in memory - there is no ambiguity with absolute long. MOVE.L $123456,D1 - gets the long word at address $123456.

4.3. Relative

This mode will probably be the most used with QL programs as all code should be relocatable. This means that it never assumes that it is running at a specific location in memory. Some early QL programs were written to run at a specific location in memory and this caused no end of problems when memory expansions became available. I think Psion chess was one of the guilty ones.

However, relative addressing simply means, relative to where the program counter is. The program counter is always pointing at the address of the instruction in memory after the current one. An example of relative addressing is this small loop and the jump back to the start of the loop :

Start    MOVEQ #1000,D0
Loop     SUBQ #1,D0
         BNE.S Loop(PC)  

This is a small and totally useless fragment of code. The relative address mode is in the BNE.S LOOP(PC) instruction - it says - branch to the label called 'loop', relative to where the program counter is currently pointing, if the result of the subtraction was not zero. The jump is specified in the code as a negative number, not the actual address of where the label 'loop' is at.

This negative number (in the example above) is how many bytes are to be added to the program counter to get the address of the next instruction to be executed. The jump can be forwards as well as backwards.

Using relative addressing means that the program can be loaded anywhere in memory and still work. If absolute addressing was used, the program would always have to be loaded at the same address if a crash was to be avoided. The example above is the equivalent of the following SuperBasic code :

1000 REMark Start
1010 LET D0 = 1000
1020 REMark Loop
1030 LET D0 = D0 - 1
1040 IF D0 <> 0 THEN GOTO (1040 - 10)

Note

Because of the slightly different way that assembler works, the calculation of the destination line is not quite accurate. When the BNE.S instruction is being executed, the program counter is already set to the following instruction. In the SuperBasic example above, the subtraction of 10 from 1040 should realy be 20 from 1050. However, it shall remain as above for now.

4.4. Address Register Indirect

This mode is called 'indirect' because the address register in question is not the operand in the instruction. It simply serves as a pointer to the operand. In an earlier example we cleared out the first byte of screen memory by using absolute addressing like this :

CLR.B    $20000 

This instruction could have been carried out using address register indirect mode as follows :

MOVEA.L    #$20000,A1
CLR.B      (A1) 

All that this is doing is setting address register A1 with the value 131072 (decimal) which is 20000 (hexadecimal). It then clears out the first byte at that address. This is the same as this SuperBasic example :

1000 LET A1 = 131072
1020 POKE A1,0 

The register's name is put in between a pair of brackets to signify that it is the memory address held in the register that will be acted upon and not the register itself.

4.5. Register Indirect With Displacement

This mode is similar to the above, except that a displacement is added or subtracted from the address register to give the final address to be operated upon. Using the above example again, we can zeroise the first 4 byes of screen memory as follows :

MOVEA.L    #$20000,A1
CLR.W      (A1)
CLR.W      2(A1) 

This time we use word sized operations, these simply affect 16 bits instead of 8 as with the byte sized operations. The displacement is the number outside of the brackets and it is added to the address registers contents to create the address to be operated upon. The displacement can be any signed number that will fit into 16 bits. (-32768 to +32767)

4.6. Register Indirect With Displacement And Index

It's starting to get complicated now. This is another mode where we have an address register and a displacement to consider, but this time we have an index as well. In this case the displacement has been reduced to 8 bits only giving a range of -128 to +127. The format of this addressing mode is :

CLR.W    2(A2,A0.L)

The contents of A2 is added to A0 to get the first address then the displacement is added to give the final result. The 16 bits of memory at the final address is cleared out. (Like POKE_W A2 + A0 + 2, 0). In this case the entire 32 bit value of A0 is added to A2, this is indicated by the '.L' after the second register - the index.

If the suffix had been omitted or was '.W. (which is the default if omitted) then the lower 16 bits of A0 would have been used instead of the whole 32. Take note that the 16 bits will be 'sign extended' to a full 32 bits and this can have unpleasant side effects if the value in bit 15 is a 1 as this will cause a negative index to be generated. There will be more on sign extension later.

The first register specified is always treated as 32 bit (.L) and does not require a '.L' suffix - most, if not all, assemblers will reject it anyway.

4.7. Register Indirect With Pre Decrement Or Post Increment

These addressing modes are used for stack operations, usually. The format of the pre-decrement instruction is :

MOVE.L    D0,-(A7) 

And for post-increment it is :

MOVE.L    (A7)+,D0 

The actions carried out are as follows for pre-decrement : The value in A7 is decremented (reduced) by the size of the data to be stored (byte, word or long) then the contents of D0 are stored at the location pointed to by A7. In SuperBasic this equates to the following code fragment :

1000 LET A7 = A7 - 4
1010 POKE_L A7, D0 

The opposite action takes place with post-increment, as follows : The contents of the memory address pointed to by A7 is copied into D0, then the address held in A7 id incremented by the size of the data just copied ( byte, word or long). Again, this equates to :

1000 LET D0 = PEEK_L(A7)
1010 Let A7 = A7 + 4 

4.8. Immediate

This is probably the simplest of all the addressing modes. It simply means that the data specifies the address value. For example :

MOVE.L    #100,D0 

This copies the value of 100 into data register 0. The hash sign indicates that the data is copied directly into the register. Do not get confused between this instruction and :

MOVE.L    100,D0 

Note that there is no hash. This instruction means load the CONTENTS of address 100 into register D0. This is not the same ! This is a good source of confusion for beginners - I know all about it, and sometimes still make this mistake !