#
# asm_sample.s : divide subroutine
#       2003 November    MIZUNO Y.
#
# calling sequence
#   void  divide( long long dividend, int divisor, int *quotient, int *remainder );
#

/*** define constant(s) ***/

.equ    INT_MIN,  0x80000000    # minimum negative value (= -2147483648)


/*** unsigned divide routine       ***/
/***    edx:eax / ebx -> eax & edx ***/
/***    if overflow then set CF    ***/
        .text
        .align  4
ui_divide:
        pushl   %ebp            # save ebp
        movl    %esp, %ebp      #  & make a new frame
        pushl   %ebx            # save registers
        pushl   %esi
        pushl   %edi

        /** check overflow **/
        cmpl    %ebx, %edx
        jae     ui_div_ovf      # quotient takes more than 32 bit

        /** division loop **/
        movl    $0, %ecx        # ebx:ecx = a divisor << 32
        movl    $0x80000000,%edi # edi: bit pattern to set quotient
        movl    $0, %esi        # esi: quotient
10:
        shrl    $1, %ebx        # shift a divisor 1 bit right
        rcrl    $1, %ecx
        cmpl    %ebx, %edx      # divisor < dividend ?
        jb      30f
        ja      20f
        cmpl    %ecx, %eax      # compare lower word
        jb      30f
20:                             # Here, dividend (edx:eax) >= shifted divisor (ebx:ecx)
        subl    %ecx, %eax
        sbbl    %ebx, %edx      # subtract divisor (ebx:ecx) from dividend (edx:eax)
        orl     %edi, %esi      # set 1 bit of a quotient
30:
        shrl    $1, %edi        # to the next bit
        jnz     10b             # repeat from bit-31 to bit-0

        /** end **/
        movl    %eax, %edx      # edx <- remainder
        movl    %esi, %eax      # eax <- quotient
        clc                     # normal return
ui_div_ret:
        popl    %edi            # restore registers
        popl    %esi
        popl    %ebx
        popl    %ebp
        ret

ui_div_ovf:
        stc                     # error return
        jmp     ui_div_ret

/**************************/
/***    main routine    ***/
/**************************/
        .align  4
        .globl  _divide
_divide:
        pushl   %ebp            # save ebp
        movl    %esp, %ebp      #  & make a new frame
        pushl   %ebx            # save registers
        pushl   %esi

        /** check signs of a dividend and a divisor **/
        movl    $0, %esi        # esi: sign flag
        movl    8(%ebp),  %eax  # low word of a dividend
        movl    12(%ebp), %edx  # high word  "     "
        cmpl    $0, %edx
        jns     10f
        movl    $3, %esi        # dividend is negative
        notl    %eax            # change a dividend edx:eax to positive
        notl    %edx
        addl    $1, %eax        # (make 1's complement and add 1)
        adcl    $0, %edx
10:
        movl    16(%ebp), %ebx  # get a divisor
        cmpl    $0, %ebx        # is negative ?
        jns     20f
        xorl    $2, %esi        # bit 2: dividend & divisor have different signs
        negl    %ebx            # change a divisor to positive

        /** divide as unsigned integers **/
20:
        call    ui_divide       # edx:eax / ebx
        jc      div_overflow    # overflow

        /** negate results according to signs **/
        testl   $1, %esi        # dividend is negative ?
        jz      30f
        negl    %edx            # negate a remainder
30:
        testl   $2, %esi        # dividend & divisor have different signs ?
        jz      40f
        negl    %eax            # negate a quotient
        cmpl    $0, %eax
        jg      div_overflow    # if not negative value then overflow
        jmp     div_ret
40:
        cmpl    $0, %eax
        js      div_overflow    # if negative value then overflow

        /** store the results **/
div_ret:
        movl    20(%ebp), %ebx
        movl    %eax, (%ebx)    # store the quotient
        movl    24(%ebp), %ebx
        movl    %edx, (%ebx)    # store the remainder

        /** end **/
        popl    %esi            # restore registers
        popl    %ebx
        popl    %ebp
        ret

        /** overflow **/
div_overflow:
        movl    $INT_MIN, %eax  # quotient  <- INT_MIN
        movl    $INT_MIN, %edx  # remainder <- INT_MIN
        jmp     div_ret
