Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Необычное использование аппаратного умножителя
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Rst7
Вот, сижу, мастерю один проектик. Понадобилось сделать Хаффмана. В принципе, ничего сложного, однако работа с битовыми полями - это всегда узкое место процов с отсутствием комманд для обработки данных такого класса. Например, при упаковке по Хаффману необходима процедура, которая запишет в выходной поток n бит значения - вызывается примерно так:
Код
write_bits(huff->codes[val],huff->bits[val])

где первый параметр - код, а второй - собственно его размер в битах. Вот обычно эта функция - write_bits и несет основные затраты по времени выполнения. Почему так происходит - понятно, надо сдвигать входные биты в нужное положение, вычислять новое положение в буфере и т.д.

Посему, сразу кодить не стал, сел - подумал... biggrin.gif

И вот что надумал...

У нас есть аппаратный умножитель, который за 2 такта даст нам сдвинутый сразу в двух направлениях байт - и влево, и вправо. Конечно, в качестве множителя надо использовать маску - 0x01,0x02,...,0x80 и в результате, после выполнения комманды MUL в R0 будем иметь байт данных, сдвинутый влево, в R1 - сдвинутый вправо. Это уже отлично, одной коммандой мы готовим данные для OR с текущим байтом и для занесения следующих данных в накопитель. Однако, соответствующую текущему моменту маску еще надо получить. Она зависит как от текущей битовой позиции в выходном буфере, так и от размера битовых данных. Опять пришлось поразмышлять. Размышления натолкнули на идею хранить битовую позицию в буфере тоже в виде маски:
Код
0x80(биты 7...0 свободны),0x40(бит 7 с данными, биты 6...0 свободны),....,0x01(биты 7...1 с данными, бит 0 свободен)


Если размер символа в битах тоже задать в виде маски, то происходит чудо wink.gif - умножение (аппаратное, конечно) размера на битовую позицию даст новую позицию и она же является необходимым (точнее, в 2 раза меньше) множителем для сдвига символа. Причем, в зависимости, от того, какой байт результата, старший или младший, не равен 0 - то это и есть выбор двух альтернатив - новый символ требует перехода через границу байта или не требует.
Осталась маленькая тонкость, что новая позиция как множитель данных в 2 раза меньше (т.е. сдвинута вправо). Можно конечно сдвинуть при помощи LSL, но лучше использовать тот факт, что FMUL - есть Rs*Rd<<1, это поможет в оптимизации.

Букаф много (могут ниасилить), посему код:

Код
//Кластеризация вручную, при необходимости изготавливается нормальная структура
struct
{
  char bitstream_byte;
  char bitstream_bit;
  char *bitstream;
};

//Инициализация битового потока
//p - указатель на первый байт выходного буфера
void init_bitstream(char *p)
{
  bitstream_byte=0;
  bitstream_bit=0x80;
  bitstream=p;
}

//Финализация потока - слив неслитого
//bp - указатель на первый байт выходного буфера (для расчета размера)
//на выходе - размер потока в байтах
unsigned int finish_bitstream(char *bp)
{
  char *p=bitstream;
  if (bitstream_bit!=0x80) *p++=bitstream_byte;
  return p-bp;
}

void write_bits_by_mask(char sym, char msb);

//Запись n бит из символа sym, необходимо, чтобы неиспользуемые биты равнялись 0
void write_n_bits(char sym, char n)
{
  //Генерация обратной маски
  static __flash const char mask[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
  write_bits_by_mask(sym,mask[n-1]);
}

//Записать в битовый поток биты из sym
//Биты записываются от 0 до бита, определенного установленным битом в msb в обратном порядке
//msb=0x80 sym=0000000a
//msb=0x40 sym=000000ab
//msb=0x20 sym=00000abc
//msb=0x10 sym=0000abcd
//msb=0x08 sym=000abcde
//msb=0x04 sym=00abcdef
//msb=0x02 sym=0abcdefg
//msb=0x01 sym=abcdefgh
//Запись идет от 7 к 0, например запись символов 000abcde, 0000xyzt будет лежать в байтах как
//[abcdexyz][t.......]
//Если есть возможность хранить msb, лучше применять эту функцию, меньше накладных расходов
void write_bits_by_mask(char sym, char msb)
{
  char b=bitstream_byte;
  char m=bitstream_bit;
  char *p=bitstream;
  //--> Если есть возможнось, эта часть инлайнится во внешний цикл
  unsigned short i;
  i=__multiply_unsigned(m,msb); //ih - множитель для sym, чтобы согласовать положение
  if (i>>8)
  {
    //Требуется сдвиг влево
    m=i>>8;
    i=__fractional_multiply_unsigned(sym,m);
    b|=i;
  }
  else
  {
    //Требуется сдвиг вправо
    m=i;
    i=__fractional_multiply_unsigned(sym,m);
    b|=i>>8;
    *p++=b;
    b=i;
  }
  //<-- Конец части для инлайна
  bitstream=p;
  bitstream_byte=b;
  bitstream_bit=m;
}


Компилятор - IAR 5.10

Ну и листинг, который особо радует глаз (приведу только внутренности функции write_bits_by_mask)
Код
     54          void write_bits_by_mask(char sym, char msb)
   \                     write_bits_by_mask:
     55          {
   \   00000000   2F5B               MOV     R21, R27
   \   00000002   2F6A               MOV     R22, R26
   \   00000004   2F20               MOV     R18, R16
     56            char b=bitstream_byte;
   \   00000006   ....               LDI     R30, LOW(_A_bitstream_byte)
   \   00000008   ....               LDI     R31, (_A_bitstream_byte) >> 8
   \   0000000A   8130               LD      R19, Z
     57            char m=bitstream_bit;
     58            char *p=bitstream;
   \   0000000C   81A2               LDD     R26, Z+2
   \   0000000E   81B3               LDD     R27, Z+3
   \   00000010   8101               LDD     R16, Z+1
     59            //--> Если есть возможнось, эта часть инлайнится во внешний цикл
     60            unsigned short i;
     61            i=__multiply_unsigned(m,msb); //ih - множитель для sym, чтобы согласовать положение
   \   00000012   9F01               MUL     R16, R17
     62            if (i>>8)
   \   00000014   2011               TST     R1
   \   00000016   F021               BREQ    ??write_bits_by_mask_0
     63            {
     64              //Требуется сдвиг влево
     65              m=i>>8;
   \   00000018   2D41               MOV     R20, R1
     66              i=__fractional_multiply_unsigned(sym,m);
     67              b|=i;
   \   0000001A   032C               FMUL    R18, R20
   \   0000001C   2930               OR      R19, R0
   \   0000001E   C005               RJMP    ??write_bits_by_mask_1
     68            }
     69            else
     70            {
     71              //Требуется сдвиг вправо
     72              m=i;
   \                     ??write_bits_by_mask_0:
   \   00000020   2D40               MOV     R20, R0
     73              i=__fractional_multiply_unsigned(sym,m);
   \   00000022   032C               FMUL    R18, R20
     74              b|=i>>8;
     75              *p++=b;
   \   00000024   2931               OR      R19, R1
   \   00000026   933D               ST      X+, R19
     76              b=i;
   \   00000028   2D30               MOV     R19, R0
     77            }
     78            //<-- Конец части для инлайна
     79            bitstream=p;
   \                     ??write_bits_by_mask_1:
   \   0000002A   83A2               STD     Z+2, R26
   \   0000002C   83B3               STD     Z+3, R27
     80            bitstream_byte=b;
   \   0000002E   8330               ST      Z, R19
     81            bitstream_bit=m;
   \   00000030   8341               STD     Z+1, R20
     82          }
   \   00000032   2FA6               MOV     R26, R22
   \   00000034   2FB5               MOV     R27, R21
   \   00000036   9508               RET


Конечно, если есть возможность, выжимку кода необходимо вставлять прямо в цикл генерации символов, чтобы меньше оверхеда было на загрузку/выгрузку. В этом случае добавление битового поля будет занимать 10 или 12 тактов, в зависимости от того, происходит переход через границу байта или не происходит.

Опять же, если есть возможность, надо хранить размеры символов в виде масок, а не в обычном виде - это уберет оверхед на функцию write_n_bits (7 тактов).

Такая функция, кстати, будет полезна тем, кто пишет вывод пропорциональных символов на графический дисплей - вывод можно здорово ускорить.

Вообще, подобный подход можно использовать там, где необходимо организовывать сдвиги на произвольное количество бит.
SasaVitebsk
Цитата(Rst7 @ May 7 2008, 21:14) *
Вообще, подобный подход можно использовать там, где необходимо организовывать сдвиги на произвольное количество бит.

Интересно! Я тоже в одном месте использовал нестандартно FMUL.

Правда если есть возможность организовывать групповые сдвиги (например маска и значение), то условиями меньше места займёт.

По такому принципу.
Код
maska =0x3;
if((X & 2)==0)
{
   maska = 0x30;
   Color <<= 4;
}
if((X & 1)==0)
{
   maska <<=2;
   Color <<=2;
}
Rst7
Цитата
Правда если есть возможность организовывать групповые сдвиги (например маска и значение), то условиями меньше места займёт.


В этом случае конечно да.

Ну а как Вам такой способ?
Код
//  110   char maska =0x3;
        LDI     R18, 3
//  111   if (X&1) maska=0x0C;
    SBRC    R16,0
        LDI     R18, 12
//  112   if (X&2) maska=__swap_nibbles(maska);
    SBRC    R16,1
        SWAP    R18
//  114   c=(Color*0x55)&maska;
        LDI     R16, 85
        MUL     R17, R16
        AND     R0, R18

На входе R16 - X, R17 - Color, на выходе R0 - цвет, R18 - маска. К сожалению, тут компилятор красоту с SBRC ниасилил, это руками писано.
galjoen
А вот мне только-что мысль пришла - ещё не проверял. Так-что если глупость написал - не пинайте.
Мне кажется, что с помощью MUL можно узнать чётное или нечётное кол-во единиц в байте. Если байт, кол-во единичных битов которого на чётность проверяется, на FF умножить, то в b7 результа будет ТОЛЬКО от чётности зависеть. Я прав? Теоретически вроде так получается.
vet
galjoen
2*255 = 510 = 0x01FE, 7 бит=1
3*255 = 765 = 0x02FD, 7 бит=1
Rst7
Цитата(galjoen @ May 7 2008, 22:49) *
А вот мне только-что мысль пришла - ещё не проверял. Так-что если глупость написал - не пинайте.
Мне кажется, что с помощью MUL можно узнать чётное или нечётное кол-во единиц в байте. Если байт, кол-во единичных битов которого на чётность проверяется, на FF умножить, то в b7 результа будет ТОЛЬКО от чётности зависеть. Я прав? Теоретически вроде так получается.


К сожалению не работает, но ход мысли понятен, ведь в 7ом бите получается сумма всех бит. Правда, вот переносы из младших разрядов все портят. Могу предложить такой вариант - обработать по половине бит с принятием мер против распространения переноса (вроде тоже самый быстрый получается)

Код
;Вход R16 - байт для подсчета
;abcdefgh
;
    MOV    R18,R16
    LDI    R17,0x55
    AND    R16,R17    ;0b0d0f0h
    FMUL    R16,R17
;теперь в R0 суммы
;b0d0f0h0
;d0f0h000
;f0h00000
;h0000000
;как видим, перенос в седьмой бит отсутствует в любом случае
    MOV    R16,R0    ;Сохраняем результат по половине битов и повторяем для второй
    LSR    R18    ;0abcdefg
    AND    R18,R17    ;0a0c0e0g
    FMUL    R18,R17
;теперь в R0 суммы
;a0c0e0g0
;c0e0g000
;e0g00000
;g0000000
    EOR    R16,R0    ;Выход - бит 7 R16 - бит четности
vet
сдвигами четность все равно шустрее выйдет:
Код
        MOV tmp,@0
        SWAP tmp
        EOR tmp,@0
        MOV @0,tmp
        LSR @0
        EOR @0,tmp
        SBRS @0,2
        SUBI @0,1; мл.бит регистра @0 равен 1, если в байте четное число единиц

UPD: убрал выдвигание мл.бита в C для немедленного ветвления
Rst7
Цитата
сдвигами четность все равно шустрее выйдет:


Да, точно, я помнил, что коротенькая там процедура, но было лень восстанавливать по памяти, посему показалось, что с умножением быстрее.
_Pasha
Америки не открою, и особой экзотики в использовании умножения не будет, но...
Самое быстрое деление 16-битного делимого на 8-битный делитель без остатка.
Методом поразрядного приближения.
Код
div16x8:
// yL = (zL:zH)/yH
// registers used yL,yH,zL,zH,r0,r1 (6)
            ldi    yL,0x80
            mul    yL,yH
            cp    zL,r0
            cpc    zH,r1
            brsh    dv00
            clr    yL
dv00:
            ori    yL,0x40
            mul    yL,yH
            cp    zL,r0
            cpc    zH,r1
            brsh    dv01
            andi    yL,0xbf
dv01:
            ori    yL,0x20
            mul    yL,yH
            cp    zL,r0
            cpc    zH,r1
            brsh    dv02
            andi    yL,0xdf
dv02:
            ori    yL,0x10
            mul    yL,yH
            cp    zL,r0
            cpc    zH,r1
            brsh    dv03
            andi    yL,0xef
dv03:
            ori    yL,0x08
            mul    yL,yH
            cp    zL,r0
            cpc    zH,r1
            brsh    dv04
            andi    yL,0xf7
dv04:
            ori    yL,0x04
            mul    yL,yH
            cp    zL,r0
            cpc    zH,r1
            brsh    dv05
            andi    yL,0xfb
dv05:
            ori    yL,0x02
            mul    yL,yH
            cp    zL,r0
            cpc    zH,r1
            brsh    dv06
            andi    yL,0xfd
dv06:
            ori    yL,0x01
            mul    yL,yH
            cp    zL,r0
            cpc    zH,r1
            brsh    dv07
            andi    yL,0xfe
dv07:
            ret // result = yL

// total clocks = 56 + call/ret

Может, кому пригодится smile.gif
ae_
Как быть, когда результат (zL:zH)/yH > 255? проверять данные перед делением?
_Pasha
Цитата(ae_ @ May 31 2008, 14:55) *
Как быть, когда результат (zL:zH)/yH > 255?


Вычесть из делимого и повторить деление остатка.
Код
mul yL,yH
sub zL,r0
sbc zH,r1
brcc pc+2
ret
push yL
;repeat div
pop yh
add yL,yH
...etc


Ну, в данном случае такое не применимо.
А применимо оно в операциях вида N= M*256 / K, где M и K примерно равны. И там, где при этом надо очень быстро все считать.
Rst7
Цитата(vet @ May 8 2008, 09:54) *
сдвигами четность все равно шустрее выйдет:
Код
        MOV tmp,@0
        SWAP tmp
        EOR tmp,@0
        MOV @0,tmp
        LSR @0
        EOR @0,tmp
        SBRS @0,2
        SUBI @0,1; мл.бит регистра @0 равен 1, если в байте четное число единиц

UPD: убрал выдвигание мл.бита в C для немедленного ветвления


Случайно пришла в голову мысль (не подумайте, что я тут полгода моск себе парил biggrin.gif ), что шестой бит выражения __multiply_unsigned((v^(v>>1))&0x55,0x55) будет соответствовать четности переменной v

Код
;R16 - вход
        LDI     R17, 85
        MOV     R18, R16
        LSR     R18
        EOR     R16, R18
        ANDI    R16, 0x55
        MUL     R16, R17
;6й бит R0 - четность


12 байт, 7 тактов smile.gif
singlskv
Цитата(Rst7 @ Dec 14 2008, 00:05) *
Случайно пришла в голову мысль что шестой бит выражения __multiply_unsigned((v^(v>>1))&0x55,0x55) будет соответствовать четности переменной v
Код
;6й бит R0 - четность
ИМХО, очень красиво... beer.gif
но я бы умножал на 0xAA для дальнейшего сдвига в С,
но это так в качестве дальнейшего улучшения...
Rst7
Цитата(singlskv @ Dec 14 2008, 21:11) *
ИМХО, очень красиво... beer.gif
но я бы умножал на 0xAA для дальнейшего сдвига в С,
но это так в качестве дальнейшего улучшения...

Да вообщем пофиг, что в перенос двигать, что bst делать, что sbrs/sbrc.

Кстати, для ARM'а весьма красиво смотрится выражение типа if (((v^(v<<1))&0xAA)*0x55)&0x80) - конечно, в arm-режиме, в тумбе - так себе.
singlskv
Цитата(Rst7 @ Dec 14 2008, 22:57) *
Да вообщем пофиг, что в перенос двигать, что bst делать, что sbrs/sbrc.
я в том смысле что есть выбор, толи в перенос толи sbrs/sbrc.
Цитата
Кстати, для ARM'а весьма красиво смотрится выражение типа if (((v^(v<<1))&0xAA)*0x55)&0x80) - конечно, в arm-режиме, в тумбе - так себе.
А v -8ми битный ?

Аа... Вы о том как это оттранслирует компилятор ?
Rst7
Цитата(singlskv @ Dec 14 2008, 22:08) *
я в том смысле что есть выбор, толи в перенос толи sbrs/sbrc.
А v -8ми битный ?

Дык после &170 уже пофиг smile.gif Вообще, на архитектурах типа арма, мипса, ппц я рекомендую использовать регистровые переменные только размером в регистр, иначе ведро оверхеда обеспечено.

Цитата
Аа... Вы о том как это оттранслирует компилятор ?

Ну вменяемый компилятор оттранслирует это в пять команд плюс переход. Просто выражение со сдвигом в другую сторону не может быть обработано бесплатным сдвигом операнда в арм-режиме.
Rst7
Ну, продолжим наши игры. На этот раз под руки попало деление. 16 бит беззнаковое на 16 бит беззнаковое. Обычное деление в столбик - почти 200 тактов (зависит от операндов).

Если не жмет место во флеше и необходимо ускорить деление 16 на 16, предлагаю такую процедуру (код великоват, спрятал под спойлером)

CODE

#define ZL R30
#define ZH R31

PUBLIC fast_divu16

RSEG CODE:CODE:NOROOT(1)

// 92 UINT16 fast_divu16(UINT16 a, UINT16 cool.gif
fast_divu16:
// 93 {
LDI ZH,shift_mask_and_log>>8
CLR R3
// 101 if ((d=b>>8)!=0)
TST R19
BREQ byte_div
large_div:
// 103 UINT8 __flash *p=shift_mask_and_log+d;
// 104 s=*p;
MOV ZL,R19
LPM R22,Z ;s - R22
INC ZH
// 105 p=shift_mask_and_log+((b*s)>>8)+0x100;
MUL R19,R22
MOV ZL,R0
MUL R18,R22
ADD ZL,R1
// 106 c=*p<<8;
LPM R23,Z
// 107 p+=0x100;
INC ZH
// 108 c|=*p;
LPM ZH,Z
// 109 c=(UINT32)((UINT32)c*a)>>16;
MUL R23,R17
MOVW R21:R20,R1:R0
MUL ZH,R16
MOV R2,R1
MUL R23,R16
ADD R2,R0
ADC R20,R1
ADC R21,R3
MUL ZH,R17
ADD R2,R0
ADC R20,R1
ADC R21,R3
// 110 c=(UINT32)((UINT32)c*s)>>8;
MUL R21,R22
MOV R21,R1
MOV R2,R0
MUL R20,R22
MOV R20,R2
OR R20,R1
// 111 a-=b
SUB R16,R18
SBC R17,R19
; BRCS zero_result
//b - R19:R18, c - R21:R20, a - R17:R16, s - R22, e - ZH:ZL
// 112 e=b*c-a;
MUL R19,R20
MOV ZH,R0
MUL R18,R21
ADD ZH,R0
MUL R18,R20
MOV ZL,R0
ADD ZH,R1
SUB ZL,R16
SBC ZH,R17
// if (e>b) c--;
CP R18,ZL ;b-e, carry=1 if b<e
CPC R19,ZH
SBC R20,R3
SBC R21,R3
// 122 return c;
MOVW R17:R16,R21:R20
RET
;zero_result:
; LDI R16,0
; LDI R17,0
; RET
result_a_or_fault:
BREQ result_a
SER R16
SER R17
result_a:
RET
// 126 if ((d=cool.gif>1)
byte_div:
CPI R18, 2
BRCS result_a_or_fault
MOV ZL,R18
// 129 s=*p;
LPM R22,Z
// 130 p+=0x100;
INC ZH
// 131 c=*p<<8;
LPM R19,Z
// 132 p+=0x100;
INC ZH
// 133 c|=*p;
LPM R18,Z
// 134 c=(UINT32)((UINT32)c*a)>>16;
MUL R19,R17
MOVW R21:R20,R1:R0
MUL R18,R16
MOV R2,R1
MUL R19,R16
ADD R2,R0
ADC R20,R1
ADC R21,R3
MUL R18,R17
ADD R2,R0
ADC R20,R1
ADC R21,R3
// 135 a-=b;
SUB R16,ZL
SBCI R17,0
; BRCS zero_result
// 136 e=d*c-a;
MUL R21,ZL
MOV R19,R0
MUL R20,ZL
MOV R18,R0
ADD R19,R1
SUB R18,R16
SBC R19,R17
// if (e>b) c--;
CP ZL,R18 ;b-e, carry=1 if b<e
CPC R3,R19
SBC R20,R3
SBC R21,R3
// 122 return c;
MOVW R17:R16,R21:R20
RET

ASEGN NEAR_F:CODE:ROOT,0FD00H
//Таблица масок и обратных величин. Должна быть с круглого адреса
shift_mask_and_log:
DB 255, 128, 64, 64, 32, 32, 32, 32, 16, 16, 16, 16, 16, 16, 16, 16, 8
DB 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, 4, 4, 4
DB 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
DB 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
DB 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
DB 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 128, 85, 64, 51, 42, 36, 32, 28, 25
DB 23, 21, 19, 18, 17, 16, 15, 14, 13, 12, 12, 11, 11, 10, 10, 9, 9, 9
DB 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5
DB 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
DB 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
DB 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
DB 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
DB 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1
DB 86, 1, 52, 171, 147, 1, 114, 154, 70, 86, 178, 74, 18, 1, 16, 57
DB 122, 205, 49, 163, 34, 171, 62, 217, 124, 37, 212, 137, 67, 1, 194
DB 136, 81, 29, 236, 189, 145, 103, 63, 25, 245, 210, 177, 145, 115, 86
DB 58, 31, 6, 237, 213, 190, 168, 147, 126, 106, 87, 69, 51, 34, 17, 1
DB 241, 225, 211, 196, 182, 169, 156, 143, 130, 118, 106, 95, 84, 73
DB 62, 52, 42, 32, 22, 13, 4, 251, 242, 233, 225, 217, 209, 201, 193
DB 186, 178, 171, 164, 157, 150, 144, 137, 131, 125, 119, 113, 107, 101
DB 95, 90, 84, 79, 74, 68, 63, 58, 53, 49, 44, 39, 35, 30, 26, 21, 17
DB 13, 9, 5, 1, 253, 249, 245, 241, 237, 234, 230, 226, 223, 219, 216
DB 213, 209, 206, 203, 200, 196, 193, 190, 187, 184, 181, 179, 176, 173
DB 170, 167, 165, 162, 159, 157, 154, 152, 149, 147, 144, 142, 139, 137
DB 135, 132, 130, 128, 126, 123, 121, 119, 117, 115, 113, 111, 109, 107
DB 105, 103, 101, 99, 97, 95, 93, 91, 89, 88, 86, 84, 82, 81, 79, 77
DB 75, 74, 72, 71, 69, 67, 66, 64, 63, 61, 60, 58, 57, 55, 54, 52, 51
DB 49, 48, 47, 45, 44, 42, 41, 40, 38, 37, 36, 34, 33, 32, 31, 29, 28
DB 27, 26, 25, 23, 22, 21, 20, 19, 18, 16, 15, 14, 13, 12, 11, 10, 9, 8
DB 7, 6, 5, 4, 3, 2


END


Собственно деление представляет из себя выборку из таблицы значения, обратного делителю, и умножение на него делимого с последующей коррекцией результата. При делителе меньше 256 выборка производится непостредственно (таблица на 256 значений), а вот при большем делителе делается хитрость:

1. Определяется двоичный порядок делителя (в виде маски для последующего сдвига при помощи mul)
2. Делитель быстро сдвигается (нормализуется) в диапазон 0x80...0xFF.
3. Эта мантисса служит индексом в таблице обратных значений.
4. Обратное значение опять сдвигается при помощи умножений на ту же маску. Т.е. (1/(b*n))*a*n=a/b;
5. Умножение на делимое и коррекция, как и в случае делитель<256.

Вот такие пироги в худшем случае занимают 69 тактов и 176+768 байт флеша на собственно функцию и таблички.

Единственно что, лень доводить до ума, при делимом больше чем ~40000 и делителе в районе 256...512 бывает ошибается на 1 - возвращает результат на 1 больше чем надо. Если кому сильно необходимо, могут допилить (судя по всему, необходима отдельная ветка для делителей 0x100...0x1FF c отдельной табличкой обратных значений).
galjoen
Цитата(Rst7 @ Jan 23 2009, 18:21) *
Ну, продолжим наши игры.

Вроде разорбрался с вашим кодом. Для этого понаписал комментариев. Разбирался на асме..
Код
;//  111       a-=b; отсюда начинаются непонятки... :wassat:
    SUB    R16,R18; вычли из делимого (a) делитель (b).
    SBC    R17,R19; Зачем? Токо для проверки на 0?
;    BRCS    zero_result
;//b - R19:R18, c - R21:R20, a - R17:R16, s - R22, e - ZH:ZL
;//  112       e=b*c-a;
    MUL    R19,R20; ст.б. делителя * мл.б. рез-та
    MOV    ZH,R0
    MUL    R18,R21; мл.б. делителя * ст.б. рез-та
    ADD    ZH,R0
    MUL    R18,R20; мл.б. делителя * мл.б. рез-та
    MOV    ZL,R0; получили в ZH:ZL 2 мл. байта произведения
    ADD    ZH,R1; делителя на результат (восстановленное делимое)
    SUB    ZL,R16; вычли разницу между делимым и делителем т.е.
    SBC    ZH,R17; вычли делимое и прибавили делитель
; Зачем нужно прибавлять делитель? Для того чтоб сравнить с 0?
; Но ведь сравнивать-то будем с делителем.
;// if (e>b) c--;
    CP    R18,ZL;b-e, carry=1 if b<e
    CPC    R19,ZH; C=1 если разница больше собственно делителя
; А не глюк-ли это? Зачем разницу с ПРИБАВЛЕННЫМ ДЕЛИТЕЛЕМ сравнивать
; с делителем? Это ведь эквивалентно сравнению разницы с нулём!
    SBC    R20,RG00; вычитаем перенос - это
    SBC    R21,RG00; и есть собственно коррекция
;//  122       return c;
    MOVW    R17:R16,R21:R20
    RET
; А вот мой вариант. Начиная с непонятного места. :rolleyes:
    mul    R19,R20; ст.б. делителя * мл.б. рез-та
    mov    R2,R0; это корректно т.к. здесь R1=0
    mul    R18,R21; мл.б. делителя * ст.б. рез-та
    add    R2,R0; это корректно т.к. здесь R1=0
    mul    R18,R20; мл.б. делителя * мл.б. рез-та
    add    R1,R2; в R1:R0 получили восстановленное делимое
; Вычитаем из, восстановленного делимого, делимое реальное.
    sub    R0,R16; результат (разницу между восстановленным
    sbc    R1,R17; делимым и реальным) получаем в R1:R0
; видимо таблица обратных величин составлена так, чтобы результат мог
; получится только на 1 больше. Т.е. здесь не может быть отрицательного
; числа. Так? Если не так, то здесь нужно проверить C (перенос) на =1
; командой brcs (был заём). И пойти по другой ветке алгоритма. Или-же
; делитель для того и прибавлялся, чтобы здесь получилось >0?
; Хотя как-то зыбко всё это... Я бы написал подругому. Потом приведу
; свой вариант. Отличия начинаются с этого места.
    cp    R18,R0; сравним разницу с делителем, и если получилось
    cpc    R19,R1; больше - вычитаем единицу из результата.
; Сейчас C (перенос) =1 показывает, что был заём, и нужно вычесть 1.
    sbc    R20,RG00; вычитаем перенос - это и есть
    sbc    R21,RG00; собственно коррекция (на 0 или -1)
; Вот получился аналог, но на 3 такта быстрее и с исправленным багом.
; Ну вовсяком случае (насчёт бага) я так думаю... :biggrin:

; А вот как написал бы я (начиная с того места):
    brcc    large_div1; C=0, восстановленное - реальное делимое >=0
; Разница<0. При коррекции нужно будет прибавлять еденицу.
    add    R0,R18; к отрицательному числу (разнице)
    adc    R1,R19; прибавляем положительное (делитель)
    brlt    large_div2; <0 - коррекция не требуется (хотя корректность применение этой команды здесь под вопросом)
    inc    R20; увеличим мл. байт результата
    brne    large_div2; Z=0 - ст. байт увеличивать не нужно
    inc    R21; увеличим ст. байт результата
    rjmp    large_div2; на продолжение
large_div1:; Разница>=0. Здесь собственно тоже самое.
    cp    R18,R0; сравним разницу с делителем, и если получилось
    cpc    R19,R1; больше - вычитаем единицу из результата.
; Сейчас C (перенос) =1 показывает, что был заём, и нужно вычесть 1.
    sbc    R20,RG00; вычитаем перенос - это
    sbc    R21,RG00; и есть собственно коррекция
large_div2:    
; Т.е. здесь коррекция была на -1, 0 или +1. Если по результатам тестирования
; этого окажется недостаточно, то можно сделать и -2, -1, 0, +1. Или ещё как.

Я собственно собираюсь переписать всё на асм. У вас там есть ещё пара мест где хотелось бы подправить. Но это потом. А пока жел-но разобраться с непонятным местом...
Rst7
Цитата
Я собственно собираюсь переписать всё на асм.


Гм. Я разве не на асме код выложил? Для тех, кто в танке - была написана рыба на Си, отлажена в MSVS, затем собрана IAR'ом, и листинг допилен вручную.

Щас продолжу, случайно не ту кнопку нажал smile.gif

Я не пойму, что Вас смущает?

a - делимое;
b - делитель;
с - результат, сначала приближение, потом он корректируется;
e - ошибка, равная b*c-a;

Если внести a-=b в выражение ошибки, то это эквивалентно e=b*c-( a - b )=b*c-a+b=b*(c+1)-a;

Если ошибка превышает делитель, то корректируем результат (уменьшением).
galjoen
Цитата(Rst7 @ Jan 27 2009, 14:22) *
Гм. Я разве не на асме код выложил? Для тех, кто в танке - была написана рыба на Си, отлажена в MSVS, затем собрана IAR'ом, и листинг допилен вручную.

Ну вот в наследство от Си и осталось... Если изначально на асме писать - лучше получится.
Цитата(Rst7 @ Jan 27 2009, 14:22) *
Щас продолжу, случайно не ту кнопку нажал smile.gif

Я не пойму, что Вас смущает?

a - делимое;
b - делитель;
с - результат, сначала приближение, потом он корректируется;
e - ошибка, равная b*c-a;

Если внести a-=b в выражение ошибки, то это эквивалентно e=b*c-( a - b )=b*c-a+b=b*(c+1)-a;

Если ошибка превышает делитель, то корректируем результат (уменьшением).

У вас, как вы и пишете, b*(c+1)-a сравнивается с b (делителем). Это эквивалентно b*c сравнить с a.
Приведём (преобразуем) ваш вариант к такому виду:
b*(c+1)-a?>b
b*c+b-a?>b
b*c-a?>0
b*c?>a
и если условие выполняется, то вы уменьшаете c (корректируете результат)
Вот так получается? Если так, то это же неверно!
Вот это меня и смущает biggrin.gif .
А нужно сравнивать так:
b*c-a?>b
И если условие выполняется - вычитать из c еденицу.
Rst7
Цитата
Приведём (преобразуем) ваш вариант к такому виду:
....
b*c?>a
и если условие выполняется, то вы уменьшаете c (корректируете результат)


Согласен. Переумничал. Дело в том, что изначально там было весьма хитрое последовательное приближение за несколько этапов. Потом убралось, но не целиком. Оттуда и издержки, а совсем не от компилятора Си.

Цитата
Вот так получается? Если так, то это же неверно!


Почему?
galjoen
Цитата(Rst7 @ Jan 27 2009, 15:51) *
Почему?

Не совпадает с тем, что в описании.
А вообще тут всё зависит от того, нужно-ли округлять результат деления. В том случае получилось с обрезанием.

Еще хотелось бы узнать как вы считали таблицу обратных величин. Ну не в ручную же. Выложите код если можно.
Это нужно чтобы изменить коррекцию. Чтобы результат корректировался на +1, 0 и -1. Это добавляет всего 3 такта. М.б. удастся ту 1 при ~40000 скорректировать.
Ну и другие мысли по оптимизации на асме есть. Сделаю - выложу.
Rst7
Цитата
Не совпадает с тем, что в описании.


Где не совпадает?

Цитата
А вообще тут всё зависит от того, нужно-ли округлять результат деления. В том случае получилось с обрезанием.


Операция / в Си не округляет результат. Так что все правильно.

Цитата
Еще хотелось бы узнать как вы считали таблицу обратных величин. Ну не в ручную же. Выложите код если можно.


Можно.

Это считатель:
Код
void Generate_log_table(void)
{
    unsigned int i=2;
    do
    {
        log_table[i]=((0x10000UL/i)+1);
        i++;
    }
    while(i<256);
}

Только оно потом ручками подправленно для i=1;

Цитата
Это нужно чтобы изменить коррекцию. Чтобы результат корректировался на +1, 0 и -1. Это добавляет всего 3 такта. М.б. удастся ту 1 при ~40000 скорректировать.


Не помню, но не получалось. Там значащих битов не хватает.

Цитата
Ну и другие мысли по оптимизации на асме есть.


Вы бы не забывали соблюдать соглашения о вызовах сишного компилятора, полезнее будет.
galjoen
Цитата(Rst7 @ Jan 27 2009, 17:58) *

Ну вот, наваял:
CODE

.DEF RG00=R2 ; регистр всегда равный 0x00
;.DEF RGFF=R3 ; регистр всегда равный 0xFF
.DEF RBN0=R4 ; чётный регистр в диапазоне 2..15
.DEF RBN1=R5 ; нечётный регистр в диапазоне 2..15 (пара к предыдущему)
.DEF RBN2=R6 ; чётный регистр в диапазоне 2..15
.DEF RBX0=R16 ; чётный регистр в диапазоне 16..25
.DEF RBX1=R17 ; нечётный регистр в диапазоне 16..25 (пара к предыдущему)
.DEF RBX2=R18 ; чётный регистр в диапазоне 16..25
.DEF RBX3=R19 ; нечётный регистр в диапазоне 16..25 (пара к предыдущему)
.DEF RBX4=R20 ; чётный регистр в диапазоне 16..25
.DEF RBX5=R21 ; нечётный регистр в диапазоне 16..25 (пара к предыдущему)
.DEF ZL=R30
.DEF ZH=R31
; eor RG00,RG00 ; RG00=0x00
; ldi RBX0,0xFF
; out 0x3D,RBX0
; out 0x3E,RG00
;dfdf: rcall f16d
; rjmp dfdf


f16d:
; RBX1:RBX0=R17:R16=делимое=a
; RBX3:RBX2=R19:18=делитель=b
; RBX5:RBX4=R21:R20=результат=с
eor RG00,RG00 ; RG00=0x00 (хотя я обычно делаю это
; только один раз при инициализации)
ldi ZH,high(tb_3e<<1) ; ст. байт адреса таблиц (3 шт.)
tst RBX3 ; ст. байт делителя
breq f16db ; Z=1 - делим на байт
f16wd: ; делим на слово
; прочтем из таблицы соответствующий сдвигатель
mov ZL,RBX3 ; ст. байт делителя
lpm RBN2,Z ; s - сдвиг
; приведём делитель в диапазон 0x80..0xFF (по сдвигателю)
mul RBX3,RBN2 ; ст. байт делителя
mov ZL,R0
mul RBX2,RBN2 ; мл. байт делителя
or ZL,R1 ; ZL=приведённый делитель
; получим обратную величину из таблицы по приведённому делителю
inc ZH ; ук-ль на мл. байты обратной
lpm RBN0,Z ; мл. байт обратной из таблицы
inc ZH ; ук-ль на ст. байты
lpm RBN1,Z ; ст. байт обратной из таблицы [21]
; множим делимое на обратную величину (результат в ZH:ZL, RBX5:RBX4)
mul RBN1,RBX1 ; ст. б. обратного * ст. байт делимого
movw ZH:ZL,R1:R0 ; щас результат получим в ZH:ZL
mul RBN0,RBX0 ; мл. б. обратного * мл. б. делимого
movw RBX5:RBX4,R1:R0
mul RBN1,RBX0 ; ст. б. обратного * мл. б. делимого
add RBX5,R0
adc ZL,R1
adc ZH,RG00
mul RBN0,RBX1 ; мл. б. обратного * ст. б. делимого
add RBX5,R0
adc ZL,R1
adc ZH,RG00 ; ZH:ZL=несдвинутый и
; нескорректированный результат [37]
; сдвинем результат назад на величину сдвигателя s (RBN2)
mul ZH,RBN2 ; ст. байт
movw RBX5:RBX4,R1:R0
mul ZL,RBN2 ; мл. байт
or RBX4,R1 ; RBX5:RBX4= нескорректированный результат
; восстановим делимое по нескорректированному результату
mul RBX2,RBX4 ; мл.б. делителя * мл.б. рез-та
movw ZH:ZL,R1:R0
mul RBX3,RBX4 ; ст.б. делителя * мл.б. рез-та
add ZH,R0
mul RBX2,RBX5 ; мл.б. делителя * ст.б. рез-та
add ZH,R0 ; ZH:ZL=восстановленное делимое [52]
; если (восстановленное делимое)>(делимое), то (результат)--
cp RBX0,ZL ; RBX1:RBX0=делимое
cpc RBX1,ZH ; если C=1 - корректируем
sbci RBX4,0x00 ; из RBX5:RBX4=(нескорректированному
sbci RBX5,0x00 ; результату) вычитаем C (перенос)
; вычисляем (восстановленное делимое ZH:ZL)+(делитель RBX3:RBX2)
add ZL,RBX2
adc ZH,RBX3 ; ZH:ZL += RBX3:RBX2
brcs f16wd1 ; C=1 - переполнение (без проверки)
; если ZH:ZL=<RBX1:RBX0, то (результат)++
cp RBX0,ZL
cpc RBX1,ZH ; если C=0 - корректируем (++)
sbci RBX4,0xFF ; к RBX5:RBX4=(нескорректированному
sbci RBX5,0xFF ; результату) прибавляем инверсный C
f16wd1:
movw RBX1:RBX0,RBX5:RBX4 ; результат в RBX1:RBX0 [64]
ret ; [68]

f16db: ; делим слово на байт (0=RBX3)
cpi RBX2,0x02 ; проверим случаи 0 и 1
brcs f16db01
; прочтем из таблицы соответствующий сдвигатель (в ZL уже весь делитель)
mov ZL,RBX2 ; мл. байт делителя
lpm RBN2,Z ; s - сдвиг
; получим обратную величину из таблицы по делителю
inc ZH ; ук-ль на мл. байты обратной
lpm RBN0,Z ; мл. байт обратной из таблицы
inc ZH ; ук-ль на ст. байты
lpm RBN1,Z ; ст. байт обратной из таблицы
; множим делимое на обратную величину (результат в RBX5:RBX4, ZH:ZL)
mul RBN1,RBX1 ; ст. б. обратного * ст. байт делимого
movw RBX5:RBX4,R1:R0 ; щас результат получим в RBX5:RBX4
mul RBN0,RBX0 ; мл. б. обратного * мл. б. делимого
movw ZH:ZL,R1:R0
mul RBN1,RBX0 ; ст. б. обратного * мл. б. делимого
add ZH,R0
adc RBX4,R1
adc RBX5,RG00
mul RBN0,RBX1 ; мл. б. обратного * ст. б. делимого
add ZH,R0
adc RBX4,R1
adc RBX5,RG00 ; RBX5:RBX4=(нескорректированный результат)
; восстановим делимое по нескорректированному результату
mul RBX2,RBX4 ; мл.б. делителя * мл.б. рез-та
movw ZH:ZL,R1:R0
mul RBX3,RBX4 ; ст.б. делителя * мл.б. рез-та
add ZH,R0
mul RBX2,RBX5 ; мл.б. делителя * ст.б. рез-та
add ZH,R0 ; ZH:ZL=восстановленное делимое
; если (восстановленное делимое)>(делимое), то (результат)--
cp RBX0,ZL ; RBX1:RBX0=делимое
cpc RBX1,ZH ; если C=1 - корректируем
sbci RBX4,0x00 ; из RBX5:RBX4=(нескорректированному
sbci RBX5,0x00 ; результату) вычитаем C (перенос)
; вычисляем (восстановленное делимое ZH:ZL)+(делитель RBX2)
add ZL,RBX2
adc ZH,RBX3 ; ZH:ZL += RBX3:RBX2
brcs f16db1 ; C=1 - переполнение (без проверки)
; если ZH:ZL=<RBX1:RBX0, то (результат)++
cp RBX0,ZL
cpc RBX1,ZH ; если C=0 - корректируем (++)
sbci RBX4,0xFF ; к RBX5:RBX4=(нескорректированному
sbci RBX5,0xFF ; результату) прибавляем инверсный C
f16db1:
movw RBX1:RBX0,RBX5:RBX4 ; результат в RBX1:RBX0
ret

f16db01: ; делитель = 0..1
tst RBX2 ; проверим 0 или 1
brne f16db11 ; Z=0 - (результат)=(делимому)
or RBX0,RBX1 ; проверим деление 0 на 0
ldi RBX0,0xFF ; это переполнение при
ldi RBX1,0xFF ; делении на 0 такое
brne f16db11 ; Z=0 - щас не 0/0
ldi RBX0,0x01 ; при делении 0 на
ldi RBX1,0x00 ; 0 результат =1
f16db11:
ret
ENDCOD: ; эта метка токо шоб корректировался выровненный адрес
.org ((ENDCOD+0x7F)&0xFF80)
tb_3e:
; таблица сдвигателей
.db 0xFF, 0x80, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20 ; 00..07
.db 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 ; 08..0F
.db 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 ; 10..17
.db 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 ; 18..1F
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 20..27
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 28..2F
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 30..37
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 38..3F
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 40..47
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 48..4F
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 50..57
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 58..5F
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 60..67
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 68..6F
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 70..77
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 78..7F
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 80..87
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 88..8F
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 90..97
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 98..9F
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; A0..A7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; A8..AF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; B0..B7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; B8..BF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; C0..C7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; C8..CF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; D0..D7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; D8..DF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; E0..E7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; E8..EF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; F0..F7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; F8..FF
; младший байт обратных величин
.db 0x00, 0x00, 0x00, 0x55, 0x00, 0x33, 0xAA, 0x92 ; 00..07
.db 0x00, 0x71, 0x99, 0x45, 0x55, 0xB1, 0x49, 0x11 ; 08..0F
.db 0x00, 0x0F, 0x38, 0x79, 0xCC, 0x30, 0xA2, 0x21 ; 10..17
.db 0xAA, 0x3D, 0xD8, 0x7B, 0x24, 0xD3, 0x88, 0x42 ; 18..1F
.db 0x00, 0xC1, 0x87, 0x50, 0x1C, 0xEB, 0xBC, 0x90 ; 20..27
.db 0x66, 0x3E, 0x18, 0xF4, 0xD1, 0xB0, 0x90, 0x72 ; 28..2F
.db 0x55, 0x39, 0x1E, 0x05, 0xEC, 0xD4, 0xBD, 0xA7 ; 30..37
.db 0x92, 0x7D, 0x69, 0x56, 0x44, 0x32, 0x21, 0x10 ; 38..3F
.db 0x00, 0xF0, 0xE0, 0xD2, 0xC3, 0xB5, 0xA8, 0x9B ; 40..47
.db 0x8E, 0x81, 0x75, 0x69, 0x5E, 0x53, 0x48, 0x3D ; 48..4F
.db 0x33, 0x29, 0x1F, 0x15, 0x0C, 0x03, 0xFA, 0xF1 ; 50..57
.db 0xE8, 0xE0, 0xD8, 0xD0, 0xC8, 0xC0, 0xB9, 0xB1 ; 58..5F
.db 0xAA, 0xA3, 0x9C, 0x95, 0x8F, 0x88, 0x82, 0x7C ; 60..67
.db 0x76, 0x70, 0x6A, 0x64, 0x5E, 0x59, 0x53, 0x4E ; 68..6F
.db 0x49, 0x43, 0x3E, 0x39, 0x34, 0x30, 0x2B, 0x26 ; 70..77
.db 0x22, 0x1D, 0x19, 0x14, 0x10, 0x0C, 0x08, 0x04 ; 78..7F
.db 0x00, 0xFC, 0xF8, 0xF4, 0xF0, 0xEC, 0xE9, 0xE5 ; 80..87
.db 0xE1, 0xDE, 0xDA, 0xD7, 0xD4, 0xD0, 0xCD, 0xCA ; 88..8F
.db 0xC7, 0xC3, 0xC0, 0xBD, 0xBA, 0xB7, 0xB4, 0xB2 ; 90..97
.db 0xAF, 0xAC, 0xA9, 0xA6, 0xA4, 0xA1, 0x9E, 0x9C ; 98..9F
.db 0x99, 0x97, 0x94, 0x92, 0x8F, 0x8D, 0x8A, 0x88 ; A0..A7
.db 0x86, 0x83, 0x81, 0x7F, 0x7D, 0x7A, 0x78, 0x76 ; A8..AF
.db 0x74, 0x72, 0x70, 0x6E, 0x6C, 0x6A, 0x68, 0x66 ; B0..B7
.db 0x64, 0x62, 0x60, 0x5E, 0x5C, 0x5A, 0x58, 0x57 ; B8..BF
.db 0x55, 0x53, 0x51, 0x50, 0x4E, 0x4C, 0x4A, 0x49 ; C0..C7
.db 0x47, 0x46, 0x44, 0x42, 0x41, 0x3F, 0x3E, 0x3C ; C8..CF
.db 0x3B, 0x39, 0x38, 0x36, 0x35, 0x33, 0x32, 0x30 ; D0..D7
.db 0x2F, 0x2E, 0x2C, 0x2B, 0x29, 0x28, 0x27, 0x25 ; D8..DF
.db 0x24, 0x23, 0x21, 0x20, 0x1F, 0x1E, 0x1C, 0x1B ; E0..E7
.db 0x1A, 0x19, 0x18, 0x16, 0x15, 0x14, 0x13, 0x12 ; E8..EF
.db 0x11, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09 ; F0..F7
.db 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 ; F8..FF
; старший байт обратных величин
.db 0x00, 0x00, 0x80, 0x55, 0x40, 0x33, 0x2A, 0x24 ; 00..07
.db 0x20, 0x1C, 0x19, 0x17, 0x15, 0x13, 0x12, 0x11 ; 08..0F
.db 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0C, 0x0B, 0x0B ; 10..17
.db 0x0A, 0x0A, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08 ; 18..1F
.db 0x08, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06 ; 20..27
.db 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05 ; 28..2F
.db 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04 ; 30..37
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 38..3F
.db 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 ; 40..47
.db 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 ; 48..4F
.db 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02 ; 50..57
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 58..5F
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 60..67
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 68..6F
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 70..77
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 78..7F
.db 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 80..87
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 88..8F
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 90..97
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; 98..9F
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; A0..A7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; A8..AF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; B0..B7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; B8..BF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; C0..C7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; C8..CF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; D0..D7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; D8..DF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; E0..E7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; E8..EF
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; F0..F7
.db 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 ; F8..FF

; Нашёл у вас ошибочку:
;result_a_or_fault:
; CPI R18,1 ; вы "потеряли" эту команду
; BREQ result_a
;
; У меня получилось тоже 69 тактов в худшем случае, но ошибок
; не бывает (или я пока не нашёл). Изменил:
; 1. Таблицу обратных величин. Вычисляю её по формуле
; log_table[i]=(0x10000UL/i), а не log_table[i]=((0x10000UL/i)+1)
; как было у вас.
; 2. Расположение старших и младших байтов в таблице обратных величин.
; Ну это дело вкуса.
; 3. Корректирую результат на +1, 0 или -1. У вас корретировался
; только на 0 и -1. Видимо для этого и была предназначена та, убранная
; мною, еденичка в формуле. Но такой коррекции было недостаточно.
; Оттуда и ошибки.
; 4. Соптимизировал кое-какие вычисления. См. текст.
;
; На АВР проверил конечно не всё варианты делимое+делитель. Проверял
; алгоритм на компьютере (там проверил всё), а на АВР с компьютерного
; Си переносил вручную. Надо сказать, что с некоторых пор совершенно
; не использую Си на АВР. Забыл как дурной сон. Поэтому м.б. не
; выполнил все Си-шные соглашения. Но если вы мне их напомните - могу
; подкорректировать код.

Комментарии внутри. В конце.
Rst7
Цитата
Поэтому м.б. не
; выполнил все Си-шные соглашения. Но если вы мне их напомните - могу
; подкорректировать код.


Для IAR:
Вход:
Делимое R17:R16
Делитель R19:R18
Выход:
Частное R17:R16

Можно не сохранять регистры R0...R3,R16...R23,R30,R31.

Никаких предустановленных регистров нет.

Но на самом деле, по алгоритму - там надо было решать вопрос не коррекцией на +1 и -1, а добавить значащих бит в таблицу обратных величин в диапазоне 0x80-0xFF. Тогда нет необходимости в лишней проверке.

Через пару дней положу новый вариант.
galjoen
Цитата(Rst7 @ Jan 28 2009, 09:57) *
Но на самом деле, по алгоритму - там надо было решать вопрос не коррекцией на +1 и -1, а добавить значащих бит в таблицу обратных величин в диапазоне 0x80-0xFF. Тогда нет необходимости в лишней проверке.

Я попробовал что-то подобное промоделировать на компьютере - у меня не получилось. Формулу, по которой таблицы вычисляются, менял. Всё равно результат на еденичку пляшет. Я даже дополнительно сдвигал на 1 разряд налево приведённый делитель (старший бит всё равно всегда 1 равен) - всё равно с ошибками получалсь. Т.е. без коррекции видимо не обойтись...

Вообще меня не столько быстрое делении интересует, как быстрое вычисление арктангенса. Точнее быстрый переход от декартовых координат к полярным (там и деление конечно используется). С вычислением амплитуды проблем нет, а вот угол...
galjoen
Сделал 50-ти тактовую версию беззнакового деления 16/16. Но с ошибками. Ошибки в "правильную" сторону и не больше чем на 1. Т.е. что-то вроде округления.
CODE

.DEF RG00=R2 ; регистр всегда равный 0x00
;.DEF RGFF=R3 ; реги
;.DEF RBN0=R4 ; чётный регистр в диапазоне 2..15
;.DEF RBN1=R5 ; нечётный регистр в диапазоне 2..15 (пара к предыдущему)
.DEF RBN0=R22 ; чётный регистр IAR!
.DEF RBN1=R23 ; нечётный регистр (пара к предыдущему) IAR!
.DEF RBN2=R3 ; чётный регистр в диапазоне 2..15 IAR!
.DEF RBX0=R16 ; чётный регистр в диапазоне 16..25
.DEF RBX1=R17 ; нечётный регистр в диапазоне 16..25 (пара к предыдущему)
.DEF RBX2=R18 ; чётный регистр в диапазоне 16..25
.DEF RBX3=R19 ; нечётный регистр в диапазоне 16..25 (пара к предыдущему)
.DEF RBX4=R20 ; чётный регистр в диапазоне 16..25
.DEF RBX5=R21 ; нечётный регистр в диапазоне 16..25 (пара к предыдущему)
.DEF ZL=R30
.DEF ZH=R31
eor RG00,RG00 ; RG00=0x00
ldi RBX0,0xFF
out 0x3D,RBX0
out 0x3E,RG00
dfdf: rcall f16d
rjmp dfdf


f16d:
; RBX1:RBX0=R17:R16=делимое=a + возвращаем результат
; RBX3:RBX2=R19:18=делитель=b
; RBX5:RBX4=R21:R20=результат=с, но возвращаем в RBX1:RBX0
eor RG00,RG00 ; RG00=0x00 (хотя я обычно делаю это
; только один раз при инициализации)
ldi ZH,high(tb_3e<<1) ; ст. байт адреса таблиц (3 шт.)
cpi RBX3,1
brcc f16d1 ; C=0 - делим не на байт

f16db: ; делим слово на байт (0==RBX3)
cpi RBX2,0x02 ; проверим случаи 0 и 1
brcs f16db01
; прочтем из таблицы соответствующий сдвигатель
mov ZL,RBX2 ; мл. байт делителя
lpm RBN2,Z ; s - сдвиг
; приведём делитель в диапазон 0x100..0x1FF (по сдвигателю)
mul RBX2,RBN2 ; делитель
mov ZL,R0 ; ZL=приведённый делитель
cpi ZL,0 ; его проверим на 0x100
breq f16db1 ; Z=1 получилось 0x100 - не множим
; получим обратную величину из таблицы по делителю
inc ZH ; ук-ль на мл. байты обратной
lpm RBN0,Z ; мл. байт обратной из таблицы
inc ZH ; ук-ль на ст. байты
lpm RBN1,Z ; ст. байт обратной из таблицы
; множим делимое на обратную величину (результат в ZH:ZL, RBX5:RBX4)
mul RBN1,RBX1 ; ст. б. обратного * ст. байт делимого
movw ZH:ZL,R1:R0 ; щас результат получим в ZH:ZL
mul RBN0,RBX0 ; мл. б. обратного * мл. б. делимого
movw RBX5:RBX4,R1:R0
mul RBN1,RBX0 ; ст. б. обратного * мл. б. делимого
add RBX5,R0
adc ZL,R1
adc ZH,RG00
mul RBN0,RBX1 ; мл. б. обратного * ст. б. делимого
add RBX5,R0
adc ZL,R1
adc ZH,RG00 ; ZH:ZL=несдвинутый результат
; сдвинем результат назад на величину сдвигателя s (RBN2)
mul ZH,RBN2 ; ст. байт
movw RBX1:RBX0,R1:R0
mul ZL,RBN2 ; мл. байт
or RBX0,R1 ; RBX5:RBX4= нескорректированный результат
ret ; [50]
f16db1:; приведённый делитель, это 0x100
mul RBX0,RBN2
mov ZL,R1
mul RBX1,RBN2
movw RBX1:RBX0,R1:R0
or RBX0,ZL
ret

f16db01: ; делитель = 0..1
cpi RBX2,0 ; проверим делитель - 0 или 1
brne f16db11 ; Z=0 - (результат)=(делимому)
or RBX0,RBX1 ; проверим деление 0 на 0
ldi RBX0,0xFF ; это переполнение при
ldi RBX0,0xFF ; делении на 0 такое
brne f16db11 ; Z=0 - щас не 0/0
ldi RBX0,0x01 ; при делении 0 на
ldi RBX1,0x00 ; 0 результат =1
f16db11: ; делили на 0
ret

f16d1: breq f16dp ; делим на 0x100..0x1FF
f16wd: ; делим на 0x200..0xFFFF
; прочтем из таблицы соответствующий сдвигатель
mov ZL,RBX3 ; ст. байт делителя
lpm RBN2,Z ; s - сдвиг
; приведём делитель в диапазон 0x100..0x1FF (по сдвигателю)
mul RBX3,RBN2 ; ст. байт делителя
mov ZL,R0
mul RBX2,RBN2 ; мл. байт делителя
or ZL,R1 ; ZL=приведённый делитель
breq f16wd1 ; Z=1 получилось 0x100 - не множим
; получим обратную величину из таблицы по приведённому делителю
inc ZH ; ук-ль на мл. байты обратной
lpm RBN0,Z ; мл. байт обратной из таблицы
inc ZH ; ук-ль на ст. байты
lpm RBN1,Z ; ст. байт обратной из таблицы
; множим делимое на обратную величину (результат в ZH:ZL, RBX5:RBX4)
mul RBN1,RBX1 ; ст. б. обратного * ст. байт делимого
movw ZH:ZL,R1:R0 ; щас результат получим в ZH:ZL
mul RBN0,RBX0 ; мл. б. обратного * мл. б. делимого
movw RBX5:RBX4,R1:R0
mul RBN1,RBX0 ; ст. б. обратного * мл. б. делимого
add RBX5,R0
adc ZL,R1
adc ZH,RG00
mul RBN0,RBX1 ; мл. б. обратного * ст. б. делимого
add RBX5,R0
adc ZL,R1
adc ZH,RG00 ; ZH:ZL=несдвинутый результат
; сдвинем результат назад на величину сдвигателя s (RBN2)
mul ZH,RBN2 ; токо ст. байт т.к. результат в этом
mov RBX0,R1 ; случае м.б. токо однобайтовый
ldi RBX1,0 ; RBX5:RBX4= нескорректированный результат
ret ; [49]

f16wd1: ; приведённый делитель 0x100. Но не из делителя 0x100.
mul RBX1,RBN2 ; мл. байт делимого * сдвигатель
mov RBX0,R1 ; мл. байт результата
ldi RBX1,0 ; ст. байт рез-та точно =0
ret

f16dp: ; делим слово на 0x100..0x1FF
cpi RBX2,0
breq f16dp1 ; делим слово на 0x100
mov ZL,RBX2 ; мл. байт делителя (уже приведённого)
; получим обратную величину из таблицы по делителю
inc ZH ; ук-ль на мл. байты обратной
lpm RBN0,Z ; мл. байт обратной из таблицы
inc ZH ; ук-ль на ст. байты
lpm RBN1,Z ; ст. байт обратной из таблицы
; множим делимое на обратную величину (результат в ZH:ZL, RBX5:RBX4)
mul RBN1,RBX1 ; ст. б. обратного * ст. байт делимого
movw ZH:ZL,R1:R0 ; щас результат получим в ZH:ZL
mul RBN0,RBX0 ; мл. б. обратного * мл. б. делимого
movw RBX5:RBX4,R1:R0
mul RBN1,RBX0 ; ст. б. обратного * мл. б. делимого
add RBX5,R0
adc ZL,R1
adc ZH,RG00
mul RBN0,RBX1 ; мл. б. обратного * ст. б. делимого
add RBX5,R0
adc ZL,R1
adc ZH,RG00 ; ZH:ZL=несдвинутый результат
; сдвинем результат, в нашем случае >>8
mov RBX0,ZH
ldi RBX1,0
ret ; [40]
f16dp1: ; делим на 0x100
mov RBX0,RBX1 ; мл. байт результата
ldi RBX1,0 ; ст. байт рез-та точно =0
ret

ENDCOD: ; эта метка токо шоб корректировался выровненный адрес
.org ((ENDCOD+0x7F)&0xFF80)
tb_3e:
; таблица сдвигателей
.db 0xFF, 0x00, 0x80, 0x80, 0x40, 0x40, 0x40, 0x40 ; 00..07
.db 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 ; 08..0F
.db 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 ; 10..17
.db 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 ; 18..1F
.db 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 ; 20..27
.db 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 ; 28..2F
.db 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 ; 30..37
.db 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 ; 38..3F
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 40..47
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 48..4F
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 50..57
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 58..5F
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 60..67
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 68..6F
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 70..77
.db 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 ; 78..7F
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 80..87
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 88..8F
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 90..97
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; 98..9F
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; A0..A7
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; A8..AF
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; B0..B7
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; B8..BF
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; C0..C7
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; C8..CF
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; D0..D7
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; D8..DF
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; E0..E7
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; E8..EF
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; F0..F7
.db 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 ; F8..FF
; младший байт обратных величин
.db 0xFF, 0x01, 0x04, 0x09, 0x10, 0x19, 0x24, 0x30 ; 00..07
.db 0x3F, 0x4F, 0x61, 0x75, 0x8A, 0xA1, 0xBA, 0xD5 ; 08..0F
.db 0xF1, 0x10, 0x2F, 0x51, 0x74, 0x98, 0xBE, 0xE6 ; 10..17
.db 0x0F, 0x3A, 0x66, 0x94, 0xC3, 0xF4, 0x26, 0x5A ; 18..1F
.db 0x8F, 0xC5, 0xFD, 0x36, 0x71, 0xAD, 0xEA, 0x28 ; 20..27
.db 0x68, 0xA9, 0xEC, 0x30, 0x75, 0xBB, 0x02, 0x4B ; 28..2F
.db 0x95, 0xE0, 0x2C, 0x79, 0xC8, 0x18, 0x69, 0xBB ; 30..37
.db 0x0E, 0x62, 0xB7, 0x0E, 0x65, 0xBD, 0x17, 0x72 ; 38..3F
.db 0xCD, 0x2A, 0x88, 0xE6, 0x46, 0xA7, 0x08, 0x6B ; 40..47
.db 0xCF, 0x33, 0x99, 0xFF, 0x66, 0xCF, 0x38, 0xA2 ; 48..4F
.db 0x0D, 0x79, 0xE5, 0x53, 0xC1, 0x31, 0xA1, 0x12 ; 50..57
.db 0x83, 0xF6, 0x6A, 0xDE, 0x53, 0xC9, 0x3F, 0xB7 ; 58..5F
.db 0x2F, 0xA8, 0x22, 0x9C, 0x18, 0x94, 0x10, 0x8E ; 60..67
.db 0x0C, 0x8B, 0x0A, 0x8B, 0x0C, 0x8D, 0x10, 0x93 ; 68..6F
.db 0x17, 0x9B, 0x20, 0xA6, 0x2D, 0xB4, 0x3B, 0xC4 ; 70..77
.db 0x4D, 0xD6, 0x61, 0xEC, 0x77, 0x03, 0x90, 0x1D ; 78..7F
.db 0xAB, 0x3A, 0xC9, 0x58, 0xE9, 0x7A, 0x0B, 0x9D ; 80..87
.db 0x30, 0xC3, 0x56, 0xEA, 0x7F, 0x14, 0xAA, 0x41 ; 88..8F
.db 0xD8, 0x6F, 0x07, 0x9F, 0x38, 0xD2, 0x6C, 0x06 ; 90..97
.db 0xA1, 0x3D, 0xD9, 0x75, 0x12, 0xAF, 0x4D, 0xEC ; 98..9F
.db 0x8A, 0x2A, 0xC9, 0x6A, 0x0A, 0xAB, 0x4D, 0xEF ; A0..A7
.db 0x91, 0x34, 0xD8, 0x7B, 0x20, 0xC4, 0x69, 0x0F ; A8..AF
.db 0xB5, 0x5B, 0x02, 0xA9, 0x50, 0xF8, 0xA1, 0x49 ; B0..B7
.db 0xF3, 0x9C, 0x46, 0xF0, 0x9B, 0x46, 0xF2, 0x9D ; B8..BF
.db 0x4A, 0xF6, 0xA3, 0x51, 0xFE, 0xAC, 0x5B, 0x0A ; C0..C7
.db 0xB9, 0x68, 0x18, 0xC8, 0x79, 0x2A, 0xDB, 0x8C ; C8..CF
.db 0x3E, 0xF1, 0xA3, 0x56, 0x09, 0xBD, 0x71, 0x25 ; D0..D7
.db 0xD9, 0x8E, 0x43, 0xF9, 0xAF, 0x65, 0x1B, 0xD2 ; D8..DF
.db 0x89, 0x40, 0xF8, 0xB0, 0x68, 0x21, 0xDA, 0x93 ; E0..E7
.db 0x4C, 0x06, 0xC0, 0x7A, 0x35, 0xEF, 0xAA, 0x66 ; E8..EF
.db 0x22, 0xDD, 0x9A, 0x56, 0x13, 0xD0, 0x8D, 0x4B ; F0..F7
.db 0x09, 0xC7, 0x85, 0x44, 0x03, 0xC2, 0x81, 0x41 ; F8..FF
; старший байт обратных величин
.db 0xFF, 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9 ; 00..07
.db 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2, 0xF1 ; 08..0F
.db 0xF0, 0xF0, 0xEF, 0xEE, 0xED, 0xEC, 0xEB, 0xEA ; 10..17
.db 0xEA, 0xE9, 0xE8, 0xE7, 0xE6, 0xE5, 0xE5, 0xE4 ; 18..1F
.db 0xE3, 0xE2, 0xE1, 0xE1, 0xE0, 0xDF, 0xDE, 0xDE ; 20..27
.db 0xDD, 0xDC, 0xDB, 0xDB, 0xDA, 0xD9, 0xD9, 0xD8 ; 28..2F
.db 0xD7, 0xD6, 0xD6, 0xD5, 0xD4, 0xD4, 0xD3, 0xD2 ; 30..37
.db 0xD2, 0xD1, 0xD0, 0xD0, 0xCF, 0xCE, 0xCE, 0xCD ; 38..3F
.db 0xCC, 0xCC, 0xCB, 0xCA, 0xCA, 0xC9, 0xC9, 0xC8 ; 40..47
.db 0xC7, 0xC7, 0xC6, 0xC5, 0xC5, 0xC4, 0xC4, 0xC3 ; 48..4F
.db 0xC3, 0xC2, 0xC1, 0xC1, 0xC0, 0xC0, 0xBF, 0xBF ; 50..57
.db 0xBE, 0xBD, 0xBD, 0xBC, 0xBC, 0xBB, 0xBB, 0xBA ; 58..5F
.db 0xBA, 0xB9, 0xB9, 0xB8, 0xB8, 0xB7, 0xB7, 0xB6 ; 60..67
.db 0xB6, 0xB5, 0xB5, 0xB4, 0xB4, 0xB3, 0xB3, 0xB2 ; 68..6F
.db 0xB2, 0xB1, 0xB1, 0xB0, 0xB0, 0xAF, 0xAF, 0xAE ; 70..77
.db 0xAE, 0xAD, 0xAD, 0xAC, 0xAC, 0xAC, 0xAB, 0xAB ; 78..7F
.db 0xAA, 0xAA, 0xA9, 0xA9, 0xA8, 0xA8, 0xA8, 0xA7 ; 80..87
.db 0xA7, 0xA6, 0xA6, 0xA5, 0xA5, 0xA5, 0xA4, 0xA4 ; 88..8F
.db 0xA3, 0xA3, 0xA3, 0xA2, 0xA2, 0xA1, 0xA1, 0xA1 ; 90..97
.db 0xA0, 0xA0, 0x9F, 0x9F, 0x9F, 0x9E, 0x9E, 0x9D ; 98..9F
.db 0x9D, 0x9D, 0x9C, 0x9C, 0x9C, 0x9B, 0x9B, 0x9A ; A0..A7
.db 0x9A, 0x9A, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98 ; A8..AF
.db 0x97, 0x97, 0x97, 0x96, 0x96, 0x95, 0x95, 0x95 ; B0..B7
.db 0x94, 0x94, 0x94, 0x93, 0x93, 0x93, 0x92, 0x92 ; B8..BF
.db 0x92, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x90 ; C0..C7
.db 0x8F, 0x8F, 0x8F, 0x8E, 0x8E, 0x8E, 0x8D, 0x8D ; C8..CF
.db 0x8D, 0x8C, 0x8C, 0x8C, 0x8C, 0x8B, 0x8B, 0x8B ; D0..D7
.db 0x8A, 0x8A, 0x8A, 0x89, 0x89, 0x89, 0x89, 0x88 ; D8..DF
.db 0x88, 0x88, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86 ; E0..E7
.db 0x86, 0x86, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84 ; E8..EF
.db 0x84, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82 ; F0..F7
.db 0x82, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80 ; F8..FF
;
;Для IAR:
;Вход:
;Делимое R17:R16
;Делитель R19:R18
;Выход:
;Частное R17:R16
;Можно не сохранять регистры R0...R3,R16...R23,R30,R31.
;Никаких предустановленных регистров нет.

; Версия 2.0
; Изменения:
; 1. Убрал коррекцию.
; 2. Считает не точно. Иногда ошибается на 1 в большую сторону.
; Зависит от соотношения делимое/делитель. Получается что-то вроде
; округления результата.
; 3. В худшем случае 50 тактов.
; 4. Делитель привожу в диапазон 0x100..0x1FF. В рассчётах не использую
; старший бит т.к. он всегда =1.
; 5. Соответственно пересчитал таблицу сдвигателей.
; 6. Таблицу обратных величин считаю по формуле
; ((0x1000000/(0x100+i+0))+1).
; Пока не всё проверил (на компьютере всё). Перепроверю - тогда выложу
; ещё раз.

Подробные комментарии внутри. Внизу.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.