Introduction

I’ve been reading up on the 680x0 CPU family.  One of the things I wanted to get my head around are the differences between this CPU and the 6502, the CPU I know. Data handling, address manipulation and basic arithmetic are drastically enhanced in the 680x0.

If you know your 6502 CPU architecture then this short list of differences might be interesting.

Memory and Byte order

Do not think that because the CPU is 32 bits it also accesses and uses memory like this. The 680x0 still looks at its memory as BYTES. This is important to know, as some operations require to you align addresses or data to even bytes in memory. So this is not really a difference! :) What a way to start this list.

The 6502 stores numeric values as little-endian. This means that the low byte of a value is first in memory, and then the high byte. The 680x0 uses big-endian: values are stored with the high byte first.

Example 16 bit value #$12a0:

  • 6502:  stored as #$a0, #$12
  • 680x0: stored as #$12, #$a0

Number size

Most commands in the 680x0 require an indication of how big the value we’re working with is. This is done by using .b.w, and .l, meaning byte (8 bits), word (16 bits) and longword (32 bits) respectively. Operations are performed only on those bits. So using MOVE.B #$11, D0 will set the lowest 8 bits in D0 only. The rest is unmodified.

Data registers

The well known .A, .X and .Y registers are absent on the 680x0. Instead, there are 8 data registers: D0 to D7. The load commands (LDA, LDX, LDY) for the old registers are gone, of course, and replaced by the, very important and multi-use, MOVE instruction.

Example:

  • 6502: LDA #$10
  • 680x0: MOVE.B #$10, D0

And as each data register is 32 bit, it’s so much easier to work with larger numbers, like:

  • MOVE.B #$12, D0
  • MOVE.W #$1234, D0
  • MOVE.L #$12345678, D0

Tip

Although a data register is 32 bits, the data bus itself is 16 bits. So 32 bits values will transfer slower than 16 or 8 bit values, because it will require two data transfers. Something to remember: if you can get away with using 16 bits or less, do it.

Address registers

These were not present on the 6502, we needed to use memory locations to store pointers.  This meant that this data was OUTSIDE the CPU. On the 680x0, the address registers are INSIDE the CPU and therefore they are much more efficient.

You can use A0 to A6. You should stay away from A7 as it is used as the stack pointer by the CPU. But having 7 address registers and 8 data registers is incredibly useful to someone who previously had 3 data registers and a few zero page locations to work with.

One of the ways to ‘walk’ through addresses on the 6502 was to use the zero page. You put pointers to the data into an address in the zero page (preferably, because zero page addressing was faster than higher page addressing), and then used zero page indirect indexed addressing:

Example

Read bytes from address $1000 onwards until read byte is a zero:

  	LDA #$00
  	STA $FA
  	LDA #$10
  	STA $FB
  	LDY #$00
loop:
  	LDA ($FA),Y
  	BEQ end
  	...
  	INY
  	JMP loop
end:
  	RTS

This is done in 680x0 by setting the address registers, and using +:

  	MOVE.W #$1000, A0
loop:
  	MOVE.B (A0)+, D0
  	BEQ end
  	...
  	BRA loop
end:
  	RTS

The address in A0 is incremented after reading it, because we use (A0)+. The fun thing is, you can use +(AO) and then the value in A0 is increased before it is read. Really powerful.  This also eliminates the need for self modifying code, where we modified addresses that reference locations in the machine. The modifying is still done, of course (in A0), but it is CPU controlled and we cannot destroy the program this way because it is not happening in memory.

There are variants to using the address registers:

MOVE.W 8(A1), D1 will first add 8 to the address pointer in A1 and put the word from that location into D1. The big difference between this and the first example is that it does not modify the address pointer in A0, and it is more like the 6502 example seen above. Another example:

MOVE.W 8(A1,D1.L), D2 will add 8 to the address pointer in A1, and then the 32 bit number in D1 to that pointer as well, and it will then put the word from that location into D2.  This adds so much choice to the way we can construct loops, its almost overwhelming. I will probably stick to the more simple versions when I begin creating some programs. :)

Branching

We need branching so we can create logic and loops. As expected, the branching possibilities of the 680x0 are numerous. The same instructions as on the 6502 are available (BEQ, BNE, BPL, BMI, BCC, BCS etc) but there are more added.

The Test, Decrement And Branch variety, or DBcc is one I really like because it is two commands in one. First, the test is performed and if this test fails a branch is performed to the label specified. If the test passes, program flow continues. Also, a data register (acting as a counter) is decremented and while this value is not -1, the program is branching back to the label as specified.

Here is an example of a WHILE count =>0 loop, which exits if the read value is 0:

Example

  	MOVE.W #10, D0   ; we do this ten times
loop:
  	...
  	TST.W (A1)+     ; test value of address in A1
  	DBNE D0, loop   ; branch if not zero, or counter is positive

This is the same as:

Example

  	MOVE.W #9, D0   ; we do this ten times
loop:
  	TST.W (A1)+     ; test value of address in A1  
  	BEQ end         ; if address value is 0, go to end
  	SUB.W  #1, D0   ; subtract 1 from counter
  	BPL loop        ; If >= 0, branch to loop  
end:
  	...

I know which one I like more!

Conclusion

The 680x0 sure is a fun CPU family. It fixes so many things that were cumbersome in the 6502 and the other 8 bit chips. There is more of a choice on how to tackle programming challenges.