Продолжаю свои неуверенные попытки разобраться с ASM. Почему не С, не спрашивайте. Вразумительного ответа кроме - я мазохист, у меня нет. То, что математика на ASM это полный мрак догадывался и раньше, но теперь столкнулся с этим в полный рост. Возникла необходимость извлечь квадратный корень. При этом аргумент не помещается ни 8 ни в 16 бит. Длительные поиски в инете готовых решений (не у меня-же первого возникла такая необходимость) выдали вот это Спойлер: Код Код (C++): ;******************************************************************************* ********************* ;* ;* Подпрограмма вычисления квадратного корня для 24-ти битного аргумента * ;* (c) 1998 Alexander Trush http://trush.da.ru trush@ropnet.ru 2:5020/392.40 ;* ; Вы можете свободно использовать, распространять, модифицировать этот ; код до тех пор, пока вы указываете моё авторство и распространяете ; это требование. Кроме того, я хотел бы получать уведомление о ; применении этого кода по e-mail (trush@kbotd.ru). sqrt24: ldi mask,1; Используем маску в регистре, т.к. команды EORI нет... ldi count,12; Последний бит значения корня не требует специального ; подхода - есть запас от потери битов аж 4 бита clr16 work_h, work_l clr16 dist_h, dist_l; Очистка накопителя значения корня sqrt24_1: cpi src_h, 64 cpc work_l, dist_l cpc work_h, dist_h brcs sqrt24_2 subi src_h, 64 sbc work_l, dist_l sbc work_h, dist_h sqrt24_2: rol dist_l rol dist_h eor dist_l, mask; Инверсия младшего бита значания корня, ; т.к. при заёме должны писать 0, и 1 при отсутствии lsl src_l; Сдвиг аргумента на 2 бита rol src_m; work_h:work_l:src_h:src_m:src_l <<= 2 rol src_h rol work_l rol work_h lsl src_l rol src_m rol src_h rol work_l rol work_h dec count; Получили ли все биты значения корня кроме последнего?.. brne sqrt24_1 cp dist_l, work_l cpc dist_h, work_h adc dist_l, src_m; Команды ADCI нет, а src_m точно здесь равен 0 adc dist_h, src_m ret ;****************************************************************************** В логику нахождения корня пока даже не пытаюсь вникнуть. Прошу помочь разобраться с задействованными регистрами. Всего подпрограмма использует 9 регистров. mask - маска для eor (присваивается в программе) count - счётчик work_h, work_l - рабочая пара для вычислений dist_h, dist_l - пара регистров в которой будет результат src_h, src_m, src_l - 3 байта аргумента Я правильно разобрался? И возникают сомнения команды Код (C++): clr16 work_h, work_l clr16 dist_h, dist_l; Как я понял это очистка регистровой пары. Но что-то в справочнике по ASM AVR я таких не видел. Оно на Atmega8 работать будет?
Несколько лет осваивал Unix, уже писал программы на Си, RSX-11M и фортран - не в счет, RSX-11M ушел на покой. Начал освоение Си под MS-DOS, и тут меня перевели из отдела прикладного программирования в отдел системного программирования... Первое, что начальник отдела дал из заданий - написать калькулятор на ассемблере под MS-DOS. Как хочешь. Графика, псевдографика но чтобы можно было пользоваться под MS-DOS. Как это было сложно после Си. Потом освоился. Потом смог часть простых программ уже писать в отладчике на асме и сохранить в файл с расширением .com Без компиляторов. Потом 80386, 80486, SX, DX, Pentium. И везде есть отличия в ассемблерных командах. А Си не изменился!!! Причем нисколько! В С++ появились дополнительные возможности, но блин Си который начал осваивать в 1987, работает до сих пор!!! Да и UNIX, который был 1987, тот же Linux сейчас. Порой сидишь, тупишь а из памяти всплывают и команды и параметры. Может Си? ИМХО: Асм нужен только профессионалам, работающих под конкретный проц, и пишущих нижний уровень. Я писал на Асме библиотеки для Си под свою ОС.
В свое время году в 2004, на AtMega8 делал устройство для выбора оптимальной фазы из трех входящих в дом. Тоже возник вопрос вычисления квадратного корня без float. Вот функция из того проекта: Код (C++): unsigned sqrt_cpu2(long l) { long rslt_2; long div_2; rslt_2 = l; div_2 = l; if (l <= 0) return 0; while (1) { div_2 = (l / div_2 + div_2) / 2; if (rslt_2 > div_2) rslt_2 = div_2; else return (unsigned) rslt_2; } return (unsigned)rslt_2; }
Вместо Код (C++): clr16 work_h clr16 work_l писать Код (C++): clr16 work_h, work_l Не вижу выигрыша от применения макроса. Это-же для С. Или его как-то можно в ASM применить? Как я уже писал - я не профессионал. Это хобби. Пишу для собственного удовольствия. На данном этапе мне интересен ассемблер. Пробую в нём разобраться. Сроки у меня не горят. Вот и пробую то, что мог на Arduino написать менее чем за день, изобразить в кодах ассемблера.
Напрасно. Очень часто макросом (инлайн или constexpr функцией) заменяют даже одну единственную команду. А выигрыш, ну прикинь, что лучше для дальнейшего сопровождения программы, запись Код (C++): PORTB |= (1 << 4); или запись Код (C++): turmScreenLightON(); Если не видишь разницы, то ... со временем увидишь
Кто-бы спорил, я не буду. Как говорится - великое видится на расстоянии. И да, код из первого поста проверил в симуляторе. Всё работает. В регистры src_h, src_m, src_l кидаешь 24 бита аргумента и в регистры dist_h, dist_l выпадает целая часть корня квадратного. При этом дробная часть не просто отбрасывается а происходит округление по всем правилам математики. Может кому сгодится.
Если я правильно разобрался с симулятором то получается следующее Загнал FF FF FF - получил 289 тактов Загнал 3 миллиона 2D C6 C0 - получил 271 такт Отбросил старший байт 00 C6 C0 - получил 260 тактов. Количество тактов практически не зависит от размера аргумента.
Там есть counter=12. Пока он не дойдет до 0 из цикла не выйдем. Цикл занимает большую часть кода. Мне показалась задачка интересной, но увы, я не выдержал и 3ех кругов. В экселе завел все переменные в столбцы и начал шагать по тактам. С асмом плохо знаком, поэтому приходилось каждый раз читать про команду, следить за С...не, не запрыгну на коня. Лучшее что я нашел по описаниям команд это евстисфеев. На гавру описания команд те же, что и у автора, но куцообрезанные. Да и по этим трем тактам я не понял что за маневр прибавлять 64 к старшему байту, а потом выдвигание его в край, это добавило отчаяния в разбор кода... В вашей симуляции можно по шагам пройтись? Если да, то проделайте это и соберите все результаты за каждый такт и проанализируйте уже готовое.
Вот поэтому я говорю, что Асм штука интересная, но для профессионалов, которые пишут под один МК или проц долгое время. Для любителей, склонных менять МК, слишком нецелесообразно каждый раз, к архитектуре МК и его управляющим регистрам еще и разбираться с Асмом, когда есть универсальный Си. Но это ИМХО.
Тут не всё так просто. Существует несколько разных методов. Половинного деление, метод Ньютона, преобразования прямоугольника в квадрат и.т.д. Прямого метода расчёта нет, все методы основаны на последовательных итерациях до заданной точности. Учитывая отсутствие аппаратного делителя их самостоятельная реализация для ASM, да ещё для 2х и более байтных чисел для начинающего неподъёмная задача.
Конечно, я порыскал в гугле, но тут влезает штука с битом переполнения (бит С), которая и сбила меня. За час не смог разобраться, больше уделить времени не могу. институтские задачки я щелкал, а тут, признаюсь, не мое
Я может что-то не понимаю или ещё чего, но строчка Код (C++): b = sqrt(a); вычисляет квадратный корень из 3000000 за 15 тактов на 328PB. Это в симуляторе по таймеру без прескалера))
Вот об этом я и говорил. Не, я на ассемблер, причем незнакомый, только тогда когда без этого уже не обойтись. А так - только Си. А свободное время лучше на что-то полезное потратить.