# What's this assembly doing?

I have been trying to figure out the assembly for part of a DOS game and there is an operation that keeps getting called that uses all 4 registers. I can see what each line does but I can't for the life of me figure out what all the code together is meant be doing.

Can anyone give me some idea?

The code is:

``````seg000:3825 some_math_op_on_regs proc far; CODE XREF: sub_72C6+19FP
seg000:3825                              ; sub_72C6+1DDP ...
seg000:3825       cmp     cl, 10h
seg000:3828       jnb     short loc_383A ; Jump if CF=0
seg000:382A       mov     bx, dx         ; c register is < 16; move d to b
seg000:382C       shr     ax, cl         ; Shift a right by value in c (logical)
seg000:382E       sar     dx, cl         ; Shift d right by value in c (arithmetic)
seg000:3830       neg     cl             ; Negate c (2's complement)
seg000:3835       shl     bx, cl         ; Shift b left by value in c (logical)
seg000:3837       or      ax, bx         ; OR a and b, store result in a
seg000:3839       retf
seg000:383A ; --------------------------------------------------------------------
seg000:383A
seg000:383A loc_383A:                    ; CODE XREF: some_math_op_on_regs+3j
seg000:383A       sub     cl, 10h        ; c register is >= 16; subtract 16 from c
seg000:383D       xchg    ax, dx         ; Switch values in a and d
seg000:383E       cwd                    ; Convert word to doubleword
seg000:383F       sar     ax, cl         ; Shift a right by value in c (arithmetic)
seg000:3841       retf
seg000:3841 some_math_op_on_regs endp
``````

This looks like a 32-bit shift right compiler helper. In 16-bit era, 32-bit numbers were represented by a pair of registers, in this case `ax:dx`. The check for 16 is an optimization: if the shift is over 16, the low register value is lost completely, so it can be discarded and replaced by `dx>>(shift-16)`, while the high register is filled with the sign bit as the result of the `cwd` instruction. Here's the (lightly) commented source code from the Borland C runtime library which seems to match yours:

``````;[]-----------------------------------------------------------------[]
;|      H_LRSH.ASM -- long shift right                               |
;[]-----------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
;
;       Copyright (c) 1987, 1992 by Borland International
;

INCLUDE RULES.ASI

_TEXT   segment public byte 'CODE'
assume  cs:_TEXT
public  LXRSH@
public  F_LXRSH@
public  N_LXRSH@

N_LXRSH@:
pop     bx                      ;fix up for far return
push    cs
push    bx
LXRSH@:
F_LXRSH@:
cmp     cl,16
jae     lsh@small
mov     bx,dx                   ; save the high bits
shr     ax,cl                   ; now shift each half
sar     dx,cl
;
;                       We now have a hole in AX where the lower bits of
;                       DX should have been shifted.  So we must take our
;                       copy of DX and do a reverse shift to get the proper
;                       bits to be or'ed into AX.
;
neg     cl
shl     bx,cl
or      ax,bx
retf
lsh@small:
sub     cl,16                   ; for shifts more than 15, do this
; short sequence.
xchg    ax,dx                   ;
cwd                             ; We have now done a shift by 16.
sar     ax,cl                   ; Now shift the remainder.
retf
_TEXT   ends
end
``````

It appears to be a 32 bit right shift with the 32 bit number provided in `dx:ax`, and `cl` being the number of bits to shift.

If you assume `cl` is over 16, a right shift by more than 16 bit only needs to care about the upper 16 bit, which are stored in `dx`, because the lower 16 bit are shifted out anyway.

So that's exactly what the 2nd block does. If `cl` larger than 16, move `dx` (upper 16 bit) into `ax` and convert it to a 32 bit number, subtract 16 from `cl` because this is implicitly done by ignoring the lower 16 bit, then shift the upper part (which is now `dx:ax` thanks to the `cwd`) by that number.

I didn't try to understand the top part, but my assumption is it does exactly the same for shift widths below 16 bits.

Basically, it's a 32 bit right shift done in 16 bit architecture.