Заголовок сообщения: Re: Хитрые, необычные алгоритмы и код
Добавлено: Пн сен 24, 2012 11:21:17
Друг Кота
Карма: 67
Рейтинг сообщений: 1060
Зарегистрирован: Чт сен 18, 2008 12:27:21 Сообщений: 19635 Откуда: Столица Мира Санкт-Петербург
Рейтинг сообщения:0 Медали: 1
Хм... Тут и так дофига прилепленых тем.
_________________ [ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ] Измерить нннада?
А Вы гляньте внимательнее на свой инструмент. Я довольно много пользовался ассемблерами для mcs51 от AVOCET (это где-то в середине 90-ых) и Keil. У обоих уже были предопределены AR0..AR7 (Absolute Register), причём они зависели от директивы USING, которая указывала, какой сейчас банк регистров используется. Асмом 2500A.D. я пользовался некоторое время до AVOCET ASM51/C51 и сейчас не уверен, но, кажется, у него тоже такая возможность была. Итого я спокойно писал
Код:
USING 1 TF1_isr: _push <PSW,ACC,AR0> ; Тут AR0 равно 8, так как объявлен банк 1. ... _pop <AR0,ACC,PSW> reti
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Компания MEAN WELL пополнила ассортимент своей широкой линейки светодиодных драйверов новым семейством XLC для внутреннего освещения. Главное отличие – поддержка широкого спектра проводных и беспроводных технологий диммирования. Новинки представлены в MEANWELL.market моделями с мощностями 25 Вт, 40 Вт и 60 Вт. В линейке есть модели, работающие как в режиме стабилизации тока (СС), так и в режиме стабилизации напряжения (CV) значением 12, 24 и 48 В.
...Для преобразования в BCD на asm применял такой код...
Здравствуйте. В качестве пятничной развлекухи прогнал в студии Ваш код. Преобразование занимает 2254 такта. Затем сделал тоже самое по алгоритму, предложенному здесь уважаемым umup. Преобразование занимает 798 тактов. Спойлер
Код:
; Преобразователь BIN24-BCD8
.INCLUDE "2313def.inc" .CSEG
START: LDS R16,$70 LDS R17,$71 LDS R18,$72
CLR R28 ; BCD OUT 10'000'000,1'000'000 CLR R29 ; BCD OUT 100'000,10'000 CLR R30 ; BCD OUT 1'000,100 CLR R31 ; BCD OUT 10,1
LDI R24,24 bin24bcd8: subi r31,-0x33 ;add 0x33 sbrs r31, 3 ;if carry to bit 3 subi r31, 3 ;subtract 3 sbrs r31, 7 ;if carry to bit 7 subi r31, 0x30 ;subtract 0x30
subi r30,-0x33 ; \n" /*add 0x33*/ sbrs r30, 3 ; \n" /*if carry to bit 3,*/ subi r30, 3 ; \n" /*subtract 3*/ sbrs r30, 7 ; \n" /*if carry to bit 7,*/ subi r30, 0x30 ; \n" /*subtract 0x30*/
subi r29,-0x33 ;\n" /*add 0x33*/ sbrs r29, 3 ;\n" /*if carry to bit 3,*/ subi r29, 3 ;\n" /*subtract 3*/ sbrs r29, 7 ;\n" /*if carry to bit 7,*/ subi r29, 0x30 ;\n" /*subtract 0x30*/
subi r28,-0x33 ;\n" /*add 0x33*/ sbrs r28, 3 ;\n" /*if carry to bit 3,*/ subi r28, 3 ;\n" /*subtract 3*/ sbrs r28, 7 ;\n" /*if carry to bit 7,*/ subi r28, 0x30 ;\n" /*subtract 0x30*/
; по совету avreal!!!!!
lsl R18 rol R17 ;shift input*/ rol R16
rol r31 rol r30 rol r29 rol r28
dec R24 ;\n" brne bin24bcd8 ;repeat for all bits*/ ; опционально. Распаковка упакованного BCD по регистрам MOV R20,R28 SWAP R20 ANDI R20,$0F ANDI R28,$0F MOV R21,R28
lsl R18 rol R17 rol R16 ; r16 bit 7 in C rol r31 ; shift into LSB of output rol r30 rol r29 rol r28
вместо
Код:
lsl r31 rol r30 rol r29 rol r28
sbrc R16, 7 ;skip if msbit of input =0*/ ori R31, 1 ;set lsb of output*/
lsl R18 rol R17 ;shift input*/ rol R16
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
При выводе текстовых строк на дисплей очень часто многие строки используются лишь один раз в программе (например при выводе строк меню). Было-бы очень удобно написать как в языках высокого уровня что-то типа PrintString(“Hello World!”), где выводимая строка и место в программе где она выводятся (вызов функции вывода) расположены рядом в программе. Этого можно добиться на ассемблере, поместив выводимую строку сразу после вызова функции PrintString. Идея эта не новая, но я не видел ее имплементации в микроконтроллерах, поэтому привожу свою имплементацию на примере архитектуры MSP430.
Итак, мы будем выводить строку следующим образом:
Код:
call #PrintString ; output procedure call DB "Hello World!" ; trailing zero is added EVEN clr.w R5 ; next instruction in code
Первая строчка в этом коде – собственно вызов функции вывода строки текста, описанная ниже. Сама текстовая строка определена во второй строчке кода. При заключении текста для вывода в двойные кавычки IAR IDE автоматически добавит в конец строки 0, используемый как критерий окончания строки. Последняя строчка кода – это продолжение программы, я просто поместил туда какую-то команду (clr.w R5) для теста. Наконец, директива EVEN говорит компилятору разместить эту команду начиная с четного адреса. В MSP430 каждая машинная команда должна размещаться по четному адресу в памяти.
Таким образом, сама строка для вывода в функцию PrintString явно не передается и у этой строки даже нет имени. Однако, ее начальный адрес есть не что иное, как адрес инструкции следующей после вызова функции, т.е. адрес возврата из функции, который помещается в стек командой call. После вывода текстовой строки нам надо вернуться не на ее начало для продолжения кода, а на инструкцию после нее (в нашем примере на команду clr.w R5). Следовательно, нужно будет скорректировать адрес возврата в стеке. В процедуре вывода я опустил сам вывод и оставил лишь инструкции для извлечения символов строки для памяти. Прошу прощения за нарушения табуляции - не знаю как здесь с этим бороться - в редакторе IDE все выглядит красиво.
Спойлер
Код:
PrintString: mov.w @SP, R4 ; pointer to string character ps1: mov.b @R4+, R5 ; character to output inc.w 0(SP) ; update the return address tst.b R5 ; end of string? jz ps2 ; YES - break the loop
; put here instructions for character processing
jmp ps1 ; loop back
ps2: bit.w #1, 0(SP) ; move to the next even address jz ps3 ; if needed inc.w 0(SP) ; even alignment of the return address ps3: ret
Процедура начинается с копирования адреса первого символа текстовой строки в R4 (этот адрес как отмечено выше равен адресу возврата из функции). Таким образом, R4 будет поинтер на выводимый символ в строке, который извлекается из памяти и помещается в R5. После этого адрес возврата в стеке увеличивается на 1 и происходит проверка на окончание строки, т.е. на равенство R5 нулю. Если полученный символ не 0, мы его выводим и переходим на начало цикла с меткой ps1. В противном случае выходим из цикла на метку ps2. Там текущий адрес возврата в стеке проверятся на четность, и если он оказался нечетным, то к нему добавляется 1 (последствия директивы EVEN).
Функцию можно сделать более универсальной, чтобы иметь возможность работать со строками размещенными непосредственно за ней или передаваемых функции в качестве параметра через стек. В стек при этом следует заносить адрес начала строки для вывода, а в теле функции добавится еще одна точка входа. В нижеприведенном коде точки входа в функцию помечены метками PrintString2 и PrintString3. При этом вызовы можно осуществить следующим образом:
Код:
; первый вариант вызова с передачей параметра через стек push.w #hi ; passing parameter string call #PrintString2 ; using another entry point
; второй вариант вызова с размещением строки сразу под вызовом функции call #PrintString3 ; output procedure call DB "Hello World!" ; trailing zero is added EVEN clr.w R5 ; next instruction in code
В первой строчке этого кода функции передается адрес текстовой строки для вывода. Сама строка определена где-то в другом месте программы, например как
hi: DB "Hi, there"
Во втором варианте вызова функции строку для вывода следует разместить в коде сразу после вызова как и раньше.
Таким образом, при первом способе передачи параметра через стек адрес возврата в стеке изменять не нужно, в то время как при втором способе вызова нужно увеличивать его на 1 после обработки каждого символа. Для этого в регистр R6 помещается либо число 0 либо 1, и значение этого регистра прибавляется к адресу возврата в стеке в любом случае.
При входе в функцию PrintString2 адрес возврата находится на вершине стека, а адрес строки для вывода непосредственно под ним. Сначала адрес возврата извлекается из стека и копируется в R6. Таким образом, адрес строки для вывода оказывается на вершине стека и он помещается в R4. После копирования адреса начала строки в R4 он больше не нужен в стеке и он заменяется на адрес возврата, хранимый в R6. Это позволяет в основной программе избавиться от удаления адреса строки в стеке после вызова функции.
Спойлер
Код:
PrintString2: pop.w R6 ; load return address in R6 mov.w @SP, R4 ; load string address in R4 mov.w R6, 0(SP) ; put return address in stack clr.w R6 ; do not update the return address jmp ps4
PrintString3: mov.w #1, R6 ; add 1 to the return address mov.w @SP, R4 ; pointer to string character ps4: mov.b @R4+, R5 ; character to output add.w R6, 0(SP) ; update the return address tst.b R5 ; end of string? jz ps5 ; YES - break the loop
; put here instructions for character processing
jmp ps4 ; loop back
ps5: bit.w #1, 0(SP) ; move to the next even address jz ps6 ; if needed add.w R6, 0(SP) ; even alignment of the return address ps6: ret
Эти примеры можно легко адаптировать на случай если нужно сохранять рабочие регистры (или другие данные) в стеке при работе функции или если происходит работа с 20-битными адресами в архитектуре MSP430X.
При выводе текстовых строк на дисплей очень часто многие строки используются лишь один раз в программе (например при выводе строк меню). Было-бы очень удобно написать как в языках высокого уровня что-то типа PrintString(“Hello World!”), где выводимая строка и место в программе где она выводятся (вызов функции вывода) расположены рядом в программе. Этого можно добиться на ассемблере, поместив выводимую строку сразу после вызова функции PrintString. Идея эта не новая, но я не видел ее имплементации в микроконтроллерах, поэтому привожу свою имплементацию на примере архитектуры MSP430. Итак, мы будем выводить строку следующим образом:
Код:
call #PrintString ; output procedure call DB "Hello World!" ; trailing zero is added EVEN clr.w R5 ; next instruction in code
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Вот еще трюк для деления 4-значного десятичного числа (т.е. не превышающего 9999) на 10. Для этого его надо просто умножить на "магическую константу" 6554 и отбросить младшие 16 бит результата. Иными словами, справедливо равенство
[х / 10] = (x * 6554) >> 16
где [x/10] - целая часть от деления. Основанием для этого является школьная формула для суммы геометрической прогрессии степеней 1/4:
1 - 1/4 + 1/16 - 1/64 + ... = 1/(1 + 1/4) = 4/5
Если поделить обе части на 8, получим
1/8 - 1/32 + 1/128 - ... = 1/10
Таким образом, x/10 = х/8 - х/32 + x/128 - ... и после имножения обеих частей на 2^16, окончательно получим
Вопрос сколько членов (бесконечного) знакочередующегося ряда следует взять для достижения требуемой точности? Оказалось, что при х < 16384 достаточно взять указанные выше 7 членов ряда, которые в сумме дают то самое число 6554. Реализация этого способа особенно эффективна на МК с аппаратным 16х16 перемножителем, например MSP430 или PIC24. Для первого из них, например, программа деления числа в R4 занимает 4 операции и выполняется за 11 циклов:
Для чисел превосходящих 16384 начинает накапливаться погрешность и результат оказывается неточным. Однако, положение можно легко исправить добавлением еще двух членов ряда и умножением результата на 2^20. Магическая константа в этом случае будет иной:
[x / 10] = (x * 104858) >> 20
Эта формула справедлива для любых 16-битных чисел. Однако, для ее эффективной реализации желательно иметь 32х32 аппаратный перемножитель, поскольку магическая константа 17-битная и результат умножения может быть максимум 33-битным. Опять-же для архитектуры MSP430X с аппаратным перемножителем MPY32 программа будет аналогична предыдущей.
Этот алгоритм был применен в программе вычисления цифр у чисел с 4 десятичными знаками. Полный текст программы ниже. По сравнению со стандартным алгоритмом, основанным на схеме Хорнера, этот чуть медленнее (около 100 циклов против 81). Однако, мне нужно было иметь каждую цифру в отдельности, а не BCD представление, получаемое в схеме Хорнера, чтобы потом не вырезать из него цифры. В процессе алгоритма еще вычисляется и остаток от деления на 10, но это пока не так красиво и основано на специальной таблице, поэтому от комментов я здесь воздержусь.
Спойлер
Код:
mov.b #4, R7 ; digit counter clr.b R8 ; pointer correction for cmp.w #8192, R4 ; the 2nd part of table jl d2b ; it is needed for numbers mov.b #16, R8 ; exceeding 8192
"Магические" константы, по мне, получаются гораздо проще X/10=X*K/65536 или K=65536/10=6553,6~6554. Для 20-разрядного числа X/10=X*L/2^20 или L=1048576/10=104857,6~104858. Перед отбрасыванием младшего слова значение старшего бита этого слова прибавить к результату. Получается правильное математическое округление.
Код:
;результат умножения в R16...R19 в формате старший-младший CLR ZH LSL R18 ; старший бит, имеющий вес 0.5 в С-флаг ADC R17,ZH ADC R16,ZH
Так, мелочь, даже на эту тему не тянет. Но я предпочитаю в качестве рабочей пары регистров использовать не R16, R17, а R24, R25 (которые обзываю WL, WH соответственно). С этой парой работают adiw/sbiw и вышеприведенный фрагмент будет таким (R22..R25 -- младший-старший).
Код:
SBRC R23, 7 ADIW R24, 1
Понятно, что при этом я держу младший байт данных в регистре с меньшим номером, но это и более естественно для AVR, и совместимо с С-компиляторами.
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Заголовок сообщения: Re: Хитрые, необычные алгоритмы и код
Добавлено: Чт окт 11, 2012 09:29:44
Друг Кота
Карма: 67
Рейтинг сообщений: 1060
Зарегистрирован: Чт сен 18, 2008 12:27:21 Сообщений: 19635 Откуда: Столица Мира Санкт-Петербург
Рейтинг сообщения:0 Медали: 1
AVR. Задержка на 2 такта одним машинным словом:
Код:
rjmp PC+1;
вместо двух слов
Код:
nop; nop;
_________________ [ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ] Измерить нннада?
Именно этот вариант показан в регулируемой задержке в этой же теме. Там же для однословной 3-тактовой задержки в случае наличия регистра, который можно испортить, использован LPM. Ещё когда-то обсуждалось, что в прерывани Z при этом можно не сохранять В регулируемой задержке уже есть всё равно испорченный регистр, поэтому LPM R, Z. Если регистр R0 свободный в этом месте кода, то можно просто LPM.
Также 2-тактовую задержку сделает
Код:
adiw R24, 0 ; или другая пара
но при наличии rjmp . использовать как задержку смысла нет, зато как однословную проверку регистровой пары на 0 - запросто.
_________________ Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Опять извечная проблема: преобразование 16-битного беззнакового целого числа в строку (BCD).
Приводимый ниже рекордно быстрый алгоритм годится только для архитектуры MSP430X. В ней имеется таймер реального времени RTC_B, в состав которого входит регистр BIN2BCD. Странно, что в документации slau272a и slau208k на семействa F5xx/F6xx и FR57xx, соответственно, об этом регистре нет ни слова в тексте. Есть лишь краткое упоминание этого регистра в составе регистров RTC, где ему посвящено лишь одно предложение, поэтому его очень легко не заметить. Согласно ДШ, в этот регистр следует сначала записать 12-битное число, а при чтении из него получите 16-битное BCD этого числа. Проще не бывает. Вот пример преобразования 12-битного числа в R4 в BCD в R5. Процедура занимает всего 6 циклов !!!
Код:
mov.w R4, &BIN2BCD mov.w &BIN2BCD, R5
Почему в документации упомянуты лишь 12-битные исходные числа? Ведь максимальное 12-битное число - это 4095, а четырех BCD знаков в 16-битном регистре достаточно для представления чисел вплоть до 9999. Это для меня осталось загадкой. Я попробовал переводить числа вплоть до 9999 - все действительно работает! Более того, оказалось, что при записи в этот регистр чисел от 10000 до 16383 (т.е. 14-битных) в регистре остается лишь последние 4 цифры BCD, а для чисел от 16384 до 65535 процессор оставляет лишь 14 младших бит числа и переводит полученный результат в BCD. Отмечу, что работа этого регистра не эмулируется в симуляторе IAR, по крайней мере я не знаю как его заставить. Однако, посмотреть регистр в работе можно с помощью внутрисхемного отладчика. Я проверял его на процессоре MSP430FR5728.
На основе этого регистра можно сделать быструю процедуру перевода 16-битных беззнаковых целых в R4 в BCD в R5. Процедура использует расширенный набор инструкций MSP430X для работы с 20-битными регистрами и занимает лишь 19 циклов процессора!!!
Работает этот код так: исходное 16-битное число N в 20-битном регистре R4 сдвигается циклически на 3 бита вправо через бит С. При этом в 16 младших битах R4 получается N/8, которое не превосходит 8191, т.е. может быть непосредственно переведено в BCD двумя следующими инструкциями. Эти инструкции не изменяют бит С, который все еще содержит бит 2 числа N, а его биты с номером 1 и 0 после сдвига находятся соответственно в битах 19 и 18 регистра R4. После этого выполняются 3 операции 20-битной десятичной арифметики сложения R5 с самим собой и битом С. Фактически, производятся 3 итерации схемы Хорнера. В результате 5 символов BCD представления получаются в 20-битном регистре R5. Если нужно восстановить исходное число в R4 после работы алгоритма, в конец кода следует добавить инструкцию "rlax.a R4".
Отмечу, что в состав RTC_B также входит регистр BCD2BIN для преобразования BCD в двоичный код, но о нем как-нибудь в другой раз.
Еще одна извечная проблема: реализация деления 32-битных чисел с плавающей точкой, представленных в формате IEEE-754 (С-тип float). Цель – скорость. Архитектура – 16-битные МК семейства RL78 фирмы Renesas.
Выше я описал алгоритм быстрого обращения числа, который имеет смысл для процессоров, не имеющих аппаратной поддержки деления. В противном случае есть шанс выполнить операцию быстрее. Семейство RL78 оснащено аппаратным 32/32 делителем целых чисел, вычисляющим для 32-битных чисел 32-битную целую часть и 32-битный остаток от деления за 16 циклов. Этот делитель оформлен как периферийный модуль, независимый от процессора. “Хитрость” алгоритма в том, что пока делитель занят вычислением результата, процессор одновременно можно загрузить еще чем-то полезным для алгоритма. Таким образом получится что-то типа параллельных вычислений. Приводимый алгоритм использует 3 операции аппаратного деления и гарантированно работает только для нормализованных чисел и вычисляет частное за 97 - 110 циклов в зависимости от данных.
Напомню, что в формате IEEE-754 число с плавающей точкой представленно 23-битной мантиссой и 8-битным порядком. Для вычисления частного нужно грубо говоря вычесть порядки и поделить мантиссы. Грубо – потому что для результата меньшего 1 потребуется еще нормализация результата, а к разности порядков нужно добавить bias в соответствии со стандартом. Кроме того, следует учесть возможное переполнение порядка и специальные случаи представления чисел типа 0 или бесконечности. Приведенная ниже программа делит число с символьным именем dev, расположенное в RAM (точнее в ее области, соответствующей saddrp в терминологии адресации RL78), на число под именен der, расположенное там-же. Результат помещается в RAM под именем res.
Перед делением из чисел вырезаются мантиссы и дополняются целой частью, которая для нормализованных чисел всегда равна 1. Фактически, принудительно ставится единица в бите 23 мантиссы каждого из чисел. Полученные мантиссы делимого и делителя сравниваются и если первая не меньше второй, то делимое сдвигается на 7 бит влево. В противном случае оно сдвигается на 8 бит влево (в этом случае из порядка результата следует вычесть 1). Затем полученное 31- или 32-битное делимое делится на 24-битный делитель аппаратным делителем МК. Таким образом, получаем старшие 8 бит мантиссы окончательного результата, включая целую часть. Нетрундо видеть, что старший из полученных 8 бит всегда равен 1. Этот бит равен целой части нормализованного результата, при условии что нет проблем с порядком, например переполнения. На втором шаге остаток от деления (получаемый в 16-битных регистрах MDCH:MDCL) сдвигается на 8 бит влево и загружается в 16-битные регистры делимого (MDAH:MDAL). Сдвиг производится за 11 циклов путем пересылки байтов. Содержимое регистров делителя (MDBH:MDBL) при этом не меняется. После второго деления получаем следуюшие 8 бит результата. Затем опять сдвигаем остаток на 8 бит влево и производим третье деление на делитель, в результате чего получаются последние 8 (из 24) бит мантиссы результата.
Пока производится первое деление мантисс, процессор вычисляет порядок результата. Это занимает 10 циклов из 16 необходимых для деления. В программе ниже я не привел обработку специальных случаев переполнения или “недополнения” (underflow) порядков, при необходимости это легко сделать по усмотрению пользователя. Во время второго деления процессор помещает результат первого деления в регистры результата res, а также вычисляет и помещает туда-же знак частного. Это тоже занимает 10 циклов из 16. Во время третьего деления процессор помещает в регистры res вычисленные ранее вторые 8 бит результата. Это также занимает менее 16 циклов, требуемых для деления, так что остается достаточно времени на обработку специальных случаев (в приводимой тестовой программе эта обработка отсутствует).
В алгоритме имеется резерв по точности. Я имею в виду, что после 3 делений можно получить до 27 бит результата (точнее 26 или 27 в зависимости от нормализации на первом шаге). Дополнительные 2-3 бита можно использовать, например, для повышения точности или округления 24-битной мантиссы. Для этого перед вторым и третьим делениями мантиссу делимого следует сдвигать на 9 бит влево а не на 8 (при этом ее бит целой части попадет в бит 31 регистра делимого). В этом случае после второго и третьего делений будем получать по 9 бит результата (а не по 8 как ранее).
Отмечу, что в имеющейся на момет написания версии системы IAR Kickstart отсутствует эмуляция аппаратного делителя/перемножителя в симуляторе. Отладка алгоритма производилась живьем (внутрисхемно) на МК R5F1006A подсемейства RL78G13. Кроме того, для этого процессора в составе его регистров в IAR отсутствуют MDAH и MDAL (?!) Для подсемейства R78G14 имеется шанс по небольшому ускорению алгоритма за счет того, что регистры данных перемножителя/делителя совмещены с регистрами общего назначения МК.
cmp A, B bh sh7 ; dividend > divisor bnz sh8 ; dividend < divisor movw BC, AX ; preserve AX movw AX, dev ; MSB(dividend) = MSB(divider) cmpw AX, der movw AX, BC ; restore AX bh sh8 ; dividend > divisor bnz sh7 ; dividend < divisor
sh8: movw MDAH, AX ; shift dividend 8 bits left mov A, dev ; compose LSW(dividend) clrb X dec H ; adjust the bias br ft0
sh7: shrw AX, 1 ; shift dividend 7 bits left movw MDAH, AX ; load dividend MSW mov A, dev clrb X rorc A, 1 xch A, X mov1 A.7, CY xch A, X ft0: movw MDAL, AX ; load dividend MSW
;---------- set1 MDUC.0 ; first division movw BC, der+2 ; compute the order shlw BC, 1 ; B = divisor order (biased) movw AX, dev+2 shlw AX, 1 ; A = dividend order (biased) sub A, B ; subtract the orders add A, H ; add the bias clrb X shrw AX, 1 movw res+2, AX ; order
ft1: mov A, MDUC bt A.0, ft1 ; wait for completion
movw AX, MDAL ; get 8 bits of result movw BC, AX ; save it temporarily
movw AX, MDCL ; dividend = remainder shifted movw tmp, AX movw AX, MDCH xch A, X mov X, tmp+1 movw MDAH, AX ; new devidend MSW mov A, tmp clrb X movw MDAL, AX ; new devidend LSW
;---------- set1 MDUC.0 ; second division movw AX, BC ; get back the first quotient xch A, X ; A = result bits clr1 A.7 ; remove hidden 1 or A, res+2 mov res+2, A ; first byte of result + order
mov A, der+3 ; compose the sign xor A, dev+3 ; msb(A) = sign bit and A, #0x80 or A, res+3 mov res+3, A ; sign + order
ft2: mov A, MDUC bt A.0, ft2 ; wait for completion
movw AX, MDAL ; get 8 bits of result movw BC, AX
movw AX, MDCL ; dividend = remainder shifted movw tmp, AX movw AX, MDCH xch A, X mov X, tmp+1 movw MDAH, AX ; new devidend MSW mov A, tmp clrb X movw MDAL, AX ; new devidend LSW
;---------- set1 MDUC.0 ; third division movw AX, BC ; get back the second quotient xch A, X mov res+1, A ; second byte of result ft3: mov A, MDUC bt A.0, ft3 ; wait for completion
movw AX, MDAL ; get 8 bits of result xch A, X mov res, A ; third byte of result nop END
К вопросу о хитрых алгоритмах. Полезная книга: Генри Уоррен "Алгоритмические трюки для программистов" (оригинальное название "Hacker's Delight" ). Есть сайт посвящённый этой книге - http://www.hackersdelight.org/ (Google-Перевод) СпойлерКнига в Сундуке Кота
Язык программирования Си, препроцессор, макросы, 8-bit AVR
Далеко не все в должной степени уделяют внимание препроцессору. И потом появляются обсуждения подобные этому. Всё бы ничего, но утверждение, что для работы с ножками МК нужно три define-а, IMHO, показывает недостаточное знание темы. Никого ругать и переубеждать не собираюсь. Как говориться "на вкус и цвет...".
Я взял из своих исходников некоторые макросы. "Причесал" их и собрал в один файл. (Надеюсь нигде не ошибся.) Возможно они не идеальны , но работают. Примеры в комментариях и под спойлером. Спойлер
Код:
#include <avr/io.h>
#include "avr_io_macros.h"
#define LED1 B, 1 #define LED2 B, 2
void main() { char a;
PIN_INIT_AS_INPUT(LED1); PIN_PULLUP_ON(LED1); a = PIN_GET(LED1); PIN_PULLUP_OFF(LED1);
Сама по себе тема макросов банальна. Поэтому прошу - хватит с макросами для ассемблера и Си (достаточно примеров от Чана и этих). Кому интересно посмотрит, почитает документацию - разберётся.
_________________ Когда уже ничего не помогает - прочтите, наконец, инструкцию. Лучший оптимизатор находится у вас между ушей. (Майкл Абраш, программист Quake и QuakeII) Избыток информации ведёт к оскудению души - Леонтьев А. (сказано в 1965 г.)
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 18
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения