|
Необычное использование аппаратного умножителя |
|
|
|
May 7 2008, 17:14
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Вот, сижу, мастерю один проектик. Понадобилось сделать Хаффмана. В принципе, ничего сложного, однако работа с битовыми полями - это всегда узкое место процов с отсутствием комманд для обработки данных такого класса. Например, при упаковке по Хаффману необходима процедура, которая запишет в выходной поток n бит значения - вызывается примерно так: Код write_bits(huff->codes[val],huff->bits[val]) где первый параметр - код, а второй - собственно его размер в битах. Вот обычно эта функция - write_bits и несет основные затраты по времени выполнения. Почему так происходит - понятно, надо сдвигать входные биты в нужное положение, вычислять новое положение в буфере и т.д. Посему, сразу кодить не стал, сел - подумал... И вот что надумал... У нас есть аппаратный умножитель, который за 2 такта даст нам сдвинутый сразу в двух направлениях байт - и влево, и вправо. Конечно, в качестве множителя надо использовать маску - 0x01,0x02,...,0x80 и в результате, после выполнения комманды MUL в R0 будем иметь байт данных, сдвинутый влево, в R1 - сдвинутый вправо. Это уже отлично, одной коммандой мы готовим данные для OR с текущим байтом и для занесения следующих данных в накопитель. Однако, соответствующую текущему моменту маску еще надо получить. Она зависит как от текущей битовой позиции в выходном буфере, так и от размера битовых данных. Опять пришлось поразмышлять. Размышления натолкнули на идею хранить битовую позицию в буфере тоже в виде маски: Код 0x80(биты 7...0 свободны),0x40(бит 7 с данными, биты 6...0 свободны),....,0x01(биты 7...1 с данными, бит 0 свободен) Если размер символа в битах тоже задать в виде маски, то происходит чудо  - умножение (аппаратное, конечно) размера на битовую позицию даст новую позицию и она же является необходимым (точнее, в 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 тактов). Такая функция, кстати, будет полезна тем, кто пишет вывод пропорциональных символов на графический дисплей - вывод можно здорово ускорить. Вообще, подобный подход можно использовать там, где необходимо организовывать сдвиги на произвольное количество бит.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 26)
|
May 7 2008, 18:22
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(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; }
|
|
|
|
|
May 7 2008, 18:50
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата Правда если есть возможность организовывать групповые сдвиги (например маска и значение), то условиями меньше места займёт. В этом случае конечно да. Ну а как Вам такой способ? Код // 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 ниасилил, это руками писано.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
May 8 2008, 07:24
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(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 - бит четности
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
May 8 2008, 07:54
|
Знающий
   
Группа: Свой
Сообщений: 550
Регистрация: 16-06-04
Из: Казань
Пользователь №: 32

|
сдвигами четность все равно шустрее выйдет: Код 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 для немедленного ветвления
--------------------
Главная линия этого опуса ясна мне насквозь!
|
|
|
|
|
May 29 2008, 19:41
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Америки не открою, и особой экзотики в использовании умножения не будет, но... Самое быстрое деление 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 Может, кому пригодится
|
|
|
|
|
May 31 2008, 18:57
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(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 примерно равны. И там, где при этом надо очень быстро все считать.
|
|
|
|
|
Dec 13 2008, 21:05
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(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 для немедленного ветвления Случайно пришла в голову мысль (не подумайте, что я тут полгода моск себе парил  ), что шестой бит выражения __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 тактов
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Dec 14 2008, 19:11
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

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

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(singlskv @ Dec 14 2008, 21:11)  ИМХО, очень красиво... но я бы умножал на 0xAA для дальнейшего сдвига в С, но это так в качестве дальнейшего улучшения... Да вообщем пофиг, что в перенос двигать, что bst делать, что sbrs/sbrc. Кстати, для ARM'а весьма красиво смотрится выражение типа if (((v^(v<<1))&0xAA)*0x55)&0x80) - конечно, в arm-режиме, в тумбе - так себе.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Dec 14 2008, 20:08
|
дятел
    
Группа: Свой
Сообщений: 1 681
Регистрация: 13-05-06
Из: Питер
Пользователь №: 17 065

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

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(singlskv @ Dec 14 2008, 22:08)  я в том смысле что есть выбор, толи в перенос толи sbrs/sbrc. А v -8ми битный ? Дык после &170 уже пофиг  Вообще, на архитектурах типа арма, мипса, ппц я рекомендую использовать регистровые переменные только размером в регистр, иначе ведро оверхеда обеспечено. Цитата Аа... Вы о том как это оттранслирует компилятор ? Ну вменяемый компилятор оттранслирует это в пять команд плюс переход. Просто выражение со сдвигом в другую сторону не может быть обработано бесплатным сдвигом операнда в арм-режиме.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Jan 23 2009, 15:21
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Ну, продолжим наши игры. На этот раз под руки попало деление. 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  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=  >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 отдельной табличкой обратных значений).
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Jan 27 2009, 10:38
|
Знающий
   
Группа: Свой
Сообщений: 841
Регистрация: 10-05-07
Из: Чебоксары (Россия)
Пользователь №: 27 640

|
Цитата(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. Или ещё как. Я собственно собираюсь переписать всё на асм. У вас там есть ещё пара мест где хотелось бы подправить. Но это потом. А пока жел-но разобраться с непонятным местом...
|
|
|
|
|
Jan 27 2009, 11:22
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата Я собственно собираюсь переписать всё на асм. Гм. Я разве не на асме код выложил? Для тех, кто в танке - была написана рыба на Си, отлажена в MSVS, затем собрана IAR'ом, и листинг допилен вручную. Щас продолжу, случайно не ту кнопку нажал  Я не пойму, что Вас смущает? a - делимое; b - делитель; с - результат, сначала приближение, потом он корректируется; e - ошибка, равная b*c-a; Если внести a-=b в выражение ошибки, то это эквивалентно e=b*c-( a - b )=b*c-a+b=b*(c+1)-a; Если ошибка превышает делитель, то корректируем результат (уменьшением).
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Jan 27 2009, 12:11
|
Знающий
   
Группа: Свой
Сообщений: 841
Регистрация: 10-05-07
Из: Чебоксары (Россия)
Пользователь №: 27 640

|
Цитата(Rst7 @ Jan 27 2009, 14:22)  Гм. Я разве не на асме код выложил? Для тех, кто в танке - была написана рыба на Си, отлажена в MSVS, затем собрана IAR'ом, и листинг допилен вручную. Ну вот в наследство от Си и осталось... Если изначально на асме писать - лучше получится. Цитата(Rst7 @ Jan 27 2009, 14:22)  Щас продолжу, случайно не ту кнопку нажал  Я не пойму, что Вас смущает? 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 (корректируете результат) Вот так получается? Если так, то это же неверно! Вот это меня и смущает  . А нужно сравнивать так: b*c-a?>b И если условие выполняется - вычитать из c еденицу.
|
|
|
|
|
Jan 27 2009, 12:51
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата Приведём (преобразуем) ваш вариант к такому виду: .... b*c?>a и если условие выполняется, то вы уменьшаете c (корректируете результат) Согласен. Переумничал. Дело в том, что изначально там было весьма хитрое последовательное приближение за несколько этапов. Потом убралось, но не целиком. Оттуда и издержки, а совсем не от компилятора Си. Цитата Вот так получается? Если так, то это же неверно! Почему?
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Jan 27 2009, 14:41
|
Знающий
   
Группа: Свой
Сообщений: 841
Регистрация: 10-05-07
Из: Чебоксары (Россия)
Пользователь №: 27 640

|
Цитата(Rst7 @ Jan 27 2009, 15:51)  Почему? Не совпадает с тем, что в описании. А вообще тут всё зависит от того, нужно-ли округлять результат деления. В том случае получилось с обрезанием. Еще хотелось бы узнать как вы считали таблицу обратных величин. Ну не в ручную же. Выложите код если можно. Это нужно чтобы изменить коррекцию. Чтобы результат корректировался на +1, 0 и -1. Это добавляет всего 3 такта. М.б. удастся ту 1 при ~40000 скорректировать. Ну и другие мысли по оптимизации на асме есть. Сделаю - выложу.
|
|
|
|
|
Jan 27 2009, 14:58
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата Не совпадает с тем, что в описании. Где не совпадает? Цитата А вообще тут всё зависит от того, нужно-ли округлять результат деления. В том случае получилось с обрезанием. Операция / в Си не округляет результат. Так что все правильно. Цитата Еще хотелось бы узнать как вы считали таблицу обратных величин. Ну не в ручную же. Выложите код если можно. Можно. Это считатель: Код 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 скорректировать. Не помню, но не получалось. Там значащих битов не хватает. Цитата Ну и другие мысли по оптимизации на асме есть. Вы бы не забывали соблюдать соглашения о вызовах сишного компилятора, полезнее будет.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Jan 28 2009, 05:25
|
Знающий
   
Группа: Свой
Сообщений: 841
Регистрация: 10-05-07
Из: Чебоксары (Россия)
Пользователь №: 27 640

|
Цитата(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. Соптимизировал кое-какие вычисления. См. текст. ; ; На АВР проверил конечно не всё варианты делимое+делитель. Проверял ; алгоритм на компьютере (там проверил всё), а на АВР с компьютерного ; Си переносил вручную. Надо сказать, что с некоторых пор совершенно ; не использую Си на АВР. Забыл как дурной сон. Поэтому м.б. не ; выполнил все Си-шные соглашения. Но если вы мне их напомните - могу ; подкорректировать код.
Комментарии внутри. В конце.
|
|
|
|
|
Jan 28 2009, 06:57
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата Поэтому м.б. не ; выполнил все Си-шные соглашения. Но если вы мне их напомните - могу ; подкорректировать код. Для IAR: Вход: Делимое R17:R16 Делитель R19:R18 Выход: Частное R17:R16 Можно не сохранять регистры R0...R3,R16...R23,R30,R31. Никаких предустановленных регистров нет. Но на самом деле, по алгоритму - там надо было решать вопрос не коррекцией на +1 и -1, а добавить значащих бит в таблицу обратных величин в диапазоне 0x80-0xFF. Тогда нет необходимости в лишней проверке. Через пару дней положу новый вариант.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Jan 28 2009, 15:53
|
Знающий
   
Группа: Свой
Сообщений: 841
Регистрация: 10-05-07
Из: Чебоксары (Россия)
Пользователь №: 27 640

|
Цитата(Rst7 @ Jan 28 2009, 09:57)  Но на самом деле, по алгоритму - там надо было решать вопрос не коррекцией на +1 и -1, а добавить значащих бит в таблицу обратных величин в диапазоне 0x80-0xFF. Тогда нет необходимости в лишней проверке. Я попробовал что-то подобное промоделировать на компьютере - у меня не получилось. Формулу, по которой таблицы вычисляются, менял. Всё равно результат на еденичку пляшет. Я даже дополнительно сдвигал на 1 разряд налево приведённый делитель (старший бит всё равно всегда 1 равен) - всё равно с ошибками получалсь. Т.е. без коррекции видимо не обойтись... Вообще меня не столько быстрое делении интересует, как быстрое вычисление арктангенса. Точнее быстрый переход от декартовых координат к полярным (там и деление конечно используется). С вычислением амплитуды проблем нет, а вот угол...
|
|
|
|
|
Jan 30 2009, 10:06
|
Знающий
   
Группа: Свой
Сообщений: 841
Регистрация: 10-05-07
Из: Чебоксары (Россия)
Пользователь №: 27 640

|
Сделал 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). ; Пока не всё проверил (на компьютере всё). Перепроверю - тогда выложу ; ещё раз.
Подробные комментарии внутри. Внизу.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|