SX Microcontroller Math Method

Divide 48 bit int by 24 bit int to 48 bit int

by Nikolai Golovchenko

Dividend	DS 6
Divisor		DS 3
Temp		DS 4
BitCount	DS 1


;**********************************************************************
;
;48 BY 24 BIT UNSIGNED DIVISION (Non-restoring method)
;
;**********************************************************************
;
;     Input: Dividend, 6 bytes (Dividend+0 MSB, ... , Dividend+5 LSB)
;            Divisor, 3 bytes (Divisor+0 MSB, ... , Divisor+2 LSB)
;
;    Output: Dividend, 6 bytes (Dividend = Dividend / Divisor)
;            Z  - division by zero
;            NZ - okay
;
; Temporary: Temp, 4 bytes - remainder
;            BitCount, 1 byte - counter
;
;      Size: 59 instructions
;
;Max timing: 3+5+6+32*48-2+7+3=1558 cycles
;
;**********************************************************************

DIVIDE_48by24

        ; Test for zero division
	mov	W, Divisor
	or	W, Divisor+1
	or	W, Divisor+2
	snz
	ret			; divisor = zero, not possible to calculate -
				; return with set zero flag (Z)

        ; clear remainder
	clr	Temp
	clr	Temp+1
	clr	Temp+2
	clr	Temp+3

	mov	W, #48		; initialize bit counter
	mov	BitCount, W

DIVIDE_LOOP_48by24
	; shift in next result bit and 
	; shift out next bit of dividend
	; (first time in loop carry is garbage, but
	; it's okay)
	rl	Dividend+5
	rl	Dividend+4
	rl	Dividend+3
	rl	Dividend+2
	rl	Dividend+1
	rl	Dividend
        ; shift in highest bit from dividend through carry in temp
	rl	Temp+3
	rl	Temp+2
	rl	Temp+1
	rl	Temp

	mov	W, Divisor+2	; load w with LSB of divisor
				; to use it in subtraction or
				; addition afterwards

	; If reminder is positive or zero,
	; subtract divisor from remainder,
	; else add divisor to remainder
	snb	Temp.7
	jmp	Div48by24_add

        ; subtract 24 bit divisor from 32 bit temp,
        ; divisor LSB is in w
        sub	Temp+3, W	; subtract LSB

	mov	W, Divisor+1	; get middle byte
	sc			;  if overflow ( from prev. subtraction )
	movsz	W, ++Divisor+1	; incresase source
	sub	Temp+2, W	; and subtract from dest.

	mov	W, Divisor	; get top byte
	sc			;  if overflow ( from prev. subtraction )
	movsz	W, ++Divisor	; increase source
	sub	Temp+1, W	; and subtract from dest.

	mov	w, #1		; if borrow,
	sc                      ; subtract one 
	sub	Temp, w         ; from Temp MSB

	jmp	DIVIDE_NEXT_48by24 ; jump to loop end

Div48by24_add
        ; add 24 bit divisor to 32 bit temp
        ; divisor LSB is in w

	add	Temp+3, W	; add LSB

	mov	W, Divisor+1	; middle byte
	snc 			; check carry for overflow from previous addition
	movsz	W, ++Divisor+1	; if carry set we add 1 to the source
	add	Temp+2, W	; and add it if not zero

	mov	W, Divisor	; MSB byte
	snc			; check carry for overflow from previous addition
	movsz	W, ++Divisor
	add	Temp+1, W	; add

	mov	w, #1		; if borrow,
	snc                     ; add one
	add	Temp, w         ; to Temp MSB

DIVIDE_NEXT_48by24
	; carry contains next result bit
	decsz	BitCount	; decrement loop counter (carry not influenced)
	jmp	DIVIDE_LOOP_48by24 ; another run

        ; finally shift in the last carry
	rl	Dividend+5
	rl	Dividend+4
	rl	Dividend+3
	rl	Dividend+2
	rl	Dividend+1
	rl	Dividend

	clz                     ; done, clear zero flag (NZ) and
	ret			; return

;**********************************************************************





Nikolai Golovchenko says:

In case there is a lot of multi-byte calculations in a program, it might be beneficial to turn on the carry-in option of SX processor. That would increase 8-bit addition/subtraction just by one instruction or less to prepare carry. But for multibyte operations the code size reduction is 2 instructions per each byte, except for the least significant one. In result 16 bit and higher operations will take less space and execute faster.

When the carry-in option is turned on, the original code should be modified a little. Actually, the changes should be made only to the code that uses add/sub instructions. For example, for 8 bit addition


	mov w, x
	add y, w

carry clearing instruction should be inserted before addition (if carry isn't cleared already)


	mov w, x
	clc
	add y, w

And similarly, 8 bit subtraction is changed from


	mov w, x
	sub y, w

to


	mov w, x
	stc
	sub y, w

On the contrary to addition, carry (borrow) should be set for subtraction.

And 16 bit add/sub example:

Change from


	mov w, x0
	add y0, w
	mov w, x1
	skpnc
	movsz w, ++x1
	add y1, w

to


	clc
	mov w, x0
	add y0, w
	mov w, x1
	add y1, w

That saves 1 or 2 (if carry is cleared already) instructions. For 24 bit operations that figure is higher: 3 or 4 instructions.

Here is a division routine rewritten for the case when CARRYX option is used. Now the routine is better by about 13% in both code size and execution speed!


        DEVICE  SX28AC, BANKS8, PAGES4
        DEVICE  OSCHS3, TURBO, OPTIONX, CARRYX
        RESET start

        ORG 8
Dividend        DS 6
Divisor         DS 3
Temp            DS 4
BitCount        DS 1

;6651AF33BC6C = ABCDEF * 987654
        org     1h
start
        mov     Dividend, #$66
        mov     Dividend+1, #$51
        mov     Dividend+2, #$af
        mov     Dividend+3, #$33
        mov     Dividend+4, #$bc
        mov     Dividend+5, #$6c

        mov     Divisor, #$98
        mov     Divisor+1, #$76
        mov     Divisor+2, #$54

        call    DIVIDE_48by24

;**********************************************************************
;
;48 BY 24 BIT DIVISION (Non-restoring method)
; *Using CARRYX option of SX*
;**********************************************************************
;
;     Input: Dividend, 6 bytes (Dividend+0 MSB, ... , Dividend+5 LSB)
;            Divisor, 3 bytes (Divisor+0 MSB, ... , Divisor+2 LSB)
;
;    Output: Dividend, 6 bytes (Dividend = Dividend / Divisor)
;            Z  - division by zero
;            NZ - okay
;
; Temporary: Temp, 4 bytes - remainder
;            BitCount, 1 byte - counter
;
;      Size: 51 instructions
;
;Max timing: 3+5+6+28*48-2+7+3=1366 cycles
;
;**********************************************************************

DIVIDE_48by24

        ; Test for zero division
        mov     W, Divisor
        or      W, Divisor+1
        or      W, Divisor+2
        snz
        ret                     ; divisor = zero, not possible to calculate -
                                ; return with set zero flag (Z)

        ; clear remainder
        clr     Temp
        clr     Temp+1
        clr     Temp+2
        clr     Temp+3

        mov     W, #48          ; initialize bit counter
        mov     BitCount, W

DIVIDE_LOOP_48by24
        ; shift in next result bit and 
        ; shift out next bit of dividend
        ; (first time in loop carry is garbage, but
        ; it's okay)
        rl      Dividend+5
        rl      Dividend+4
        rl      Dividend+3
        rl      Dividend+2
        rl      Dividend+1
        rl      Dividend
        ; shift in highest bit from dividend through carry in temp
        rl      Temp+3
        rl      Temp+2
        rl      Temp+1
        rl      Temp

        mov     W, Divisor+2    ; load w with LSB of divisor
                                ; to use it in subtraction or
                                ; addition afterwards

        ; If reminder is positive or zero,
        ; subtract divisor from remainder,
        ; else add divisor to remainder
        snb     Temp.7
        jmp     Div48by24_add

        ; subtract 24 bit divisor from 32 bit temp,
        ; divisor LSB is in w
        stc
        sub     Temp+3, W       ; subtract LSB
        mov     W, Divisor+1    ; get middle byte
        sub     Temp+2, W       ; and subtract from dest.
        mov     W, Divisor      ; get top byte
        sub     Temp+1, W       ; and subtract from dest.
        clr     W               ; subtract borrow
        sub     Temp, w         ; from Temp MSB

        jmp     DIVIDE_NEXT_48by24 ; jump to loop end

Div48by24_add
        ; add 24 bit divisor to 32 bit temp
        ; divisor LSB is in w
        clc
        add     Temp+3, W       ; add LSB of Divisor to Temp+3
        mov     W, Divisor+1    ; 
        add     Temp+2, W       ; 
        mov     W, Divisor      ; 
        add     Temp+1, W       ; add MSB of Divisor to Temp+1
        clr     W               ; add carry to Temp
        add     Temp, w         ;

DIVIDE_NEXT_48by24
        ; carry contains next result bit
        decsz   BitCount        ; decrement loop counter (carry not influenced)
        jmp     DIVIDE_LOOP_48by24 ; another run

        ; finally shift in the last carry
        rl      Dividend+5
        rl      Dividend+4
        rl      Dividend+3
        rl      Dividend+2
        rl      Dividend+1
        rl      Dividend

        clz                     ; done, clear zero flag (NZ) and
        ret                     ; return

;**********************************************************************