Friday, March 1, 2024

ALPP 01-01 -- Accumulating Integer Results in the M6809, 6800, 6801, and M68000

Accumulating Integer Results
on the M6809, 6800, 6801, and M68000

(Title Page/Index)

What is an accumulator?

When you're working through a problem of some kind, you generally need some place to keep a record of your current results. As an example, think of adding up a total:

8 + 5 + 2 + 7 + 4 = 26 (if I didn't mess up the math)

If you can do that in your head, you probably don't even think about it, but you probably do keep a running total:

8
+ 5 = 13
+ 2 = 15
+ 7 = 22
+ 4 = 26 (Okay, I didn't mess up the math)
If you do that on a piece of paper the way I just did it here, your "accumulator area" is the space to the right of the = sign next to the current number you're adding at each step. 

But if you do it in you're head, unless you've trained yourself (or have natural photographic memory or something), you only keep the running total (the accumulated value) and the number you are currently adding. That's your effective accumulator in your head. 

It's the natural current focus point of calculations.

In the 6800/6801 and the 6809, you have two small dedicated accumulators, usually called A and B. (In the 6801 and 6809, you can concatenate them together for a single, medium-sized Double accumulator D, for some operations, more later.)

In the 68000, you have eight large data registers which can be used as accumulators, usually called D0 to D7. (Usually. Some assemblers use different conventions.)

As a totally contrived sequence of operations, let's do the above in 6800 assembly language:

* Adding a sequence of constant values:
	LDAA	#8	; Gotta start somewhere.
	ADDA	#5
	ADDA	#2
	ADDA	#7
	ADDA	#4
	NOP		; landing pad for breakpoint 

(6801 is the same, or can be.) 

  • LDAA is the mnemonic for the "Load accumulator A" (LoaD Accumulator A) instruction (operation code).
  • The # hash mark indicates that the numeric value is immediate.
  • ADDA is for "ADD to accumulator A". 
  • NOP is a no-operation op code, in this case, as noted, for a landing pad where I can set a breakpoint.
  • And the notes after the semicolon are comments for human readers (mostly), and the computer (mostly) ignores them.

Now, this entire sequence is known in advance, and should usually be optimized to a single instruction

	LDAA	#26	; optimized version

But if we optimize away the operations now, we can't see what they are doing. So we won't do that.  Not yet.

If you are wondering where the result is supposed to show up, good. 

Let's break out a debugger and trace it through:

% t on
% b 100a
Breakpoint set at 100A
% c 1000

          0 A=00 B=00 X=0000 SP=00FF ------          1000: 86 08    LDA #08   EA=1001 D=08
          1 A=08 B=00 X=0000 SP=00FF ------          1002: 8B 05    ADDA #05  EA=1003 D=05
          2 A=0D B=00 X=0000 SP=00FF ------          1004: 8B 02    ADDA #02  EA=1005 D=02
          3 A=0F B=00 X=0000 SP=00FF ------          1006: 8B 07    ADDA #07  EA=1007 D=07
          4 A=16 B=00 X=0000 SP=00FF H-----          1008: 8B 04    ADDA #04  EA=1009 D=04

Breakpoint!
>         5 A=1A B=00 X=0000 SP=FF8A ------          100A: 01       NOP                      

Looks good, right? Nothing happening in B, X, and SP. The H half-carry flag gets set and then goes away, we see the PC counting through the code, and the machine code is there with the assembler mnemonics. 

(Above, EA is something we call the effective address of the operand, and D is the data operand referenced at the effective address, more on that later.)  

But, wait, ... since when does 8 + 5 = what? D? And we don't see 26 in there at the end, or anywhere?

What went wrong? Computers are never wrong! (cough.) 

Must be something we did. 

Let's try a different sequence of numbers. Here's a well-known one:

1 + 2 + 3 + 4 + 5 + 6 = 21

Yep. ( 7 × 6 ) ÷ 2 is 21. Code:

	LDAA	#0	; Clear A out.
	ADDA	#1	; Add 1 to A.
	ADDA	#2	; Add 2.
	ADDA	#3	; Etc.
	ADDA	#4
	ADDA	#5
	ADDA	#6
	NOP		; landing pad
That ought to work. Trace it in the debugger:
% b 100e
Breakpoint set at 100E
% t on
% c 1000
          0 A=00 B=00 X=0000 SP=FF8A ------          1000: 86 00    LDA #00   EA=1001 D=00
          1 A=00 B=00 X=0000 SP=FF8A ---Z--          1002: 8B 01    ADDA #01  EA=1003 D=01
          2 A=01 B=00 X=0000 SP=FF8A ------          1004: 8B 02    ADDA #02  EA=1005 D=02
          3 A=03 B=00 X=0000 SP=FF8A ------          1006: 8B 03    ADDA #03  EA=1007 D=03
          4 A=06 B=00 X=0000 SP=FF8A ------          1008: 8B 04    ADDA #04  EA=1009 D=04
          5 A=0A B=00 X=0000 SP=FF8A ------          100A: 8B 05    ADDA #05  EA=100B D=05
          6 A=0F B=00 X=0000 SP=FF8A ------          100C: 8B 06    ADDA #06  EA=100D D=06

Breakpoint!
>         7 A=15 B=00 X=0000 SP=FF8A H-----          100E: 01       NOP                      

Much better. Load in a 0 and the result in A is 0. Add 1 to get 1, add 2 to get 3, and 3 to get 6, add 4 to get A ...

Erk. Giving 8 + 5 = D a D grade is understandable. What grade do we give to 6 + 4 = A?

Heh. The computer is not (this time) wrong. 

So let's talk about

Hexadecimal!

Simple debuggers like the one I'm using here display many results in hexadecimal instead of decimal, to save time, space on the screen, writing code, and just to make things more meaningful. (You doubt that last one? Trust me on this.)

For the time being, and maybe a quick refresher, here's a quick hexadecimal-to-decimal conversion chart, up to 31ten:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 8 9 A B C D
E F
 
16
17 18
19
20
21
22
23
24
25
26 27
28
29
30
31
10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F

So, 8 + 5 does, in fact, equal Dsixteen (D base sixteen) in the usual (modern) way of writing hexadecimal.

And 6 + 4 does equal Asixteen (A base sixteen).

Yes, it's inconvenient when you've spent so much of your life getting used to decimal, but we'll try to take time to make sure we don't get lost in the hexadecimal. 

So, from the conversion chart, 22ten is 16sixteen. Maybe that's confusing, but let's look at why.

Remember that each column in base ten is a power of ten, increasing to the left. (That's most-significant column first.) 

22ten is 2 times ten plus 2 times one: 

twenty plus two, or 20ten + 2ten.

And each column in base sixteen is a power of sixteen, increasing to the left.

16sixteen is 1 times sixteen plus 6 times one: 

sixteen plus six, or 10sixteen + 6sixteen

(To help us not get confused, we can read that as "one-zero base sixteen plus six base sixteen".)

So, working in hexadecimal (and checking in decimal),

8sixteen
+ 5sixteen = Dsixteen (13ten)
+ 2sixteen = Fsixteen (15ten)
+ 7sixteen = 16sixteen (16ten + 6ten = 22ten)
+ 4sixteen = 1Asixteen (16ten + 10ten = 26ten)

There's our 26!

And, 

0sixteen
+ 1sixteen = 1sixteen (1ten)
+ 2sixteen = 3sixteen (3ten)
+ 3sixteen = 6sixteen (6ten)
+ 4sixteen = Asixteen (10ten)
+ 5sixteen = Fsixteen (15ten)
+ 6sixteen = 15sixteen (16ten + 5ten = 21ten)

And that's enough of non-decimal numeric bases for the moment.  

For comparison, the 6809 version of the first sequence is almost the same in assembler:

	LDA	#8	; 6809 mnemonic: LoaD accumulator A
	ADDA	#5	; Mnemonic shared with 6800/6801.
	ADDA	#2
	ADDA	#7
	ADDA	#4 

Looking at that in a 6809 simulator:

% b 100a
Breakpoint set at 100A
% t on
% c 1000

          0 A=00 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            1000: 86 08        LDA #$08
          1 A=08 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            1002: 8B 05        ADDA #$05
          2 A=0D B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            1004: 8B 02        ADDA #$02
          3 A=0F B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            1006: 8B 07        ADDA #$07
          4 A=16 B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --H-----            1008: 8B 04        ADDA #$04

Breakpoint!
>         5 A=1A B=00 X=0000 Y=0000 U=0000 S=00FF P=00 --------            100A: 12           NOP                        

That looks a lot like the 6800 emulation session, which is no real surprise. An extra index register (Y), an extra stack register (U), the direct page register (P). But it's a long line already, so the effective address and data are left out. And the accumulated sums are the same, as they should be.

And the 68000 version looks like this, using default register width instructions (which we will need to talk about later):

	MOVEQ	#8,D0	; MOVEQ lets us ignore register width.
	ADDQ	#5,D0	; Default register width is okay.
	ADDQ	#2,D0	; D0 is equivalent to 6800 A? Not quite?
	ADDQ	#7,D0
	ADDQ	#4,D0

MOVEQ is for "MOVE Quick"; ADDQ is for "ADD Quick". Both are somewhat optimized versions of the move and add instructions. MOVEQ affects the entire register, so any register size for the following ADD instructions is okay for the range of numbers we are using here.  (MOVEQ operands can be from -128 to 127; ADDQ operands can be from 1 to 8. Again, more later.)

With all the registers in the 68000, doing a one-line complete register dump becomes rather awkward. You'd need a screen able to show something like 300 characters per line, and then you'd wear your eyes out looking for what changed on each line. So you don't do that.

Here's what the top part of the screen looks like in the devpak debugger on the Hatari Atari ST emulator screen:

d0 = 00000000  ....    a0 = 00000000 602E 0206 00E0 0030 0001  ..........
d1 = 00000000  ....    a1 = 00000000 602E 0206 00E0 0030 0001  ..........
d2 = 00000000  ....    a2 = 00000000 602E 0206 00E0 0030 0001  ..........
d3 = 00000000  ....    a3 = 00000000 602E 0206 00E0 0030 0001  ..........
d4 = 00000000  ....    a4 = 00000000 602E 0206 00E0 0030 0001  ..........
d5 = 00000000  ....    a5 = 00000000 602E 0206 00E0 0030 0001  ..........
d6 = 00000000  ....    a6 = 00000000 602E 0206 00E0 0030 0001  ..........
d7 = 00000000  ....    a7 = 00000000 602E 0206 00E0 0030 0001  ..........
SR:0000    U          ssp = 00000000 602E 0206 00E0 0030 0001  ..........
PC:00E00030  bra $E0004E

After the data register on each line is the ASCII interpretation of the contents of the data register. Then, after the address register on  each line is the contents of ten bytes of  memory in hexadecimal, then the same ten bytes shown as if they were ASCII characters. SR is the status register, ssp is the system stack pointer, PC is the program counter. And that's a lot to take in.

Even without the extra interpretations and memory content, you can see the problem, right?

If the debugger would just show you the registers that changed, that would be great. But I don't have one of those kinds of debuggers for the 68000. 

I can pretend to be one, and I guess that will have to do. Here's what it might look like:

% t on
% b 1800a
Breakpoint set at 0001800A
% c 18000

  0 D0=00000000 A7=00017FF8 ----- 00018000: 7008 MOVEQ   #8,D0
  1 D0=00000008 A7=00017FF8 ----- 00018002: 5A40 ADDQ    #5,D0
  2 D0=0000000D A7=00017FF8 ----- 00018004: 5440 ADDQ    #2,D0
  3 D0=0000000F A7=00017FF8 ----- 00018006: 5E40 ADDQ    #7,D0
  4 D0=00000016 A7=00017FF8 ----- 00018008: 5840 ADDQ    #4,D0

Breakpoint!
> 5 D0=0000001A A7=00017FF8 ----- 0001800A: 4E71 NOP

Now, you're probably wanting to try some of this fun stuff yourself.

I plan to show how to build a hexadecimal calculator as part of the tutorial, but that's a bit down the road, yet.

Until then, hexadecimal math is supported by many desktop calculators on personal and workstation computers. Look for such things as programmer mode, and you'll probably find binary and octal modes as well within the programmer mode. 

Or, if you have a Unix or Linux OS workstation, you probably also have the Unix command-line arbitrary precision basic calculator utility, bc. That allows you to set the input and output conversion bases to arbitrary positive bases (greater than one), and can be fun and instructive to play with:

obase=16
ibase=16
8 + 8
10
8 + 5 + 2 + 7 + 4
1A

Or, the programming language Forth has a similar conversion base, just one for both input and output:

16 BASE !  ok
8 5 + 2 + 7 + 4 + . 1A  ok

(You might find post-fix math confusing, I suppose.) 

Many versions of Forth are available, I tend to use gforth, because it is available in the Debian and Ubuntu repositories.

Playing with hexadecimal math is fun, but what about to using a debugger like I have shown above to learn assembler? 

There are, of course, many ways to get hardware and/or software to practice on. 

Hardware is good, but I don't have room for hardware. So I am using Joe H. Allen's emulator EXORsim for the 6800, 6801, and 6809, and the Atari ST emulator Hatari for the 68000. Since it will be useful for the reader to follow along, I'll explain at least one way to get each of these, but this chapter is long enough already.

What about other CPUs, like the 8080, Z-80, 6502, 8086, ARM, RISC-V, Power PC, ... ? 

This is a fairly time-intensive project. Doing just the four I've already described above is plenty to fill my plate. And add to that describing how to set up the environment to run the code. 

But when I get to a good pause point, I do intend to come back and add chapters to demonstrate code for some of those processors.

So, how to get the emulators ...

(Title Page/Index


ALPP -- Assembly Language Programming Primer -- Title Page

Assembly Language Primer

Joel Matthew Rees

Amagasaki, Japan

Copyright 2024 Joel Matthew Rees


Preface


Basics

  1. Accumulating Integer Results in the M6809, 6800, 6801, and M68000
  2.