Как выполнить 64-разрядное разделение с помощью 32-разрядной инструкции divide?

Это (AFAIK) конкретный вопрос в этой теме.

ситуация такая:

У меня есть встроенная система (консоль для видеоигр) на основе 32-битного микроконтроллера RISC (вариант NEC V810). Я хочу написать математическую библиотеку с фиксированной точкой. Я читаю в этой статье, но сопровождающий исходный код написан в сборке 386, поэтому он не может использоваться напрямую и легко модифицироваться.

V810 имеет встроенный integer multiply / divide, но я хочу использовать формат 18.14, упомянутый в вышеупомянутой статье. Это требует деления 64-битный int на 32-битный тип int, а V810 не только (signed или unsigned) 32-бит/32-бит дивизии (которая производит 32-разрядное частное и 32-разрядный остаток).

Итак, мой вопрос: как смоделировать 64-битное/32-битное деление с 32-битным/32-битным (чтобы учесть предварительный сдвиг дивиденда)? Или, чтобы взглянуть на проблему с другой стороны, каков наилучший способ разделить фиксированная точка 18.14 другой с использованием стандартных 32-битных арифметических / логических операций? ("лучший" означает самый быстрый, самый маленький или оба).

Алгебра, (V810) сборка и псевдо-код все в порядке. Я буду вызывать код из C.

спасибо заранее!

EDIT: как - то я пропустил этот вопрос... Тем не менее, ему все равно потребуется некоторая модификация, чтобы быть суперэффективным (он должен быть быстрее, чем div с плавающей запятой, предоставляемый v810, хотя он может уже находиться...), поэтому не стесняйтесь делать мою работу за меня в обмен на очки репутации;) (и кредит в моей библиотечной документации, конечно).

2 ответов


GCC имеет такую процедуру для многих процессоров с именем _divdi3 (обычно реализуется с помощью общего вызова divmod). вот. Некоторые ядра Unix также имеют реализацию, например FreeBSD.


если ваш дивиденд без знака 64 бита, ваш делитель без знака 32 бита, архитектура i386 (x86),div инструкция по сборке может помочь вам с некоторой подготовкой:

#include <stdint.h>
/* Returns *a % b, and sets *a = *a_old / b; */
uint32_t UInt64DivAndGetMod(uint64_t *a, uint32_t b) {
#ifdef __i386__  /* u64 / u32 division with little i386 machine code. */
  uint32_t upper = ((uint32_t*)a)[1], r;
  ((uint32_t*)a)[1] = 0;
  if (upper >= b) {   
    ((uint32_t*)a)[1] = upper / b;
    upper %= b;
  }
  __asm__("divl %2" : "=a" (((uint32_t*)a)[0]), "=d" (r) :
      "rm" (b), "0" (((uint32_t*)a)[0]), "1" (upper));
  return r;
#else
  const uint64_t q = *a / b;  /* Calls __udivdi3 in libgcc. */
  const uint32_t r = *a - b * q;  /* `r = *a % b' would use __umoddi3. */
  *a = q;
  return r;
#endif
}

если строка выше с __udivdi3 не компилируется для вас, используйте __div64_32 функция из ядра Linux:https://github.com/torvalds/linux/blob/master/lib/div64.c