-2

How can I reverse a register value at byte level?

mov al,12  -----> how can i reverse al value
to 21

Here's what I have tried :

mov bx,4321      ;i want to make bx 1234

mov cl,04          ;cl for rotation value

xchg bh,bl          ; now bx will 2143  

mov al,bh         ;moving 21 to al

rol al,cl             ; rotate al 4 times  to left now al is 12`

mov bh,al      ; bh is 12 setuped ,time for bl

;---------------------------
mov al,bl   ;moving lower byte  43 to al

rol al,cl     ; rotate 4 times to left now al will 34

mov bl,al      ; moving 34 to bl  

Now bx must contain a reversed number 1234; the problem was the number is in hex that is 10e1h or 4321.

When I reverse it is 1e01h but this value is not expressing 1234.

1234 is 04d2. The value I am getting is 7681d.

halfer
  • 19,824
  • 17
  • 99
  • 186
foby bus
  • 23
  • 2
  • 9
  • 2
    Reversing nibbles and reversing decimal digits is not the same thing, which is the task that you want to do? – harold Dec 21 '18 at 14:20
  • 2
    If you want to reverse in decimal you will have to use decimal arithmetic. – Jester Dec 21 '18 at 14:39
  • `12 = 0b00001100`. `21 = 0b00010101`. Bit-reversing their binary representations does not reverse the decimal digits, because 10 is not a power of 2. `rol al,4` would swap hex digits, though, because 16 = 2^4 and a byte contains two nibbles (hex digits). – Peter Cordes Dec 21 '18 at 18:01
  • 1
    Instead of `rol al,4` he should rather use `mov cl,4; rol al,cl` because shift and rotate operations with immediate operand above 1 were actually introduced with 80286. – vitsoft Dec 22 '18 at 17:22
  • 1
    @vitsoft :EMU8086 which the OP has tagged this for does support `rol al,4` although what it does is convert it into 4 consecutive `rol al, 1` . The shift operations with an immediate bit count were introduced on the 80186 not the 80286. – Michael Petch Dec 23 '18 at 20:35

2 Answers2

4

While the other answers are giving you a direct solution to your problem, I'd like to write something about the theory because I think it will help you next time:

As you have already written, the decimal number 1234 is written as 4D2 in hexadecimal system and 4321 is written as 10E1.

This means that the operation "reverting a number" leads to different results in different numeral systems:

In decimal system "reverting" 1234 leads to 4321. In hexadecimal system "reverting" 4D2 leads to 2D4. Using a fixed length of 4 hexadecimal digits (16-bit registers!) however "reverting" 04D2 will lead to 2D40...

If some operation only works in a certain base(*), you have to consider the following:

Using a computer working with bytes you can easily perform operations in base-256: xchg bh,bl will "revert" the two digits of the number in base-256 system.

Performing operations in a base 2^N (such as binary, octal or hexadecimal) are possible using shifting and rotating.

However operations on other bases (such as decimal) you will require to calculate the single digits, perform the operation and calculate the (binary) number from the digits.

For reverting a decimal number the following pseudo-code might work:

  A = input (here: 1234)
  B = 0
mainLoop:
  digit =  A mod 10  (get the right digit of A)
  A = A/10           (remove the right digit from A)
  B = 10*B + digit   (append the digit to B)
  if A>0: jump to mainLoop

In assembler the code could look like this:

    mov ax,1234   ; ax = "A" in the pseudo-code
    mov cx,0      ; cx = "B" in the pseudo-code
    mov bx,10     ; The base we are working in
mainLoop:
    xchg ax,cx    ; "mul" can only work with ax
    mul bx        ; Results: ax = "10*B"
                  ;          dx = overflow (typically 0)
    xchg ax,cx    ; Change ax and cx back: cx="10*B"
    mov dx,0      ; Prepare dx for "div"
    div bx        ; Perform division and modulo
                  ; Result:
                  ;   ax = "A/10"
                  ;   dx = "A MOD 10" = "digit"
    add cx,dx     ; "B = 10*B+digit"
    cmp ax,0
    ja mainLoop
                  ; Here cx will contain the "reverted" number

(*) The operation you want to perform is not "reverting a number" but "reverting a decimal number".

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
2

For reversing bits in a byte, use a lookup table. For reversing bits in a word use the lookup table to reverse the lowest 8 bits, then rol ax,8, then use the lookup table to reverse the other 8 bits.

For reversing (4-bit) nibbles in a byte, use rol al,4. To reverse nibbles in a word use rol al,4; rol ax,8; rol al,4.

For reversing decimal digits in a byte or word; don't. Instead, change the code that prints decimal digits. The reason is that the conversion from integer (e.g. the value 1234) to string (e.g. the characters "1234") typically generates characters in the reverse order and has to do extra work to reverse the characters; so a "print_reversed_decimal()" would do less work (and reversing the number some other way and then reversing it again when it's printed is twice as much work for nothing!). Alternatively you could use BCD instead (where each nibble contains a decimal digit). In this case you can reverse decimal digits by using the "reverse nibbles" approach, and printing the number after becomes much cheaper (shifts and masks instead of division and modulo).

Note that the maths for reversing decimal digits in an integer is something like:

    k = 10000000;
    result = 0;
    while(value > 0) {
        digit = input % 10;
        result += digit * k;
        input /= 10;
        k /= 10;
    }

However, you'd have to determine the right value for k first (which depends on whether leading zeros are ignored or reverse - e.g. if 012 becomes 210 or 021). Also note that this is expensive (divisions, modulo and multiplication inside a loop), which is why you'd want to do everything possible to avoid doing it. Of course if the range of numbers is small enough (e.g. values from 000 to 199 only) then you could use a lookup table to do this quickly.

Brendan
  • 35,656
  • 2
  • 39
  • 66
  • You can accumulate `result` with `result = result * 10 + digit`, like the other answer is doing. That's the normal algorithm for string->integer, starting with the MSD. So take the low digit of `value` and treat it as the next digit of `result`, and you're all set without needing to know how many decimal digits there will be. Also avoids an expensive `k/=10`. (And BTW, division by compile-time constants is much cheaper than the HW `div` instruction. [Why does GCC use multiplication by a strange number in implementing integer division?](https://stackoverflow.com/q/41183935)) – Peter Cordes Dec 22 '18 at 00:33
  • But yes, +1 for just printing / storing digits in LSD-first order from the normal int->string algorithm. – Peter Cordes Dec 22 '18 at 00:34
  • @PeterCordes: Using `result = result * 10 + digit` would be fine for the "leading zeros ignored" case, but for "leading zeros reversed" it would involve headaches (at a minimum, completely different terminating conditions for the loop). For fun, in theory there are an infinite number of leading zeros (e.g. `1` is just shorthand for `...0001`) so maybe `pow(10, INFINITY)` is the right initial value for `k`. ;-) – Brendan Dec 22 '18 at 00:54
  • You can just set a fixed iteration count if you want to pretend your register holds a fixed-width decimal number. There's no need to actually *use* a `k`. The algo I described will just multiply by 10 and add zero for the "extra" iterations, tacking on trailing zeros to the result. Yes, leading zeros are an issue, but I don't see how your algo makes it any better. – Peter Cordes Dec 22 '18 at 00:58
  • 2
    @PeterCordes: My algo allows a single piece of example code to be followed by "you'd have to determine the right value for k". Using `result = result * 10 + digit` doesn't have this property - you'd have to have 2 pieces of example code just to convey the same basic idea (one for "leading zeros ignored" and another for "leading zeros reversed"), and (given that nobody, possibly including OP, is likely to ever want to do this in the first place) I'm not sure I can see the justification for "twice as many examples". – Brendan Dec 22 '18 at 01:19
  • It's hardly "twice as many", just mentioning that you can replace a zero-check with a counter in your one example. The obvious justification is that it saves a lot of work, simplifying implementation in asm. Plus, your current example doesn't provide a way to determine `k` for the no-leading-zeros case, which is the only one that's unambiguous without extra assumptions. – Peter Cordes Dec 22 '18 at 01:31
  • @Brendan +1 yes. a lookup table would probably be the way to go here. and although we do not know much about the motivation for the OP, your notes about bespoke printers clearly make good sense. but i am intrigued by your *fervent desire to normalise data wrt your comments here! imo this is not always desirable; it can lead to unpleasant single points of failure! (why i am fond of stuff like git). &&thx to you i just learned a little about the rum&uncanny intel AAx instructions haha oh&&w00t! i got my fascinating hat ;) – violet313 Dec 22 '18 at 13:27