Divide2LONGs

Purpose: Divide a SIGNED INTEGER value by another SIGNED INTEGER value.
Assumes:   Each value may be either positive or negative.
Passed: R25:R24:R23:R22 LONG lDividend the numerator; the number to be divided
R21:R20:R19:R18 LONG lDivisor the denominator
Returns: R25:R24:R23:R22 LONG lQuotient the whole portion of the resulting division
R21:R20:R19:R18 LONG lRemainder the remaining portion (or, mNumerator - mDivisor * mQuotient)
Alters: R0, R18 through R25, and the FLAGs (SREG)
 

Divide2ULONGs

Purpose: Divide an UNSIGNED LONG value by another UNSIGNED LONG value.
Assumes:   The Most Significant Bit (MSBit) of each is clear (i.e., both positive.)
Passed: R25:R24:R23:R22   ULONG   ulDividend the numerator; the number to be divided
R21:R20:R19:R18 ULONG ulDivisor the denominator
Returns: R25:R24:R23:R22 ULONG ulQuotient the whole portion of the resulting division
R21:R20:R19:R18 ULONG ulRemainder   the remaining portion (or, ulNumerator - ulDivisor * ulQuotient)
Alters: R0, R18 through R25, and the FLAGs (SREG)

Example:   The source code for this example is rather lengthy (about 80 lines):
.ORG   0x1000

DivisionDemo:
   ADIW    R26, 0                 ; If no parameter was passed in a command
   BREQ    AEarly_Ret             ;   line tail, just exit immediately
   MOVW    R24, R26               ; Point to the buffer and parse any value
   CALL    AParseValue            ;  string which it
   SBRS    R21, BIT_PV_VALID      ; Exit if no value could be parsed
Early_Ret:
   RET
   ADIW    R26, 0                 ; If the first ParseValue() function returned
   BREQ    AEarly_Ret             ;  no pointer, there's no point in going on

   CALL    APushAll               ; Since quite a few will be needed
   MOVW    R2, R22                ; LSWord of the dividend
   MOVW    R4, R24                ; MSWord of the dividend
   MOV     R6, R21                ; BIT_PV_... FLAGs returned by ParseValue()
   MOVW    R24, R26               ; Point to the remaining command line tail
   CALL    AParseValue
   SBRS    R21, BIT_PV_VALID      ; If a second value (the divisor) was able
   RJMP    APopAll_Ret            ;  to be parsed, it is in R25:R24:R23:R22
   MOVW    R10, R22               ; LSWord of the divisor
   MOVW    R12, R24               ; MSWord of the divisor
   MOV     R14, R21               ; BIT_PV_... FLAGs 2nd ParseValue() returned

   MOVW    R22, R2                ; Move the first value parsed (the dividend)
   MOVW    R24, R4                ;  into R25:R24:R23:R22
   MOV     R19, R6                ; In order to use the BIT_PV_NEGATIVE FLAG
   LDI     R27, ACSZ_Divided >> 8 ; Point to the string to print afterward
   LDI     R26, ACSZ_Divided & 0xFF
   RCALL   ADisplayValueAndString ; The local subroutine, below

   MOVW    R22, R10               ; Move the divisor (2nd value) parsed into
   MOVW    R24, R12               ;  R25:R24:R23:R22
   MOV     R19, R14               ; The FLAGs the second ParseValue() returned
   LDI     R27, 0x3AD2 >> 8       ; Pointer to the " = ", which already exists
   LDI     R26, 0x3AD2 & 0xFF     ;  in FLASH memory
   RCALL   ADisplayValueAndString

   MOVW    R22, R2                ; Now setup for the actual division:
   MOVW    R24, R4                ;  load the numerator   in R25:R24:R23:R22
   MOVW    R18, R10               ;  load the denominator in R21:R20:R19:R18
   MOVW    R20, R12

   OR      R6, R14                ; Combine to get a single sign bit
   SBRS    R6, BIT_PV_NEGATIVE    ; If neither were specified as negative
   CALL    ADivide2ULONGs         ;  values, perform an unsigned division
   SBRC    R6, DBIT_PV_NEGATIVE
   CALL    ADivide2LONGs          ; Otherwise, call the signed division logic
   CALL    ADumpRegisters
   MOVW    R10, R18               ; Save the remainder, which was returned in
   MOVW    R12, R20               ;  R21:R20:R19:R18

   MOV     R19, R25               ; Quotient is already in R25:R24:R23:R22
   LDI     R27, ACSZ_Remain >> 8
   LDI     R26, ACSZ_Remain & 0xFF
   RCALL   ADisplayValueAndString

   MOVW    R22, R10               ; Move the remainder to R25:R24:R23:R22
   MOVW    R24, R12               ;  to setup to display it
   MOV     R19, R25
   LDI     R27, 0x3AE0 >> 8       ; 0x3AE0 is the string 0x0D, 0x0A, NULL
   LDI     R26, 0x3AE0 & 0xFF     ;  (Carriage Return, Line Feed, NULL)
   RCALL   ADisplayValueAndString

PopAll_Ret:
   JMP     APopAll                ; Must use a JUMP for every PopAll

DisplayValueAndString:
   LDI     R21, 0                 ; Set copy desination = NULL pointer
   LDI     R20, 0
   LDI     R18, 1 << BIT_FMT_PRINT | 1 << BIT_FMT_COMMAS
   SBRC    R19, BIT_PV_NEGATIVE   ; Select based on the sign BIT passed
   ORI     R18, 1 << BIT_FMT_SIGNED
   CALL    AFormatULONG
   MOVW    R24, R26               ; Transfer the string point to R25:R24
   JMP     APrintFLASHASCIIz

CSZ_Divided:  .STRING  " divided by "
CSZ_Remain:   .STRING  " remainder "
Test it:
m>@ 4096 1000 99
1,000 divided by 99 = 10 remainder 10
m>@ 4096   0xC00000 0x765
12,582,912 divided by 1,893 = 6,647 remainder 141
m>@ 4096  -1000  10
-1,000 divided by 10 = -100 remainder 0
m>@ 4096  -1000  -98
-1,000 divided by -98 = 10 remainder 20
m>@ 4096   0x8000001   4
134,217,729 divided by 4 = 33,554,432 remainder 1
m>  ; There are so many possibilities; just try some
m>                                                                                         
Notes:       Negation is used to simplify division. A SIGNED LONG value can be converted to its UNSIGNED LONG counterpart, both both numbers treated as UNSIGNED during the division then converted back.
These division routines will return incorrect results when the values become VERY large, that is, when the Most Significant Bit of the Dividend is set (so, when they are greater than 0x80000000 = 2,147,483,648.)
Dropin: 
(setup
code)
;   Setup for the Divide2LONGs or Divide2ULONGs() calls:
; Passed:  R25:R24:R23:R22 lDividend or ulDividend, i.e., the numerator
;          R21:R20:R19:R18 lDivisor  or ulDivisor, i.e., the denominator
; Returns: R25:R24:R23:R22 lQuotient
;          R21:R20:R19:R18 lRemainder
; Alters:  R0, R18 through R25, and the FLAGs
Also see:The FormatULONG, Multiply, and Negate System Functions.