8

In a nutshell, why can I not shift a byte using this way?

LD A, 11110000b  ; I want this to be 00001111b
LD C, 4
SRA C
OUT (0), A  ; doesn't give correct number

What is the proper way of shifting a byte by a fixed amount?

I could do it using this pretty sketchy way:

LD A, 11110000b
RRA
RRA
RRA
RRA
OUT (0), A  ; Gives 00001111b

Some context: so it has been some tough days, and day-by-day I was assembling my breadboard Z80 computer, that uses a CMOS Z80, so I can debug (single step) the CPU easily. It has 256 bytes RAM and 256 bytes ROM, and using a simple 74573 D-type latch, I added a single output port to the CPU.

I have done testing that my memory configuration, 573 and everything is okay, and it was. Now I was writing subroutines to interface a simple 1602 LCD and in the arithmetic involved I had to right shift the accumulator by 4, a thing that I didn't think was hard. But the LCD never worked correctly using the way I mentioned first using the SRA instruction and only worked when I ran the instruction RRA four times.

I literally am pretty new to assembly, and I apologize if what I am asking about is pretty basic. So what I am missing up here?

4
  • 1
    Going by <jgmalcolm.com/z80/advanced/shif#sra>, sra x is x >>= 1, not a >>= x. It stands for ‘shift right, arithmetic’, not ‘shift right the accumulator’. Commented Jun 19, 2021 at 21:02
  • 1
    What is so ‘sketchy’ about rra (other than it being a bit rotation instruction instead of a bit shift)? Commented Jun 19, 2021 at 21:05
  • @user3840170 I really don't know but I don't feel good of executing an instruction multiple times to shift.... and I don't really like making a 'for loop' (some how i don't know in assembly) to execute the instruction efficiently... Commented Jun 19, 2021 at 21:30
  • 4
    @ShamsM.Monem But you want to do multiple bits, don't you? Then doing the shift multiple times is the natural way. Anything else is decadent :))
    – Raffzahn
    Commented Jun 19, 2021 at 22:08

1 Answer 1

23

TL;DR:

Simply because SRA C doesn't shift A by the content of C, but shifts C right by one with keeping bit 7 (sign) static.


Z80 and shifting:

While the Z80 did add some nice new shifts and rotates, like the mentioned Shift Left Arithmetic for all 7 registers and memory, it still only shifts by one bit position per execution. So any shift by more than one position will, like with many other 8-bit CPUs, require repetition - as loop or unrolled, as in:

    LD  A,11110000b  ;(*1)
    SRA A
    SRA A
    SRA A
    SRA A
    OUT (0),A

Why not?

Well, doing multi position shift would either

  • require a barrel shifter - not really cool as that needs quite a lot of real estate, whereas a single bit shifter comes almost free with regular ALU design.

  • Or need a micro program loop to handle that case - again rather costly.

Keep in mind, (classic) 8 bit CPUs are not about making life comfortable, but getting work done at all - they are only one step above no CPU.

So is there a better way?

Yes, use RRA. It takes only 4 T-states while SRA takes 8.

    LD  A,11110000b
    RRA
    RRA
    RRA
    RRA
    OUT (0),A

This is due the fact that SRA is an Z80 enhancement which needs, while doing essentially the same operation, a prefix byte (0CBh), which in itself eats up 4 T-states.

RRA is a basic 8080 instruction (*2), encoded in a single byte (01Fh) and thus able to be executed in 4 T-states. In 8080 assembly it's called RAR.

Using RRA thus brings 3 advantages at once:

  • More compact code
  • Faster execution
  • Compatibility with other 8080 compatible CPUs

Yes but ...

As Supercat reminds us, there's an odd pair of instructions rotating nibble wise:

  • RLD - Rotate Left Digit and
  • RRD - Rotate Right Digit

They rotate a byte pointed to by HL thru the lower 4 bits of A.

  • RLD moves the top 4 bit of a memory location into the lower 4 bit of A, moves the lower 4 bit of that location into the higher 4 and stores the lower 4 of the original content of A into the lower 4. Confusing? Lake a look here: enter image description here

  • RRD does the same just the other way around: enter image description here

Sounds great, especially when considering that output is more often than not multiple bytes from memory, which are pointed to by HL anyway. To output both nibbles of a byte pointed to by HL code might look like this:

    LD   A,(HL)
    AND  0Fh
    OUT  (0),A
    RLD
    AND  0Fh
    OUT  (0),A

(Outputting the lower 4 first)

The whole works has just two tiny disadvantages:

  • it destroys the input byte and
  • it isn't much faster at all

While the 4 SRA take 16 T-states, RLD takes 18 - but saves an additional LD - which in turn may or may not be needed depending on the over all program structure.

This two, plus the fact that the vast majority of Z80 programmers will need to look at the manual when encountering an RLD/RRD, make a good reason to stay with the classic 8080 compatible RRA sequence.


*1 - It's bad practice to add spaces in assembler parameters, as that's not portable -especially not if one wants to use tools contemporary to that CPUs.

*2 - Not to be confused with RR A, which is the same but in Z80 encoding, like SRA, with a 0CBh prefix and 8 T-states.

12
  • 3
    For other examples that have the lower 4 bits set, you might have to and a with 11110000b beforehand and clear the carry bit
    – S.S. Anne
    Commented Jun 19, 2021 at 22:13
  • 1
    @S.S.Anne AND already clears the carry bit, not that it makes any difference if you're not using RRCA (or RLCA).
    – Neil
    Commented Jun 20, 2021 at 8:39
  • 1
    The Z80 also includes instructions to move rotate ranges of memory four bits at a time. Unfortunately, there are no register-based equivalents.
    – supercat
    Commented Jun 20, 2021 at 21:40
  • 1
    @S.S.Anne Not really - see the added section - depending on the surrounding program it may not save anything but create additional problems. These two instructions do really only make sense when operation on longer BCD strings. (Also, for your remark about ANDing, cleaning output should always done when needed, not premature - that only makes reading harder.)
    – Raffzahn
    Commented Jun 20, 2021 at 23:36
  • 1
    @Raffzahn: RRD/RLD aren't necessarily only four BCD--they could be used in any situation where one would need to shift a sequence of bytes left or right by four, such as shifting a black and white bitmap left or right by four pixels, or a four-bits-per-pixel bitmap left or right one pixel. Still, probably not as broadly useful as they could have been with register operands.
    – supercat
    Commented Jun 21, 2021 at 4:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .