5

I was wondering if there is a command/method to perform modulo in Motorola 68000 assembly?

I want to perform d4 mod 1000 and d3 mod 100.

Current I am using the following formula but this take several lines,

if a mod n then a - (n * int(a/n))

I have seen this formula for d0 mod d1

CLR.L D2
MOVE.W D0,D2
DIVU D1,D2
SWAP D2

Thanks for the answers.

Dartuso
  • 122
  • 1
  • 8
  • 2
    If you want this to run fast, look at gcc output for a C function that does what you want. It will use a multiplicative inverse for the division, which on most CPUs runs much faster than division. On modern x86, it's significantly faster to multiply+shift, then multiply again and subtract from the original dividend, than it is to use the hardware `div` instruction to get the remainder directly. – Peter Cordes Oct 14 '17 at 04:53
  • 2
    [Why does GCC use multiplication by a strange number in implementing integer division](https://stackoverflow.com/questions/41183935/why-does-gcc-use-multiplication-by-a-strange-number-in-implementing-integer-divi). When writing asm by hand, the easiest way to generate a constant for yourself is to put [`unsigned foo(unsigned x) { return x/1000; }`](https://godbolt.org/g/U4jk3n) or `%` into a C compiler. – Peter Cordes Oct 14 '17 at 05:01

2 Answers2

8

The DIVU instruction does precisely what you are looking for. When you perform DIVU, the long word of the destination is divided by the word of the source. In your case, you wrote:

DIVU D1, D2

So, D2 is being divided by D1. In the quotient, there are two parts returned. The high order word of D2 will contain the remainder (the modulus) while the low order word contains the quotient. This is why you typically see a SWAP d2. This moves the remainder to the low order word.

David Hoelzer
  • 15,862
  • 4
  • 48
  • 67
  • Thanks very informative, how would one go about clearing a high or lower order word from a data register? – Dartuso Oct 13 '17 at 23:49
  • 2
    You could do the following: `CLR.W D2` followed by `SWAP D2` That clears the low order word (the quotient) and then performs the swap. – David Hoelzer Oct 13 '17 at 23:55
  • You forget that if the quotient gets so large that it will not fit in 16 bits, the DIVU instruction gives up and marks an Overflow in the condition code register and the result becomes unreliable. – Gunnar Vestergaard Jan 01 '20 at 00:13
2
  1. Perform division, result is quotient in bottom 16 bits, modulus in top 16 bits
  2. Set quotient to zero so...
  3. ..when you swap the modulus it is a valid 32 bit value

START
       DIVU  #1000,D4
       CLR.W D4             ; delete quotient
       SWAP  D4             ; modulus from top to bottom 16 bits

       DIVU  #100,D3
       CLR.W D3             ; delete quotient
       SWAP  D3             ; modulus from top to bottom 16 bits

One thing that this code doesn't handle is whether the result would be larger than #ffff(65535), for which you need extra code, possibly a test to check if D3/D4 is greater than the divisor shifted up by 16 bits.

As the modulus will be a number between 0 and 99/999, you could alternatively use EXT.W after the swap instead of CLR.W before the swap. Note that EXT sign extends a value.

vogomatix
  • 4,856
  • 2
  • 23
  • 46