Pull to refresh

Comments 78

Вася не может, так как занят — ему надо дождаться когда Стив закончит отжимание что бы принимать следующие указания для перевода. А это будет ооочень не скоро, так как Стив из неторопливых контроллеров (зря что ли англичанин?).
Вася не отладчик, Вася дал задание и свободен. А когда и при каких условиях(конфигурация контроллера) Стив будет выполнять это задание — его это не касается. Безупречно переведенная команда может быть и не выполнена вовсе из-за неудачной конфигурации контроллера — забыли отключить сторожевую собаку, так Стив может и не дойти до форточки так как выполнение программы сбросится на начало, и он будет до смерти подыматься со стула и делать пару шагов.
С Васей дружить достаточно просто, но сначала нужно подружиться со Стивом. Причём даже скорее не подружиться — а узнать о его возможностях — чтобы заведомо не просить Васю заставить Стива сделать то, чего Стив сделать не сможет физически. Например следовало бы знать, что Стив физически не сможет отжаться такое количество раз — тогда бы мы не тратили время Васи и Стива на такую ерунду, а могли бы напрячь Стива чем нить более полезным.
Боюсь такими темпами вы объясните (и поймете сами) все об ассемблере лет за десять.

Я вас сразу предупреждаю, что изучение ассемблера сопровождается ломкой психики и вы уже не сможете просто программировать на более высокоуровневых языках.

Вот я излагал свой опыт habrahabr.ru/qa/43668/ и никто мне не помог, а товарищ DIHALT гнусно предложил попробовать более тяжелые вещества. :-)
Ломка психики налицо — но это скорее плюс, чем минус. Дело в том, что мыслить ассемблерно довольно весело. Скажем можно ассемблизировать стройку — девеча помогал товарищу делать гараж — мы дружной толпой собрались у него — нужно было обшить стены профлистом — а на крыше пробить плёнку влагозащитную и тоже прикрутить профлист. Я просто изначально потратил 20 минут, чтобы просчитать заранее пошагово как лучше и быстрее выполнить эту работу — в результате гараж разбился на 3 части — Стены, Крыша и пульт управления. На крыше было 2 человека, на стенах 2 человека. Я был одним из тех, кто был на стенах, плюс я ещё как бы был на пульте. Ещё была куча инструмента и материала. Так же было 2 зоны по высотности — крыша и пол — и на крыше был как бы повышеный приоритет. Работали следующим образом — с крыши поступали команды именно на выполнение — то есть нельзя было просто взять и сказать кому то из тех двоих — «Давайте лист» -а потом второй кричал — «Нет, подождите, я тут ещё не закрутил саморез!» — Было договорено, что с крыши кричат только тогда, когда они готовы принять материал, или когда что-то срочно нужно. На стенах было то же самое — только мы решали вдвоём что нам нужно и потом шли это брать в такую «зону данных» место где лежал материал и инструменты. Если мы несли лист и нам кричали, что им тоже нужен — то мы доносили свой, потом несли лист им, потом возвращались к своим действиям. Вроде всё просто — но суть в том, что я заранее планировал всё на несколько действий вперёд- в результате мы закончили гараж за пару часов. И такой способ мышления в обычных, бытовых делах очень здорово помогает в жизни. Во всяком случае он однозначно помогает избавиться от таких гадостей как рассеяность, забывчивость и склонность к беспорядку. По сути программирование — это планирование. Планирование в зависимости от происходящего. И ассемблер даёт это понять как ничто другое. Да — это можно сказать небольшое сумасшествие, но оно полезное.
Не знаю может быть у меня неправильная психика, а может быть всё было слишком рано в детстве.
Первым языком был фортран, а первый компилятор ассемблера я написал сам для Z80 в месте с текстовым редактором имея на руках только таблицу опкодов…
Пользоваться PHP это не разу не мешает, более того я не гнушаюсь рубинами и питоном хотя для решения повседневных задач руки тянутся к ружью С\С++.
Более того, языками высокого уровня я как правило пользуюсь с ещё большим цинизмом чем те кто ничего кроме них не знает…
Это ты просто осознаешь тот цинизм с которым тебе приходится это делать… а ассемблер небось забыл уже за давностью лет, поэтому он тебе и не мешает.
Да, есть еще такая штука как контекст, люди умеют держать некоторые вещи в пределах определенного контекста, поэтому программируя на ЯВУ ассемблер им не мешает — он запечатан наглухо в другом контексте, который где-то далеко на складах мозга. А печальное зрелище как раз у тех кто пытается все языки скинуть в один контекст, типа это все одно и то же только разные наречия, оправдываясь тем что алгоритм он на любом языке алгоритм но это не так — каждый язык несет свою специфику.
Не-не-не, под цинизмом я понимаю джедайство как способность строкой единой выразить то, что у нуба на мониторе не поместится.
И наоборот, так называемая оптимизация в связке PHP-SQL получается почти-что рефлекторно, и удобоваримость кода не страдает.
ИМХО всё дело в то что я железячник, чего и всем желаю, это право не сложно и всегда полезно размять мозги.
Дело же не в железе или ассемблере, дело в кругозоре который ещё никому не повредил.
Хотите поломать психику попрограммируйте ПЛИС или ПЛК на LD. Вот там мозги рвет хорошо. Представь, что у вас вдруг в программе все строки начали выполнятся одновременно.
Что то вроде SMP. А вообще для тех кто собирал приемники в радиокружке или просто увлекался радиотехникой это не порвет мозг. :-)
Ладдер — это да… Как говорится, попробуй из букв «у», «й» и «х» сложить слово «вечность» :)))
Да не, многие вещи на нем делаются не включая мозг ваще. А главное это работает надежно и без ошибок. Я так LD вообще люблю :)
Я его тоже люблю. Но некоторые вещи предпочитаю делать в STL. Несмотря на то, что STL — write only. Даже свои исходники, даже с комментариями. А ещё шеф мне запретил пользоваться командой TAK: это, говорит, ты такой умный. А не местный техник, которому, не дай Б-г, через год-другой зачем-нибудь понадобится что-то в программе изменить. Сперва он, вообще, спросил — что это за недокументированная команда такая

А я просто делал небольшие вычисления и решил перебросить местами аккумуляторы, чтоб вычисление упростить…
Да, по поводу скорости — я конечно продвинулся немного дальше, чем описано в статье=) Дело в том, что я считаю так: если что-то объясняешь — то нужно быть уверенным, что каждое твоё слово понято, причём понято правильно. Поэтому я стараюсь начинать с совсем уж детсадовских объяснений — чтобы основная база знаний была. Чтобы потом можно было не задумываясь говорить о каких то вещах из предыдущих статей, которые читающий понимал бы как само собой разумеющееся.
Это все хорошо… но не стоит сильно перегибать, потом эти понятия у людей останутся в мозгу на очень долго. Как бы чего не вышло потом, не ограничило бы потолок их развития т.к. с некоторого уровня нужны будут усилия чтобы напрочь выветрить эти понятия из головы иначе «да здравствует вечный детсад».
Может это, приписку в конце дописать что дескать не стоит запоминать эти аналогии… Поняли — хорошо, через час забыли и выбросили из головы. И чтобы их лишний раз не закреплять, в следующей статье надо брать совсем другие персонажи/аналогии.
Сходил по ссылке на ваш предыдущий опыт. Разрешите уточнить диагноз?
Вы начали с ассемблера PDP-11, он очень прост, логичен, и поэтому способен влюбить в себя. Так что заболевание ваше имеет изначально любовную природу.
Нас в свое время в институте учили программировать процессор машины ДВК-2 (система команд там тоже PDP-11) вообще в кодах, потом мы перешли к мнемоникам, а только в конце третьего курса получили в руки Си (Turbo C 2.0, уже на 386-х). И все справлялись, даже студенты, в обычной жизни далекие от компьютера (кафедра наша называлась «Кафедра автоматики», занимались мы тем, что сейчас называется встроенными системами).
С тех пор остался полезный навык — хотя бы примерно представлять, во что транслируется высокоуровневый код, и как он исполняется процессором.
я прошел во многом похожий путь, а потому осмелюсь сказать что этот навык полезен не всем, и не всегда.
Мне большого труда стоило научиться доверять оптимизатору.
и если тебе надо вычислить:
x = (a+b) * (a-b)
y = (a+b) / (a-b)
то нужно так и писать, а не заводить переменные сначала под a+b, потом a-b и т.п.
Это уже следующий уровень, близкий к тому, чтобы стать настоящим джедаем.
Я же имел в виду избежание классической ошибки совсем юных падаванов, которые пишут (я утрирую, конечно) что-то вроде «temp = x; x = y; y = temp;», не задумываясь о типах и размерах этих самых x и y. Особенно пикантно, если так пишут на каком-нибудь perl-е или фортране, где размер x, y и temp может быть произвольным.
Вот про идеальные знания Васи вы приукрасили. Ассемблеров целый зоопарк, под каждое семейство микроконтроллеров свои версии.
Ну тут я думаю прокатит аналогия с тем, что англичанин — это микроконтроллер AVR, а Вася круто знает английский. Скажем какое то другое семейство контроллеров это Японец, а ассемблер Игнат круто знает японский.
Опять же про зоопарк я буду писать дальше, и с каждой статьёй будет больше и больше нюансов — сейчас мне просто важно, чтобы самый главный принцип был понятен.
По моему вы все только запутали. Ассемблер очень легко понимается если начать изучать его до какого-либо другого языка программирования. А если после, то надо четко себе уяснить, что на самом деле нет никакого языка программирования Ассемблер. Это не язык. Это лишь более удобное обозначение команд процессора. А значит все тлен и безысходность.

У вас нет ни переменных, ни типов, ни операторов, ни функций. Ничего нет. И бесполезно пытаться это там искать.

Что есть? А есть только куча ячеек и набор команд которые с этими ячейками что то делают. Причем команда MOV A,B и MOV A,C это две ПРИНЦИПИАЛЬНО разные команды. Т.к. одна копирует из В в А, а другая из С в А и это принципиально разные вещи :) А раз так, то команды MOV A, D вполне может и не быть хотя казалось бы…

А все эти типы, переменные, функции начинают обитать в вашем воспаленном сознании.

Ну и еще надо различать директивы компилятора и команды. Что первое это приказ о том как правильно размещать и замещать код, а второе собственно программа. Не более того.
И еще важный принцип ассемблера в том, что «по аналогии» тут по умолчанию не работает, на чем все и спотыкаются. Если в С, например, мы можем написать prinf(«One»);, то по аналогии будет работать и printf(«Two»); и это интуитивно понятно. То в ассемблере это не работает. В предыдущем комменте пример с MOV это наглядно показывает.
Мне кажется, вы как это переусложнили ситуацию, или я вас не понимаю — уж слишком мало работал с ассемблером.
MOV [Y],[X] копирует из X в Y. Не более того. Почему «MOV A,D» может не существовать? Если укажете несуществующий адрес в памяти или несуществующий регистр, то конечно, но в целом то аналогия сохраняется. Мы же не сможем вызвать printf(abc), если переменной «abc» не существует, но это же не означает, что «команда» перестала существовать?
да не. все проще. Во-первых не надо лишних скобок. В ассемблере это обычно указывает на косвенную адрессацию: т.е. X — это регистр X, а [X] это память по адресу хранящемуся в X.
А вот «Почему «MOV A,D» может не существовать?» — вот может не существовать и все тут. Непочему. Может кодов команд не хватило для такой команды, может это архитектурно неудобно. Но это всего-лишь будет ознчать что вам надо будет сделать что-то вроде
MOV Z,D
MOV A,Z
ну вот тут и есть коренное различие между ассемблером и прочими языками. Нет команды «MOV». Есть команда «MOV Z,D». Есть команда «MOV A,Z». А команды «MOV» — нет. 'MOV' — это просто удобное слово, которым мы можем обозначить множество команд.
На самом деле, если быть честным это не совсем так.
Дело в том, что опкоды как правило берутся далеко не от балды, и в них есть своя логика, первые несколько бит собственно опкод, последующие — аргументы.
Но с тем же успехом ее может и не быть и команды могут быть набраны кодами от балды, с точки зрения компилятора ничего не поменяется.
В теории конечно могут быть, но на практике камни с опкодами от балды мне не попадались.
Полагаю что это как-то связано со схемотехникой оных. Бывает что недокументированный опкод просто не выполняется, бывает что выполняется иначе чем предполагалось логикой.

Не, я прекрасно понимаю о чём выше идёт сыр бор, но истина дороже…
… а вообще было бы правильно сказать что ассемблер не язык, но способ представления опкодов в более удобной для понимания человеком форме…
Именно со схемотехникой и связаны. Так просто проще.
MOV EAX, EDX — это просто чуть более понятная человеку запись числа 0x89D0 (возмем, для определенности, Intel syntax для x86-64), ни больше, ни меньше, и сама по себе запись ничего никуда не копирует. И если для вышеуказанной команды опкод существует — она будет работать, а если нет — не будет, хотя и построена по аналогии. Мне никто не мешает написать mov eax, DWORD PTR CS:0x0, которое будет транслированно в 0х2E8B042500000000, но команду mov DWORD PTR CS:0x0, DWORD PTR DS:0x0, построенную «по аналогии» транслировать уже не во что — нет такого опкода.
Вы, скорее всего, не работали с ассемблерами для простых процессоров с минимальным числом опкодов. Это у x86 есть опкоды почти на все случаи жизни, а у МК бывает так, что mov работает только между этими несколькими регистрами, операции с отдельными битами — вообще с одним единственным регистром, а сохранность содержимого вот этого регистра вам никто не гарантирует. И таких ограничений бывает столько и таких забавных, что лучшый способ не погрязнуть в них — не строить вообще никаких аналогий, как Ди и предлагает.
Ага, особенно тут доставляет какой-нибудь PIC12. Микрочип еще тут цинично издевается в своей рекламе «Простой процессор, всего 35 команд!»
Потому, что не все системы команд ортогональны.
В ассемблере AVR есть такие странности, например команда работающая с битами регистров работает только с одной половиной регистров т.к. не хватило в КОПе лишнего бита под адрес регистра.
и нет команды ADDI (добавление константы к регистру) — почему — не понятно, скорей всего из-за того что её можно реализовать через вычитание SUBI отрицательного значения константы — вот и сэкономили целый код под инструкцию.
Спасибо гуру ассемблера за то, что мониторите мои статейки — потому что я могу что-то объяснить не верно — а вы можете подправить. Особенно это будет важно дальше. Спасибо ещё раз большое!
В качестве упражнения для понимания ассемблера я бы рекомендовал написать ассемблер.
Простейший, без директив, без макросов и прочих высокоуровневых конструкций, на том языке, который вы уже знаете.
А если по хардкору, то прямо в машинных кодах процессора, который вы видите впервые (но имеете на него документацию и errata, иначе это не упражнение, а мазохизм). ;)
Это сродни обучению плавать методом выбрасывания из лодки, конечно, но зато понимание приходит раз и насовсем, и далее разобраться о очередным вариантом ассемблера большого труда уже не составляет, была бы документация.
Это тоже крутой способ. Но я пошёл другим путём — эти статейки как раз для тех, кто панически боится воды — если таких выкинуть с лодки — то они скорее всего утонут, а если и выживут, то потом ни к одному водоёму не подойдут. Поэтому если сравнивать с плаванием — то мы сейчас скорее занимаемся водными процедурами — типа обтирания мокрым полотенцем. Потом попробуем ножки намочить в ванне. Потом под душем постоять, потом в ванне, наполовину наполненной полежать — в общем до похода в бассейн ещё далеко, не говоря уже о купании в открытом море=)
Есть более гуманный способ. Дисассемблировать несколько небольших программ (например, простых библиотечных функций), взяв их коды прямо из памяти, и водя пальцем по таблицам кодов и режимов адресации. А потом посмотреть, получилось ли что-нибудь осмысленное.
Для слабых процессоров (да и некоторых не очень слабых) хорошо подходит функция деления. И что-нибудь из string.h.
Вот теперь представь, что ты это говоришь человеку, который вообще ни разу программировать не пробовал, и даже не представляет что есть таблицы кодов, что есть библиотечные функции и как вообще эта память работает=) Ну просто мне не хотелось называть статью «Ассемблер для чайников» ибо это моветон, но по сути что-то близкое.
Ну, как-то так я и начинал. Пара месяцев на фортране за программирование не считается :)
Да, первая программа вводится с пульта. Всё по-взрослому.
Пытался сделать так для S/360. Уперся именно в подсистему ввода-вывода. Решил что пробутстрапить что-нибудь алгол-подобное черес IFOX проще, мы не ищем легких путей, реалистичнее.
Мне кажется, что статья как то абсурдно издалека идёт.

Ведь вот скажем в статье объясняется что такое «команда» и «директива компилятора». Но это ведь не чисто ассемблерные понятия, это понятия полезные для любого языка программирования. Чую впереди рассказы «что такое алгоритм», «что такое ветвление/условие/цикл». Это замечательно, но это опять же будут понятия, необходимые для любого языка разработки.

Но если человеку нужно прочитать десяток абзацев что бы понять, что такое команда… У него впереди огромные проблемы в освоении программирования.
Вам не кажется — статья действительно абсурдно издалека идёт! И в этом то вся фишка. На самом деле скорее всего впереди будут рассказы и о ветвлении и о циклах — и если это полезно для любого языка — то это же хорошо!
И я буду несказанно рад, если 1 человек, который вообще не вкуривает программирование сможет благодаря моим статейкам что-нибудь написать хотя бы через 5 лет. Ведь тот, кто это понимает и считает чем то самим собой разумеющимся — не станет «учиться» по этим статьям. А тот, кто в них найдёт что то новое для себя — тому эти статьи и адресованы.
Ну а всем ассемблерщикам — просто по фану — почитать, посмеяться, поправить если где что не так.
UFO just landed and posted this here
А вот на мой взгляд, нет особой разницы на каком языке программирования писать (ну кроме, скажем, lisp, prolog и прочих не императивных языков). Если вы пишете на c/c++/c#/java/python/perl/sh/delphi и не можете понять ассемблер, значит вам не нужно его понимать. Просто потому, что это означает, что вы не умеете программировать.
Это означает, что нужно учиться программировать. А это можно делать практически на любом языке.
DIHALT (честь ему и хвала за статьи! :) ) немного утрирует, говоря про отсутствие аналогий, возводя ассемблер в несколько элитраный класс…

Ассемблер очень простой язык. Есть несколько глобальных переменных (регистры). Один (ну иногда несколько) глобальный массив байтов — память. Ну и набор операций которые можно с ними делать. Иногда (в некоторых ассемблерах) любые операции можно делать с любыми переменными (регистрами), иногда только с некоторыми (то, о чем говорил DIHALT). Но вот если вы всю жизнь программировали на C, и вам сказали, что сегодня у вас есть только переменные с именами a,b,c,d,e,h,l. Вам нужно посчитать сумму d+e+h+l, но складывать можно только две переменных, и одна из них обязательно 'a'. Неужели у вас уйдет более минуты на то, чтобы придумать алгоритм с указанными ограничениями?
Ну скажем, переход с любого императивного языка будет немного замедлен, когда речь пойдёт о действиях вроде «произвести операцию с числом, целиком не помещающимся в регистр» например. Или работа со строками, например… Фанаты ООПа опять же будут огорчены.
Короче, язык то простой, но легко перейти на него будет не очень легко, простите за тавтологию. Мозги переключать придётся, придется учиться думать немного другими категориями.
здрассьте… много у нас нынче императивных языков с безразмерными переменными? Я-бы скорее предположил проблемы типа «а чо он мне exception тогда не кинул?». Со строками да. Но все-ж, если человек писал на C, либо вдумчиво на C++, то он знает, что строковых переменных не бывает…
А фанаты ООП… Ну если вы пытаетесь делать ООП на ассемблере, то где-то в архитектуре проекта у вас явно закралась ошибка.
Почему же? ООП на ассемблере — это очень мощная штука. Если принять несколько несложных соглашений о том, как реализуются и используются объекты в вашей ассемблерной программе — то все преимущества ООП станут доступны вам.

Со стороны процессора поддержка ООП заключается в наличии индексной адресации. То есть вы можете считать или записать память по указателю со смещением. Например, LD A,(IX+4) на ассемблере Z80. При этом IX — регистр, в котором хранится указатель. Команда считывает память по адресу IX+4.

Другое дело, что индексная адресация в некоторых процессорах, таких как Z80, является дорогой. Команды с индексной адресацией на таких процессорах исполняются значительно дольше прочих команд доступа к памяти. На таких процессорах, если стоят требования оптимизации по скорости, есть смысл постараться избавиться от индексной адресации, но это все равно не мешает реализации ООП. Просто некоторые методы объектов будут выглядеть не так интуитивно понятно, как могли бы.

У других процессоров (например, серии PIC16) индексная адресация вообще отсутствует. Тут приходится уже вынужденно реализовывать объекты без индексной адресации. Но все равно это возможно, если нужно. С какими-то затратами по объему кода и времени его исполнения, конечно. Тут программист должен решить для себя, что ему важнее — скорость и размер программы или наличие в ней красивых объектов.
Автор, мне кажется, что вы подошли к ассемблеру уж слишком издалека. Ваша концепция «русского, англичанина и переводчика» применима не только к ассемблеру, но и к любому компилятору. Но если ваша статья рассчитана на человека, знакомого с программированием, то такие люди, как правило, уже знакомы с концепцией компиляции программ.

При этом к собственно к отличиям программирования на ассемблере от программирования на других компилируемых языках программирования, вы так и не подошли.
Подошёл я издалека, специально. По следующим статьям будет понятно, что это именно ассемблер. Моя статья рассчитана как раз на человека далёкого от программирования вообще. А к отличиям программирования на ассемблере я подойду медленно, но решительно в следующих статьях.
А что там понимать-то в ассемблере? Это не сложнее чем пользоваться микроволновкой.
Поставил время, температуру в регистры. Запустил операцию. Через какое-то время результат готов.
Микроволновка — очень простая модель процессора с одной инструкцией :-)
Точно так же можно сказать — «А что понимать в химии? Залил 2 реактива, получил на выходе то, что тебе нужно.» Однако в данном случае мы не сможем выйти за рамки этих двух реактивов. То есть получить тот же результат с помощью других реактивов не получится, хотя это было бы может быть более экономично и быстро — потому что мы не знаем, как эти реакции протекают, почему они протекают, почему атом водорода замещается на атом кислорода — и так далее. С ассемблером — как я считаю — нужно разбираться с самого низа — с микроконтроллера, с памяти, с регистров и если хватит терпения, времени и будет интерес — то с устройства процессора на физическом уровне. То есть понятно, что есть арифметически — логический узел — но как он работает на физическом уровне? Это, опять же, как я считаю, важно, если ты хочешь не просто уметь писать на ассемблере а писать на ассемблере правильно, зная, что если ты напишешь в AVR studio такую то команду — то в микроконтроллере произойдёт вот такая замечательная вещь. Да в конце концов это просто интересно.
Ассемблер понять мне помог брейнфак. Ну как, помог — после составления на нём каких-нибудь известных алгоритмов, снимается какой-то психологический барьер и изучение ассемблера идёт только так, без всяких аналогий и попыток сопоставить конструкции языка с элементами быта.
Думаю, похожий эффект был бы после составления программ для машины Поста, или Тьюринга.
Дело в том, что есть люди с техническим мышлением(ну там математическим, логическим) а есть с абстракционным(типа мыслить образами и т.п.) так вот почему то по умолчанию считается, что люди с абстрактным мышлением хуже вкуривают коддинг нежели технари. Вот я например прочитал у DI HALT'A статью его старую, которую он «чисто поржать» оставил — а вот мне она не только поржать доставила. Я прочитал её, а после неё оригинальную статью из курса по AVR. На примере звонка в дверь и туалета мне гораздо легче далась оригинальная статья по прерываниям — хотя там ничего сверхсложного и нет, но мне проще было представить что я пошёл открывать дверь и тут жесточайшим образом надавило на клапан — в результате пришлось отложить открытие дверей(общий цикл программы) уйти на прерывание в туалет, а снимая штаны крикнуть ОТКРОЮ ЧЕРЕЗ 5 МИНУТ! (что было бы аналогично подстраховке с записью содержимого регистра в стек) затем спокойно сделать дела, воспользоваться туалетной бумагой или сходить в ванну(обработчик прерывания) а потом спокойно идти и открывать дверь, при этом не беспокоясь, что тот, кто стоит за дверью подумает что меня нет дома, так как я ему крикнул что открою чуть позже.
То есть на пальцах понял суть > подучил матчасть. Мне лично так проще. Думаю кому-то тоже.
опять же — в данном случае я немного по другому сделал — то есть первоначально в статье — был звонок в дверь — и это было прерыванием, а поход в туалет был бы другим прерыванием -с большим приоритетом. Но представил ситуацию я по другому, и не менее правильно.
извините, но я я не понял. чем директива отличается от оператора, хотя сам по себе в институте реализовал библиотеку для вычислений (крипторафия) сверхбольших простых чисел и операций над ними с помощью регистров xmm (на макро ассемблере) для наборов инструкций mmx, sse, sse2, прочитав книгу по ассемблеру
Оператор — это команда железке. Директива — описание того, что ассемблер должен сделать с командами и данными которые мы вводим. Как-то так.
Например есть такая директива DEF. Она позволяет нам взять и регистру R17 задать произвольное имя например TEMPERATURE — и потом пихать в него данные с датчика температуры(условный пример). По ходу написания программы — нам не нужно вспоминать в какой регистр мы писали данные в R17 или в R28 — мы просто забираем данные из TEMPERATURE. А при компиляции везде будет просто машинный код — например 001F.
Оператору ты просто суёшь 2 значения и он с ними делает то что должен. Даёшь оператору вычитания 2 регистра — он выполняет математически вычитание и записывает результат во второй регистр — тут ты просто приказываешь железяке вычислить значение своим АЛУ. У тебя было скажем в первом регистре 8 бит «01001000» — а во втором регистре другие 8 бит «11001100» — то есть фактически каждая единичка это 5 вольт в ячейке — а 0 — 0.4 вольта в ячейке. Когда ты пишешь оператор вычитания — то токи из ячеек начинают бежать через кучу транзисторов, меняют направление в зависимости от того в каком бите какой был ток — и в конце концов прибегают в совсем другом порядке на регистр в который записывается исходное значение. Теперь у тебя в этом регистре совсем другой байт — и он соответствует результату вычитания, если перевести в десятичную систему.
Оператор — это грубо говоря запуск этих токов по цепи, а директива это мануал для компилятора — который будет собирать твой код в прошивку.
Директива — это оператор для препроцессора, который оперирует исходным кодом. Иногда, это команда среде в которой делается проект — например остановка процесса компиляции если какая-то вычислимая константа выходит за заданный диапазон.
В итоге, ни одна директива не попадает в результат компиляции, но они ВЛИЯЮТ на ход процесса компиляции.
Например можно прервать компиляцию с ошибкой, если пытаетесь присвоить 8-битному регистру вычисляемую константу которая вдруг стала больше 255. Это делается директивами. Либо вместо ошибки, заменить на код который работает с 16-битными константами. Тогда компилятору уйдет 16-битный код вместо 8-битного.
Директивы иногда влияют на результат компиляции. Какой-нибудь ALIGN может повлиять на адреса кодов и данных в откомпилированном коде. Директивы выделения и заполнения памяти — приводят к тому, что в образе программы оказывается та информация, которая была дана. Директивы разделения на секции (код — константы — данные) тоже влияют на итоговый порядок байтов в памяти.
Они влияют лишь на порядок а не на программу. Директивой нельзя заставить программу выполнить какое-то действие — максимум это включить или исключить тот или иной кусок кода, задать направление компиляции — в общем они не влияют на генерируемый код, просто помогают его упорядочить и работают они в большинстве случаев над исходным кодом программы, еще до компилятора.
Довольно типичный фрагмент кода на Z80:
  cp b
  jr c,_1
  add a,d
  db -2
_1:
  add a,e


(что-то вроде if(a>=b) a+=d; else a+=e; )
Здесь db -2 — директива, отводящая память для одного байта и заполняющая его значением 0FEh. В сочетании с последующей командой она превращается в исполняемый код CP 83h, который в данном контексте ни на что не влияет, но свою функцию — пропустить один байт — команда выполняет. Так что директива заставляет программу что-то выполнить.

Это типичное исключение и использование директивы не по назначению. А что делать, если надо дать возможность включить в код какие-то таблицы данных. Это скорей побочная возможность, нежели основная. Поскольку адресное пространство общее, это позволяет записать команду как данные.
Поскольку в те годы когда я начинал изучать компьютеры ассемблер был основным языком (если ты хотел написать что-то более-менее человеческое), то у меня проблем перехода с высокого уровня на низкий не было. Наоборот, я достаточно мучительно изучал С и не понимал зачем там всё так сложно (как мне казалось) по сравнению с ассемблером. Например, в АСМе я всегда понимаю как я работаю с указателем и знаю с каким шагом я его инкрементирую, в С же это зависит от типа указателя и ошибку (кмк) при малом опыте совершить гораздо проще чем в АСМе.

Если кому-то интересно изучение ассемблера в наиболее наглядном виде, я предложил бы процессор Intel 8080. Его ассемблер наиболее типичен, он лёг в основу более позднего Z80 (где были добавлены регистры и некоторые операции, а также изменены мнемоники на более правильные и универсальные). После этого можно попробовать изучить 8086 (он уже значительно сложнее за счёт сегментов и их адресации).

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

В принципе, в ассемблере нет ничего сложного. Регистры — те же переменные. А когда изучаешь чужой код, узнаёшь много красивых конструкций. Например, цикл (код Intel 8080):

MOV A,080h

M1: DEC A
[........]
CMP A, 0h
JNZ M1

Можно записать так:

MOV A,080h

M1: DEC A
[........]
ORA A
JNZ M1

А можно так:

M0:

MOV A,080h

M1:
[........]
DEC A
JNZ M1

Т.к. команда DEC сама устанавливает флаг нуля (Z) при достижении значения 0. И в более старших процессорах (8086 итд) тоже работает.

Ещё на асме можно оптимизировать код, используя модификацию самого пространства кода, т.е. своим же кодом менять содержимое ячеек программы. Например, в последнем примере ячейка [M0+1] содержит константу 0, которая на самом деле не константа: её можно перезаписать командой STA M0+1.
Видимо нужно было тему назвать «Как понять ассемблер на AVR» — я в начале поста написал:
«Сразу поясню что тема касается микроконтроллеров AVR — и я пока ещё не знаю, пригодится ли этот пост тем, кто хочет использовать ассемблер для любой другой задачи.»
Мне сейчас интересен именно ассемблер для AVR — потому что для моих задач контроллеры AVR — идеальное решение — они мощные, у них куча переферии, найти можно в любом «юном технике», стоят недорого, можно заменить модель без сильных головняков, датчиков и всяких навесных убердевайсов под них куча. Для конструирования устройств самое то!
И пост висит не только в хабе по ассемблеру — но и в хабе «прграммирование микроконтроллеров» И в «электронике для начинающих».
Я тут и дальше юзаю асм исключительно для AVR. Надеюсь что это временно=)
Ничего не хочу сказать плохого про AVR, возможно, они действительно идеальны для ваших задач, но я бы все равно советовал неспеша переходить на ARM. Не холивара ради, просто совет.
После работы с каким-нибудь STM32F4, читая о том, что AVR «мощные» и у них «куча переферии», как-то внутренне улыбаешься. STM32F1xx есть в любом магазине, стоят они не намного дороже, при грамотной разводке (т.е. почитать не только даташит на конкретный чип, а еще и compatibility note) поставить чип другой серии тоже проблема не большая, а навесные убердевайсы вообще мало привязаны к конкретной архитектуре и производителю МК. AVR — это глобально и надежно, но будущее все же за 32-битными МК, как мне кажется. Да и выбор производителя, IDE и всего остального — значительно шире.
Обязательно приму к сведению. Спасибо.
Atmel сейчас очень качественно отреагировали на экспансию АРМов, несколько сдвинув свою область применения.
А именно — очень малопотребляющие микроконтроллеры. Даже если у СТМки отключить вообще всю периферию она не будет потреблять так же мало, как АВРка, у них сейчас (у АВР) ток в активном режиме порядка *микро*ампер. СТМка столько даже во сне не всегда потребляет)

Поэтому со счетов их списывать совсем не стоит.
Stanislave26, а более конкретно про устройство AVR будет?
Когда то я писал на ассемблере для однокристальных микроэвм BEчегототам.
там было важно сначала разобраться в устройстве самого процессора — т.е. какая память, какие порты, разрядность таже…
Не ассемблер надо «пытаться понимать», а устройства и принципы работы процессоров. Тогда ассемблер станет понятен сам по себе. А ещё лучше стараться разобраться во всю, так сказать, глубину. От работы транзистора, до адресной шины, кэшей, внеочередности, среды передачи и тд и тп. Славно нынешнее поколение поверхностными знаниями. Знает ассемблер, но понятия не имеет о том как реализуеся, к примеру, команда деления в процессорах.
О! Про реализацию деления (или хотя бы умножения) была бы отличная статья. А то Пети-яблоки…
В связи с этим не могу не посоветовать книгу Ч. Петцольда «КОД. Тайный язык информатики.», выпущенную еще в 2001 году.
UFO just landed and posted this here
Со следующей статьи буду писать более конкретно именно про AVR. В связи с тем что половина людей просит писать в стиле «У Пети было 3 яблока» а другая половина наоборот критикует такое «детсадовское» предоставление материала — я буду в начале статьи коротко описывать саму суть проблемы на пальцах — а потом более подробно давать техническую информацию. Ну это после того, как про устройство микроконтроллера добью. Спасибо за полемику!
Sign up to leave a comment.

Articles