7

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:3832       add     cl, 10h        ; Add 16 to c
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
9

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
;       All Rights Reserved.
; 

        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
        add     cl,16
        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
5

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.

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.