Tuesday, February 11, 2020

33209: A little about the 6800 (and Others)

Chapter 7: Wandering Eyes

Chapter 7.5: A Little about the 6800 (and Others)


This chapter is for the geek in you. It's also important background information, to motivate the directions taken by various people in the story.

The 6800 had the primary binary operators that could be implemented for its two accumulators (A and B) in integrated circuits of the early 1970s:
  • Addition (ADD) and subtraction (SUB)
  • ADD and SUB taking previous carries into account (ADC and SBC)
  • Compare (CMP -- subtract without storing the result, only setting condition code flags.)
  • Data movement (LDA and STA -- load and store accumulator)
  • Bit logic: AND, OR, and XOR (exclusive-or)
  • Bit test (BIT -- bit AND without storing the result, only setting condition code flags.) 
  • Compare index register (CPX -- the index as one operand instead of an accumulator)
  • Load and store index and stack register (LDX, STX, LDS, STS -- also one operand index or stack pointer instead of accumulators)

*(Reserved, undefined); **6801 only; ***CPX has full flags on 6801
6800/6801
Binary
Operators
Ranks 8-B
Accumulator A,  D, X, or SP
ImmediateDirectIndexedAbsolute
$8X$9X$AX$BX
1000XXXX1001XXXX1010XXXX1011XXXX
SUBA10XX0000$80$90$A0$B0
CMPA10XX0001$81$91$A1$B1
SBCA10XX0010$82$92$A2$B2
**SUBD10XX0011**$83**$93**$A3**$B3
ANDA10XX0100$84$94$A4$B4
BITA10XX0101$85$95$A5$B5
LDAA10XX0110$86$96$A6$B6
STAA10XX0111*($87)$97$A7$B7
EORA10XX1000$88$98$A8$B8
ADCA10XX1001$89$99$A9$B9
ORAA10XX1010$8A$9A$AA $BA
ADDA10XX1011$8B$9B$AB$BB
***CPX10XX1100$8C$9C$AC$BC
JSR10XX1101**BSR=$8D**$9D$AD$BD
LDS10XX1110$8E$9E$AE$BE
STS10XX1111*($8F)$9F$AF$BF

*(Reserved, undefined); **6801 only
6800/6801
Binary
Operators
Ranks C-F
Accumulator A,  D, X
ImmediateDirectIndexedAbsolute
$CX$DX$EX$FX
1100XXXX1101XXXX1110XXXX1111XXXX
SUBB11XX0000$C0$D0$E0$F0
CMPB11XX0001$C1$D1$E1$F1
SBCB11XX0010$C2$D2$E2$F2
**ADDD11XX0011**$C3**$D3**$E3**$F3
ANDB11XX0100$C4$D4$E4$F4
BITB11XX0101$C5$D5$E5$F5
LDAB11XX0110$C6$D6$E6$F6
STAB11XX0111*($C7)$D7$E7$F7
EORB11XX1000$C8$D8$E8$F8
ADCB11XX1001$C9$D9$E9$F9
ORAB 11XX1010$CA$DA$EA $FA
ADDB11XX1011$CB$DB$EB$FB
**LDD11XX1100**$CC**$DC**$EC**$FC
**STD11XX1101*($CD)**$DD**$ED**$FD
LDX11XX1110$CE$DE$EE$FE
STX11XX1111*($CF)$DF$EF$FF


The 6800 also had several unary operators. (Unary operators are binary operators with one of the operands implicit):
  • Increment and decrement (INC, DEC -- add and subtract 1)
  • shift and rotate left and right 1:
    • "Arithmetic" Shift Left (ASL -- equivalent to adding operand to itself. Ignores sign, or, in other words, the previous sign disappears.)
    • "Arithmetic" and "Logical" Shift Right (ASR  and LSR -- equivalent to dividing by 2. ASR preserves the high bit as a sign bit, LSR clears the high bit.)
    • Bit rotate left and right (ROL, ROR --  shifts, but the previous carry bit gets shifted in to replace the vacated bit.)
    All shifts and rotates save the bit shifted out in the carry bit, so the rotates are nine-bit rotations.
    Note that shifting n times is multiplying or dividing by 2n.
  • Clear (CLR -- load or store 0)
  • Bit Complement (COM: invert the bits, equivalent to subtracting the operand from 255 or bit XOR-ing with $FF. $FF == 255, or eight bits set to 1s.)
  • Negate (NEG: subtract the operand from 256. 256 == $100, one more than fits in eight bits. Equivalently, complement and add 1 would do the same, even though 256 is too big for eight bits.)
  • Value test (TST -- subtract zero from operand. Tells the program whether the operand is zero or negative, without altering the operand.)

*(Reserved, undefined)
6800/6801
Unary
Operators
Ranks 4-7
AccumulatorMemory
ABIndexedAbsolute
$4X$5X$6X$7X
0100XXXX0101XXXX0110XXXX0111XXXX
NEG01XX0000$40$50$60$70
---01XX0001*($41)*($51)*($61)*($71)
---01XX0010*($42)*($52)*($62)*($72)
COM01XX0011$43$53$63$73
LSR01XX0100$44$54$64$74
---01XX0101*($45)*($55)*($65)*($75)
ROR01XX0110$46$56$66$76
ASR01XX0111$47$57$67$77
ASL01XX1000$48$58$68$78
ROL01XX1001$49$59$69$79
DEC01XX1010$4A$5A$6A $7A
---01XX1011*($4B)*($5B)*($6B)*($7B)
INC01XX1100$4C$5C$6C$7C
TST01XX1101$4D$5D$6D$7D
JMP01XX1110*($4E)*($5E)$6E$7E
CLR01XX1111$4F$5F$6F$7F


Of the unary operators, the right shifts and the bit rotates are the only operators that can't be synthesized easily from one or more binary operators.

(Can't be synthesized easily. Nine-bit rotates can be synthesized from shifts by saving the carry bit, shifting the argument, then moving the carry bit in. And right shifts are synthesized by left rotates followed by setting the upper bits appropriately. Right shifts are probably the most especially useful of unary operators.)

However, all the 6800 unary operators can operate directly on memory, which can save a lot of code that would otherwise have to save the accumulator, do the math, and then restore the accumulator.

Hopefully, you are wondering how the operands are specified. This is the strength of the 6800, what sets it apart from the 8080. But it is also one of the places the engineers missed a bet.

Each of the binary operators (or instructions) has a version for each accumulator.  The other operand, and the single operand for unary operators, is specified as
  • the value of a byte of memory at an absolute sixteen-bit address ("Extended", or absolute mode)
  • the value of the byte of memory immediately  following the instruction (immediate mode, marked by '#')
  • or the value pointed at by the index register offset by the value of the byte following the instruction (indexed mode, marked by ',X')
  • ... or the value at one of the first 256 locations in memory ("Direct", or page 0 absolute mode, optionally marked by '<').
The direct mode saves a byte, and the time of a memory cycle, somewhat softening the cost of saving registers to memory and then restoring them.

A quick example:
DSAVE EQU $70 ; Variable locations in memory
ACCM32 EQU $72 ; (Not normal way to declare.)
PSP EQU $76
*
ADD32 LDX PSP ; parameter stack
* Note: LDX <PSP also works
    STD DSAVE
    LDD 2,X ; 1st addend low word
    ADDD 6,X ; 2nd addend low word    STD ACCM32+2 ; save intermediate
    LDD ,X ; 1st addend high word
* Note: LDD 0,X also works
    ADCB 5,X ; 2nd addend high word
    ADCA 4,X ; the rest of it
    INX ; deallocate 1st addend location
    INX ; Increment X four times.
    INX ; See inherent operators below.
    INX
    STD ,X ; reuse 2nd addend location
    LDD ACCM32+2
    STD 2,X
    STX PSP ; update parameter stack
    RTS ; go home
*
* Note: Call it like this:
    JSR ADD32 ; probably a 16 bit Extended mode address

***** Kibbitzing aside: *****

Unfortunately, while the unary operators/instructions have both the indexed and extended mode versions -- operating directly on memory, and a version for each accumulator, there is not a direct mode version. Perhaps the designers thought they were running out of space in the 8-bit op-code map.

Kibitzing is free and you can always say things shoulda-been, and there is always the problem of hypothesis contrary to fact when you do, but I think if I had been on the design team I'd have agitated for moving the inherent operators which I mention below out of one of the lower four ranks, to clear space for a direct mode version.

This is not a serious problem in any of the existing implementations of the 6800 that I know of, because, other than saving an operation cycle and a byte of code, direct is exactly the same in effect as extended with the high byte of the address equal to zero.

But if there were a direct mode version of the unary operators, the intent that the direct page be used to overcome the scarcity of registers would have been more clear, and much of the false information that circulated about the limits of the 6800 instruction set could have been avoided.

Also, thinking about extensions of the 6800 architecture that have never been implemented, if you wanted to put the direct page in its own address space, the lack of direct address mode unary op-codes would be a problem, which is something to keep in mind.

***** End kibbitzing aside. *****

In addition, there are several instructions that are called "inherent" mode, where the operands are all implicit in the instruction. These include
  • manipulating result flag bits (TPA, TAP -- transfer Processor flags to/from A accumulator -- carry, zero, minus, overflow, interrupts)
  • incrementing and decrementing the index and stack pointers (INX, DEX, INS, DES)
  • moving and adding/subtracting between accumulators (ABA, SBA, TBA TAB)
  • pushing and popping the accumulators to/from the stack (PSH, PUL. Note that the "pop from stack" operator in Motorola mnemonics is "PUL".)
  • moving the stack pointer to/from the index register to get at parameters (TSX, TXS)
  • returning from subroutines and interrupts (RTS, RTI)
  • waiting for interrupts (WAI)
  • Software interrupt (SWI -- useful for debugging and/or system calls)
Then there are the flow of control instructions which operate on the program counter (PC -- called instruction pointer or IP in many other CPUs):
  • Jump and call/jump-to-subroutine (JMP and JSR -- only indexed and extended modes.)
  • Near branch and call (BRA, BSR -- one byte of signed offset is added to program counter.)
  • Conditional branches (Bcc -- condition encoded in the instruction.):
    • BEQ, BNE (zero bit -- equal or not equal after subtract or compare.)
    • BPL, BMI (negative bit -- plus or minus.)
    • BCC, BCS (carry bit -- clear or set, complemented in unsigned compare in BHI and BLS.)
    • BHI, BLS (combination carry and zero for unsigned comparison, high and low-or-same.)
    • BVC, BVS (overflow bit -- used in signed comparisons.)
    • BGT, BGE, BLE, BLT (combination overflow, zero, and negative bits for signed compares -- greater than, greater than or equal, less than or equal, less than)

*(Reserved, undefined; **6801 only)
6800/6801
Inherent
Operators
Ranks 0-3

Status, XAccumulatorBranchStack, et. al.
$0X$1X$2X$3X
0000XXXX0001XXXX0010XXXX0011XXXX
$X000XX0000*(HCF=$00)SBA=$10BRA=$20TSX=$30
$X100XX0001NOP=$01CBA=$11**BRN=$21INS=$31
$X200XX0010*($02)*($12)BHI=$22PULA=$32
$X300XX0011*($03)*($13)BLS=$23PULB=$33
$X400XX0100**LSRD=$04*($14)BCC=$24DES=$34
$X500XX0101**ASLD=$05*($15)BCS=$25TXS=$35
$X600XX0110TAP=$06TAB=$16BNE=$26PSHA=$36
$X700XX0111TPA=$07TBA=$17BEQ=$27PSHB=$37
$X800XX1000INX=$08*($18)BVC=$28**PULX=$38
$X900XX1001DEX=$09DAA=$19BVS=$29RTS=$39
$XA00XX1010CLV=$0A*($1A)BPL=$2A **ABX=$3A
$XB00XX1011SEV=$0BABA=$1BBMI=$2BRTI=$3B
$XC00XX1100CLC=$0C*($1C)BGE=$2C**PSHX=$3C
$XD00XX1101SEC=$0D*($1D)BLT=$2D**MUL=$3D
$XE00XX1110CLI=$0E*($1E)BGT=$2EWAI=$3E
$XF00XX1111SEI=$0F*($1F)BLE=$2FSWI=$3F


I think that pretty much covers the instruction set of the 6800, so I'll offer the following unbalanced summary chart:

*6801 only, **varies from 6800
6800/6801
Op-codes
Outline
Ranks
InhBRccInhUnaryBinary
A/B
$0X$1X$2X$3X$4X-$7X$8X-$FX
0000:0001:0010:0011:01MM:1AMM:
$R0:0000(HCF)SBABRATSXNEGSUB
$R1:0001NOPCBA*BRNINS$R1CMP
$R2:0010$02$12BHIPULA$R2SRC
$R3:0011$03$13BLSPULBCOM*SUBD
*ADDD
$R4:0100*LSRD$14BCCDESLSRAND
$R5:0101*ASLD$15BCSTXS$R5BIT
$R6:0110TAPTABBNEPSHARORLDA
$R7:0111TPATBABEQPSHBASRSTA
$R8:1000INX$18BVC*PULXASLEOR
$R9:1001DEXDAABVSRTSROLADC
$RA:1010CLV$1ABPL*ABXDECORA
$RB:1011SEVABABMIRTI$RBADD
$RC:1100CLC$1CBGE*PSHXINC**CPX
*LDD
$RD:1101SEC$1DBLT*MULTST*BSR/**JSR
*STD
$RE:1110CLI$1EBGTWAIJMPLDS
LDX
$RF:1111SEI$1FBLESWICLRSTS
STX

All of this is done with relatively few transistors, about 4000 to 5000 gates, less than ten thousand individual transistors, making it possible to manufacture the part using the technology available in the early- to mid-1970s.

In the process of building these, however, they developed new technology that enabled building more complex CPUs that could multiply and divide and such without having to add and subtract repeatedly.

When you design a computer's central processing unit, you may not realize it, but you use a mathematical technique of reducing the abstract general computing machine to real hardware.

Each CPU design reflects the what the engineers were focusing on when they performed the reduction.

RISC, "Reduced Instruction Set Computer", is therefore something of a pun, overloading the word "reduced" in a somewhat confusing way. The mathematical reduction in RISC architecture is performed with a focus on simplifying the instruction set -- reducing the number of instructions. (Didn't I say engineers love puns?)

When the 4004 was designed, the focus was on the functionality of a single accumulator in an absolute, incomplete reduction of the general computing model. It had to be that way, to fit a functional calculator controller into the available space on an integrated circuit using the technologies available and techniques understood at the beginning of the seventies. The 8080 inherited that focus, through the 8008, adding separate index functionality in the form of index registers.

Intel's engineers were aware of the importance of indexing, so they designed it in a way that a base index address could be saved in one pair of eight bit registers, to be copied to the index register pair where offsets could be added as necessary. Each register had a purpose, and you moved data to the necessary register to operate on it.

According to one story I've heard, Motorola's engineers started with an eight-bit implementation (reduction) of a minicomputer that they had built for their internal use, internally called the 680. According to another, the 6800 was a reduction of the PDP-11 architecture. Either way, they focused on a pair of general-purpose accumulators, adding an index register that implicitly performed offsets.

Lack of generalization in the 8080 was a bottleneck, and the Z-80 was developed as an extension to the 8080, to get around that lack. The Z-80 was the source of the "80" in Tandy Radio Shack TRS-80 brand name (making the TRS-80 Color Computer a bit ironically named).

The 6800's generalizations also had some lacks. Having only one index register can be clumsy. Constant offsets are not the only math you want to do on index addresses. And math between the two accumulators was severely limited, with no direct math between accumulator and index.

The 6502 was developed in no small part as a response to those lacks. It allows variables in page zero (6502 equivalent of the 6800 direct page) to be used directly as pointers, which helps it keep in the same ballpark, performance-wise, as the Z-80, and gives it advantages over the 8080.

Motorola developed the 6801 to address the worst of the lacks in the 6800 while keeping the transistor count low, to save room for other functions in systems-on-a-chip (SOC) applications.
  • The 6801 adds instructions which pair the A and B accumulators, A as the high byte, to load, store, add, subtract, and shift sixteen bit values. This significantly simplifies manipulating pointers.
  • It also adds an instruction to directly add the B accumulator to the index register, directly supporting small variable offsets.
  • CPX, the comparison for the index register, is generalized in effect on the flags. Again, this improves index handling.
  • It adds a hardware multiply instruction, 8 bit by 8 bit, yielding sixteen bits, useful for many things, including speeding array index math and a sixteen bit multiply more than four times as fast as the 6800 multiplying one bit at a time.
  • It adds a JSR call into the direct page, which might seem odd, but it makes it less costly to implement a small number of short convenience subroutines.
  • It adds Pushing and PUL-ing (popping) the index register, which corrects what seems like an odd omission in the 6800.)
  • It adds an officially documented BRN "branch never" bookend to branch always. This was actually an undocumented operator in the 6800, but the 6801 makes it official, allowing its use as a No-op that balances branch instructions, which can be useful for timing and as a debugging marker.   
These improvements bring the 6801 up to the same class of performance as the 6502 and the Z-80 -- in overall performance, an engineer skilled in programming the 6502, Z-80, and 6801 could get similar performance from each in many general applications, although each had niches they were better for. The companies that produced them also produced a variety of special purpose microcontrollers based on or similar to them, for specific classes of applications, as the market moved forward in the 1980s.



*****

That's too much tech for one chapter, but I'll add a little more arithmetic, for background.

The smallest bit of information in a binary computer is called a "bit". You may think this is a pun, and it is, but it is also an acronym: "Binary digIT".
A bit can take two values, but the values can be interpreted in many ways, for example:

Values of a Bit
Values:OnOff
Numeric:10
Logic:TrueFalse
Sign:MinusPlus
Gender:Non-traditionalTraditional

are some possible ways of interpreting the values of a bit. Note, in particular, that the actual assignments are arbitrary. "On" could also be interpreted as "plus", for instance.

Two bits together can take four values. Some example interpretations might be

Values of Two Bits Strung Together
Values:On-OnOn-OffOff-OnOff-Off
Unsigned Numeric:3210
Signed Numeric:-1-210
Logic:TrueTheoreticalFantasyFalse
Sign:Plus-RealPlus-ImaginaryMinus-RealMinus-Imaginary
Vegetable:PotatoOnionCabbageCarrot

The fundamental size of a piece of information for a computer has been traditionally called a "byte".  Yes, this is a pun, pure and simple.

It's also good advice:
Work on a problem a byte at a time!

(Sorry. Don't know what gets into me.)

Most modern computers use an eight bit byte, which allows 28, or 256 possible values. (And most modern computers usually actually work on problems four or eight bytes at a time.)

Numeric interpretations quickly spring to mind, such as 0 (00000000) to 255 (11111111), or -128 (10000000) to 127 (01111111). Other interpretations are not just possible, but are quite often used. For example, one might imagine an encoding in which male, female, and 254 other genders are encoded in an eight-bit byte. Or a byte could show the state of eight on-off switches on the control panel of some machine.

More typical is the US ASCII encoding, which actually only uses seven bits. Uppercase 'A', for instance, is encoded as decimal 65, and lowercase 'z' as decimal 122. '0' through '9' are decimal 48 through 57, which may seem confusing until you consider carefully the difference between numerals and numbers, or between the names of numbers and the value of numbers. The period, '.', is decimal 46.

(Unicode uses more bits, but it's a messy topic that would suck all the steam out of the story to cover here.)

So we can use numbers as codes for letters and words and symbols, and all sorts of ideas, which means we can also store and work symbolically on text and many kinds of data.

And we can use numbers to specify the color of a dot on a screen or a piece of paper, so we can also store pictures.

This provides us with ways to store and manipulate pretty much any information we could want. Sort of.

Interpretations are arbitrary. The binary number
01101101011000010110100101101100
could easily be read as binary equivalent of the decimal number
1,835,100,524
But If you put the most significant byte last, as Intel has been influencing the world more and more to do, it would be  
swap order: 01101100 01101001 01101101 01100001
compress:  01101100011010010110110101100001
decimal: 1,818,848,609
Or it could be the text string
mail
Of if the Intel byte order played tricks on you, it could be
liam
Or it could be interpreted as a floating point number under several different standards. Depending on the standard, it could be very large or very small.

Even as integers unsullied by byte order issues, the numbers have limits.

As mentioned before, a byte in a 6800 or 6502 or about any microprocessor you'll see is eight bits. (In some other computer CPUs, especially older mainframes, a byte will be a different size -- nine, six, seven, twelve, etc.)

Again, as mentioned before, an 8-bit register or memory cell can store a number between 0 and 255, inclusive. That's not a lot, but you can string several together. Two 8-bit registers treated together can store a number between 0 and 65,535, inclusive.

Unsigned Integer Sizes vs. Bit Length
SizebitsMinimumMaximumas Power of 2
1 bit1 bit0121-1
2 bits2 bits0322-1
1 nibble4 bits01524-1
1 byte8 bits025528-1
2 bytes16 bits065,535216-1
4 bytes32 bits04,294,967,295232-1
8 bytes64 bits018,446,744,073,709,551,615264-1

(Nibble is also sometimes spelled "nybble".)

The same numbers can be read as signed numbers, in various ways. Two's complement is currently the most common method:

Signed Integers vs. Bit Length (Two's Complement)
SizebitsMinimumMaximumas Power of 2
1 bit1 bitplusminus--
2 bits2 bits-21-21 ~ 21-1
1 nibble4 bits-87-23 ~ 23-1
1 byte8 bits-128127-27 ~ 27-1
2 bytes16 bits-3276832767-215 ~ 215-1
4 bytes32 bits-2,147,483,6482,147,483,647-231 ~ 231-1
8 bytes64 bits-9,223,372,036,854,775,8089,223,372,036,854,775,807-263 ~ 263-1


It's tempting to show some limits for the current most common standard floating point numbers, but this really is probably enough tech for this chapter.

*****

Well, wait. I need to mention the 6809.

Some people think the 68000 was Motorola's answer to Intel's 8086.

That isn't really a good comparison, and it isn't accurate.

The 68000 was Motorola's answer to the ARM, almost ten years too early. Or it was Motorola's answer to AMD's x86-64, about twenty years too early and 32 bits short. More to the point, it was Motorola's answer to Intel's iAPX 432, which was another architecture that was a lot too much, too far ahead of its time.

That the 68000 was successful so far ahead of its time is something to think about, but I want to point to what was actually Motorola's answer to the 8086: the 6809. (Perhaps it would be more clear if I said it was the answer to the 8088.)

The 6809 is often mistaken for a stepping stone between the 6800 and the 68000. But they were developed simultaneously, with different but similar design goals.

The 6801, by the way, is thought to have been influenced by the design work on the 6809.

The 6809 was targeted more towards extending the 6800's reach into the sixteen-bit controller market, which it did quite well.

For various reasons (including the ever-mistaken "Must not compete with ourselves!" mantra), Motorola's SOC offerings split between the true-8-bit 6805 series and the half-16-bit 6801 series for half a decade, then moved ahead building on the 6801 instead of the 6809.

Although some see the 68HC11 as a stripped-down 6809, the indexed modes tells the tale. The 68HC11 is simply a 6801 with an additional index register and hardware divide, and appropriate instruction extensions. The 68HC12 and 68HC16 continued building on the 6801, neither fully offering the advanced programming model that the 6809 offered, although the 68HC16 offered a twenty bit address space and three index registers, with an indexable stack.

This advanced programming model could almost be achieved in the 8086, but Intel designed that for the mis-engineered stack frame approach to nested local context. BP and SP are in the same segment unless overridden. (We won't yet mention the half-baked nature of the segments up until the 80386, since everyone knows about that.)

The 68000 could be used to implement the model, but no one did because Intel was busy pushing stack frames. And IBM, ...

But that gets way ahead of my story.


[Backed up at https://joel-rees-economics.blogspot.com/2020/02/bk-33209-little-about-6800-and-others.html.]

No comments:

Post a Comment

33209: Discovering the 6800 -- Parents and Polygamy

A Look at the 8080/TOC "Whoa, Merry, look who's here!" Jim said, sotto voce. He, Roderick, and I were at our lab table ...