4

I'm trying to understand the instruction sets of old microcontrollers, especially the 6502.

The documentation of the instruction set that can be found here lists two shift instructions (beside the rotate instructions):

ASL - arithmetic shift left

LSR - logical shift right

Why are there no arithmetic shift right and logical shift left instructions?

I know that one can simply add a number to itself to logically shift it to the left, but a dedicated instruction would be much more convenient and also faster, even if it does just that. But a missing arithmetic shift right instruction does not make sense to me.

uzumaki
  • 1,743
  • 17
  • 32
  • 2
    Logical shift left is same as arithmetic shift left, so there is no need for LSL. ASR & LSR are different. Why there is only one is a question for the chip designer. Sometimes instructions for signed numbers are omitted. Like SSE2 has `PMULUDQ` but not `PMULDQ`. – W. Chang Jan 25 '19 at 03:06
  • 5
    If the value to arithmetically shift right is in the accumulator you could do it as `cmp #$80` `ror a` – Michael Petch Jan 25 '19 at 04:10
  • 2
    The 6502's ALU has a right-shift bit-to-bit signal chain used by the LSR and ROR operations, and there are dedicated gates and control signals within the circuitry to activate the path. For left-shift, however, there's already the arithmetic carry chain with its own gates and control signals - and rather than cram in additional shift-specific control logic, the ASL and ROL instructions are implemented as self-addition of the A contents, with appropriate handling of the C flag. – Jeremy Mar 27 '19 at 08:31
  • By the way, shouldn't ASL really be called LSL, given that an arithmetic shift is usually considered to be one that preserves the sign? (Ah, I see that @JeremyP has already pointed this out.) – Jeremy Mar 27 '19 at 08:51

4 Answers4

5

A logical shift left is the same as arithmetic shift left, so there's no need to distinguish the two. Many assemblers for other platforms consider LSL and ASL mnemonics as synonyms (for example ARM).

As for lack of arithmetic shift right, you can always do CMP #80/ROR A to perform the arithmetic shift right on the accumulator.

Karol S
  • 9,028
  • 2
  • 32
  • 45
4

I've always thought that particular pair of instructions to be a bit of a con. There are no arithmetic shifts at all on the original 6502. Arithmetic shifts preserve the sign of the number being shifted and the so called ASL only does that by virtue of the fact that the 6502 uses 2's complement.

The original 6502 only has two shift operations: shift right and shift left with bit shifted out going to the carry flag and the bit shifted in being set to zero. For shifts left with 2's complement, there is no distinction between arithmetic and logical. However, for shifts right, an arithmetic shift has to preserve the sign bit. This means, to do it, you would need circuitry to propagate the top bit of input into the shifter to the top bit of the output and an extra control line to select between that and 0. These all require more transistors and an arithmetic shift can be simulated with a rotate.

    CLC
    LDA number
    BPL positive
    SEC
positive:
    ROR A

The primary driver behind the design of the 6502 was to make it outperform the Motorola 6800 at a lower price. This was done by stripping inessential features out of the 6800 so they could make the transistor count as low as possible. The 6502 has a transistor count of around 3,500. The earlier 6800 has a transistor count of around 4,500. Inessential features like a proper arithmetic shift were therefore left out.

In fact, given that shifts can be emulated by clearing the carry flag and then doing a rotate, I'm surprised they bothered with them at all. The designers must have found they could add the shift instructions with a minimal increase in complexity, much like the SBC instruction is exactly the same as the ADC instructions but with the bits of the second operand inverted.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • Is there no bit-test instruction or other efficient way to set the carry flag from the MSB of the accumulator? I guess branches are cheap-ish on non-pipelined CPUs, but would `LDA number` / `ROL A` / `LDA number` be shorter/faster? (A quick look at http://www.6502.org/tutorials/6502opcodes.html says that LDA doesn't set the C flag, and ROL A looks like a good bet to shift the MSB into C. (i.e. ADC A to itself). Of course that only helps for a right-shift by 1 because it requires that the original value is available to load, while yours could use ORA instead of LDA to set S for another BPL. – Peter Cordes Jan 29 '19 at 10:49
  • @PeterCordes I'd have to count the cycles, but I think your way of getting the carry flag might be faster than mine. If you are using a zero page address, it would certainly be faster. – JeremyP Jan 29 '19 at 16:19
  • 1
    @PeterCordes If you don't mind trashing an index register, `LDA number` / `TAX` / `ROL A` / `TXA` would be even better – JeremyP Jan 29 '19 at 16:22
  • 1
    @PeterCordes - as per other answers, CMP #$80 sets the carry flag from the MSB. – Jeremy Mar 27 '19 at 08:18
  • @Jeremy: yeah, KarolS's answer (from a couple months after this comment thread) has a *much* implementation of arithmetic right shift than this. It deserves more upvotes for showing that it's not a problem to implement ASR efficiently. – Peter Cordes Mar 27 '19 at 08:22
  • @PeterCordes It's much more efficient than mine but it is still a small issue. It's still two extra clock cycles per shift and two bytes. – JeremyP Mar 27 '19 at 09:44
  • Yeah, so it does still demand an explanation for the absence of a real ASR instruction, so KarolS's answer unfortunately doesn't answer the actual question, while the other two take a solid stab at it: leaving out a low-priority feature to save transistors. In most code it won't get a lot of use anyway, and when needed it can be emulated without much pain. – Peter Cordes Mar 27 '19 at 10:33
2

Why is there no arithmetic shift right instruction, why are there so few registers, why is there no multiplication/division instruction, why is there no SIMD, why is there no floating point support, why is there no MMU, ... ?

Back when 6502 was designed there were (relatively severe by modern standards) limits on both the number of transistors and the complexity of the CPU. Sacrifices were made to make it a viable to design and manufacture, and signed integers are "barely needed".

Note: The main reason "signed" is used is that C (and later languages) have the wrong default (it assumes "signed" for almost all integers unless you tell it otherwise) and people are too lazy to type "unsigned" when they don't need signed; so a lot of software uses (slightly more expensive) signed arithmetic for no reason other than laziness. In general, most of the situations where you actually do need signed numbers you're dealing with "real world values" and need floating point anyway (things like array indexes, addressses/pointers, sizes, character/code points, pixel values, distances, ... are all naturally unsigned).

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • While this is true, of course, I wonder why they didn't just call the instructions LSR/LSL. Maybe they though ASL just looks better. When I learned assembly as a kid going by the C64 manual I was so confused as to the distinction between one shift being "logical" and one being "arithmetic", with no Googles. – Greg H Feb 08 '20 at 02:54
2

As others have pointed out, an arithmetic right shift in the A register can be synthesised with

        CMP #$80        ; copy sign bit of A into carry
        ROR A

Note that when you do a LDA before this, it's shorter (by two bytes) and faster than the code given in JeremyP's answer.

To ASR a memory location, you can use the following technique from the Nesdev wiki:

        LDA addr        ; copy memory into A
        ASL A           ; copy sign bit of A into carry (shorter than CMP)
        ROR addr

This leaves the flags set the same as LSR, but unlike LSR it destroys the contents of the accumulator.

Community
  • 1
  • 1
cjs
  • 25,752
  • 9
  • 89
  • 101