21

The Z80 has an instruction RLD, which apparently treats the lower 4 bits in the accumulator and the full 8 bits in (HL) as a twelve bit integer which it then rotates left by 4 bits. The carry flag does not participate in the rotation and the rest of the accumulator is left alone.

Correspondingly, there's an RRD.

Why would the designers put something like that in? I don't imagine it's was a simple by-product of some other part of the design, but I can't think of a use-case which the other shift/rotate instructions wouldn't do a reasonably good job at.

  • 7
    I think because of BCD math like you might use to implement a decimal calculator, but that's just a guess. Great question! – Wayne Conrad May 29 '18 at 15:38
25

Per Zilog's Z80 user manual:

Two BCD digit rotate instructions (RRD and RLD) allow a digit in the accumulator to be rotated with the two digits in a memory location pointed to by register pair HL (See Figure 10). These instructions allow for efficient BCD arithmetic.

Given that DAA exists for addition and subtraction, I'm going to hazard a guess that the motivation was long multiplication and division, permitting the shift-a-digit step with A acting as carry.

E.g. to derive long multiplication from first principles:

3 * 29
= 3 * (2 * 10 + 9)
= (3 * 2) * 10 + 3 * 9

RLD will give you the * 10 step.

If it were 3 * 2983471 there'd be six * 10 steps, manipulating what becomes a four-byte answer.

If it were 23 * 2983471 then that'd be twice as much work, to get 2 * 2983471, multiply that by ten, then calculate and add in 3 * 2983471. So twelve * 10 steps.

I'd also be likely to have obtained the 2 in isolation by a divide-by-ten step. Which RRD would help with.

10

RLD and RRD are simple 4-bit shifts of BCD-coded values. As they are shifting by one decimal digit right or left, they effectively result in a multiplication (or division) by 10 (assumed A is 0) when used repeatedly on a multi-digit BCD value or a multiplication by 10 plus an addition of the lower digit in A. A acts as a sort of "decimal carry" in this operation.

You can use this in a multiplication (or division) routine for decimals parallel to the pen-and-paper method you learned at school.

Multiplying a multi-digit packed BCD number at DATA by 10, with the byte count (half the number of decimal digits) in COUNT, would look like the below:

        LD      HL, DATA
        LD      B, COUNT
        XOR     A
ROTAT:  RLD
        INC     HL
        DJNZ    ROTAT

Outside of BCD-coded decimals, you could be using these commands to scroll a ZX Spectrum screen left or right by 4 pixels, for example.

  • Probably worth mentioning that your example assumes little-endian BCD. IOW, RLD should start at the right-hand end, using A to hold the intermediate carries as HL moves leftwards through the number; conversely, a RRD loop starts at the left-hand end and works rightwards. – Toby Speight Mar 27 at 15:39
4

I think the primary usage case for something like that would be shifting a multi-byte quantity left or right by four bits. One could rotate a 56-bit value (e.g. the mantissa of a floating-point value) left by 4 bits by doing something like:

    ld h,myThing >> 8  ; Assume it doesn't cross a page boundary
    ld bc,4
    and a ; Clear carry
loop:
    ld l,myThing & 255
    rl (hl)
    inc l
    rl (hl)
    inc l
    rl (hl)
    inc l
    rl (hl)
    inc l
    rl (hl)
    inc l
    rl (hl)
    inc l
    rl (hl)
    djnz loop

but it could be done much faster as:

    ld h,myThing >> 8  ; Assume it doesn't cross a page boundary
    xor a  ; Could use "ld a,0" if carry was important for some reason

    ld l,myThing & 255
    rld (hl)
    inc l
    rld (hl)
    inc l
    rld (hl)
    inc l
    rld (hl)
    inc l
    rld (hl)
    inc l
    rld (hl)
    inc l
    rld (hl)

I'm not sure the value of that is greater than the value of other features that could have been provided for the same amount of effort and silicon, but I think something like the above would be the intended usage pattern. Still, the feature would have value either in cases where things are being shifted by exactly four bits, or in performance-sensitive code (like floating-point math) where values may need to be shifted by N bits and it would not be uncommon for N to be 3, 4, or 5. I'm not sure exactly how big the thing to be shifted would need to be to make such optimizations worthwhile, but the requirement that objects be stored in RAM might limit the value of such optimizations when shifting smaller things. If a 32-bit value needed to be shifted, the first approach could be changed to:

    ld hl,(myThing)
    ld de,(myThing+2)
    ld bc,4
    and a ; Clear carry
loop:
    add hl,hl
    rl  e
    rl  d
    djnz loop
    ld (myThing),hl
    ld (myThing+2),de

greatly reducing the cost of the loop. The fact that the rld form doesn't need the loop would likely make it more efficient than running the loop four times, but if "rld" had allowed code to specify a register rather than (HL) it could have been much more useful in the small-objects case.

3

These instructions appear to be useful for converting between BCD numbers and their printed representation. They would also be useful for printing numbers in hexadecimal format. accessing a BCD or hex value 4 bits at a time corresponds to one input or output character.

  • 1
    I think their main usefulness would be when shifting multi-byte quantities, both in the case where one wants a shift by four, and also in the cases where one wants a shift by 3 or 5 [as might occur fairly often when working with floating-point numbers], since shifting by 4 and then shifting one bit forward or backward would be faster than shifting left 3 times or moving data forward a byte and shifting backward 3 times. – supercat May 29 '18 at 15:48
  • @Ken Would you be able to put some code together to illustrate what you mean? – Wilson May 29 '18 at 15:52
  • @Wilson I am unfortunately not a Z80 programmer, but the idea is similar to code given in other answers: loop through a number 4 bits at a time and do something with the 'current' 4-bit 'digit'. to convert a BCD digit to ASCII you would add 48, so that 0 becomes ASCII '0' (48+0 = 0) and 9 becomes ASCII '9' (48+9 = 57). Converting ASCII to BSD you would do the reverse, subtracting 48. – Ken Gober May 30 '18 at 15:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.