Для дальнейшего изложения касающегося языков программирования и компиляторов необходимы опpеделенные сведения об устройстве вычислительной машины и системе команд. Если они Вам известны, этот pаздел можно пpопустить. В первоначальном варианте этого текста (Машинный код и язык ассемблера) рассматривался только устаревший процессор Intel 8086. Он (текст) был написан когда уже существовали процессоры 80486 и Pentium, но часто они использовались просто как быстродействующие 8086.
Здесь рассматриваются лишь небольшие подмножества наборов команд (немного больше двадцати команд) различных процессоров, получивших в свое время широкое распространение. Подмножества команд выбраны так, что их достаточно для написания широкого класса прикладных программ, и в частности, компиляторов языков высокого уровня. Для ряда рассматриваемых здесь процессоров это было сделано и это является некоторой гарантией корректности написанного. Но все равно ошибки могут быть.
Для проверки использовались реальные машины и/или программные эмуляторы - не все рассмотренные здесь системы доступны.
В ряде случаев вместо общего формата команды приводится несколько частных вариантов, обычно это оговаривается. Большая часть этих вариантов соответствует командам процессора 8080, по-видимому самого простого из рассматриваемых.
Скорее всего, описание неполное, сложно ничего не упустить в малом объеме. Для сравнения только один документ "AMD64 Architecture Programmer’s Manual Volume 3: General Purpose and System Instructions" (Revision 3.20, май 2013) имеет объем 610 страниц. Так что все стало сложнее...
Однажды мне в руки попала книжка Т. Мартина "Физические основы электротехники". Она ничинается так.
Согасно древнему индусскому сказанию Земля покоится на спине слона, который в свою очередь стоит на спине черепахи. На этом объяснение обрывается - сведений относительно точек опоры черепахи не приводится. В этом сказании при всей нелепости исходной идеи заложена глубокая мысль о том, что всякое последовательное исследование причин физического явления ограничено определенными пределами, зависящими от уровня физических знаний. С некоторого определенного этапа исследования ответить на вопрос "почему" невозможно без получения дальнейшей, более глубокой информации. Это означает, что любое объяснение должно ограничиваться своей "черепахой".
Возможно, здесь есть неточности. В других источниках слонов четыре или шесть, в третьих нет ни слонов ни черепахи, а есть три кита. Черепаха или киты плавают в океане, куда налит океан не сообщается.
То, что здесь рассматривается называется архитектурой набора команд. Проводя аналогию можно сказать что это "черепаха" (или "слон"). Это довольно низкий уровень, но ниже него располагаются уровень микроархитектуры, уровень логических схем, уровень элементов схем...
Здесь нужно упомянуть о микросхемах FPGA (Field-programmable gate array) и CPLD (Complex programmable logic device). С их помощью можно самостоятельно реализовать процессор, причем, если ограничиться немногим это довольно просто. Наверное, это существенно не равно разработке процессора на простых логических микросхемах и тем более, на дискретных элементах, но все же это нечто большее, чем написание программного эмулятора.
Насколько я понял, FPGA отличаются от CPLD только числом элементов (в FPGA их больше) и способом хранения конфигурации (соответственно, в статической и flash-памяти). По-видимому, FPGA предпочтительнее - они содержат больше элементов и не имеют ограничения на число циклов записи конфигурации. Если верить документации, микросхемы CPLD Altera MAX II EPM240/570/1270 допускают всего сто циклов записи, но встечаются утверждения, что в реальности все не настолько плохо.
Для удобства сравнения описание каждого из процессоров будет распределено по перечисленным ниже разделам. В настоящее время закончены схемы регистров и описания процессоров EDSAC, IBM 701, IBM 704, IBM System/360, PDP-11, Intel 4004, Intel/AMD (8080, 8086, i386 и Athlon64), MOS6502, микроконтроллеров 8051, PIC10/12/16, AVR и ARM, а также схемы регистров некоторых других процессоров. Для одной страницы очень много:)
Если нужна информация по какому-то одному процессору, имеет смысл в двух дополнительных окнах открыть соответсвующие части разделов Логическое устройство и Форматы команд.
Вычислительная машина состоит из аpифметического устpойства, устpойства упpавления, опеpативной памяти и устpойств ввода-вывода. Аpифметическое устpойство и устpойство упpавления вместе обpазуют центpальный пpоцессоp. Помимо логических схем пpоцессоp обычно содеpжит набоp ячеек памяти (pегистpов):
Некоторые из регистpов могут быть способны выполнять несколько различных функций. Можно построить процессор, в котором регистры данных и адреса отсутствуют (стековая машина). И наоборот, можно построить процессор, в котором есть довольно большое количество регистров данных и нет возможности подключения оперативной памяти.
Для временного хранения и обработки чисел с плавающей точкой обычно используется отдельный набор регистров, и соответствующие логические схемы, но они есть не во всех системах. Отдельные регистры не являются необходимыми, но так сложилось - по-видимому из-за малой разрядности целочисленных регистров в старых системах.
Вычисления с плавающей точкой можно реализовать программно с использовнием только целых чисел. И существует много задач, для которых целых чисел достаточно.
Мы привыкли к тому, что машины работают с восьмибитными байтами и словами, состоящими из двух, четырех или восьми байт. Числа с плавающей точкой размещаются в четырех или восьми байтах и содержат знак, мантиссу и порядок. Так было не всегда. В первых машинах регистры содержали больше восьми бит (17, 36, 48, 60 бит и вряд ли это все варианты). Для представления (естественно, приближенного) вещественных чисел использовались форматы с фиксированной точкой. Байт по-видмому появился в машинах IBM System/360, но это неточно.
Пpогpамма - это последовательность команд, каждая из котоpых пpедставлена опpеделенным кодом (числом). В пpоцессе pаботы вычислительная машина считывает из опеpативной памяти команду, на котоpую указывает указатель команды, исполняет ее и увеличивает значение указателя команды так, чтобы он указывал на следующую команду. Затем цикл повтоpяется.
Реальные системы обычно сложнее, в них исполняемая программа может прерываться при наступлении тех или иных событий (например, при нажатии клавиши на клавиатуре или изменении показаний часов), работать с оперативной памятью могут и другие устройства (например, контроллеры дисковых накопителей могут записывать данные в память или читать их из памяти их без участия процессора, это нужно для увеличения скорости передачи и исключения потерь данных). Наконец, процессор может быть не один. Тем не менее до сих пор (2017 год) выпускаются системы точно соответствующие указанной простой модели выборки/выполнения, например, микроконтроллеры PIC10F200. Эта же микросхема является примером системы без оперативной памяти - имеются только 16 регистров.
Команды можно pазделить на тpи гpуппы:
Могут существовать комбиниpованные команды, напpимеp извлечение числа из памяти и сложение его с дpугим числом, находящимся в pегистpе.
Запомнить коды команд может быть сложно, с целью упрощения каждой команде ставится в соответствие мнемоника, состоящая из имени команды (обычно сокращения одного или нескольких слов) имен используемых регистров и/или числовых параметров (напиример, значений констант или адресов оперативной памяти). Существуют программы (ассемблеры), преобразующие последовательности мнемонических обозначений в исполняемый код. Мнемоники зависят от используемого процессора, в различных ассемблерах для одного и того же процессора мнемоники тоже могут отличаться.
Обычные регистры данных позволяют хранить целые числа, и может быть, двоично-кодированные десятичные числа (две десятичные цифры в одном байте), полезность последних по-видимому невелика. Не делается различий между беззнаковыми числами и числами со знаком, но обычно предполагается, что отрицательные числа представляются в так называемом дополнительном коде. Его идея основана на следующем преобразовании разности двух положительных чисел:
A - B = A + (2^N - 1) - B - (2^N - 1) = A + (((2^N - 1) - B) + 1) - 2^N = A + (!B + 1) - 2^N
Здесь N - число разрядов сумматора, а
!B + 1
и есть дополнительный код числа B. Если A больше или равно B, сумма A и дополнительного кода B будет не меньше 2^N, и чтобы вычесть из нее 2^N нужно просто отбросить перенос из старшего разряда. Если же A меньше B, переноса из старшего разряда не будет и это означает, что результат вычитания не может быть представлен (нигде выше отрицательные числа не предполагались).
Дополнительный код был использован для замены вычитания комбинацией отрицания и сложения (т.е. для исключения из арифметического устройства отдельной схемы вычитания), но он же может служить и для представления отрицательных чисел. Если число B отрицательно, оно представляется "положительным" числом
!|B| + 1
Половина чисел (от 0 до 2^(N - 1) - 1) считаются неотрицательными, оставшаяся половина (от 2^(N - 1) до 2^N - 1) отрицательными. Перебрав все варианты (их немного) можно убедиться, что если результат сложения и вычитания неотрицаелен и он находится в указанном выше диапазоне, он и окажется на выходе сумматора. Если же результат отрицателен и также находится в указанном выше диапазоне отрицательных чисел, на выходе сумматора окажется его дополнительный код. Факт переполнения может быть зафиксирован, об этом немного ниже.
Модуль наименьшего допустимого отрицательного числа превышает на единицу наибольшее допустимое положительное число.
Отдельные разряды регистpа состояния используются для хpанения информации о pезультате выполнения последней команды и, возможно, для управления работой процессора, например, для запрета обработки прерываний. Обычно регистр состояния содержит следующие признаки, которые учитываются при выполнении команд условных переходов:
C
|
Устанавливается, если при сложении или вычитании произошел перенос из старшего разряда
|
V
|
Устанавливается, если при сложении или вычитании произошло знаковое переполнение
|
Z
|
Устанавливается, если результат равен нулю
|
N
|
Устанавливается, если результат отрицателен (т.е. старший разряд результата содержит единицу)
|
Правила установки признаков C, Z и N тривиальны (но одни процессоры при выполнении вычитания инвертируют признак C, а другие этого не делают), признак V устанавливается если имел место перенос в старший разряд и не имел места перенос из старшего разряда или, наоборот, если не имел места перенос в старший разряд и имел место перенос из старшего разряда. Это неочевидно, но именно это позволяет корректно выполнить сравнение чисел, имеющих разные знаки. Если эти числа достаточно велики, при вычитании произойдет переполнение и знаковый разряд будет искажен. Проблему можно было бы решить, добавив еще один разряд в сумматор, но это потребовало бы большего числа элементов, чем для схемы несовпадения.
Убедиться в корректности правила формирования признака переполнения и полезности его для сравнения чисел со знаком можно перебором всех комбинаций знаков операндов. Например, если уменьшаемое A положительно, а вычитаемое B отрицательно, их разность определяется формулой:
{A} + !{B} + 1 = A + !(2^N - |B|) + 1 = A + ((2^N - 1) - (2^N - |B|)) + 1 = A + |B|
Фигурные скобки означают машинное представление числа, т.е. оно само, если число положительно и дополнительный код его абсолютной величины, если число отрицательно. N - число разрядов сумматора.
В соответствии с этой формулой сумматор складывает представление A и инверсный код представления B, причем на вход переноса младшего разряда сумматора подается единица.
В знаковых разрядах обоих операндов нули, следовательно перенос из знакового разряда невозможен, но перенос в знаковый разряд может произойти. В этом случае будет установлен признак переполнения.
Остальные три случая рассматриваются аналогичным образом. Если знаки операндов совпадают, переполнение не происходит и значение признака знака корректно. Условия переходов на основании сравнения чисел со знаком содержат два признака - N и V. Рассмотрев все четыре комбинации знаков несложно доказать эквивалентность условий:
(A < B) <=> (N != V)
(A >= B) <=> (N = V)
Если числа предполагаются неотрицательными, результат вычитания определяется формулой:
{A} + !{B} + 1 = A + ((2^N - 1) - B) + 1 = 2^N + (A - B)
Если уменьшаемое A больше или равно вычитаемому B, произойдет перенос из старшего разряда. Если A меньше B, переноса не будет. Одни (многие?) процессоры в этом случае инвертируют признак C, другие этого не делают. Процессоры 80x86 инвертируют признак C, MOS6502 нет.
Есть процессоры, в которых флаги отсутствуют (напиример, MIPS). В них условные переходы могут зависеть от значений в одном или двух регистрах, например, переход может выполняться при равенстве значения нулю.
Опеpативная память (запоминающее устойство с пpоизвольной выбоpкой) состоит из ячеек, похожих на pегистpы пpоцессоpа (но часто имеющих другую физическую реализацию), каждая из них имеет адpес - число, указывающее к какой именно ячейке пpоисходит обpащение.
Для хранения программы и обрабатывемых данных может использоваться один общий блок оперативной памяти или два разных. По-видимому, в большей части компьютеров используется общий блок, а вот в микроконтроллерах часто используются разные блоки. В этом есть определенная логика - программа микроконтроллера обычно неизменна (или меняется редко) и должна сохраняться при отключении питания, про обрабатываемые данные первого сказать нельзя, а второе может быть и не нужно. Соответственно, для построения памяти программ и данных используются совершенно разные элементы. Кроме того, часто в микроконтроллерах число разрядов слова памяти программ отличается от числа разрядов слова памяти данных. Это не означает, что разнородные модули памяти нельзя поместить в общее адресное пространство, но так было посчитано удобным.
В pаботе вычислительной машины важную pоль игpает стек. Это область опеpативной памяти, адресуемая указателем стека (SP - Stack Pointer), для работы с ней предназначены команды помещения в стек (push) и извлечения из него (pop). Стек можно пpедставлять как стопку книг - вы кладете новую книгу на уже лежащие и можете взять лишь веpхнюю из них. Для полного сходства стопка должна лежать на потолке (обычно, но не всегда, максимально возможное значение SP соответствует пустому стеку):
|
|
Если направить ось адресов вниз, стопка книг будет находиться на дне колодца:
|
|
Стек используется для оpганизации вызова подпpогpамм и для хpанения пpомежуточных pезультатов вычислений. В некоторых простых системах стек может использоваться только для оpганизации вызова подпpогpамм. В IBM System/360 аппаратная поддержка стека вообще отсутствует, но это не значит, что его нельзя организовать.
|
Стековая машина. Содержит оперативную память и три регистра - указатель стека SP,
указатель на начало глобальных данных DP и указатель команды. Объем памяти, разрядность
ее ячейки и разрядность регистров могут быть различны. Без регистра DP в принципе можно
обойтись, память может разделена на память программы и память данных - как
в микроконтроллерах.
Регистры данных также есть, но они используются только исполняющей системой. Программисту они недоступны. Все операции выполняются над данными, находящимися на вершине стека. Например, при выполнении операции сложения два числа извлекаются из стека, складываются, затем результат помещается на вершину стека. Была реализована Н.Виртом в виде программы Pascal-S, представляющей собой компилятор подмножества языка Pascal и собственно стековую машину. |
|
EDSAC - Electronic Delay Storage Automatic Calculator. Первый(?) электронный компьютер. Может обращаться
к оперативной памяти объемом 1024 семнадцатиразрядных слов, также возможно обращение и работа с двойными
словами. Числа могут расссмативаться как целые или как дробные в диапазоне от -1 до 1 - 2-(N-1)
(N - число разрядов слова), никих чисел с плавающей точкой нет. Может выполнять умножение, а деление - нет.
При умножении числа считаются дробными, для перемножения целых нужно дополнительно сдвигать результат влево.
Содержит семидесятиодноразрядный регистр-аккумулятор AC, два тридцатипятиразрядных регистра - множитель MR (multiplier) и множимое MD (multiplicand), десятиразрядный указатель исполняемой инструкции SCT (sequence control tank). Слово tank использовано, поскольку для реализации регистров (и памяти) использовались акустические линии задержки (acousyic delay lines) - трубки со ртутью, на концах которых были установлены пьезоэлектрические преобразователи - излучатель и микрофон. Обращение к такой памяти только последовательное (т.е. за один раз можно записать или прочитать только один бит и только в подходящий момент времени). Текущие значения в регистрах и выбранной трубке памяти можно было видеть на экранах осциллографов. Длина команды - семнадцать бит. Для реализации косвенного обращения к памяти и возврата из подпрограмм необходима модификация кода соответствующей команды. По-видимому, такое безумное по нынешним представлениям решение было принято с целью упрощения аппаратуры. Подлежащая исполнению программа считывается загрузчиком с пятидорожечной перфоленты, длина команды на ленте переменная. В документации команда нызывается order, но это же слово используется и для обозначения последовательности команд, в частности загрузчика, который называется Initial Order. Загрузчиков было два - простой Initial Order 1 и "расширенный" Initial Order 2. Существует программный симулятор). По умолчанию в ней используется загрузчик Initial Order 2, если нужен Initial Order 1, его нужно выбрать. |
|
Тридцатишестиразрядный процессор 701 фирмы IBM. Может обращаться к оперативной памяти объемом 2048 тридцатишестиразрядных слов. Содержит тридцативосьмиразрядный регистр-аккумулятор AC (знаковый бит S, два бита переполнения P и Q, тридцатипятиразрядное значение), тридцатишестиразрядный регистр множителя/частного (multiplier/quitient register), двенадцатиразрядный указатель инструкции IC. Возможен доступ к половинам слов. Длина команды - восемнадцать бит (половина слова). |
|
Тридцатишестиразрядный процессор 704 фирмы IBM. Относится к той же серии, что и IBM 701, но значительно отличается. Может обращаться к оперативной памяти объемом 4096, 8192 или 32768 тридцатишестиразрядных слов. Содержит тридцативосьмиразрядный регистр-аккумулятор AC (знаковый бит S, два бита переполнения P и Q, тридцатипятиразрядное значение), тридцатишестиразрядный регистр множителя/частного (multiplier/quitient register), три (по другим данным семь) индексных регистра A, B и C и указатель инструкции ILC. Длина индексных регистров двенадцать, тринадцать или пятнадцать бит (в зависимости от установленного объема оперативной памяти) - даже на такой мелочи экономили. Длина команды - тридцать шесть бит, аппаратно реализована арифметика с плавающей точкой (одинарной точности?). В отличие от IBM 701 есть возможность косвенного обращения к памяти, для чего и были предназначены индексные регистры. |
|
Тридцатидвухразрядный процессор фирмы IBM, применявшийся в ряде вычислительных машин серии 360.
Может обращаться к оперативной памяти объемом до шестнадцати мегабайт. Содержит шестнадцать
универсальных тридцатидвухразрядных регистров и шестидесятичерырехразрядный регистр PSW,
состоящий из двадцатичетырехразрядного адреса инструкции (младшие биты 40..63, при счете
справа налево 23..0) и ряда других полей. Старшие модели также содержат четыре
шестидесятичерырехразрядных регистра для хранения операндов с плавающей точкой,
на схеме они не показаны.
Универсальные регистры могут использоваться для хранения данных и адресов памяти, адрес может формироваться как сумма значений в двух регистрах с ненулевыми номерами (они называются индексом и базой) и указанного в команде двенадцатибитного смещения. Указание нулевого регистра означает, что соответствующий компонент в сумму не входит. Отсутствует аппаратная поддержка стека(?). Аппаратная реализация разных моделей различна. Младшие модели на самом деле являются шестнадцатиразрядными системами, но их микропрограммы реализуют общие для всех машин серии команды. Разумеется, работают они медленнее. |
|
Шестнадцатиразрядный процессор машины PDP-11 фирмы DEC. Может обращаться к оперативной памяти объемом до шестидесяти четырех килобайт (в старших моделях объем памяти мог достигать четырех мегабайт, но в каждый момент времени была доступна лишь одна шестидесятичетырехкилобайтная страница?). Содержит шесть универсальных шестнадцатиразрядных регистров, шестнадцатиразрядные указатель стека SP(R6), программный счетчик PC(R7) и регистр состояния PS. |
|
Четырехразрядный микропроцессор фирмы Intel. Использовался в калькуляторе Busicom 141-PF,
но не только в нем. Собственно микропроцессор 4004 не содержит всех необходимых схем, для
его работы нужны специализированные микросхемы 4001 (ПЗУ объемом 256 байт) и
4002(4002-1, 4002-2 - ОЗУ объемом 80 полубайт, отличаюшиеся логикой выбора кристалла).
Доступ к 64 из 80 полубайт реализован "обычным" образом, для доступа к оставшимя 16 полубайтам
используются специальные команды. Помимо собственно памяти эти микросхемы содержат регистр адреса,
порт ввода-вывода (4001, 4002 - порт только вывода) и необходимую логику. Обращение к памяти
требует в общем случае трех команд - выбора банка памяти, выбора ячейки и собственно обращения
(чтения или записи).
В комплект также входит сдвиговый регистр 4003, предназначенный для увеличения числа портов ввода-вывода. Все микросхемы выпускались корпусах с шестнадцатью выводами. Для передачи адресов и данных используется четырехразрядная шина, еще пять выводов предназначены для управления работой микросхем памяти. Без использования дополнительных микросхем можно построить систему, содержащую 16 микросхем ПЗУ 4001 и 16 микросхем ОЗУ 4002 (четыре банка из двух 4002-1 и двух 4002-2). Такая система больше похожа микроконтроллер, точнее микроконтроллер похож на нее. Микропроцессор содержит четырехразрядный регистр-аккумулятор AC, результат многих операций может помещаться только в него, шестнадцать четырехразрядных регистров 0-15, двенадцатиразрядный счетчик команд PC и трехуровнеый стек L1-L3, предназначенный только для хранения адресов возврата из подпрограмм. Также имеются программно недоступный указатель стека и два одноразрядных регистра флагов (флаг переноса/заема CY и флаг TEST, связанный со входом тестового сигнала), на схеме они не показаны. При выполнении ряда операций пары соседних регистров ([0, 1]-[14, 15]) используются совместно. В частности, имеются команды загрузки восьмиразрядной константы в пару регистров. Упомянутый калькулятор Busicom 141-PF по нынешним меркам очень сложен. Помимо микропроцессора 4004 он содержит черыре микросхемы ПЗУ 4001, две микросхемы ОЗУ 4002, три сдвиговых регистра 4003 и много других элементов. |
|
Восьмиразрядный микропроцессор фирмы Intel. Может обращаться к оперативной памяти объемом
до шестидесяти четырех килобайт. Содержит восьмиразрядные регистр-аккумулятор A, результат
многих операций может помещаться только в него, шесть регистров данных B, C, D, E, H, L,
регистр состояния PSW и шестнадцатиразрядные указатель команды PC и указатель стека SP.
При выполнении ряда операций регистры данных объединяются в пары BC, DE и HL. Помимо команд обработки байтов есть несколько команд обработки шестнадцатиразрядных слов. В ряде распространееных домашних компьютеров использовался усовершенствованный клон 8080 - процессор Z80 фирмы Zilog. |
|
Восьмиразрядный микропроцессор фирмы Motorola. |
|
Восьмиразрядный микропроцессор фирмы MOS Technologies. Может обращаться к оперативной памяти объемом
до шестидесяти четырех килобайт. Содержит восьмиразрядные регистр-аккумулятор A, регистры данных/индексов X и Y,
регистр состояния P, указатеть стека S и единственный шестнадцатиразрядные регистр - указатель команды PC.
Шестнадцатиразрядные индексные регистры отсутствуют, для косвенных обращений к оперативной памяти используются пары соседних ячеек памяти с адресами от 0x0000 до 0x00FE, значения в которых используются как адреса. Имеются короткие (2 байта) команды для обращений к памяти с адресами от 0x0000 до 0x00FF. Область памяти с адресами от 0x0100 до 0x01FF используется как стек (к значению S неявно прибавляется смещение 0x0100). Применялся в ранних компьютерах фирмы Apple и игровых приставках Nintendo. |
|
Вымышленный восьмиразрядный процессор, рассматриваемый в книге Ч.Гилмора "Введение в микропроцессорную технику". Содержит восьмиразрядные регистр-аккумулятор A, результат операций может помещаться только в него, три регистра данных B, C, D, регистр состояния PSW и шестнадцатиразрядные указатель стека SP и указатель команды PC. Пара регистров BC может использоваться как шестнадцатиразрядный индексный регистр. |
|
Шестнадцатиразрядный микропроцессор фирмы Intel. Может обращаться к оперативной памяти объемом
до одного мегабайта. Содержит четыре регистра данных AX, BX, CX и DX, регистр состояния PSW,
указатели команды IP, указатель стека SP, два индексных регистра SI и DI, указатель базы BP и
четыре сегментных регистра CS, SS, DS и ES. Все регистры шестнадцатиразрядные.
Регистры данных состоят из пар восьмиразрядных регистров AH, AL, BH, BL, CH, CL, DH и DL, которые могут использоваться независимо. Регистр BX может использоваться как индекс и как база. Индексные регистры могут использоваться как шестнадцатиразрядные регистры данных. Адрес обращения к памяти формируется сложением умноженного на шестнадцать значения в сегментном регистре, значения в базовом регистре, в индексном регистре и восьми- или шестнадцатиразрядного смещения, являющегося частью команды. Сегментный регистр используеся всегда, два из трех оставшихся элементов адреса могут отсутствовать, но использование одного регистра BP невозможно. Пары регистров CS:IP и SS:SP используются совместно. |
|
Процессор К1801ВМ1. Интегральный аналог шестнадцатиразрядной мини-ЭВМ PDP-11 фирмы DEC. Применялся в бытовом компьютере БК-0010. |
|
Тридцатидвухразрядный микропроцессор фирмы Motorola. Применялся в ранних компьютерах Apple Macintosh и игровых приставках Nintendo. |
|
Тридцатидвухразрядный микропроцессор фирмы Intel. Расширение архитектуры 8086, разрядность регистров данных
и индекных регистров увеличена вдвое. Может обращаться к оперативной памяти объемом до четырех гигабайт,
имеет встроенные механизмы преобразования адресов памяти, обеспечивающие представление множества несвязных
областей памяти как одной связной и защиту от ошибочных обращений.
Назначение сегментных регистров изменено - в них хранятся смещения в таблицах дескрипторов памяти, прикладная программа [обычно] не должна изменять их. Таблицы дескрипторов заполняются операционной системой, каждый их элемент содержит начальный адрес сегмента памяти, его размер и права доступа. Помимо одной глобальнай таблицы дескрипторов могут использоваться множество локальных. По сравнению с 8086 адрес обращения к памяти формируется более сложным образом. Сначала сложением значения в базовом регистре со значением в индексном регистре (возможно, умноженным на масштабный коэффициет 2, 4 или 8) и с восьми- или тридцатидвухразрядным смещенем, являющимся частью команды, формируется логический адрес. Два из трех элементов адреса могут отсутствовать, использование одного регистра EBP невозможно. Затем сложением логического адреса с начальным адресом сегмента формируется линейный адрес. Если режим страничной трасляции выключен, физический адрес равен линейному. Иначе линейный адрес рассматривается как набор из трех полей - смещения в каталоге таблиц страниц (10 бит), смещения в таблице страниц (10 бит) и смещения в странице памяти (12 бит), эти таблицы также заполняются операционной системой. Обращение к каталогу таблиц страниц дает адрес таблицы страниц, обращение к ней - адрес страницы, сумма этого адреса и смещения дает физический адрес. Прикладная программа никак не может повлиять на эти преобразования. Адреса указанных таблиц хранятся в специальных регистрах GDTR, IDTR и CR3. В регистре LDRT хранится идекс элемента таблицы дескрипторов, содержащего адрес локальной таблицы дескрипторов. Эти регистры не показаны на схеме, прикладная программа не может изменить значения в них. |
|
Шестидесятичетырехразрядный микропроцессор фирмы AMD. Расширение архитектуры i386, разрядность регистров данных
и индекных регистров увеличена еще вдвое, однако, из 64 разрядов адреса используется 48, число линий адреса может
быть меньше (40?).
Назначение сегментных регистров опять изменено, как и механизм преобразования адресов. Логический адрес рассматривается как набор из пяти полей - четырех девятиразрядных индексов в таблицах преобразования и двенадцатиразрядного смещения.
|
|
Восьмиразрядный микроконтроллер фирмы Intel. Несмотря на некоторое сходство обозачений от 8080 отличается очень
значительно. Содержит память программ объемом до 8 килобайт(?)(не все модели), четыре банка по восемь регистров
(R0-R7), набор регистров специальных функций (SFR) и память данных, состояшую из 96 восьмиразрядных ячеек памяти
(16 ячеек BITS и 80 ячеек RAM). Некоторые модели содержат дополнительные 128 восьмиразрядных ячеек памяти.
В число SFR входят счетчик команд PCH:PCL, регистр-указатель данных DPTR(DPH:DPL), указатель стека SP, слово
состояния PSW, регистр-аккумулятор A и регистр-расширение аккумулятора B. Существуют команды для работы
с отдельными битами ячеек BITS и некоторых из SFR. К регистрам и портам можно обращаться как ячейкам
оперативной памяти. Возможно подключение внешней памяти программ и данных (не ко всем моделям).
|
|
Семейство восьмиразрядных микроконтроллеров фирмы Microchip. Содержат память программ от 256(?)
до 16384(?) слов (двенадцати- или четырнадцатиразрядных), от 16(!) до 2048 регистров
и портов ввода/вывода, размещаемых в нескольких банках (до восьми) и рабочий регистр WREG.
Это не аналог регистра-аккумулятора, в ряде команд он используется только как место хранения одного из
операндов. Результат же может записываеться в другой регистр. Обращение к регистрам возможно прямое
(по номеру в коде команды) или косвенное (по номеру, записанному в регистр FSR, в команде указывается
псевдорегистр IND). Память данных отсутствует, или, другими словами, регистры - это и есть память данных.
Для хранения адресов возврата из подпрограмм имеется небольшой стек (в некоторых моделях его емкость
всего два слова).
Число команд невелико, например PIC10F200 может выполнять всего 33 различные команды. В документации их перечисление занимает одну страницу, описание занимает еще шесть страниц. В порядке исключения здесь также приведены все команды PIC10. Число моделей контроллеров очень велико, так что приведенные цифры могут быть неточны. Семейство контроллеров PIC18 имеет несколько большие возможности, более современное семейство шестнадцатиразрядных контроллеров PIC24 и dsPIC существенно отличается. Тридцатидвухразрядные микроконтроллеры PIC32 также отличаются, это представители архитектуры MIPS.
|
|
Семейство восьмиразрядных микроконтроллеров фирмы Atmel. Содержат память программ
объемом до 8 мегабайт (реально существующий контроллер ATmega2560 содержит 256 килобайт памяти),
32 регистра данных (три пары из которых - R27:R26, R29:28 и R31:30 могут использоваться как индексы),
набор регистров (портов) ввода-вывода и память объемом до 8 килобайт(?). Указатель стека SPH:SPL
и регистр состояния SREG размещены среди портов ввода-вывода. К регистрам и портам можно обращаться
как ячейкам оперативной памяти.
|
|
Вымышленный восьмиразрядный микроконтроллер. Все команды имеют длину 1 байт, соответственно все обращения к памяти, все переходы и все вызовы подпрограмм только косвенные, в качестве индекса используется пара регистров HL. Число команд меньше 30. Плотность кода раза в два-два с половиной ниже чем у 8080. Регистр флагов F содержит только флаг переноса. Указатель стека S восьмиразрядный, сам стек размещается в младших 256 байтах памяти данных. Реализация на FPGA Altera Cyclone IV использует менее 400 ячеек (LE). Контроллер был задуман, чтобы разобраться с FPGA. |
|
Множество семейств тридцатидвухразрядных микропроцессоров и микроконтроллеров, разработанных фирмой ARM. Выпускаются разными производителями. Регистр APSR (Appication Program Status Register) содержит как минимум четыре флага (N, Z, C, V) - (Знак, Ноль, Перенос, Переполнение). Помимо показанных на схеме, процессоры содержат/могут содержать ряд других регистров, в т.ч. регистры для вычислений с плавающей точкой. Поддерживают различные наборы команд - ARM (тридцатидвухразрядные команды), Thumb (шестнадцатиразрядные команды), Thumb-2 (тридцатидвухразрядные и шестнадцатиразрядные команды). В процессе работы возможна смена используемого набора команд, но микроконтроллеры ARM этой возможности лишены (?) и поддерживают только набор команд Thumb (и может быть Thumb-2). |
|
Тридцатидвухразрядные микропроцессоры и микроконтроллеры, разработанные фирмой MIPS Computer Systems. Помимо показанных на схеме, процессоры содержат/могут содержать регистры для обработки исключений и для вычислений с плавающей точкой. За некоторыми исключениями регистры $1-$31 могут быть использованы произвольно (но есть рекомендации относительно их использования), регистр $0 содержит ноль, pc - указатель команды, в регистры hi и lo записываются результаты умножения и деления. Отсутствуют команды для работы со стеком. Отсутствует регистр флагов. Все команды тридцатидвухразрядные. |
Команды состоят из одного, двух или трех шестнадцатиразрядных полуслов, имеются четыре формата - RR (одно полуслово), RX (два полуслова), RS/SI (два полуслова) и SS (три полуслова), первый (старший) байт команды содержит код операции, остальные содержат номера регистров, смещение и/или константу.
При выполнении арифметических и логических операций модифицируется поле прзнаков PSW (Condition Code, CC, биты 34..35, при счете справа налево 29..28), способ модификации зависит от команды. Например, в результате "обычного" вычитания (S) поле признаков может иметь значение 0 (результат равен нулю), 1 (результат отрицателен), 2 (результат положителен) и 3 (переполнение). В результате "логического" вычитания (SL) поле признаков может иметь значение 0 (результат равен нулю, переноса не было), 1 (результат не равен нулю, переноса не было), 2 (результат равен нулю, перенос был) и 3 (результат не равен нулю, перенос был). В последнем случае можно считать, что CC состоит из битов переноса и не-нуля. Команды переходов содержат четырехбитовое поле MASK, каждый разряд которого соответствует одному из четырех возможных значений CC. Переход выполняется если соответствующий значению CC разряд MASK содержит единицу. Соответствие следующее:
ZMPO - Zero - Minus - Plus - Overflow
или
ELHO - Equal - Low - High - Overflow
Разряды нумеруются справо налево, так что переходу при нулевом результате соответствует 1000
,
переходу при переполнении 0001
. Переходу при ненулевом результате соответствует 0111
,
безусловному переходу - 1111
.
Команды состоят из одного, двух или трех шестнадцатиразрядных слов, первое слово содержит код операции и, в большей части команд, битовые поля, определяющие разрядность операндов (1-байт или 0-слово), используемые регистры, способы формирования адресов ячеек оперативной памяти или условия и длины переходов. Второе и третье слово - числовая константа или адрес ячейки оперативной памяти. При выполнениии арифметических и логических команд, а также команд пересылки данных устанавливаются значения битов регистра состояния PS (C - перенос, V - арифметическое переполнение, Z - ноль, N - знак).
Регистр определяется номером от 0 до 7, регистры 0-5 предназначены для хранения данных или адресов, регистр 6 - указатель стека, регистр 7 - программный счетчик.
Операнд может быть указан одним из восьми способов:
|
BBB
|
|
R
|
000
|
Операнд находится в регистре
|
(R)
|
001
|
Адрес операнда находится в регистре
|
(R)+
|
010
|
Адрес операнда находится в регистре, после выборки операнда значение в регистре увеличивается на длину операнда в байтах
|
@(R)+
|
011
|
Адрес адреса операнда находится в регистре, после выборки операнда значение в регистре увеличивается на два
|
-(R)
|
100
|
Значение в регистре уменьшается на на длину операнда в байтах, после чего используется как адрес операнда
|
@-(R)
|
101
|
Значение в регистре уменьшается на два, после чего используется как адрес адреса операнда
|
Addr(R)
|
110
|
Адрес операнда является суммой константы и значения в регистре
|
@Addr(R)
|
111
|
Адрес адреса операнда является суммой константы и значения в регистре
|
Некоторые варианты не имеют точных аналогов в процессорах Intel/AMD и их назначение может быть неясно, но определенная логика и универсальность здесь есть. Например, способ адресации (R)+ в сочетании с регистром R7 используется для загрузки константы. После считывания первого слова команды (кода операции) значение в R7 увеличивается на 2 и он указывает на следующую за кодом операции константу. Она считывается и значение в R7 увеличивается еще на 2. Этот же способ адресации совместно с регистром R6 используется для извлечения слова из стека. Он же может использоваться совместно с другими регистрами. За счет этого возможно объединение обращения к элементу массива и увеличения значения индекса этого массива. А это уже оператор ++ языка C (правда, только постфиксный). Для префиксного оператора -- также есть аппаратная поддержка.
Коды условий переходов:
|
--COND--
|
|
|
r
|
00000001
|
-
|
Без условия
|
ne
|
00000010
|
Z=0
|
Результат не равен нулю
|
eq
|
00000011
|
Z=1
|
Результат равен нулю
|
ge
|
00000100
|
N=V
|
Больше или равно, слова больше и меньше предполагают сравнение чисел со знаком
|
lt
|
00000101
|
N!=V
|
Меньше
|
gt
|
00000110
|
N=V & Z=0
|
Больше
|
le
|
00000111
|
N!=V | Z=1
|
Меньше или равно
|
pl
|
10000000
|
N=0
|
Результат положителен
|
mi
|
10000001
|
N=1
|
Результат отрицателен
|
hi
|
10000010
|
C=0 & Z=0
|
Выше, слова ниже и выше предполагают сравнение чисел без знака
|
los
|
10000011
|
C=1 | Z=1
|
Ниже или равно
|
vc
|
10000100
|
V=0
|
Переполнение не произошло
|
vs
|
10000101
|
V=1
|
Переполнение произошло
|
cc/his
|
10000110
|
C=0
|
Перенос не установлен/выше или равно
|
cs/lo
|
10000111
|
C=1
|
Перенос установлен/ниже
|
Команды состоят из одного или двух байт, первый байт состоит из двух или трех полей - основного четырехразрядного кода операции, дополнительного четырехразрядного кода операции ввода-вывода, дополнительного четырехразрядного кода операции с аккумулятором, четырехразрядного номера регистра, трехразрядного номера пары регистров и дополнительного одноразрядного кода операции, четырехразрядного кода условия перехода, части адреса. Второй байт содержит константу или часть адреса.
Условия перехода связаны со значением флага переноса CY, нулевым или ненулевым значением аккумулятора (флага нулевого результата нет), значением на входе тестового сигнала TEST или некоторыми их комбинациями.
Команды состоят из одного, двух или трех байт, первый байт содержит код операции и, возможно, трехбитовые поля (или дополненное до трех двухбитовое), определяющие регистр-приемник (DST), регистр-источник (SRC), пару регистров (RP) или условие перехода (CCC), соответствующее значению одного из битов регистра состояния PSW (C - перенос, P - четность, Z - ноль, S - знак) - их значения устанавливаются при выполнении арифметических и логических операций. Флаг переполнения отсутствует.
|
DST
|
A
|
111
|
B
|
000
|
C
|
001
|
D
|
010
|
E
|
011
|
H
|
100
|
L
|
101
|
Mem
|
110
|
|
RP
|
BC
|
00
|
DE
|
01
|
HL
|
10
|
|
CCC
|
|
|
nz
|
000
|
Z=0
|
Результат не равен нулю
|
z
|
001
|
Z=1
|
Результат равен нулю
|
nc
|
010
|
C=0
|
Перенос не установлен
|
c
|
011
|
C=1
|
Перенос установлен
|
np
|
100
|
P=0
|
Число единиц результата нечетно
|
p
|
101
|
P=1
|
Число единиц результата четно
|
ns
|
110
|
S=0
|
Результат неотрицателен (ноль в старшем разряде)
|
s
|
111
|
S=1
|
Результат отрицателен (единица в старшем разряде)
|
Коды команд представлены в двоичной системе счисления, для сокращения записи в примерах использована шестнадцатеричная. Т.к. первый байт команды состоит из трех полей - одного двухразрядного и двух трехразрядных, при использовании восьмеричной системы счисления поля также как и в двоичной видны непосредственно, но было принято решение не использовать ее.
Команды состоят из одного, двух или трех байт, первый байт содержит код операции, состоящий из двух трехбитовых и одного двухбитового поля. Во втором трехбитовом поле может размещаться способ формирования адреса. В первом поле команд условного перехода размещается код условия (CCC), соответствующее значению одного из битов регистра состояния P (C - перенос, Z - ноль, N - знак и V - переполнение) - их значения устанавливаются при выполнении арифметических и логических операций, биты N и Z устанавливаутся также и при выполнении операций передачи данных.
|
Cпособ формирования адреса
|
BBB
|
|
ZP
|
Addr8
|
001
|
Восьмиразрядный адрес байта в нулевой странице является частью команды
|
ZPX
|
Addr8+X
|
101
|
Восьмиразрядный адрес, являющийся частью команды, складывается со значением в регистре X
|
ZPY
|
Addr8+Y
|
101
|
Восьмиразрядный адрес, являющийся частью команды, складывается со значением в регистре Y (только в командах ldx и stx)
|
ABS
|
Addr16
|
011
|
Шестнадцатиразрядный адрес является частью команды
|
ABX
|
Addr16+Y
|
111
|
Шестнадцатиразрядный адрес, являющийся частью команды, складывается со значением в регистре X
|
ABY
|
Addr16+Y
|
110
|
Шестнадцатиразрядный адрес, являющийся частью команды, складывается со значением в регистре Y
|
ABY
|
Addr16+Y
|
111
|
Шестнадцатиразрядный адрес, являющийся частью команды, складывается со значением в регистре Y (только в командах ldx и stx)
|
NDX
|
[Addr8+X]16
|
000
|
Шестнадцатиразрядный адрес извлекается из двух байт в нулевой странице, на которые указывает сумма являющегося частью команды восьмиразрядного адреса и зачения в регистре X
|
NDY
|
[Addr8]16+Y
|
100
|
Шестнадцатиразрядный адрес извлекается из двух байт в нулевой странице, на которые указывает являющийся частью команды восьмиразрядный адрес, после чего к нему прибавляется значение в регистре Y
|
|
CCC
|
|
|
pl
|
000
|
N=0
|
Результат неотрицателен (ноль в старшем разряде)
|
mi
|
001
|
N=1
|
Результат отрицателен (единица в старшем разряде)
|
vc
|
010
|
V=0
|
Переполнение не зафиксировано
|
vs
|
011
|
V=1
|
Зафиксировано переполнение
|
сс
|
100
|
С=0
|
Перенос сброшен
|
cs
|
101
|
C=1
|
Перенос установлен
|
ne
|
110
|
Z=0
|
Результат не равен нулю
|
eq
|
111
|
Z=1
|
Результат равен нулю
|
Команда может иметь длину от одного до шести байт. Первый байт команды всегда содержит код операции и может включать однобитовые поля направления передачи (D), величина сдвига (V), разрядности (W) трехбитовое поле, определяющее регистр (REG) или черырехбитовое условие перехода (COND), соответствующее значению одного, двух или трех битов регистра состояния PSW (CF - перенос, PF - четность, ZF - ноль, SF - знак, OF - переполнение) - их значения устанавливаются при выполнении арифметических и логических операций. Коды некоторых команд не умещаются в один байт, оставшиеся биты находятся во втором байте команды.
Второй байт обычно содержит поля, указывающие разрядность смещения/использование второго регистра (MOD, MM), регистр (REG) и способ формирования адреса, т.е. используемые индексные регистры (R/M) (байт MOD-REG-R/M) или сегментный регистр (SR). Следующие байты, если они есть, содержат смещения и данные.
Направление
|
D
|
Из регистра
|
0
|
В регистр
|
1
|
Величина сдвига
|
V
|
1
|
0
|
В регистре CL
|
1
|
Разрядность
|
W
|
Байт
|
0
|
Слово
|
1
|
Разрядность смещения
|
MM
|
Отсутствует
|
00
|
Байт
|
01
|
Слово
|
10
|
Второй операнд-регистр, R/M=REG
|
11
|
|
REG
|
AX/AL
|
000
|
BX/BL
|
011
|
CX/CL
|
001
|
DX/DL
|
010
|
SP/AH
|
100
|
DI/BH
|
111
|
BP/CH
|
101
|
SI/DH
|
110
|
|
SR
|
CS
|
01
|
SS
|
10
|
DS
|
11
|
ES
|
00
|
Cпособ формирования адреса
|
R/M
|
DS:[BX+Ofs]
|
111
|
DS:[SI+Ofs]
|
100
|
DS:[DI+Ofs]
|
101
|
DS:[Ofs16](MM=00)/SS:[BP+Ofs](MOD!=00)
|
110
|
DS:[BX+SI+Ofs]
|
000
|
DS:[BX+DI+Ofs]
|
001
|
SS:[BP+SI+Ofs]
|
010
|
SS:[BP+DI+Ofs]
|
011
|
|
COND
|
|
|
o
|
0000
|
OF=1
|
Произошло переполнение
|
no
|
0001
|
OF=0
|
Переполнение не произошло
|
b/nae
|
0010
|
CF=1
|
Перенос установлен (a ниже b), слова ниже и выше предполагают сравнение чисел без знака
|
nb/ae
|
0011
|
CF=0
|
Перенос не установлен (a не ниже b)
|
z/e
|
0100
|
ZF=1
|
Результат равен нулю
|
nz/ne
|
0101
|
ZF=0
|
Результат не равен нулю
|
be/na
|
0110
|
CF=1 | ZF=1
|
a ниже или равно b
|
a
|
0111
|
CF=0 & ZF=0
|
a выше b
|
s
|
1000
|
SF=1
|
Результат отрицателен
|
ns
|
1001
|
SF=0
|
Результат неотрицателен
|
p
|
1010
|
PF=1
|
Младший байт результата содержит четное число единиц
|
np
|
1011
|
PF=0
|
Младший байт результата содержит нечетное число единиц
|
l
|
1100
|
SF!=OF
|
a меньше b, слова меньше и больше предполагают сравнение чисел со знаком
|
nl/ge
|
1101
|
SF=OF
|
a больше или равно b
|
le
|
1110
|
SF!=OF | ZF=1
|
a меньше или равно b
|
g
|
1111
|
SF=OF & ZF=0
|
a больше b
|
Коды команд представлены в двоичной системе счисления, для сокращения записи в примерах использована шестнадцатеричная. Второй байт команды состоит из трех полей - одного двухразрядного и двух трехразрядных, так что при использовании восьмеричной системы счисления поля видны непосредственно, но было принято решение не использовать ее.
Команда может иметь длину от одного до одиннадцати байт. Первый байт команды всегда содержит код операции и может включать однобитовые поля направления передачи (D), величина сдвига (V), разрядности (W) трехбитовое поле, определяющее регистр (REG) или черырехбитовое условие перехода (COND). Коды некоторых команд не умещаются в один байт, оставшиеся биты находятся во втором байте команды.
За кодом операции может следовать байт MOD-REG-R/M, за ним может следовать байт масштаба, индекса и базы адреса (SCALE-INDEX-BASE, SIB). Следующие байты, если они есть, содержат смещения и данные. Коды REG такие же как и в процессоре 8086 (но по умолчанию они соответствуют восьми- и тридцатидвухразрядным регистрам), коды R/M изменены, они определяют индексный регистр (в роли которого может выступать любой регистр данных) или наличие байта SIB.
Вместо шестнадцатиразрядных значений предполагаются тридцатидвухразрядные, но с помощью предшествующих команде префиксных байтов это может быть изменено.
Cпособ формирования адреса
|
R/M
|
DS:[EAX+Ofs]
|
000
|
DS:[EBX+Ofs]
|
011
|
DS:[ECX+Ofs]
|
001
|
DS:[EDX+Ofs]
|
010
|
DS:[ESI+Ofs]
|
110
|
DS:[EDI+Ofs]
|
111
|
DS:[Ofs32](MOD=00)/SS:[EBP+Ofs](MOD!=00)
|
101
|
Используется байт SIB
|
100
|
При использовании байта SIB адрес формируется по формуле
SEG:[BBB + SS * IND + Ofs]
Такой способ формирования адреса может показаться излишне сложным, но он удобен при обработке массивов простых типов, добавление базы, как и в 8086, упрощает работу с данными в стеке.
Наличие и разрядность Ofs определяется полем MM(MOD) или полем BBB(BASE), два из трех слагаемых могут отсутствовать, селектор SEG определяется базовым регистром - SS, при использоваии EBP/ESP и DS в остальных случаях, значения полей SS(SCALE), IND(INDEX) и BBB(BASE) приведены ниже:
Множитель
|
SS
|
|||||||||||||||||||||||||||||||||||
x1
|
00
|
|||||||||||||||||||||||||||||||||||
x2
|
01
|
|||||||||||||||||||||||||||||||||||
x4
|
10
|
|||||||||||||||||||||||||||||||||||
x8
|
11
|
Индекс
|
IND
|
EAX
|
000
|
EBX
|
011
|
ECX
|
001
|
EDX
|
010
|
ESI
|
110
|
EDI
|
111
|
EBP
|
101
|
Нет
|
100
|
База
|
BBB
|
EAX
|
000
|
EBX
|
011
|
ECX
|
001
|
EDX
|
010
|
ESI
|
110
|
EDI
|
111
|
Ofs32(MM=00)/EBP(MM!=00)
|
101
|
ESP
|
100
|
Команды имеют значительное сходство с командами i386, но для работы с шестидесятичетырехразрядными операндами (равно как и с добавленными регистрами R8-R15) команде должен предшествовать префикс REX, четыре младших бита которого используются для указания разрядности операндов (W), расширения слева поля REG байта MOD-REG-R/M (R), поля IND байта SIB (X), поля R/M байта MOD-REG-R/M или поля BBB байта SIB или поля REG кода операции (B). Единица в бите W указывает на использование шестидесятичетырехразрядных операндов. Префикс имеет следующую структуру:
0100
|
W
|
R
|
X
|
B
|
Значения префикса от 0x40 до 0x47 совпадают с однобайтными командами inc и dec процессоров 8086 и i386, так что возможно использование только длинной (с байтом MOD-REG-R/M) формы этих команд.
Если значение шестидесятичетырехразрядной константы может быть записано в четыре байта, старшие четыре байта могут отсутствовать, перед выполнением команды будет выполнено расширение тридцатидвухразрядной константы со знаком или без него. Это в ряде случаев позволяет уменьшить длину команды.
Шестидесятичетырехразрядный адрес может использоваться лишь в нескольких командах (?).
Команды имеют длину от одного до трех байт. При выполнениии арифметических, логических команд и некорорых других команд устанавливаются значения битов регистра состояния PSW (C - перенос, AC - дополнительный перенос, OV - переполнение P - четность, бит нулевого результата отсутствует). PSW также содержит два бита выбора банка регистров (RS1:RS0).
Число команд довольно велико.
Команды имеют фиксированную длину - одно двенадцати- (PIC10) или четырнадцатиразрядное (PIC12/PIC16) слово, состоящее из нескольких полей. Код операции в трех-шести(?) старших битах присутствует всегда, в оставшихся битах в зависимости от команды находятся номер регистра, флаг направления, значение константы или младшие биты адреса перехода. При выполнениии арифметических, логических команд и некорорых других команд устанавливаются значения битов регистра состояния STATUS (C - перенос, DC - дополнительный перенос, Z - ноль). В большинстве моделей длины команды недостаточно для указания регистра, выполнение команды зависит от значения битов выбора банка регистра состояния или регистра выбора банка (это зависит от типа контроллера) . Длины команды также недостаточно для хранения полного адреса, поэтому старшие биты загружаются из регистра PCLATCH, в который они должны быть записаны перед выполнением перехода.
Связь длины команды с обозначением микросхемы по-видимому отсутствует, так что сказанное выше не совсем верно. В частности, микроконтроллеры PIC16F59 в сорокавыводном корпусе PDIP и сорокачетырехвыводном TQFP являются представителями PIC10.
Команды состоят из одного или двух шестнадцатиразрядных слов, второе слово содержит адрес данных, адрес перехода или его часть. При выполнениии арифметических, логических команд и некорорых других команд устанавливаются значения битов регистра состояния SREG (C - перенос, Z - ноль, N - отрицательное значение, V - переполнение, S - знак, H - дополнительный перенос). Значение флага S устанавливается равным единице при несовпадении значений флагов N и V, и нулю при совпадении. Наличие этого дополнительного флага делает достаточной зависимость команд условных переходов только от одного флага регистра состояния.
Вымышленный восьмиразрядный микроконтроллер, реализованный на отладочной плате FPGA Cyclone IV. Все команды имеют длину 1 байт - предпоагалось, что это упростит реализацию. Память программ и память данных могут иметь объем до 64 килобайт каждая, первые 256 байт памяти данных могут использоваться как адресуемый регистром S стек. 8 портов ввода/вывода находятся в своем адресном пространстве.
Арифметические операции выполняются над парами регистров B/D и C/E, в разных вариантах реализации результат операции помещается в регистр A и/или в первый регистр пары (из-за не слишком обоснованного желания не выполнять в одном такте чтение из регистра и запись в него же). Используемая пара регистров определяется одним битом команды. Из-за того-же желания не изменять читаемый регистр был вариант, в котором команды инкремена и декремента записывали измененное значение регистра в регистр А и изменить значение в самом регистре можно было с помощью последующей команы пересылки (mov). Это все можно обойти введением дополнительного скрытого от программиста регистра, да и проблема изменения читаемого регистра существует только если он построен на защелках (latch). Двухступенчатые триггеры свободны от этого недостатка.
Обращения к памяти, переходы и вызовы подпрограмм только косвенные, в качестве индекса используется пара регистров HL. Одна команда может читать или записывать только один байт.
Из 256 кодов команд не используются 12 из группы команд переходов - добавление каких-то новых команд может потребовать отказа от реализованых. Восемь бит на код команд это очень мало и не зря во многих микроконтроллерах используются более длинные коды.
Регистры имеют следующие трехразрядные коды:
Регистр
|
Код
|
Примечание
|
B
|
000
|
|
C
|
001
|
|
D
|
010
|
|
E
|
011
|
|
H
|
100
|
|
L
|
101
|
|
A/F
|
110
|
Интерпретация кода зависит от команды
|
S
|
111
|
|
Пары регистров имеют следующие одноразрядные коды:
Пара
|
Код
|
B/D
|
0
|
C/E
|
1
|
Команды ARM состоят из одного тридцатидвухразрядного слова и содержат ряд полей, в том числе четырехразрядное поле условия. За счет этого любая команда может быть пропущена без использования условного перехода. Также есть поле запрета/разрешения модификации регистра флагов APRS. Команды обработки данных двух- и трехадресные, т.е. два операнда могут быть взяты из двух регистров, а результат помещен в третий.
Большинство команд Thumb не содержат поля условия, вместо него в набор команд Thumb-2 был введен так называемый IT-блок (If-Then-(Else)-блок) - шестнадцатиразрядная команда, определяющая условия выполнения одной-четырех последующих команд. Что касается модификации регистра флагов APRS, то одни инструкции модифицируют его, другие нет и это может быть источником ошибок при написании программ на ассемблере.
Коды условий переходов следующие:
|
COND
|
|
|
eq
|
0000
|
Z=1
|
Результат равен нулю
|
ne
|
0001
|
Z=0
|
Результат не равен нулю
|
cs/hs
|
0010
|
С=1
|
Перенос установлен (higher or same - a выше или равно b, слова ниже и выше предполагают сравнение чисел без знака)
|
cс/lo
|
0011
|
С=0
|
Перенос не установлен
|
mi
|
0100
|
N=1
|
Знак установлен
|
pl
|
0101
|
N=1
|
Знак не установлен
|
vs
|
0110
|
V=1
|
Переполнение установлено
|
vс
|
0111
|
V=0
|
Переполнение не установлено
|
hi
|
1000
|
C=1 & Z=0
|
a выше b
|
ls
|
1001
|
C=0 | Z=1
|
a ниже или равно b
|
ge
|
1010
|
N=V
|
a больше или равно b
|
lt
|
1011
|
N!=V
|
a меньше b
|
gt
|
1100
|
N=V & Z=0
|
a больше b
|
le
|
1101
|
N!=V | Z=1
|
a меньше или равно b
|
Команды MIPS состоят из одного тридцатидвухразрядного слова и содержат ряд полей, в старших шести битах размещается код операции, определяющий также один из трех форматов команды. При нулевом коде операции действие определяется младшими шестью битами команды (кодом функции).
|
|
|
|
la D, V(0, 0)
|
|
|
|
|
l D, F(X, B)
|
|
|
|
|
lh D, F(X, B)
|
|
|
|
|
st S, F(X, B)
|
|
|
|
|
sth S, F(X, B)
|
|
|
|
|
stс S, F(X, B)
|
|
|
lr D, S
|
Все приведенные ниже команды являются модификациями одной команды mov и отличаются только используемыми способами адресации и регистрами. Допустимы и все(?) другие комбинации, например пересылки память-память.
|
|
|
|
mov #Val, RRR
|
|||||||
|
|
|
|
mov @#Addr, RRR
|
|||||||
|
|
|
|
mov Addr(IND), RRR
|
|||||||
|
|
|
|
mov RRR, @#Addr
|
|||||||
|
|
|
|
mov RRR, Addr(IND)
|
|||||||
|
|
mov RRR, -(SP)
|
||||||
|
|
mov (SP)+, RRR
|
||||||
|
|
ldm C
|
|
|
|
|
fim P, C
|
|
|
fin P
|
|
|
ld R
|
|
|
xch R
|
|
|
stc
|
|
|
clc
|
|
|
cmc
|
|
|
dcl
|
|
|
src P
|
|
|
rdm
|
|
|
wrm
|
|
|
rdr
|
|
|
wrr
|
|
|
wmp
|
Универсальные команды
|
|
|
|
mvi Dst, Val
|
|
|
|
|
|
|
lxi Rp, Val
|
|
|
|
|
|
|
lxi SP, Val
|
|
|
mov Dst, M
|
|
|
mov Dst, Src
|
|
|
mov M, Src
|
|
|
push Rp
|
|
|
pop Rp
|
Неуниверсальные команды, работающие только с аккумулятором или парой регистров HL
|
|
|
|
|
|
lda Addr
|
|
|
|
|
|
|
lhld Addr
|
|
|
xchg
|
|
|
|
|
|
|
sta Addr
|
|
|
|
|
|
|
shld Addr
|
Вторая тройка бит определяет способ адресации. Если получателем или источником данных является индексный регистр X или Y, он сам не может использоваться в качестве индекса и косвенные способы формирования адреса (NDX и NDY) недоступны. Помещаться в стек могут только значения из аккумулятора и регистра состояния, извлекаемые из стека значения могут помещаться только в эти два регистра. Два младших бита кода команды определяют регистр (?). В ассемблерной записи получатель и источник данных указывается в мнемонике, а не в операнде, поэтому команд довольно много.
|
|
|
|
lda #Val
|
|
|
|
|
ldx #Val
|
|
|
|
|
ldy #Val
|
|
|
|
|
|
|
lda Addr
|
|
|
|
|
|
|
ldx Addr
|
|
|
|
|
|
|
ldy Addr
|
|
|
|
|
lda (Addr), Y
|
|
|
|
|
|
|
sta Addr
|
|
|
|
|
|
|
stx Addr
|
|
|
|
|
|
|
sty Addr
|
|
|
|
|
sta (Addr), Y
|
|
|
txa
|
|
|
tax
|
|
|
tya
|
|
|
tay
|
|
|
pha
|
|
|
pla
|
За исключением загрузки константы, операций с портами ввода/вывода и со стеком, второй байт команды - байт MOD-REG-R/M. Приведены только варианты команд, аналогичные командам 8080, прочие варианты получаются заменой значений полей MOD и R/M. Команды чтения из памяти и записи в память отличаются только битом направления D (1-й бит в коде операции).
|
|
|
[
|
|
]
|
|
mov Dst, Val
|
|
|
|
|
|
|
|
|
mov Dst, DS:[Addr]
|
|
|
|
|
mov Dst, DS:[BX]
|
|
|
|
|
|
|
|
|
mov Dst, DS:[BX+Addr]
|
|
|
|
|
mov Dst, Src
|
|
|
|
|
xchg Dst, Src
|
|
|
|
|
|
|
|
|
mov DS:[Addr], Src
|
|
|
|
|
mov DS:[BX], Src
|
|
|
|
|
|
|
|
|
mov DS:[BX+Addr], Src
|
|
|
|
|
in AL/AX, P
|
|
|
in AL/AX, DX
|
|
|
|
|
out P, AL/AX
|
|
|
out DX, AL/AX
|
|
|
push Src
|
|
|
pop Dst
|
Отличия от 8086 в разрядности операндов и в других значениях поля R/M (второй байт всех перечисленных комано, кроме загрузки константы и операций со стеком).
|
|
|
[
|
|
|
|
|
|
]
|
|
mov Dst, Val
|
|
|
|
|
|
|
|
|
|
|
|
|
mov Dst, DS:[Addr]
|
|
|
|
|
mov Dst, DS:[EBX]
|
|
|
|
|
|
|
|
|
|
|
|
|
mov Dst, DS:[EBX+Addr]
|
|
|
|
|
mov Dst, Src
|
|
|
|
|
xchg Dst, Src
|
|
|
|
|
|
|
|
|
|
|
|
|
mov DS:[Addr], Src
|
|
|
|
|
mov DS:[EBX], Src
|
|
|
|
|
|
|
|
|
|
|
|
|
mov DS:[EBX+Addr], Src
|
|
|
push Src
|
|
|
pop Dst
|
Командам, имеющим дело с шестидесятичетырехразрядными регистрами предшествует префикс REX:
|
|
|
|
mov Dst, Val
|
|
|
|
|
|
|
|
|
|
|
mov Dst, Val
|
FFFFFFFF
(-1
) будет записана как FFFFFFFFFFFFFFFF
|
|
|
|
|
|
|
|
|
|
|
|
mov Dst, Val
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mov Dst, Val
|
|
|
|
|
mov Dst, [RBX]
|
|
|
|
|
|
|
mov Dst, [RBX]
|
|
|
|
|
|
|
mov Dst, Src
|
|
|
|
|
xchg RAX, Reg
|
|
|
|
|
mov [RBX], Src
|
|
|
|
|
|
|
mov [RBX], Src
|
|
|
push Src
|
|
|
pop Dst
|
Команд передачи данных между регистрами и расположенной на кристалле небольшой памятью очень много. Имеются команды, непосредственно изменяющие значения отдельных битов некоторых ячеек памяти и портов, операндом этих команд является восьмиразрядный битовый адрес, пять бит которого преобразуются в адрес ячейки, оставшиеся три - в номер бита.
Некоторые модели содержат дополнительные 128 восьмиразрядных ячеек памяти, для доступа к ним может использоваться только косвенная адресация(т.е. адрес ячейки должен извлекаться из регистра).
Если в системе используется дополнительная микросхема памяти, получателем или источником данных всегда является регистр-аккумулятор A, адресация памяти только косвенная (с использованием регистра-указателя DPTR, или одного из регистров R0, R1) - эти четыре команды не приведены.
|
|
|
|
mov A, #C
|
|
|
|
|
mov R, #C
|
|
|
|
|
|
|
mov DPTR, #C
|
|
|
|
|
|
|
mov Dst, #C
|
|
|
|
|
mov @R, #C ;R = R0 или R1
|
|
|
mov A, R
|
|
|
mov R, A
|
|
|
|
|
mov A, Src
|
|
|
|
|
mov R, Src
|
|
|
|
|
mov Dst, A
|
|
|
|
|
mov Dst, R
|
|
|
mov A, @R ;R = R0 или R1
|
|
|
mov @R, A ;R = R0 или R1
|
|
|
|
|
|
|
mov Dst, Src
|
|
|
|
|
mov Dst, @R ;R = R0 или R1
|
|
|
|
|
mov @R, Src ;R = R0 или R1
|
|
|
movс A, @A + DPTR
|
|
|
movс A, @A + PC
|
|
|
|
|
push Src
|
|
|
|
|
pop Dst
|
|
|
|
|
clr BitAddr
|
|
|
|
|
setb BitAddr
|
|
|
|
|
cpl BitAddr
|
|
|
|
|
clrw
|
|
|
|
|
clrf F
|
|
|
|
|
movlw C
|
|
|
|
|
movwf F
|
|
|
|
|
movf F, 0
|
|
|
|
|
movf F, 1
|
|
|
|
|
swapf F, D
|
|
|
|
|
bcf F, B
|
|
|
|
|
bsf F, B
|
|
|
|
|
option
|
|
|
|
|
tris F
|
|
|
|
|
clrw
|
|
|
|
|
clrf F
|
|
|
|
|
movlw C
|
|
|
|
|
movwf F
|
|
|
|
|
movf F, 0
|
|
|
|
|
movf F, 1
|
|
|
|
|
bcf F, B
|
|
|
|
|
bsf F, B
|
|
|
ldi D, C
|
|
|
lpm
|
|
|
mov D, S
|
|
|
out P, S
|
|
|
out P, S
|
|
|
|
|
lds D, A
|
|
|
ld D, X
|
|
|
|
|
sts A, S
|
|
|
st X, S
|
|
|
ll A, C
|
|
|
lh A, C
|
|
|
mov DST, SRC
|
|
|
st SRC
|
|
|
ld DST
|
|
|
push SRC
|
|
|
pop DST
|
|
|
lpm R
|
|
|
out P, A
|
|
|
in A, P
|
|
|
cb P, B
|
|
|
sb P, B
|
|
|
movs D, C
|
|
|
|
|
movw D, C
|
|
|
|
|
movt D, C
|
4 * ((PC + 4) / 4 + S)(?)
. Адрес константы должен быть выровнен по границе слова
|
|
ldr D, [PC + S]
|
TODO
|
|
|
|
ldr D, [PC +/- S]
|
|
|
ldr D, [I + S]
|
|
|
str R, [I + S]
|
|
|
strh R, [I + S]
|
|
|
push {REGS}
|
|
|
pop {REGS}
|
|
|
lui $t, i
|
|
|
ori $t, $0, i
|
|
|
ori $t, $s, 0
|
|
|
lw $t, i($s)
|
|
|
lb $t, i($s)
|
|
|
lbu $t, i($s)
|
|
|
sw $t, i($s)
|
|
|
sb $t, i($s)
|
|
|
|
|
a D, F(X, B)
|
|
|
|
|
al D, F(X, B)
|
|
|
alr D, S
|
|
|
|
|
s D, F(X, B)
|
|
|
|
|
sl D, F(X, B)
|
|
|
slr D, S
|
|
|
|
|
m D, F(X, B)
|
|
|
mr D, S
|
|
|
|
|
d D, F(X, B)
|
|
|
dr D, S
|
В эту группу входят сложение, вычитание, битовые операции И, ИЛИ, исключающее ИЛИ и сдвиги на один разряд. Команды умножения и деления отсутствуют (только в младших моделях?).
|
|
add DST, SRC
|
||||||
|
|
sub DST, SRC
|
||||||
Первый операнд выбирается из аккумулятора, второй (если есть) - из регистра или из памяти, результат помещается в аккумулятор. В эту группу входят сложение, вычитание, и сдвиги на один разряд. Команды битовых операций, умножения и деления отсутствуют.
|
|
add R
|
|
|
sub R
|
|
|
iac
|
|
|
dac
|
Команды обработки байтов
Основные команды, первый операнд выбирается из аккумулятора, второй (если есть) - из регистра или из памяти, результат помещается в аккумулятор. В эту группу входят сложение, вычитание, битовые операции И, ИЛИ, исключающее ИЛИ и сдвиги на один разряд. Команды умножения и деления отсутствуют.
|
|
add Src
|
|
|
adc Src
|
|
|
sub Src
|
|
|
sbb Src
|
Дополнительные команды, работающие с парами регистров
|
|
dad Rp
|
|
|
inx Rp
|
|
|
dcx Rp
|
При выполнении большинства команд первый операнд выбирается из аккумулятора, результат помещается в аккумулятор. В отличие от ряда других процессоров перед выполнением сложения и вычитания признак переноса должен быть явно установлен или сброшен. Как и в 8080 команды умножения и деления отсутствуют. В командах, работающих с памятью, вторая тройка бит определяет способ формирования адреса. Приведенные команды оращаются к ячейке памяти в нулевой странице.
|
|
|
|
adc #Val
|
|
|
|
|
adc Addr
|
|
|
|
|
sbc #Val
|
|
|
|
|
sbc Addr
|
|
|
clc
|
|
|
sec
|
|
|
|
|
inc Addr
|
|
|
|
|
dec Addr
|
|
|
inx
|
|
|
dex
|
|
|
iny
|
|
|
dey
|
В эту группу входят арифметические операции, битовые операции И, ИЛИ, исключающее ИЛИ и сдвиги. Во всех командах расположение первого операнда и результата совпадают. За исключением команд умножения и деления это может быть как регистр, так и ячейка памяти. Первый операнд команды умножения всегда AX или AL, деления - DX:AX или AH:AL, результат - DX:AX или AH:AL. При делении в DX или AH помещается остаток.
|
|
|
|
add Dst, Src
|
|
|
|
|
sub Dst, Src
|
|
|
|
|
mul Src
|
|
|
|
|
div Src
|
|
|
inc Reg
|
|
|
dec Reg
|
|
|
|
|
add Dst, Src
|
|
|
|
|
sub Dst, Src
|
|
|
|
|
mul Src
|
|
|
|
|
div Src
|
|
|
inc Reg
|
|
|
dec Reg
|
Команды обработки шестидесятичетырехразрядных данных такие же, как и тридцатидвухразрядных, но им предшествует префикс REX:
|
|
|
|
|
|
add Dst, Src
|
|
|
|
|
|
|
sub Dst, Src
|
|
|
|
|
|
|
mul Src
|
|
|
|
|
|
|
div Src
|
|
|
|
|
|
|
inc Reg
|
|
|
|
|
|
|
dec Reg
|
|
|
add A, R ;[A] = [A] + [R]
|
|
|
addc A, R ;[A] = [A] + [R] + [C]
|
|
|
subb A, R ;[A] = [A] - [R] - [C]
|
|
|
mul AB
|
|
|
div AB
|
|
|
|
|
addwf F, 0
|
|
|
|
|
addwf F, 1
|
|
|
|
|
subwf F, 0
|
|
|
|
|
subwf F, 1
|
|
|
|
|
incf F, D
|
|
|
|
|
decf F, D
|
|
|
|
|
comf F, D
|
|
|
|
|
rlf F, D
|
|
|
|
|
rrf F, D
|
|
|
|
|
iorwf F, D
|
|
|
|
|
andwf F, D
|
|
|
|
|
xorwf F, D
|
|
|
|
|
iorlw C
|
|
|
|
|
andlw C
|
|
|
|
|
xorlw C
|
|
|
|
|
addlw C
|
|
|
|
|
addwf F, 0
|
|
|
|
|
addwf F, 1
|
|
|
|
|
sublw C
|
|
|
|
|
subwf F, 0
|
|
|
|
|
subwf F, 1
|
|
|
|
|
incf F, D
|
|
|
|
|
decf F, D
|
|
|
|
|
iorwf F, D
|
|
|
|
|
andwf F, D
|
|
|
|
|
xorwf F, D
|
|
|
add D, S
|
|
|
adc D, S
|
|
|
sub D, S
|
|
|
sbc D, S
|
|
|
mul R, S
|
|
|
or D, S
|
|
|
and D, S
|
|
|
eor D, S
|
|
|
subi D, C
|
|
|
ori D, C
|
|
|
andi D, C
|
|
|
adiw D, C
|
|
|
sbiw D, C
|
Во всех командых DST и SRC могут быть только B/D или C/E.
|
|
inc REG
|
|
|
dec REG
|
|
|
add DST, SRC
|
|
|
adс DST, SRC
|
|
|
sub DST, SRC
|
|
|
sbb DST, SRC
|
|
|
and DST, SRC
|
|
|
or DST, SRC
|
|
|
xor DST, SRC
|
|
|
adds R, C
|
|
|
subs R, C
|
|
|
adds D, N, M
|
|
|
subs D, N, M
|
|
|
orrs D, S
|
|
|
ands D, S
|
|
|
eors D, S
|
|
|
add $d, $s, $t
|
|
|
addu $d, $s, $t
|
|
|
sub $d, $s, $t
|
|
|
subu $d, $s, $t
|
|
|
addi $t, $s, I
|
|
|
addiu $t, $s, I
|
|
|
|
|
bc M, F(X, B)
|
|
|
|
|
b F(X, B)
|
|
|
bcr M, X
|
|
|
br X
|
|
|
|
|
bal L, F(X, B)
|
|
|
balr L, X
|
Условные переходы только относительные, безусловные переходы и переходы к подпрограмме могут быть абсолютными и относительными. Здесь приведены только команды относительных переходов, соответствующие аналогичным командам процессоров 80x86, но есть и более экзотические варианты.
|
|
|
bxx Ofs
|
|
|
|
|
jmp Ofs
|
|
|
|
|
jsr Ofs
|
|
|
rts R7
|
|
|
halt
|
Во всех командах указываются абсолютные значения адресов, так что перемещение программы из одной области памяти в другую невозможно.
|
|
|
|
jcn A ;Переход выполняется при истинности условия (I != 0) ^ ((Z != 0) & (AC = 0)) | ((C != 0) & (CY != 0)) | ((T != 0) & (TEST = 0))
|
|
|
|
|
jun A
|
|
|
|
|
jms A
|
|
|
bbl DDDD
|
Комбинированная команда (она одна?)
|
|
|
|
jsz R, A
|
Во всех командах указываются абсолютные значения адресов, так что перемещение программы из одной области памяти в другую невозможно.
|
|
|
|
|
|
jcc Addr
|
|
|
|
|
|
|
jmp Addr
|
|
|
|
|
|
|
call Addr
|
|
|
pchl
|
|
|
ret
|
В командах условных переходов указываются восьмиразрядные смещения относительно начала следующей команды, в командах безусловных переходов и переходов к подпрограмме указываются абсолютные значения адресов, так что перемещение программы из одной области памяти в другую невозможно.
|
|
|
|
bcc Ofs
|
|
|
|
|
|
|
jmp Addr
|
|
|
|
|
|
|
jsr Addr
|
|
|
rts
|
В отличие от процессора 8080 во всех командах указываются не абсолютные адреса переходов, а смещения относительно адреса, следующего за командой. Кроме того, в командах условных переходов смещение может быть только восьмиразрядным, так что диапазон переходов находится в пределах от -128 до 127. Если требуется переход с большим смещением, придется комбинировать условный переход с безусловным.
Все перечисленные ниже команды кроме int выполняют переходы в пределах одного сегмента, т.е. не изменяют значение CS. Команды переходов/вызовов/возвратов в другой сегмент кода также существуют, но в них значение CS задается абсолютно и, соответственно, перемещение программы из одной области памяти в другую невозможно. В системе MS-DOS EXE-файл содержит таблицу перемещений, содержащую адреса, сегментные части которых должны скорректированы перед запуском программы.
|
|
|
|
jcc Ofs
|
|
|
|
|
|
|
jmp Ofs
|
|
|
|
|
|
|
call Ofs
|
|
|
|
|
call [BX]
|
|
|
ret
|
|
|
|
|
int Numb
|
Коды команд не отличаются от кодов аналогичных команд процессора 8086, но вместо шестнадцатиразрядных смещений используются тридцатидвухразрядные. Добавлены команды условных переходов с тридцатидвухразрядным смещением.
|
|
|
|
jcc Ofs
|
|
|
|
|
|
|
|
|
|
|
|
|
jcc Ofs
|
|
|
|
[
|
|
|
|
|
|
]
|
|
jmp Ofs
|
|
|
|
|
|
|
|
|
|
|
call Ofs
|
|
|
|
|
call [EBX]
|
|
|
ret
|
|
|
|
|
int Numb
|
Коды команд не отличаются от кодов аналогичных команд процессора i386, добавлена команда syscall, используемая для вызовов функций ОС вместо int.
|
|
|
|
syscall
|
|
|
|
|
jz ofS
|
;Jump if Zero
|
|
|
|
|
jnz ofS
|
;Jump if Not Zero
|
|
|
|
|
|
|
jbc BitAddr, ofS
|
;Jump if Bit Clear
|
|
|
|
|
|
|
jb BitAddr, ofS
|
;Jump if Bit set
|
|
|
|
|
|
|
ljmp Addr
|
|
|
|
|
|
|
lcall Addr
|
|
|
ret
|
Комбинированные команды
|
|
|
|
djnz R, ofS
|
;Decrement and Jump if Not Zero
|
|
|
|
|
btfsc F, B
|
;Bit Test F and Skip if Clear
|
|
|
|
|
btfss F, B
|
;Bit Test F and Skip if Set
|
|
|
|
|
goto A
|
|
|
|
|
call A
|
|
|
|
|
retlw C
|
|
|
|
|
sleep
|
|
|
|
|
clrwdt
|
|
|
|
|
nop
|
Комбинированные команды
|
|
|
|
incfsz F, D
|
;Increment F and Skip if Zero
|
|
|
|
|
decfsz F, D
|
;Decrement F and Skip if Zero
|
|
|
|
|
btfsc F, B
|
;Bit Test F and Skip if Clear
|
|
|
|
|
btfss F, B
|
;Bit Test F and Skip if Set
|
|
|
|
|
goto A
|
|
|
|
|
call A
|
|
|
|
|
return
|
|
|
|
|
retlw C
|
Комбинированные команды
|
|
|
|
incfsz F, D
|
;Increment F and Skip if Zero
|
|
|
|
|
decfsz F, D
|
;Decrement F and Skip if Zero
|
|
|
sbrc R, B
|
;Skip if Bit of R is Clear
|
|
|
sbrs R, B
|
;Skip if Bit of R if Set
|
|
|
sbic P, B
|
;Skip if Bit of Io is Clear
|
|
|
sbis P, B
|
;Skip if Bit of Io is Set
|
|
|
brbc B, ofS
|
|
|
brbs B, ofS
|
|
|
|
|
jmp A
|
|
|
rjmp ofS
|
|
|
|
|
call A
|
|
|
rcall ofS
|
|
|
call A
|
|
|
ret
|
|
|
reti
|
|
|
jnc
|
|
|
jc
|
|
|
jmp
|
|
|
call
|
|
|
bcc ofS
|
|
|
it{t|e{t|e{t|e}}}
|
|
|
bx R
|
|
|
blx R
|
|
|
bltz $t, Ofs
|
|
|
bgez $t, Ofs
|
|
|
beq $s, $t, Ofs
|
|
|
bne $s, $t, Ofs
|
|
|
blez $t, Ofs
|
|
|
bgtz $t, Ofs
|
|
|
j Addr
|
|
|
jr $s
|
|
|
jal Addr
|
|
|
jalr $d, $s
|
В соответствии с традицией, первая программа должна вывести на экран приветствие вроде Hello, World! Эта программа очень проста и может быть написана непосредственно в машинном коде. Код зависит от процессора, операционной системы и используемого устройства вывода. В некотором смысле такая программа для микроконтроллера скрывает меньше.
Чтобы выполнить программу необходима соответствующая машина или ее программный эмулятор. Если речь о реальных микроконтроллерах, потребуется некоторое оборудование:
Можно купить готовую плату с установленными на ней программатором, микроконтроллером и какими-либо периферийными устройствами (как минимум, светодиодом) например, Arduino или STM Nucleo. Также можно купить готовый комплект, содержащий все перечисленное и, возможно, что-то еще.
Если покупать отдельно, можно рекомендовать
Хотя программатор USBASP и дешев, сэкономить не получится, цена программатора и микроконтроллера лишь немного меньше цены китайского клона платы Arduino UNO. К тому же эта плата способна без какого-либо дополнительного оборудования передавать данные на персональный компьютер и принимать их от него. Она также может использоваться как программатор других микроконтроллеров (не только AVR).
Запись программы в контроллер платы Arduino обычно происходит не так, как в отдельно стоящий микроконтроллер и в этом есть некоторая "магия". Часть памяти программ занимает так называемый загрузчик (bootloader) и работа контроллера начинается с исполнения его кода. Если загрузчик обнаруживает попытку записи программы, он предпринимает соответствующие действия, если нет - передает управление основному коду.
Указанная макетная плата представляется более хорошим вариантом, чем другая распространенная плата MB-102, но также не лишена недостатков - шины питания имеющегося у автора экземпляра не обеспечивают надежного контакта с тонкими выводами. Обе платы существенно хуже Wishboard WBU-504, но и намного дешевле.
Из трех сотен резисторов большая часть не понадобится, но при покупке на ebay или aliexpress это, по-видимому, самый дешевый вариант. Резисторы лучше брать углеродистые (бежевые), обозначения на них читаются лучше чем на металлопленочных (синих). Углеродистые резисторы почему-то дороже.
Нельзя покупать светодиодную матрицу 8x8 3 мм - по какой-то причине рассояние между рядами ее выводов (24 мм) не соответствует шагу отверстий на плате (2.54 мм). Матрица 8x8 3.7 мм может быть установлена, но для ее подключения потебуются короткие жесткие перемычки или две макетные платы.
Кнопки 6x6x5 мм по-видимому самые удобные. Кнопки 6x6x4.3 мм чувствуются пальцем хуже.
Предполагается использование эмулятора Edsac Simulator, вывод на телетайп. В реальную машину программа может быть введена с перфоленты, в эмулятор - из текстового файла. Первый вариант программы - простая последовательность команд вывода на телетайп, если понадобится выводить текст другой длины, программу придется полнотью переписать. Второй вариант демонстрирует работу с массивом, для чего используется самомодифицирующийся код и обойтись без этого невозможно.
Важно! Перед нажатием кнопки Start должен быть выбран загрузчик Initial Order 1.
Сразу после загрузки программы она останавливается, ее выполнение может быть продолжено нажатием кнопки Reset или многократного нажатия кнопки Single E.P. во втором случае выполняется одна комана и машина останавливается. На экранах можно видеть содержимое регистров и памяти (нужно выбрать Long Tank 1, содержащий ячейки памяти с 32 до 63).
Если заменить первую команду ZS на XS, останвка произойдет после однократного выполнения программы.
Команды приведены в том виде, в котором они пробиваются на перфоленте - буквенный код команды (один столбец), десятичный адрес операнда (ноль или больше столбцов), бит разрядности операнда/единица младшего разряда (S или D). Двоичные коды команд не приводятся, их можно увидеть на экране.
Первый вариант:
T53S [EOF position]
[32] ZS [hlt]
[33] O45S [prn]
[34] O46S [prn]
[35] O47S [prn]
[36] O48S [prn]
[37] O49S [prn]
[38] O50S [prn]
[39] O51S [prn]
[40] O52S [prn]
[41] ZS [hlt]
[42] T44S [clr Acc]
[43] E33S
[44] 0S [Tmp]
[45] *F [Letter shift]
[46] HS
[47] ES
[48] LS
[49] LS
[50] OS
[51] @S [CR]
[52] &S [LF]
Второй вариант:
T68S [EOF position]
[32] ZS [hlt]
[33] T59S [clr Acc]
[34] A56S [lda Ind0]
[35] T57S [sta Ind]
[36] A55S [lda Cmd]
[37] A57S [add Ind]
[38] T39S [sta Cmd]
[39] AS [lda Ch]
[40] T58S [sta Tmp]
[41] H58S [ldm Tmp]
[42] C54S [and One]
[43] S54S [sub One]
[44] E52S [jns 52]
[45] O58S [prn Tmp]
[46] T58S [clr Acc]
[47] A57S [add Ind]
[48] A54S [add One]
[49] A54S [add One]
[50] T57S [sta Ind]
[51] E36S [jns 36]
[52] ZS [hlt]
[53] E33S [jns 33]
[54] 0D [One]
[55] AS [Add Cmd]
[56] 060S [Ind0]
[57] 0S [Ind]
[58] 0S [Tmp]
[59] *S [Letter shift]
[60] HS
[61] ES
[62] LS
[63] LS
[64] OS
[65] @S [CR]
[66] &S [LF]
[67] 0D [EOS]
Предполагается использование эмулятора simhv/pdp11 без операционной системы, вывод на телетайп. В реальную машину программа может быть введена с помощью пульта (blinkenlight console?), в память эмулятора - с помощью команд
deposit addr, value
Команду можно сократить до первой буквы, адреса и значения должны вводиться в восьмеричной системе счисления (в реальную машину - в двоичной). Запуск эмулятора
go addr
В следующих примерах addr=10008 (020016)
Адрес Код Адрес(8) Код(8) Метка Мнемоника
----- --------- -------- ------------- ------- ---------
0200 15C2 FF74 001000 012702 177564 mov #177564, R2 ;Регистр статуса телетайпа
0204 15C0 0048 001004 012700 000110 mov #'H', R0
0208 9032 0002 001010 110062 000002 @Wait1: movb (R0), 2(R2) ;Запись в регистр данных телетайпа
020C 8BCA 001014 105712 tstb (R2) ;Проверка завершения записи
020E 80FE 001016 100376 bpl @Wait1 ;Если в разряде 7 регистра статуса телетайпа 0 - повторить проверку
0210 15C0 0065 001020 012700 000145 mov #'e', R0
0214 9032 0002 001024 110062 000002 @Wait2: movb (R0), 2(R2)
0218 8BCA 001030 105712 tstb (R2)
021A 80FE 001032 100376 bpl @Wait2
...
0252 0000 001122 000000 halt
В PDP-11 регистры устройств ввода-вывода размещаются в общем адресном пространстве памяти. Соответственно, для управления этими устройствами используются обычные команды передачи данных. Команда tstb выполняет установку разрядов Z и N регистра PS в соответствии со значением операнда. Код содержит тринадцать похожих фрагментов (приведены только первые два), что плохо.
Символы можно выводить в цикле:
Адрес Код Адрес(8) Код(8) Метка Мнемоника
----- --------- -------- ------------- ------- ---------
0200 15C2 FF74 001000 012702 177564 mov #177564, R2 ;Регистр статуса телетайпа
0204 11C1 001004 010701 mov R7, R1
0206 11C1 0014 001006 062701 000024 add R1, #24 ;R1 = @Text
020A 9440 001012 112100 @Loop: movb (R1)+, R0
020C 0305 001014 001405 beq @Exit
020E 9032 0002 001016 110062 000002 @Wait: movb (R0), 2(R2) ;Запись в регистр данных телетайпа
0212 8BCA 001022 105712 tstb (R2) ;Проверка завершения записи
0214 80FE 001024 100376 bpl @Wait ;Если в разряде 7 регистра статуса телетайпа 0 - повторить проверку
0216 01F9 001026 000771 br @Loop
0218 0000 001030 000000 @Exit: halt
021A 6548 001032 062510 @Text: dw 'H' 'e'
021C 6C6C 001034 066154 dw 'l' 'l'
021E 2C6F 001036 026157 dw 'o' ','
0220 5720 001040 053440 dw ' ' 'W'
0222 726F 001042 071157 dw 'o' 'r'
0224 646C 001044 062154 dw 'l' 'd'
0226 0021 001046 000041 dw '!' 0
Запись команд mov, add и tst в восьмеричной системе счисления легко читается, чего нельзя сказать об их записи в шестнадцатеричной системе счисления. Команды переходов и текстовые строки легко читаются в шестнадцатеричной системе счисления и нечитаемы в восьмеричной.
Возможно, после вывода символа следует проверять отсутствие ошибок в работе телетайпа (бит 15 регистра статуса телетайпа).
Предполагается использование операционной системы CP/M-80 (все программы проверены в эмуляторе MYZ80 1.24). CP/M загружает программу в область памяти с начальным адресом 0x100 и выполняет переход по адресу 0x100. Программа использует две функции CP/M - вывод символа (2) и завершение программы (0):
Адрес Код Метка Мнемоника
----- -------- ------ ---------
0100 0E 02 mvi C, 2 ;Вывод символа
0102 1E 48 mvi E, 'H'
0104 CD 05 00 call 5
0107 0E 02 mvi C, 2 ;Вывод символа
0109 1E 65 mvi E, 'e'
010B CD 05 00 call 5
....
015B 0E 00 mvi C, 0 ;Завершение программы
015D CD 05 00 call 5
Код имеет тот же недостаток, что и его прототип для PDP-11. Можно использовать функцию вывода строки, оканчивающейся символом $ (9):
Адрес Код Метка Мнемоника
----- -------- ------ ---------
0100 0E 09 mvi C, 9 ;Вывод строки
0102 11 0D 01 lxi D, @Text
0105 CD 05 00 call 5
0108 0E 00 mvi C, 0 ;Завершение программы
010A CD 05 00 call 5
010D 48 @Text: db 'H'
010E 65 db 'e'
010F 6C db 'l'
0110 6C db 'l'
0111 6F db 'o'
0112 2C db ','
0113 20 db ' '
0114 57 db 'W'
0115 6F db 'o'
0116 72 db 'r'
0117 6C db 'l'
0118 64 db 'd'
0119 21 db '!'
011A 24 db $
Наконец, можно использовать функцию вывода символа в цикле:
Адрес Код Метка Мнемоника
----- -------- ------ ---------
0100 21 1A 01 lxi H, @Text
0103 5E @Loop: mov E, M
0104 3E 00 mvi A, 0
0106 93 sub E
0107 CA 15 01 jz @Exit
010A E5 push H
010B 0E 02 mvi C, 2 ;Вывод символа
010D CD 05 00 call 5
0110 E1 pop H
0111 23 inx H
0112 C3 03 01 jmp @Loop
0115 0E 00 @Exit: mvi C, 0 ;Завершение программы
0117 CD 05 00 call 5
011A 48 @Text: db 'H'
011B 65 db 'e'
011C 6C db 'l'
011D 6C db 'l'
011E 6F db 'o'
011F 2C db ','
0120 20 db ' '
0121 57 db 'W'
0122 6F db 'o'
0123 72 db 'r'
0124 6C db 'l'
0125 64 db 'd'
0126 21 db '!'
0127 00 db 0
Предполагается использование операционной системы Apple DOS (все программы проверены в эмуляторе AppleWin, образ диска был сформирован с помощью CiderPress). Программа должна загружаться в область памяти с начальным адресом 0x0C00 (3072), который является атрибутом файла(?). Для загрузки и запуска нужно ввести две команды:
BLOAD HELLOWORLD
CALL 3072
Программа использует одну функцию Apple DOS - вывод символа (ее адрес 0xFDF0):
Адрес Код Метка Мнемоника
----- -------- ------ ---------
0C00 A9 C8 lda 'H'|0x80
0C02 20 F0 FD jsr 0xFDF0 ;Вывод символа
0C05 A9 E5 lda 'e'|0x80
0C07 20 F0 FD jsr 0xFDF0 ;Вывод символа
....
0C41 60 rts
В старшем бите выводимого символа зачем-то должна быть единица (?). Программу можно ввести с клавиатуры используя записанный в ПЗУ интерпретатор васика, но все адреса и коды придется перевести в десятичную систему(?):
POKE 3072, 169
POKE 3073, 200
...
Код имеет тот же недостаток, что и его прототип для PDP-11. Вывод строки в цикле:
Адрес Код Метка Мнемоника
----- -------- ------ ---------
0C00 A9 1E lda #Lo(Text)
0C02 85 FE sta 0xFE
0C04 A9 C0 lda #Hi(Text)
0C06 85 FF sta 0xFF
0C08 A0 00 ldy 0x00
0C0A B1 FE @Loop: lda (0xFE),y
0C0C F0 0F beq @Exit
0C0E 18 clc
0C0F 69 80 adc #0x80
0C11 20 F0 FD jsr 0xFDF0 ;Вывод символа
0C14 E6 FE inc 0xFE
0C16 D0 F2 bne @Loop
0C18 E6 FF inc 0xFF
0C1A 4C 0A 0C jmp @Loop
0C1D 60 @Exit: rts
0C1E 48 @Text: db 'H'
0C1F 65 db 'e'
0C20 6C db 'l'
0C21 6C db 'l'
0C22 6F db 'o'
0C23 2C db ','
0C24 20 db ' '
0C25 57 db 'W'
0C26 6F db 'o'
0C27 72 db 'r'
0C28 6C db 'l'
0C29 64 db 'd'
0C2A 21 db '!'
0C2B 00 db 0
Если ограничить длину строки 254 символами, можно увеличивать не адрес в ячейках 0xFE и 0xFF, а смещение в регистре Y. Вместо сложения можно и правильно использовать логическое ИЛИ.
Предполагается использование операционной системы MS-DOS (все программы проверены в эмуляторе DOSBox 0.72). При запуске программы формата COM DOS выделяет свободную область памяти, выровненную по границе сегмента (т.е. имеющую адрес с нулевыми младшими четырьмя разрядами), в ее начало записывает так называемый префикс программного сегмента (PSP) длиной 256 байт (0x100), загружает код программы начиная с адреса Seg(PSP):0x100, заполняет сегментные регистры DS и SS значением Seg(PSP) и выполняет дальний переход по адресу PSP:0x100.
Программа использует две функции DOS - вывод символа (2) и завершение программы (4C):
Адрес Код Метка Мнемоника
----- -------- ------ ---------
0100 B4 02 mov AH, 2 ;Вывод символа
0102 B2 48 mov DL, 'H'
0104 CD 21 int 21H
0106 B4 02 mov AH, 2 ;Вывод символа
0108 B2 65 mov DL, 'e'
010A CD 21 int 21H
....
014E B4 4C mov AH, 4CH ;Завершение программы
0150 B0 00 mov AL, 0
0152 CD 21 int 21H
Можно использовать функцию вывода строки, оканчивающейся символом $ (9):
Адрес Код Метка Мнемоника
----- -------- ------ ---------
0100 B4 09 mov AH, 9 ;Вывод строки
0102 BA 0D 01 mov DX, @Text
0105 CD 21 int 21H
0107 B4 4C mov AH, 4CH ;Завершение программы
0109 B0 00 mov AL, 0
010B CD 21 int 21H
010D 48 @Text: db 'H'
010E 65 db 'e'
010F 6C db 'l'
0110 6C db 'l'
0111 6F db 'o'
0112 2C db ','
0113 20 db ' '
0114 57 db 'W'
0115 6F db 'o'
0116 72 db 'r'
0117 6C db 'l'
0118 64 db 'd'
0119 21 db '!'
011A 24 db $
Наконец, можно использованить функцию вывода символа в цикле:
Адрес Код Метка Мнемоника
----- -------- ------ ---------
0100 BB 18 01 mov BX, @Text
0103 8A 17 @Loop: mov DL, DS:[BX]
0105 B0 00 mov AL, 0
0107 2A C2 sub AL, DL
0109 74 07 jz @Exit
010B B4 02 mov AH, 2 ;Вывод символа
010D CD 21 int 21H
010F 43 inc BX
0110 FB F1 jmp @Loop
0112 B4 C4 @Exit: mov AH, 4EH ;Завершение программы
0114 B0 00 mov AL, 0
0116 CD 21 int 21H
0118 48 @Text: db 'H'
0119 65 db 'e'
011A 6C db 'l'
011B 6C db 'l'
011C 6F db 'o'
011D 2C db ','
012E 20 db ' '
012F 57 db 'W'
0130 6F db 'o'
0131 72 db 'r'
0132 6C db 'l'
0133 64 db 'd'
0134 21 db '!'
0135 00 db 0
Предполагается использование операционной системы Linux/386. Использование Linux/AMD64 возможно. Программа формата ELF32 состоит из заголовка и сегмента кода, начальный логический адрес которого указываются в заголоке. Программа использует две функции - запись в файл (4) и завершение программы (1). Перед выводом строки выполняется подсчет числа символов, его можно вычислить заранее, но это не сделано чтобы продемонстрировать использование большего числа различных команд. Заголовок программы пропущен, его формирование несложно, но длина превышает длину кода:
Адрес Код Метка Мнемоника
-------- -------------- ------ ---------
08048000 7F db 7FH ;Заголовок ELF32
08048001 45 db 'E'
08048002 4C db 'L'
08048003 46 db 'F'
........
08048054 B9 80 80 04 08 mov ECX, @Text
08048059 89 CA mov EDX, ECX
0804805B 8A 02 @Loop: mov AL, [EDX]
0804805D B4 00 mov AH, 0
0804805F 2A E0 sub AH, AL
08048061 74 03 jz @Exit
08048063 42 inc EDX
08048064 EB F5 jmp @Loop
08048066 B8 04 00 00 00 @Exit: mov EAX, 4 ;Запись в файл
0804806B BB 01 00 00 00 mov EBX, 1
08048070 2B D1 sub EDX, ECX
08048072 CD 80 int 80H
08048074 B8 01 00 00 00 mov EAX, 1 ;Завершение программы
08048079 BB 00 00 00 00 mov EBX, 0
0804807E CD 80 int 80H
08048080 48 @Text: db 'H'
08048081 65 db 'e'
08048082 6C db 'l'
08048083 6C db 'l'
08048084 6F db 'o'
08048085 2C db ','
08048086 20 db ' '
08048087 57 db 'W'
08048088 6F db 'o'
08048089 72 db 'r'
0804808A 6C db 'l'
0804808B 64 db 'd'
0804808C 21 db '!'
0804808D 00 db 0
Предполагается использование операционной системы Linux/AMD64. Программа формата ELF64 состоит из заголовка и сегмента кода, начальный адрес которого выбран равным 2^32, для обращения к нему тридцатидвухразрядного адреса недостаточно. Программа использует две функции - запись в файл (1) и завершение программы (0), в силу каких-то причин номера функций и правила передачи параметров отличаются от Linux/386. Перед выводом строки также выполняется подсчет числа символов:
Адрес Код Метка Мнемоника
---------- ----------------------------- ------ ---------
0100000000 7F db 7FH ;Заголовок ELF64
0100000001 45 db 'E'
0100000002 4C db 'L'
0100000003 46 db 'F'
..........
0100000078 48 BE AD 00 00 00 01 00 00 00 mov RSI, @Text
0100000082 48 89 F2 mov RDX, RSI
0100000085 8A 02 @Loop: mov AL, [RDX]
0100000087 B4 00 mov AH, 0
0100000089 2A E0 sub AH, AL
010000008B 74 05 jz @Exit
010000008D 48 FF C2 inc RDX
0100000090 EB F3 jmp @Loop
0100000092 B8 01 00 00 00 @Exit: mov RAX, 1 ;Запись в файл
0100000097 BF 01 00 00 00 mov RDI, 1
010000009C 48 2B D6 sub RDX, RSI
010000009F 0F 05 syscall
01000000A1 B8 3C 00 00 00 mov RAX, 3CH ;Завершение программы
01000000A6 BF 00 00 00 00 mov RDI, 0
01000000AB 0F 05 syscall
01000000AD 48 @Text: db 'H'
01000000AE 65 db 'e'
01000000AF 6C db 'l'
01000000B0 6C db 'l'
01000000B1 6F db 'o'
01000000B2 2C db ','
01000000B3 20 db ' '
01000000B4 57 db 'W'
01000000B5 6F db 'o'
01000000B6 72 db 'r'
01000000B7 6C db 'l'
01000000B8 64 db 'd'
01000000B9 21 db '!'
01000000BA 00 db 0
Приведенный ниже код проверялся на микроконтроллере AT89S4051 в 20-выводном корпусе, но можно использовать и другие. Выбор может быть ограничен имеющимся программатором, по-видимому предпочтительны контроллеры, допускающие последовательное внутрисистемное (внутрисхемное) программирование (In-System Programming).
Если я правильно понял описание, AT89S4051 не имеет встроенного RC-генератора и для его работы необходим либо кварцевый резонатор с парой конденсаторов 5 пФ, либо внешний генератор. Первое явно проще.
Чтобы реализовать задуманное нужно какое-то устройство вывода. Можно использовать текстовый дисплей с контроллером HD44780, может быть, можно использовать и какой-то графический дисплей, например PCD8544 (Nokia 5110). В последнем случае самой сложной частью работы будет именно управление дисплеем и формирование символов из точек. Использовать семисегментный индикатор проще, но он может отобразить не все буквы, так что придется либо ограничиться первой половиной фразы, либо заменить слово WORLD чем-то другим, например обозначением микроконтроллера 89S4051. Как отобразить запятую тоже непонятно, да и выглядит это не очень привлекательно. Матрицы из тридцати пяти (5x7) или шестидесяти четырех (8x8) светодиодов позволяют отображать любые символы английского алфавита, бегущая строка на них выглядит вполне пристойно, но символы также нужно формировать из точек. Кроме того, не у всякого микроконтроллера имеется достаточно выводов для их непосредственного подключения (AT89S4051 имеет пятнадцать выводов, одну строку или колонку матрицы 8x8 подключить некуда, но с этим в принципе можно смириться).
Может это и не так эффектно, но если вспомнить про азбуку Морзе, то можно ограничиться одним светодиодом (проще) или миниатюрным пьезодинамиком (немного сложнее). Управление светодиодом сводится к установке или сбросу разряда порта, к выводу которого этот светодиод подключен.
Многие руководства начинаются с программы, периодически включающей и выключающей светодиод, т.е. выполняющей функцию генератора импульсов - устройства гораздо более простого, чем микроконтроллер.
Приведенные здесь программы лишь немного сложнее, а вот их эквивалент в виде электронной схемы намного сложнее простого генератора (но на микросхеме FPGA/CPLD реализуется легко).
Для написания программы и изготовления соответствующего hex-файла использовалась интегрированная среда MCU8051IDE.
Чтобы записать программу в контроллер необходим программатор. Автор использовал плату Arduino Uno и программатор, основанный на программаторе для AT89S52. Изменено и/или исправлено было следующее:
Последнее не совсем корректно, наверное было бы правильнее реализовать обмен сообщениями и ждать сообщения о готовности.
Программа управления программатором прочтет hex-файл и запишет содержащиеся в нем коды во flash-память контроллера - по сравнению с загрузкой программы в память персонального компьютера возни больше, но все это не так уж сложно.
Светодиод с ограничивающим ток резистором 220 ом должен быть подключен ко второму биту порта 1 (вывод 14 AT892S4051) и плюсу источника питания - выход микроконтроллера несимметричен. Светодиод горит при низком уровне на выходе. Хотя это и не очень хорошо, можно включать сведодиод и высоким уровнем. Для этого нужно резистором соединить выход контроллера с плюсом питания, а светодиод подключить к выходу и земле. Резистор нужен чтобы увеличить ток через светодиод. Такая схема потребляет ток и при высоком и при низком уровне на выходе.
Имеет смысл подключать контакты MOSI, MISO, SCK, RST/VPP и VCC через резисторы 220 ом, это должно защитить плату Arduino от повреждения при ошибках сборки схемы.
Сброс микроконтроллера осуществляется кратковременной подачей напряжения питания на вывод RST/VPP.
Прерывания не разрешены, так что никаких ограничений на размещение кода нет.
Адрес Код Метка Мнемоника
----- -------- ------- ---------
0000 02 00 16 ljmp INIT
0003 7E 41 PAUSE: mov CM0, #0x41 ;CM0=R6
0005 7D D7 mov CL0, #0xD7 ;CL0=R5
0007 DD FE LOOP_L: djnz CL0, LOOP_L
0009 DE FC djnz CM0, LOOP_L
000B DF F6 djnz CH0, PAUSE ;CH0=R7
000D 22 ret
000E C2 92 SIGN: clr LED ;LED=P1.2
0010 12 00 03 lcall PAUSE
0013 D2 92 setb LED
0015 22 ret
0016 75 81 2F INIT: mov SP, #0x2F
0019 7F 08 MAIN: mov CH0, #DOT ;DOT=0x08, H=....
001B 12 00 0E lcall SIGN
001E 7F 08 mov CH0, #S2S ;S2S=0x08
0020 12 00 03 lcall PAUSE
0023 7F 08 mov CH0, #DOT
0025 12 00 0E lcall SIGN
0028 7F 08 mov CH0, #S2S
002A 12 00 03 lcall PAUSE
002D 7F 08 mov CH0, #DOT
002F 12 00 0E lcall SIGN
0032 7F 08 mov CH0, #S2S
0034 12 00 03 lcall PAUSE
0037 7F 08 mov CH0, #DOT
0039 12 00 0E lcall SIGN
003C 7F 18 mov CH0, #C2C ;C2C=0x18
003E 12 00 03 lcall PAUSE
0041 7F 08 mov CH0, #DOT ;E=.
0043 12 00 0E lcall SIGN
0046 7F 18 mov CH0, #C2C
0048 12 00 03 lcall PAUSE
004B 7F 08 mov CH0, #DOT ;L=.-..
004D 12 00 0E lcall SIGN
0050 7F 08 mov CH0, #S2S
0052 12 00 03 lcall PAUSE
0055 7F 18 mov CH0, #DASH ;DASH=0x18
0057 12 00 0E lcall SIGN
005A 7F 08 mov CH0, #S2S
005C 12 00 03 lcall PAUSE
005F 7F 08 mov CH0, #DOT
0061 12 00 0E lcall SIGN
0064 7F 08 mov CH0, #S2S
0066 12 00 03 lcall PAUSE
0069 7F 08 mov CH0, #DOT
006B 12 00 0E lcall SIGN
006E 7F 18 mov CH0, #C2C
0070 12 00 03 lcall PAUSE
...
0190 7F 70 mov CH0, #M2M ;M2M=0x70
0192 12 00 03 lcall PAUSE
0195 02 00 19 ljmp MAIN
Функция PAUSE предназначена для задержки выполнения программы, при тактовой частоте 16 МГц задержка кратна 25 миллисекундам.
В соответствующем hex-файле первые восемь команд выглядят так (число слов, начальный адрес, тип записи, восемь команд, контрольная сумма), старшие байты адресов переходов предшествуют младшим(?):
:10 |
0000 |
00 |
020016 |
7E41 |
7DD7 |
DDFE |
DEFC |
DFF6 |
22 |
C292 |
C5 |
Приведен только вывод первых трех букв. Код можно сократить, написав функции вывода точки, паузы между знаками и точки, тире, паузы между знаками и тире, паузы между буквами. Но лучше в цикле выводить хранящуюся в памяти последовательность знаков и пауз:
Адрес Код Метка Мнемоника
----- -------- ------- ---------
0000 75 81 2F INIT: mov SP, #0x2F ;Вызовов функций нет, устанавливаемое после сброса значение 0x07 можно не менять
0003 90 00 1F mov DPTR, #HELLO
0006 D2 92 setb LED ;LED=P1.2
0008 7C 00 MAIN: mov IX, #0x00 ;IX=R4
000A EC SIGN: mov A, IX
000B 93 movc A, @A+DPTR ;Чтение из памяти программы
000C 60 FA jz MAIN
000E FF mov CH0, A ;CH0=R7
000F 0C inc IX
0010 B2 92 cpl LED ;Инверсия значения на выходе
0012 7E 41 LOOP_H: mov CM0, #0x41 ;CM0=R6
0014 7D D7 mov CL0, #0xD7 ;CL0=R5
0016 DD FE LOOP_L: djnz CL0, LOOP_L
0018 DE FC djnz CM0, LOOP_L
001A DF F6 djnz CH0, LOOP_H
001C 02 00 0A ljmp SIGN
001F 08 08 HELLO: db DOT, S2S ;DOT=0x08, S2S=0x08, H=....
0021 08 08 db DOT, S2S
0023 08 08 db DOT, S2S
0025 08 18 db DOT, C2C
0027 08 18 db DOT, C2C ;E=.
0029 08 08 db DOT, S2S ;L=.-..
002B 18 08 db DASH, S2S ;DASH=0x18
002D 08 08 db DOT, S2S
002F 08 18 db DOT, C2C
...
006C 70 db M2M ;M2M=0x70
006D 00 db 0
Обе программы содержат небольшую погрешность - длительности знаков и пауз определяюся не только циклом задержки, но и другими командами. Отношение длительности тире к длительности точки немного отличается от трех, при желании это можно исправить, но программы станут несколько больше и сложнее.
Приведенный ниже код проверялся на микроконтроллере PIC10F200 в 8-выводном корпусе.
Для написания программы и изготовления соответствующего hex-файла использоваалась интегрированная среда MPLAB X IDE.
Чтобы записать программу в контроллер необходим программатор. Автор использовал ту же плату Arduino Uno и собранный на макетной плате упрощенный ardpicprog (ICSP-only programmer).
Приведенная схема содержит дефект - база транзистора должна быть соединена с землей резистором 5-10 кОм. Если это не сделано, при включении или перезагрузке платы Arduino база подключена к высокоомному входу, т.е. не подключена ни к чему. Кроме того, схема на одном транзисторе сразу после включения выдает высокое напряжение. Чтобы это исключить, можно добавить инвертор на еще одном одном транзисторе и в программе для Arduino изменить на противоположные константы MCLR_RESET и MCLR_VPP:
По-видимому, имеет смысл вместо аналоговых выходов использовать цифровые:
#define PIN_MCLR 8 // 0: Reset PIC, 1: MCLR is VPP voltage -> PIC10F200.PIN8
#define PIN_ACTIVITY 10 // LED that indicates read/write activity
#define PIN_VDD 2 // Controls the power to the PIC -> 220 -> PIC10F200.PIN2
#define PIN_CLOCK 4 // Clock pin -> 220 -> PIC10F200.PIN4
#define PIN_DATA 7 // Data pin -> 220 -> PIC10F200.PIN5
#define MCLR_RESET LOW // PIN_MCLR state to reset the PIC
#define MCLR_VPP HIGH // PIN_MCLR state to apply 13v to MCLR/VPP pin
Также имеет смысл подключать контакты VDD, ICSPDAT и ICSPCLK через резисторы 220 ом.
Программа для платы Arduino не поддерживает PIC10F200, но ее можно доработать. Возможно, сделанная доработка и не совсем корректна, но что есть - то есть. Почему-то при подаче напряжения на вывод MCLR/VPP до подачи напряжения на вывод VDD некорректно выполняется чтение памяти контроллера. Стирание памяти и запись при этом могут быть выполнены.
ВНИМАНИЕ! При записи программы в контроллер нужно указывать тип устройства pic10f200w (не pic10f200)!
Светодиод с ограничивающим ток резистором 220 ом должен быть подключен ко второму биту порта GPIO (вывод 3 PIC10F200) и минусу источника питания. Светодиод горит при высоком уровне на выходе. Выход микроконтроллера симметричен, так что можно подключать нагрузку и к плюсу, в этом случае потребуется изменить команды управления выводом на противоположные. Тактовый генератор встроенный (4МГц, это единственный возможный вариант), таймер сброса (watch dog timer) выключен, вывод MCLR должен быть подключен в источнику питания через резистор 10 кОм (не обязательно?).
Возможность обработки прерываний отсутствует, исполнение кода начинается с адреса 0FF, по которому должна находиться команда загрузки в рабочий регистр калибровочного коэффициента тактового генератора (movlw) или пустая команда (nop), адрес следующей исполняемой команды - 000. Значение слова конфигурации контроллера - 0FFBH, т.е. программа на ассемблере mpasm должна начинаться с директивы
__CONFIG 0FFBH
Код программы:
Адрес Код Метка Мнемоника
----- ---- ------- ---------
0000 0A11 goto INIT
0001 0032 PAUSE: movwf CH ;CH=12H
0002 0C82 LOOP_H: movlw 082H
0003 0031 movwf CM ;CM=11H
0004 0CDC movlw 0DCH
0005 0030 movwf CL ;CL=10H
0006 02F0 LOOP_L: decfsz CL
0007 0A06 goto LOOP_L
0008 02F1 decfsz CM
0009 0A06 goto LOOP_L
000A 02F2 decfsz CH
000B 0A02 goto LOOP_H
000C 0800 retlw 0
000D 0546 SIGN: bsf GPIO, LED ;LED=2
000E 0901 call PAUSE
000F 0446 bcf GPIO, LED
0010 0800 retlw 0
0011 0CDF INIT: movlw 0DFH ;Бит OPTION.T0CS(Timer0 Clock Source Select bit)=0, вход счетчика T0 отключен от вывода GP2
0012 0002 option
0013 0C0B movlw 0BH ;Вывод 2 порта GPIO используется как выход, остальные как входы
0014 0006 tris GPIO
0015 0С02 MAIN: movlw DOT ;DOT=2, H=....
0016 090D call SIGN
0017 0С02 movlw S2S ;S2S=2
0018 0901 call PAUSE
0019 0C02 movlw DOT
001A 090D call SIGN
001B 0C02 movlw S2S
001C 0901 call PAUSE
001D 0C02 movlw DOT
001E 090D call SIGN
001F 0C02 movlw S2S
0020 0901 call PAUSE
0021 0C02 movlw DOT
0022 090D call SIGN
0023 0C06 movlw C2C ;C2C=6
0024 0901 call PAUSE
0025 0C02 movlw DOT ;E=.
0026 090D call SIGN
0027 0C06 movlw C2C
0028 0901 call PAUSE
0029 0C02 movlw DOT ;L=.-..
002A 090D call SIGN
002B 0C02 movlw S2S
002C 0901 call PAUSE
002D 0C06 movlw DASH ;DASH=6
002E 090D call SIGN
002F 0C02 movlw S2S
0030 0901 call PAUSE
0031 0C02 movlw DOT
0032 090D call SIGN
0033 0C02 movlw S2S
0034 0901 call PAUSE
0035 0C02 movlw DOT
0036 090D call SIGN
0037 0C06 movlw C2C
0038 0901 call PAUSE
...
00AD 0C1C movlw M2M ;M2M=1CH
00AE 0901 call PAUSE
00AF 0A15 goto MAIN
Функция PAUSE предназначена для задержки выполнения программы, при тактовой частоте 4 МГц задержка кратна 100 миллисекундам. Реальная задержка может отличаться. Если это недопустимо, следует скорректировать частоту тактового генератора записав подходящее значение в регистр OSCCAL. Правильное значение хранится в последнем слове памяти программ нового микроконтроллера в команде movlw.
В соответствующем hex-файле первые восемь команд выглядят так (число слов, начальный адрес, тип записи, восемь команд, контрольная сумма), старшие байты команд следуют за младшими:
:10 |
0000 |
00 |
110A |
3200 |
820C |
3100 |
DC0C |
3000 |
F002 |
060A |
CA |
Можно также в цикле формировать последовательность знаков и пауз, хранящуюся в массиве. Старые контроллеры PIC не могут читать содержимое памяти программ, но они допускают программное изменение счетчика и это может быть использовано для написания функции, возвращающей зависящее от аргумента значение:
Адрес Код Метка Мнемоника
----- ---- ------- ---------
0000 0CDF INIT: movlw 0DFH ;Бит OPTION.T0CS(Timer0 Clock Source Select bit)=0, вход счетчика T0 отключен от вывода GP2
0001 0002 option
0002 0C0B movlw 0BH ;Вывод 2 порта GPIO используется как выход, остальные как входы
0003 0006 tris GPIO
0004 0446 bcf GPIO, LED ;LED=2, начальная установка низкого уровня на выводе 2 порта GPIO
0005 0070 MAIN: clrf IX ;IX=10H
0006 0919 LOOP: call HELLO
0007 0033 movwf CH ;CH=13H
0008 0233 movf CH, F ;Установка STATUS.Z
0009 0643 btfsc STATUS, Z
000A 0A05 goto MAIN
000B 02B0 incf IX, F
000C 0C04 movlw 1 << LED
000D 01A6 xorwf GPIO, F ;Изменение состояния вывода 2 порта GPIO на противоположное
000E 0C82 LOOP_H: movlw 082H
000F 0032 movwf CM ;CM=12H
0010 0CDC movlw 0DCH
0011 0031 movwf CL ;CL=11H
0012 02F1 LOOP_L: decfsz CL
0013 0A12 goto LOOP_L
0014 02F2 decfsz CM
0015 0A12 goto LOOP_L
0016 02F3 decfsz CH
0017 0A0E goto LOOP_H
0018 0A06 goto LOOP
0019 0C1C HELLO: movlw TABLE
001A 01D0 addwf IX, W
001B 0022 movwf PCL ;Переход
001C 0802 TABLE: retlw DOT ;DOT=2, H=....
001D 0802 retlw S2S ;S2S=2
001E 0802 retlw DOT
001F 0802 retlw S2S
0020 0802 retlw DOT
0021 0802 retlw S2S
0022 0802 retlw DOT
0023 0806 retlw C2C ;C2C=6
0024 0802 retlw DOT ;E=.
0025 0806 retlw C2C
0026 0802 retlw DOT ;L=.-..
0027 0802 retlw S2S
0028 0806 retlw DASH ;DASH=6
0029 0802 retlw S2S
002A 0802 retlw DOT
002B 0802 retlw S2S
002C 0802 retlw DOT
002D 0806 retlw C2C
...
0067 081C retlw M2M ;M2M=1CH
0068 0800 retlw 0 ;EOL
В соответствующем hex-файле первые восемь команд выглядят так (число слов, начальный адрес, тип записи, восемь команд, контрольная сумма), старшие байты команд следуют за младшими:
:10 |
0000 |
00 |
DF0C |
0200 |
0B0C |
0600 |
4604 |
7000 |
1909 |
3300 |
D7 |
Приведенный ниже код проверялся на микроконтроллере PIC16F630 в 14-выводном корпусе, но можно использовать и другие, например PIC12F675 в 8-выводном корпусе. Код программы для PIC12F675 будет немного другим, нужно явно отключить аналоговый вход, сбросив второй бит порта ANSEL. Также имена портов в ассемблерном листинге должны быть другие (GPIO/TRISIO вместо PORTA/TRISA). Почему-то фирма Moicrochip использует разные имена для одного и того же порта - во всяком случае адрес и число разрядов у них совпадают.
Для написания программы и изготовления соответствующего hex-файла использоваалась упомянутая выше интегрированная среда MPLAB X IDE.
Для записи программы в контроллер использовался также упомянутый выше программатор. Подключение аналогично:
#define PIN_MCLR 8 // 0: Reset PIC, 1: MCLR is VPP voltage -> PIC16F630.PIN4
#define PIN_ACTIVITY 10 // LED that indicates read/write activity
#define PIN_VDD 2 // Controls the power to the PIC -> 220 -> PIC16F630.PIN1
#define PIN_CLOCK 4 // Clock pin -> 220 -> PIC16F630.PIN12
#define PIN_DATA 7 // Data pin -> 220 -> PIC16F630.PIN13
#define MCLR_RESET LOW // PIN_MCLR state to reset the PIC
#define MCLR_VPP HIGH // PIN_MCLR state to apply 13v to MCLR/VPP pin
Светодиод с ограничивающим ток резистором 220 ом должен быть подключен ко второму биту порта A (вывод 11 PIC16F630) и минусу источника питания. Светодиод горит при высоком уровне на выходе. Выход микроконтроллера симметричен, так что можно подключать нагрузку и к плюсу, в этом случае потребуется изменить команды управления выводом на противоположные. Тактовый генератор встроенный (4МГц), таймер сброса (watch dog timer) выключен, вывод MCLR должен быть подключен в источнику питания через резистор 10 кОм.
Прерывания не разрешены, так что никаких ограничений на размещение кода нет (при разрешенных прерываниях их обработчик должен начинаться с адреса 0004). Значение слова конфигурации контроллера - 31F4H, т.е. программа на ассемблере mpasm должна начинаться с директивы
__CONFIG 31F4H
Код программы:
Адрес Код Метка Мнемоника
----- ---- ------- ---------
0000 2811 goto INIT
0001 00A2 PAUSE: movwf CH ;CH=22H
0002 3082 LOOP_H: movlw 082H
0003 00A1 movwf CM ;CM=21H
0004 30DC movlw 0DCH
0005 00A0 movwf CL ;CL=20H
0006 0BA0 LOOP_L: decfsz CL
0007 2806 goto LOOP_L
0008 0BA1 decfsz CM
0009 2806 goto LOOP_L
000A 0BA2 decfsz CH
000B 2802 goto LOOP_H
000C 0008 return
000D 1505 SIGN: bsf PORTA, LED ;LED=2
000E 2001 call PAUSE
000F 1105 bcf PORTA, LED
0010 0008 return
0011 1683 INIT: bsf STATUS, PR0 ;PR0=5, Выбор банка 1
0012 1105 bcf TRISA, LED ;Вывод 2 порта A используется как выход
0013 1283 bcf STATUS, PR0 ;Выбор банка 0
0014 3002 MAIN: movlw DOT ;DOT=2, H=....
0015 200D call SIGN
0016 3002 movlw S2S ;S2S=2
0017 2001 call PAUSE
0018 3002 movlw DOT
0019 200D call SIGN
001A 3002 movlw S2S
001B 2001 call PAUSE
001C 3002 movlw DOT
001D 200D call SIGN
001E 3002 movlw S2S
001F 2001 call PAUSE
0020 3002 movlw DOT
0021 200D call SIGN
0022 3006 movlw C2C ;C2C=6
0023 2001 call PAUSE
0024 3002 movlw DOT ;E=.
0025 200D call SIGN
0026 3006 movlw C2C
0027 2001 call PAUSE
0028 3002 movlw DOT ;L=.-..
0029 200D call SIGN
002A 3002 movlw S2S
002B 2001 call PAUSE
002C 3006 movlw DASH ;DASH=6
002D 200D call SIGN
002E 3002 movlw S2S
002F 2001 call PAUSE
0030 3002 movlw DOT
0031 200D call SIGN
0032 3002 movlw S2S
0033 2001 call PAUSE
0034 3002 movlw DOT
0035 200D call SIGN
0036 3006 movlw C2C
0037 2001 call PAUSE
...
00AC 301C movlw M2M ;M2M=1CH
00AD 2001 call PAUSE
00AE 2814 goto MAIN
Функция PAUSE предназначена для задержки выполнения программы, при тактовой частоте 4 МГц задержка кратна 100 миллисекундам. Реальная задержка может заметно отличаться - у использованного экземпляра эта длительность менялась в пределах от 70 до 110 миллисекунд. Если это недопустимо, следует скорректировать частоту тактового генератора записав подходящее значение в регистр OSCCAL. Правильное значение хранится в последнем слове памяти программ нового микроконтроллера в команде retlw, при написании этой программы оно было благополучно стерто. Если нужна высокая точность и стабильность частоты, следует использовать кварцевый резонатор. Если его частота отличается от 4 МГц, нужно пропорционально изменить длительности сигналов (DOT, DASH, S2S, C2C, W2W) и, может быть, начальные значения в регистрах CM и CL.
В соответствующем hex-файле первые восемь команд выглядят так (число слов, начальный адрес, тип записи, восемь команд, контрольная сумма), старшие байты команд следуют за младшими:
:10 |
0000 |
00 |
1128 |
A200 |
8230 |
A100 |
DC30 |
A000 |
A00B |
0628 |
3D |
Можно также в цикле формировать последовательность знаков и пауз, хранящуюся в массиве. Старые контроллеры PIC не могут читать содержимое памяти программ, но они допускают программное изменение счетчика и это может быть использовано для написания функции, возвращающей зависящее от аргумента значение:
Адрес Код Метка Мнемоника
----- ---- ------- ---------
0000 1683 INIT: bsf STATUS, PR0 ;PR0=5, Выбор банка 1
0001 1105 bcf TRISA, LED ;LED=2, Вывод 2 порта A используется как выход
0002 1283 bcf STATUS, PR0 ;Выбор банка 0
0003 1105 bcf PORTA, LED ;Начальная установка низкого уровня на выводе 2 порта A
0004 01A0 MAIN: clrf IX ;IX=20H
0005 201A LOOP: call HELLO
0006 00A3 movwf CH ;CH=23H
0007 3000 movlw 0
0008 008A movwf PCLATH ;Для PIC16F630 можно не делать, но для МК с большой памятью программ это может быть нужно
0009 08A3 movw CH, F ;Установка STATUS.Z
000A 1903 btfsc STATUS, Z
000B 2804 goto MAIN
000C 0AA0 incf IX, F
000D 3004 movlw 1 << LED
000E 0685 xorwf PORTA, F ;Изменение состояния вывода 2 порта A на противоположное
000F 3082 LOOP_H: movlw 082H
0010 00A2 movwf CM ;CM=22H
0011 30DC movlw 0DC
0012 00A1 movwf CL ;CL=21H
0013 0BA1 LOOP_L: decfsz CL
0014 2813 goto LOOP_L
0015 0BA2 decfsz CM
0016 2813 goto LOOP_L
0017 0BA3 decfsz CH
0018 280F goto LOOP_H
0019 2805 goto LOOP
001A 3000 HELLO: movlw high TABLE
001B 008A movwf PCLATH
001C 3021 movlw low TABLE
001D 0720 addwf IX, W
001E 1803 btfsc STATUS, C
001F 0A8A incf PCLATH, F
0020 0082 movwf PCL ;Переход
0021 3402 TABLE: retlw DOT ;DOT=2, H=....
0022 3402 retlw S2S ;S2S=2
0023 3402 retlw DOT
0024 3402 retlw S2S
0025 3402 retlw DOT
0026 3402 retlw S2S
0027 3402 retlw DOT
0028 3406 retlw C2C ;C2C=6
0029 3402 retlw DOT ;E=.
002A 3406 retlw C2C
002B 3402 retlw DOT ;L=.-..
002C 3402 retlw S2S
002D 3406 retlw DASH ;DASH=6
002E 3402 retlw S2S
002F 3402 retlw DOT
0030 3402 retlw S2S
0031 3402 retlw DOT
0032 3406 retlw C2C
...
006C 341C retlw M2M ;M2M=1CH
006D 3400 retlw 0 ;EOL
Приведенный ниже код проверялся на микроконтроллере ATmega328P в 28-выводном корпусе. К этому микроконтроллеру можно подключить различные индикаторы и дисплеи, подключить светодиод естественно тоже можно.
Для микроконтроллеров с объемом памяти программ до 8192 байт (4096 слов, например ATtiny85) этот код непригоден поскольку команды переходов и вызовов подпрограмм по 22-разрядному абсолютному адресу (jmp/call) в них не реализованы. Нужно использовать команды переходов/вызовов по 12-разрядному относительному адресу (rjmp/rcall). В принципе правильно использовать эти команды всегда, когда возможно, но для начала код jmp/call проще. Кроме этого, инициализация указателя стека должна соответствовать объему памяти данных.
Светодиод с ограничивающим ток резистором 220 ом должен быть подключен к нулевому нулевому биту порта B (PB0, вывод 14 ATmega328P) и минусу источника питания. Если вместо отдельно стоящего микроконтроллера используется плата Arduino UNO, можно использовать установленный на ней и подключенный к выводу PB5 светодиод. Для этого нужно в программе заменить константу LED с 0 на 5.
Если используется Arduino Leonardo - нужно менять и порт и вывод - светодиод подключен к выводу PC7. Соответственно, нужно использовать порты DDRC и PORTC, значение константы LED должно быт 7.
ВНИМАНИЕ! Загрузка этой программы в Arduino Leonardo нарушит нормальную работу платы, хотя ничего страшного и не произойдет. В отличие от Arduino Uno эта плата не имеет отдельного преобразователя USB-USART, ее микроконтроллер Atmega32U4 сам должен следить за USB-портом и при обнаружении попытки программирования выполнять перезагрузку. Поскольку программа этого не делает, загрузка другой программы будет возможна только если примерно за 1-2 секунды до нее нажать кнопку reset.
Светодиод горит при высоком уровне на выходе. Выход микроконтроллера симметричен, так что можно подключать нагрузку и к плюсу, в этом случае потребуется изменить команды управления выводом на противоположные. Тактовый генератор встроенный 8 МГц (байт конфигурации LFUSE=0xE2, это значение по умолчанию). Если нужна высокая точность и стабильнось частоты, следует использовать кварцевый резонатор и изменить значение LFUSE (для частот 8 - 20 МГц LFUSE=0xFF). Если частота отличается от 8 МГц, нужно изменить длительности сигналов (DOT, DASH, S2S, C2C, W2W) и, может быть, начальные значения в регистрах CM и CL.
ВНИМАНИЕ! Некорректная установка байтов конфигурации может затруднить (или даже сделать невозможным?) дальнейшее использование микроконтроллера. Например, после установки LFUSE=0xFF сделать что-либо можно только после подключения 8-20 МГц кварцевого резонатора(?).
Для изготовления hex-файла использовался ассемблер avr-as, компоновщик avr-ld и программа avr-objcopy, для прошивки микроконтроллера - та же плата Arduino и программа avrdude. Инструкция здесь. Имеет смысл подключать контакты MOSI, MISO, SCK и RESET через резисторы 220 ом.
Примитивный вариант программы почти такой же, как для контроллеров 8051 и PIC:
Адрес Код Метка Мнемоника
----- --------- ------- ---------
0000 940C 000F jmp INIT
0002 E671 PAUSE: ldi CM, 0x61 ;CM=R23
0003 EA68 ldi CL, 0xA8 ;CL=R22
0004 5061 LOOP_L: subi CL, 0x01
0005 0B75 sbc CM, ZERO ;ZERO=R21
0006 F7E8 brcc LOOP_L
0007 5081 subi CH, 0x01 ;CH=R23
0008 F7C8 brcc PAUSE
0009 9508 ret
000A 9A28 SIGN: sbi PORTB, LED ;LED=0
000B 940E 0002 call PAUSE
000D 9828 cbi PORTB, LED
000E 9508 ret
000F E0D8 INIT: ldi R29, 0x08
0010 EFCF ldi R28, 0xFF
0011 BFDE out SPH, R29 ;Инициализация указателя стека, старший байт должен записываться первым!
0012 BFCD out SPL, R28
0013 E050 ldi ZERO, 0x00
0014 9A20 sbi DDRB, LED ;Вывод 0 порта B используется как выход
0015 E08F MAIN: ldi CH, DOT ;DOT=0x0F, H=....
0016 940E 000A call SIGN
0018 E08F ldi CH, S2S ;S2S=0x0F
0019 940E 0002 call PAUSE
001B E08F ldi CH, DOT
001C 940E 000A call SIGN
001E E08F ldi CH, S2S
001F 940E 0002 call PAUSE
0021 E08F ldi CH, DOT
0022 940E 000A call SIGN
0024 E08F ldi CH, S2S
0025 940E 0002 call PAUSE
0027 E08F ldi CH, DOT
0028 940E 000A call SIGN
002A E28F ldi CH, C2C ;S2S=0x2F
002B 940E 0002 call PAUSE
002D E08F ldi CH, DOT ;E=.
002E 940E 000A call SIGN
0030 E28F ldi CH, C2C ;S2S=0x2F
0031 940E 0002 call PAUSE
0032 E08F ldi CH, DOT ;L=.-..
0033 940E 000A call SIGN
0035 E08F ldi CH, S2S
0036 940E 0002 call PAUSE
0038 E28F ldi CH, DASH ;DASH=0x2F
0039 940E 000A call SIGN
003B E08F ldi CH, S2S
0036 940E 0002 call PAUSE
0038 E08F ldi CH, DOT
0039 940E 000A call SIGN
003B E08F ldi CH, S2S
003C 940E 0002 call PAUSE
003E E08F ldi CH, DOT
003F 940E 000A call SIGN
0040 E28F ldi CH, C2C
0041 940E 0002 call PAUSE
...
00F7 ED8F ldi CH, M2M ;M2M=0xDF
00F8 940E 0002 call PAUSE
00F9 940C 0015 jmp MAIN
Цикл формирования задержки использует команды вычитания (в том числе с заемом), выход из цикла осуществляется по условию установки флага переноса (заема). Этим и объясняется начальное значение счетчика (15 вместо 16 и т.п.).
В соответствующем hex-файле первые восемь команд выглядят так (число слов, начальный адрес, тип записи, восемь команд, контрольная сумма), старшие байты команд следуют за младшими:
:10 |
0000 |
00 |
0C94 |
0F00 |
71E6 |
68EA |
6150 |
750B |
E8F7 |
8150 |
BC |
Можно в цикле формировать последовательность знаков и пауз, хранящуюся в массиве:
Адрес Код Метка Мнемоника
----- --------- ------- ---------
0000 E0D8 INIT: ldi R29, 0x08
0001 EFCF ldi R28, 0xFF
0002 BFDE out SPH, R29 ;Инициализация указателя стека, старший байт должен записываться первым!
0003 BFCD out SPL, R28
0004 E030 ldi ZERO, 0x00 ;ZERO=R19
0005 E041 ldi LMASK, 1 << LED ;LMASK=R20, LED=0
0006 9A20 sbi DDRB, LED ;Вывод 0 порта B используется как выход
0007 9828 cbi PORTB, LED
0008 E0F0 MAIN: ldi R31, hi8(TABLE)
0009 E3E6 ldi R30, lo8(TABLE)
000A 95C8 LOOP: lpm ;Чтение байта из памяти программ
000B 2A03 or R0, ZERO
000C F3D9 breq MAIN
000D 9631 adiw R30, 1 ;inc R31:R30
000E B155 in TEMP, PORTB
000F 2754 eor TEMP, LMASK
0010 B955 out PORTB, TEMP ;Изменение состояния вывода 0 порта B на противоположное
0011 2D80 mov CH, R0 ;CH=R24
0012 E671 LOOP_H: ldi CM, 0x61 ;CM=R23
0013 EA68 ldi CL, 0xA8 ;CL=R22
0014 5061 LOOP_L: subi CL, 0x01
0015 0B73 sbc CM, ZERO
0016 F7E8 brсс LOOP_L
0017 5081 subi CH, 0x01
0018 F7C8 brсс LOOP_H
0019 940C 000A jmp LOOP
001B 0F0F TABLE: .byte DOT, S2S ;DOT=0x0F, S2S=0x0F, H=....
001C 0F0F .byte DOT, S2S
001D 0F0F .byte DOT, S2S
001E 2F0F .byte DOT, C2C ;C2C=0x2F
001F 2F0F .byte DOT, C2C ;E=.
0020 0F0F .byte DOT, S2S ;L=.-..
0021 0F2F .byte DASH, S2S ;DASH=0x2F
0022 0F0F .byte DOT, S2S
0023 2F0F .byte DOT, C2C
...
0040 DF0F .byte DOT, M2M ;M2M=0xDF
0041 0000 .byte EOL, EOL ;EOL=0
Приведенный ниже код проверялся на оценочной плате STM NUCLEO-F411RE с установленным на ней микроконтроллером STM32F411RET6U. Работа с периферией других микроконтроллеров может быть иной, так что нужно читать документацию.
На плате имеется светодиод, подключеный к пятому выводу порта A, он и будет использован. Внешний светодиод (с токоограничивающим резистором) можно подключить к выводу D13 платы. Используется встроенный RC-генератор c частотой 16 МГц, чтобы его использовать ничего настраивать не нужно.
Для изготовления bin-файла прошивки использовался ассемблер arm-none-eabi-as, компоновщик arm-none-eabi-ld и программа arm-none-eabi-objcopy. В отличие от рассмотренных выше микроконтроллеров файл прошивки не текстовый, он содержит образ того, что будет записано в память микроконтроллера и ничего больше. Прошивка выполняется с помощью установленного на плате программатора ST-LINK/V2-1. При подключении к компьютеру программатор виден как сменный диск, для прошивки нужно скопировать на него подготовленный bin-файл. Если снять две перемычки, программатор можно использовать для прошивки других микроконтроллеров
Программа начинается с неполной таблицы векторов прерываний. По-видимому, можно было бы обойтись первыми двумя элементами, содержащими начальные значения регистров SP и PC. В программе специально использованы различные команды для выполнение похожих действий:
Адрес Код Метка Мнемоника
--------- --------- ------- ---------
0800 0000 0202 0000 .word 0x20020000 @SP
0800 0004 0800 00A7 .word ENTRY + 1 @Entry_point @Единица в младшем бите указывает, что набор команд - Thumb
0800 0008 0800 0059 .word HNDLR + 1 @NMI_HANDLER
0800 000C 0800 0059 .word HNDLR + 1 @HardFault_HANDLER
0800 0010 0800 0059 .word HNDLR + 1 @MemManage_HANDLER
0800 0014 0800 0059 .word HNDLR + 1 @BusFault_HANDLER
0800 0018 0800 0059 .word HNDLR + 1 @UsageFault_HANDLER
0800 001C 0800 0000 .word 0 @Reserved
0800 0020 0800 0000 .word 0 @Reserved
0800 0024 0800 0000 .word 0 @Reserved
0800 0028 0800 0000 .word 0 @Reserved
0800 002C 0800 0059 .word HNDLR + 1 @SVC_HANDLER
0800 0030 0800 0059 .word HNDLR + 1 @DebugMon_HANDLER
0800 0034 0800 0000 .word 0 @Reserved
0800 0038 0800 0059 .word HNDLR + 1 @PendSV_HANDLER
0800 003C 0800 0059 .word HNDLR + 1 @SysTick_HANDLER
0800 0040 0010 46AA DOT: .word DELAY @DELAY=0x1046AA @Константы
0800 0044 0030 D3FE DASH: .word DELAY * 3
0800 0048 0010 46AA S2S: .word DELAY
0800 004C 0030 D3FE C2C: .word DELAY * 3
0800 0050 0071 EEA6 W2W: .word DELAY * 7
0800 0054 00E3 DD4C M2M: .word DELAY * 14
0800 0058 E7FE HNDLR: b HNDLR @Обработчик прерываний (пустой)
0800 005A 480A INIT: ldr R0, [PC, 40] @ldr R0, HB1E @Загрузка адреса регистра разрешения тактирования периферии
0800 005C 2101 movs R1, 1
0800 005E 6802 ldr R2, [R0, 0]
0800 0060 430A orrs R2, R1
0800 0062 6002 str R2, [R0, 0] @Включение порта A
0800 0064 4808 ldr R0, [PC, 32] @ldr R0, MR @Загрузка адреса регистра режима работы порта A
0800 0066 F240 4100 movw R1, (1 << (5 * 2))
0800 006A 6802 ldr R2, [R0, 0]
0800 006C 430A orrs R2, R1
0800 006E 6002 str R2, [R0, 0] @Пятый бит порта A - выход (по умолчанию симметричный, низкоскоростной)
0800 0070 4806 ldr R0, [PC, 24] @ldr R0, ABSR @Загрузка адреса регистра установки битов порта A
0800 0072 4907 ldr R1, [PC, 28] @ldr R1, ABRR @Загрузка адреса регистра сброса битов порта A
0800 0074 2220 movs R2, (1 << 5) @Загрузка маски для установки и сброса пятого бита порта A
0800 0076 800A strh R2, [R1, 0] @Сброс пятого бита порта A
0800 0078 F240 0495 movw R4, 0x0095 @PAUSE + 1 @Загрузка адреса функции PAUSE
0800 007C F6C0 0400 movt R4, 0x0800
0800 0080 4770 bx LR @Возврат
0800 0082 BF00 nop @Для выравнивания следующих ниже констант по границе слова
0800 0084 4002 3830 HB1E: .word RCC_AHB1ENR @RCC_AHB1ENR=0x40023830
0800 0088 4002 0000 MR: .word GPIOA_MODER @GPIOA_MODER=0x40020000
0800 008C 4002 0018 ABSR: .word GPIOA_BSRRL @GPIOA_BSRRL=0x40020018
0800 0090 4002 001A ABRR: .word GPIOA_BSRRH @GPIOA_BSRRH=0x4002001A
0800 0094 3B01 PAUSE: subs R3, 1
0800 0096 D1FD bne PAUSE
0800 0098 4770 bx LR
0800 009A B500 SIGN: push {LR} @Запись в стек адреса возврата
0800 009C 8002 strh R2, [R0, 0] @Установка пятого бита порта A
0800 009E F7FF FFF9 bl PAUSE @Переход с записью PC в LR
0800 00A2 800A strh R2, [R1, 0]
0800 00A4 BD00 pop {PC} @Возврат
0800 00A6 F7FF FFD8 ENTRY: bl INIT @Начало программы
0800 00AA F85F 306C MAIN: ldr R3, [PC, -108] @ldr R3, DOT @H=....
0800 00AE F7FF FFF4 bl SIGN
0800 00B2 F85F 306C ldr R3, [PC, -108] @ldr R3, S2S
0800 00B6 47A0 blx R4 @Косвенный переход с записью PC в LR (в R4 адрес функции PAUSE)
0800 00B8 F85F 307C ldr R3, [PC, -124] @ldr R3, DOT
0800 00BC F7FF FFED bl SIGN
0800 00C0 F85F 307C ldr R3, [PC, -124] @ldr R3, S2S
0800 00C4 FF7F FFE6 bl PAUSE
0800 00C8 F85F 308C ldr R3, [PC, -140] @ldr R3, DOT
0800 00CC F7FF FFE5 bl SIGN
0800 00D0 F85F 308C ldr R3, [PC, -140] @ldr R3, S2S
0800 00D4 FF7F FFDE bl PAUSE
0800 00D8 F85F 309C ldr R3, [PC, -156] @ldr R3, DOT
0800 00DC F7FF FFDD bl SIGN
0800 00E0 F85F 3098 ldr R3, [PC, -152] @ldr R3, C2C
0800 00E4 FF7F FFD6 bl PAUSE
0800 00E8 F85F 30AC ldr R3, [PC, -172] @ldr R3, DOT @E=.
0800 00EC F7FF FFD5 bl SIGN
0800 00F0 F85F 30A8 ldr R3, [PC, -168] @ldr R3, C2C
0800 00F4 FF7F FFCE bl PAUSE
...
0800 0302 F85F 32B0 ldr R3, [PC, -688] @ldr R3, M2M
0800 0306 FF7F FEC5 bl PAUSE
0800 030A E6CE b MAIN
Цикл формирования задержки использует команду вычитания константы, выход из него осуществляется по условию установки флага нулевого результата. Вычитание выполняется за один такт, условный переход - за два (если переход выполняется) или один такт (если нет).
Для хранения последовательности знаков и пауз можно использовать массив:
Адрес Код Метка Мнемоника
--------- --------- ------- ---------
0800 0000 0202 0000 .word 0x20020000 @SP
0800 0004 0800 0075 .word ENTRY + 1 @Entry_point @Единица в младшем бите указывает, что набор команд - Thumb
0800 0008 0800 0041 .word HNDLR + 1 @NMI_HANDLER
0800 000C 0800 0041 .word HNDLR + 1 @HardFault_HANDLER
0800 0010 0800 0041 .word HNDLR + 1 @MemManage_HANDLER
0800 0014 0800 0041 .word HNDLR + 1 @BusFault_HANDLER
0800 0018 0800 0041 .word HNDLR + 1 @UsageFault_HANDLER
0800 001C 0800 0000 .word 0 @Reserved
0800 0020 0800 0000 .word 0 @Reserved
0800 0024 0800 0000 .word 0 @Reserved
0800 0028 0800 0000 .word 0 @Reserved
0800 002C 0800 0041 .word HNDLR + 1 @SVC_HANDLER
0800 0030 0800 0041 .word HNDLR + 1 @DebugMon_HANDLER
0800 0034 0800 0000 .word 0 @Reserved
0800 0038 0800 0041 .word HNDLR + 1 @PendSV_HANDLER
0800 003C 0800 0041 .word HNDLR + 1 @SysTick_HANDLER
0800 0040 E7FE HNDLR: b HNDLR @Обработчик прерываний (пустой)
0800 0042 4808 INIT: ldr R0, [PC, 32] @ldr R0, HB1E @Загрузка адреса регистра разрешения тактирования периферии
0800 0044 2101 movs R1, 1
0800 0046 6802 ldr R2, [R0, 0]
0800 0048 430A orrs R2, R1
0800 004A 6002 str R2, [R0, 0] @Включение порта A
0800 004C 4806 ldr R0, [PC, 24] @ldr R0, MR @Загрузка адреса регистра режима работы порта A
0800 004E F240 4100 movw R1, (1 << (5 * 2))
0800 0052 6802 ldr R2, [R0, 0]
0800 0054 430A orrs R2, R1
0800 0056 6002 str R2, [R0, 0] @Пятый бит порта A - выход (по умолчанию симметричный, низкоскоростной)
0800 0058 4804 ldr R0, [PC, 16] @ldr R0, ODR @Загрузка адреса регистра данных порта A
0800 005A 4905 ldr R1, [PC, 20] @ldr R1, ABRR @Загрузка адреса регистра сброса битов порта A
0800 005C 2220 movs R2, (1 << 5) @Загрузка маски для установки и сброса пятого бита порта A
0800 005E 800A strh R2, [R1, 0] @Сброс пятого бита порта A
0800 0060 4770 bx LR @Возврат
0800 0062 BF00 nop @Для выравнивания следующих ниже констант по границе слова
0800 0064 4002 3830 HB1E: .word RCC_AHB1ENR @RCC_AHB1ENR=0x40023830
0800 0068 4002 0000 MR: .word GPIOA_MODER @GPIOA_MODER=0x40020000
0800 006C 4002 0014 ODR: .word GPIOA_ODR @GPIOA_ODR =0x40020014
0800 0070 4002 001A ABRR: .word GPIOA_BSRRH @GPIOA_BSRRH=0x4002001A
0800 0074 F7FF FF5E ENTRY: bl INIT @Начало программы
0800 0078 F240 0394 MAIN: movw R3, 0x0094 @Загрузка адреса массива
0800 007C F6C0 0300 movt R3, 0x0800
0800 0080 681С LOOP: ldr R4, [R3, 0]
0800 0082 4324 orrs R4, R4
0800 0084 d0F8 beq MAIN
0800 0086 3304 adds R3, 4
0800 0088 8805 ldrh R5, [R0, 0]
0800 008A 4055 eors R5, R2
0800 008C 8005 strh R5, [R0, 0]
0800 008E 3C01 PAUSE: subs R4, 1
0800 0090 D1FD bne PAUSE
0800 0092 E7F5 b LOOP
0800 0094 0010 46AA HELLO: .word DOT @DOT=DELAY, H=....
0800 0098 0010 46AA .word S2S @S2S=DELAY
0800 009C 0010 46AA .word DOT
0800 00A0 0010 46AA .word S2S
0800 00A4 0010 46AA .word DOT
0800 00A8 0010 46AA .word S2S
0800 00AC 0010 46AA .word DOT
0800 00B0 0030 D3FE .word C2C @C2C=3*DELAY
0800 00B4 0010 46AA .word DOT @E=.
0800 00B8 0030 D3FE .word C2C
0800 00BC 0010 46AA .word DOT @L=.-..
0800 00D0 0010 46AA .word S2S
0800 00D4 0030 D3FE .word DASH @DASH=3*DELAY
0800 00D8 0010 46AA .word S2S
0800 00DC 0010 46AA .word DOT
0800 00E0 0010 46AA .word S2S
0800 00E4 0010 46AA .word DOT
0800 00E8 0030 D3FE .word C2C
...
0800 01C0 00E3 DD4C .word M2M @M2M=14*DELAY
0800 01C4 0000 0000 .word EOL @EOL=0