Sega CD Development

Home Sega CD SLO 32X Transfer ConvSCD

Part II: Registers and Addressing Modes

All processors have a special kind of RAM called registers. Like RAM, registers store data. They are much faster than normal RAM, but you can store a lot less data in you registers than your RAM. As a general rule, you want to get as much of your work done in your registers without going out to RAM as possible so that you get optimal performance. The 68K has 16 registers named d0-d7 and a0-a7. The first 8 are data registers. Most of your number crunching will be done in these. The last 8 are address registers. As you might have guessed these store addresses. All these registers are 32-bits (4 bytes) wide.

Before I introduce you to the addressing modes on the 68K, I want to teach you an instruction, namely move. As its name suggests, move is used to move a piece of data from one location to another. It has the following form:

move.[size] [source], [destination]

Size, can be either b (byte), w (word, 2bytes), or l (longword, 4 bytes). As an example,

move.w d0, d1

would move 1 word (2 bytes) of data from register d0 to register d1

Now it's time to get acquainted with the addressing modes on the 68K. "What is an addressing mode?", you ask. Simply put, an addressing mode is a kind of thing you can put in either the or field in that move instruction I introduced you to earlier. I'm not going to introduce every single addressing mode (or at least not all the details of every one). You can look in the 68K Programmers Guide for that.

Keep in mind that while the 68K uses 32-bit addresses, it only has a 24-bit address space. The upper 8 bits are ignored so $FF0000 is equivalent to $FFFF0000.

Register Addressing:

The source or destination is a processor register.
move.w d0, d1
d1 = d0

Immediate Addressing:

The source is a constant value that becomes part of the instruction. This is useful if you want to set a register or memory location to a specific value, but you don't need to do any calculations based on it ahead of time. It also is very useful in simple mathematics calculations. Oftentimes you'll find that you need to add or subtract 1 to a register. Rather than eating up another register you can use immediate addressing. Keep in mind that all immediate values must be prefixed by a #.

move.w #1, d0
d0 = 1

Absolute Addressing:

Here the source or destination is a location in memory. You give the address of the location you want to use.

move.l $100, d0 (move a longword from memory location $100 to register d0)
move.b #1, $FF0000 (store the number 1 at memory location $FF0000)

On a Genesis, the first example would move a longword from the header of the cartridge. That location should contain the ASCII values for the string 'SEGA'.
The second example would store the number 1 in the first byte of Work RAM.

Absolute Near Addressing:

Before I explain this mode I have to tell you a little about sign extension. Sign extension is a way of putting a smaller value in a place where a longer value belongs. What happens is that the highest bit of the smaller value is copied into all the extra values in the larger number. So when $8000 is sign extended from a word value to a longword value it becomes $FFFF8000, but when $7FFF is sign extend it it becomes $00007FFF or $7FFF. If you don't want to think in binary just remember this: If the highest hex digit is greater than 7 then the extra digits will be filled with 'F's. If the highest hext digit is less than 8 then the extra digits will be filled with 0s. Note that when I say the highest digit, I mean the highest digit in the data width. $80 as a word value has a highest digit of 0.

Absoute near addressing is similar to absolute addressing except that instead of using a longword for the address a word is used. This word is sign-extended giving you access to the first and last 32K of the memory space ($0 - $7FFF and $FF8000 - $FFFFFF). Example:
move.w #1, $8000.w
Memory Location $FF8000 = 1

Pointer Addressing:

You remember those address registers? Now you get to use them. Let's say you want to access the same location in memory over and over again. You could include the address as part of the instruction every time using the absolute addressing mode, but that's kind of slow and innefficient. Instead, you can put the address in an address register and use that to reference the memory location. If you want to use or affect the value stored in the address register, just use the name of the register by itself. If you want to use or affect the memory location pointed to by the adress register, enclose the name of the register in parenthesis.

move.l #$FF0000, a0 ;Store the address $FF0000 in a0
move.w #1, (a0) ;Store the number 1 at the memory location pointed to by a0 ($FF0000)

Pointer Relative Addressing:

Let's say you've got two values stored in RAM pretty close together, but you don't want to use two address registers and you don't want to use absolute addressing. You can point an address register at the first, and then use that address register along with an offset to access the second. An offset is a number from -$8000 to $7FFF that is added to the register to calculate the address that is used. Note, this addition is not permanent. It doesn't effect the value stored in the address register.

move.l #$FF0000, a0 ;Store address $FF0000 in a0
move.w #1, (a0) ;store the number 1 at memory location $FF0000
move.w #1, 2(a0) ;store the number 1 at memory location $FF0002

PC Relative Addressing:

The program counter (PC) is a special address register that points to the current instruction. Although you can't treat it like an address register in most respects, you can use it for relative addressing in the same way that you use it with an address register. This will be more useful once we get to labels.

move.w #1, $100(pc) ;stores the value 1 at the memory location $100 bytes from the current instruction.

Pointer Addressing with Predecrement/Postincrement:

Let's say you wanted to access a bunch of sequential memory locations. You could use relative addressing to access each location, but there is a better way. With postincrement the value in the address register is incremented by the size of the last operation each time postincrement addressing is used. With postdecrement the value in the address register is decremented by the size of the operation before the operation is completed.

move.l #$FF0000, a0 ;store address $FF0000 in a0
move.b #7, (a0)+ ;store 7 at $FF0000 and add 1 to a0, a0 now equals $FF0001
move.b #4, (a0)+ :store 4 at $FF0001 and add 1 to a0
move.w #3, (a0)+ ;store 3 at $FF0002 and add 2 to a0
move.l #9, (a0)+ ;store 9 at $FF0004
;a0 is now equal to $FF0008
move.w #6, -(a0) ;store 7 at $FF0006

Whew, that took longer than I thought it would. I know this was probably a lot of material to absorb in one sitting, but don't worry about mastering this now. Just try to remember the basic concepts and remember where to find the specifics (namely in Part II of this tutorial). If you find any of this confusing just say so and I'll do my best to explain it better.

Part I Sega CD Home Part III