перевод машинного кода в ассемблер
3.1. Машинный язык и ассемблер
На своем рабочем уровне микропроцессор реагирует на список операций, называемый машинной программой. На рис. 3.1, а приведено содержимое памяти, являющееся программой на машинном языке. Эта программа начинается с адреса 2000Н с содержимым КОП ОО11 11102 и оканчивается адресом 2006Н с содержимым 0111 01102. Человеку практически невозможно понять программу, представленную в такой форме.
Программа на машинном языке на рис. 3.1, а становится несколько проще для восприятия, когда она представлена в шестнадцатеричном коде (Н-коде), как показано на рис. 3.1, б. Однако, хотя двоичные данные приведены в шестнадцатеричном коде, эта часть программы всегда рассматривается как заданная на машинном языке и оказывается трудной для понимания.
В более приемлемой форме записанная на машинном языке она могла бы выглядеть так:
1. Загрузить двоичное число (1011 0100) в аккумулятор.
Инвертировать каждый двоичный бит содержимого аккумулятора.
Поместить результаты инверсии в ячейку памяти данных 2100Н.
В этой части осуществляется перевод двоичного 8-разрядного числа в его эквивалент в инверсной форме.
Возникает вопрос: как перейти от этой формы человеческого языка, иногда длинной и сложной, к машинному языку? Ответ состоит в использовании языка простого программирования – от самого высокого уровня до машинного, представленного на рис. 3.1. Ассемблер использует слова и фразы, преобразуя их в машинный код микропроцессора.
Программа на языке ассемблер, записанная человеком, могла бы быть представлена в виде табл. 3.1.
Число различных команд микропроцессора равно 78 и для них предложено 78 имен команд. Многие базовые команды порождают несколько различных кодов операций, поэтому общее число кодов команд равно 244.
Запомнить 244 восьмиразрядных двоичных кода очень трудно, и поэтому каждому коду ставится в соответствие мнемоническое название (мнемоника) команды, которое является сокращением от английских слов, описывающих ее действие. Например, IN 25 – input data at accumulator from port 25. На русский язык переводится так – ввести данные в аккумулятор из порта с адресом 25.
Мнемонический код команд позволяет легче запомнить их функции и значительно упрощает написание программ.
Такой язык написания программ называется языком ассемблера.
А затем эту программу на ассемблере надо перевести на язык, понятный микропроцессору, в последовательность двоичных восьмиразрядных чисел.
Перевод может происходить автоматически с помощью специальных программ-трансляторов (кросс-ассемблеров или ассемблеров) или вручную с помощью таблицы кодов команд.
При работе микропроцессор реагирует на список команд, называемый машинной программой. На рис. 3.1 показаны программы: а) в двоичном машинном коде; б) в шестнадцатеричном машинном коде.
Программа на рис. 3.1, а начинается с адреса 2000Н с содержимым КОП 001111100 и оканчивается адресом 2006 с содержимым 01110110. Человеку практически невозможно понять программу, представленную в таком виде. Хотя любая микропроцессорная система будет работать только с такой программой.
Программа, представленная на рис. 3.1, б, несколько проще для восприятия, так как она представлена в шестнадцатеричном коде, но все равно она трудна для понимания. Обе эти программы написаны на машинном языке.
А как программа, записанная на машинном языке, могла быть описана человеком?
1 – загрузить двоичное число 10110100 в аккумулятор;
2 – инвертировать каждый дв
оичный бит содержимого аккумулятора;
3 – поместить результат инверсии в ячейку памяти данных 2100Н;
4 – остановить микропроцессор.
Здесь осуществляется инвертирование двоичного восьмиразрядного числа.
Возникает вопрос: как перейти от этой формы человеческого языка, иногда длинной и сложной, к машинному языку.
Язык ассемблер использует слова и фразы, преобразуя их в машинный код микропроцессора.
Суть и процедура ассемблирования показаны на рис. 3.2, где вторая команда программы представлена единственной мнемоникой из трех букв CMA – complement accumulator – инвертировать содержимое аккумулятора.
Ход ассемблирования. Сначала три буквы переведены в их эквивалент в коде АСКИ, затем эти три кода АСКИ преобразованы в определенный порядок специальной программой ассемблера, которая выдает код инверсии содержимого аккумулятора на машинном языке – 2FH.
Программа, записанная человеком на языке ассемблера, выглядит следующим образом:
Программа разделена на 4 поля:
1 – поле метки; используется не всегда;
2 – поле мнемоники, содержит точную мнемонику, установленную разработчиком. Указывает программе ассемблера операцию для выполнения;
3 – поле операнда, содержит информацию о регистрах, данных и адресах, объединенных соответствующей операцией;
4 – поле комментариев, не учитывается ассемблером и ограничивается его перепечаткой. Очень важно, так как позволяет понять события в программе.
Что такое ассемблер и нужно ли его изучать
Этому языку уже за 70, но на пенсию он пока не собирается.
Есть традиция начинать изучение программирования с вывода на экран строки «Hello world!». На языке Python, например, это всего одна команда:
Всё просто, понятно и красиво! Но есть язык программирования, в котором, чтобы получить тот же результат, нужно написать солидный кусок кода:
Это ассемблер. Только не нужно думать, что он плох. Просто Python — это язык высокого уровня, а ассемблер — низкого. Одна команда Python при выполнении вызывает сразу несколько операций процессора, а каждая команда ассемблера — всего одну операцию.
Сложно? Давайте разбираться.
Программист, консультант, специалист по документированию. Легко и доступно рассказывает о сложных вещах в программировании и дизайне.
Немного о процессорах и машинном языке
Чтобы объяснить, что такое язык ассемблера, начнём с того, как вообще работает процессор и на каком языке с ним можно «разговаривать».
Процессор — это электронное устройство (сейчас крошечная микросхема, а раньше процессоры занимали целые залы), не понимающее слов и цифр. Он реагирует только на два уровня напряжения: высокий — единица, низкий — ноль. Поэтому каждая процессорная команда — это последовательность нулей и единиц: 1 — есть импульс, 0 — нет.
Для работы с процессором используется машинный язык. Он состоит из инструкций, записанных в двоичном коде. Каждая инструкция определяет одну простую машинную операцию: арифметическую над числами, логическую (поразрядную), ввода-вывода и так далее.
Например, для Intel 8088 инструкция 0000001111000011B — это операция сложения двух чисел, а 0010101111000011B — вычитания.
Программировать на машинном языке нелегко — приходится работать с огромными цепочками нулей и единиц. Трудно написать или проверить такую программу, а уж тем более разобраться в чужом коде.
Поэтому много лет назад был создан язык ассемблера, в котором коды операций обозначались буквами и сокращениями английских слов, отражающих суть команды. Например, команда mov ax, 6 означает: «переместить число 6 в ячейку памяти AX».
Когда и как был создан ассемблер?
Это произошло ещё в сороковых годах прошлого века. Ассемблер был создан для первых ЭВМ на электронных лампах, программы для которых писали на машинном языке. А так как памяти у компьютеров было мало, то команды вводили, переключая тумблеры и нажимая кнопки. Даже несложные вычисления занимали много времени.
Проблему решили, когда ЭВМ научились хранить программы в памяти. Уже в 1950 году была разработана первая программа-транслятор, которая переводила в машинный код программы, написанные на понятном человеку языке. Эту программу назвали программой-сборщиком, а язык — языком ассемблера (от англ. assembler — сборщик).
Появление ассемблера сильно облегчило жизнь программистов. Они смогли вместо двоичных кодов использовать команды, состоящие из близких к обычному языку условных обозначений. Кроме того, ассемблер позволил уменьшить размеры программ — для машин того времени это было важно.
Как устроен язык ассемблера?
Ассемблер можно считать языком второго поколения, если за первый принять машинный язык. Он работает непосредственно с процессором, и каждая его команда — это инструкция процессора, а не операционной или файловой системы. Перевод языка ассемблера в машинный код называется ассемблированием.
Коды операций в языке ассемблера мнемонические, то есть удобные для запоминания:
Регистрам и ячейкам памяти присваиваются символические имена, например:
EAX, EBX, AX, AH — имена для регистров;
meml — имя для ячейки памяти.
Например, так выглядит команда сложения чисел из регистров AX и BX:
А это команда вычитания чисел из регистров AX и BX:
Кроме инструкций, в языке ассемблера есть директивы — команды управления компилятором, то есть программой-ассемблером.
Вот некоторые из них:
Не думайте, что ассемблер — всего лишь набор инструкций процессора с удобной для программиста записью. Это полноценный язык программирования, на котором можно организовать циклы, условные переходы, процедуры и функции.
Вот, например, код, на ассемблере, выводящий на экран цифры от 1 до 10:
Здесь действие будет выполняться в цикле — как, например, в циклах for или do while в языках высокого уровня.
Единого стандарта для языков ассемблера нет. В работе с процессорами Intel разработчики придерживаются двух синтаксисов: Intel и AT&T. Ни у того ни у другого нет особых преимуществ: AT&T — стандартный синтаксис в Linux, а Intel используется в мире Microsoft.
Одна и та же команда в них выглядит по-разному.
Например, в синтаксисе Intel:
mov eax, ebx — команда перемещает данные из регистра eax в регистр ebx.
В синтаксисе AT&T эта команда выглядит так:
Почему для разных семейств процессоров нужен свой ассемблер?
Дело в том, что у каждого процессора есть набор характеристик — архитектура. Это его конструкция и принцип работы, а также регистры, адресация памяти и используемый набор команд. Если у процессоров одинаковая архитектура, то говорят, что они из одного семейства.
Так как наборы команд для разных архитектур процессоров отличаются друг от друга, то и программы на ассемблере, написанные для одних семейств, не будут работать на процессорах из других семейств. Поэтому ассемблер называют машинно-ориентированным языком.
Кому и зачем нужен язык ассемблера?
Даже из нашего примера «Hello, World!» видно, что ассемблер не так удобен в разработке, как языки высокого уровня. Больших программ на этом языке сейчас никто не пишет, но есть области, где он незаменим:
Если вы хотите разрабатывать новые микропроцессоры или стать реверс-инженером, то есть смысл серьёзно заняться изучением языка ассемблера.
Востребованы ли программисты на ассемблере сегодня?
Конечно. Хотя на сайтах по поиску работу вы вряд ли найдёте заявки от работодателей с заголовками: «Нужен программист на ассемблере», зато там много таких, где требуется знание ассемблера дополнительно к языкам высокого уровня: C, C++ или Python. Это вакансии реверс-инженеров, специалистов по компьютерной безопасности, разработчиков драйверов и программ для микроконтроллеров/микропроцессоров, системных программистов и другие.
Предлагаемая зарплата — обычная в сфере IT: 80–300 тысяч рублей в зависимости от квалификации и опыта. Вот, например, вакансия реверс-инженера на HeadHunter, где требуется знание ассемблера:
Стоит ли начинать изучение программирования с языка ассемблера?
Нет, так делать не нужно. Для этого есть несколько причин:
Поэтому, даже если вы решили заняться профессией, связанной с ассемблером, изучение программирования вам лучше начинать с языка высокого уровня. А уж ассемблер после него будет выучить несложно.
обложка: Полина Суворова для Skillbox Media
Перевод машинного кода в ассемблер
Многие любители не испытывают серьезных трудностей в овладении БЕЙСИКом. Для этого достаточно немного практики. Но рано или поздно они приходят к барьеру «машинного кода». Как это ни печально, но некоторые так перед ним и останавливаются. Это ни в коей мере не связано с отсутствием желания или способностей, просто многие не знают, с чего начать. Если в БЕЙСИКе можно начинать с чего угодно (при ошибке компьютер сам Вас поправит), то здесь Вы оказываетесь с процессором один на один, и такой метод проб и ошибок не срабатывает.
Итак, давайте напишем первую программу в машинном коде. Прежде всего, выделим для нее область памяти. Если Вы читали нашу книгу «Большие возможности Вашего «ZX-Spectrum`а», то знаете, что для БЕЙСИКа в оперативной памяти компьютера отведена область памяти, начинающаяся с адреса, на который указывает системная переменная PROG и заканчивается адресом, на который указывает системная переменная RAMTOP. Предположим, что Вы хотите записать программу в машинных кодах, начиная с адреса 30000. Дайте команду CLEAR 29999. Эта команда установит RAMTOP в 29999 и Ваша программа будет защищена от возможной порчи из БЕЙСИКа. Даже если Вы дадите команду NEW, области памяти, находящиеся выше RAMTOP, не будут поражены.
Теперь дайте две прямые команды одну за другой:
Если все, что Вы здесь прочитали, Вам понятно, то Вы уже поняли, как составляются программы в машинных кодах. Можно, конечно, возразить, что пользы от такой программы не очень много, но сейчас не в этом суть. Важно, чтобы Вы поняли, что некая последовательность чисел может быть последовательностью команд для процессора Z-80.
Теперь давайте вернемся к нашей первой программе и попробуем ее несколько развить, чтобы она все же что-то делала. Процессор Z-80 имеет несколько регистров, у которых есть имена – «А», «В», «С» и т.д. Каждый из них может содержать одно какое-либо целое число от 0 до 255 (т.е. один байт).
Существуют десятки команд процессора, которые позволяют копировать содержимое регистров из одного в другой, а также выполнять связь с внешним миром, в т.ч. и с оперативной памятью.
Итак, мы уже готовы к тому, чтобы написать программу, которая будет перебрасывать какое-либо число из одного регистра процессора в другой.
Перевод машинный код команд в мнемокод ассемблера
Вспомним, что выполняемая программа пpедставляет собой последовательность команд (инстpукций) в кодах машины. Каждая команда занимает один или несколько байтов. Расшифровка машинного кода — процесс, сложный даже для опытного программиста.
Команда UNASSEMBLER (u или U) и служит для перевода машинного кода в мнемокод aссемблера. К примеру, введем следующую последовательность чисел, используя инструкцию ENTER, в текущий сегмент, начиная с адреса 100h:
-е100 51 b9 00 10 49 75 fd 59 c3
Убедимся в правильности записи фрагмента (длина 9 байт):
0958:100 51 B9 00 10 49 75 FD 59-C3 Q..Iu.Y.
Теперь деассемблируем этот набор кодов:
0958:100 51 PUSH CX
0958:101 B90010 MOV CX,1000
0958:105 75FD JNZ 0104
Очевидно, что это листинг подпрограммы одного цикла (счетчик в регистре СX). Слева (первая колонка) указан длинный адрес команды. Затем (вторая колонка) — значения составляющих команду байтов в машинном коде. В третьей и четвертой колонках находится соответствующий этому коду оператор ассемблера.
В командной строке UNASSEMBLER можно не указывать начальный адрес обрабатываемого кода. Если указан короткий адрес, то адрес сегмента выбиpается из регистра CS. Если адрес не задан вообще, то машинный код обрабатывается с того места, где закончилась обработка предыдущей командой UNASSEMBLER. Если после старта монитора команда вводится в первый раз и в командной стpоке отсутствует начальный адрес, то обработка машинного кода пpоизводится с адреса CS:0100.
Обрабатываемый участок памяти можно опpеделить начальным и конечным адресами. Конечный адрес должен быть коротким. Если конечный адрес не приходится на последний байт команды, она все равно расшифровывается полностью:
-u100 108
0958:100 51 PUSH CX
0958:101 B90010 MOV CX,1000
0958:105 75FD JNZ 0104
Внимание! Если вы указываете начальный адрес, то необходимо, чтобы он приходился на начало команды. В противном случае полученный листинг может оказаться бессмысленным.
1.8 Выполнение программы под управлением монитора
После загрузки очень аккуратно введем следующее:
-e 100 b9 40 00 b2 41 b4 02 cd 21 49 75 f9 cd 20
Мы загрузили в память последовательность машинных кодов, составляющих программу.
Прежде чем пустить программу на выполнение, посмотрим ее листинг в мнемокоде aссемблера. Введем команду UNASSEMBLER:
0958:0100 B94000 MOV CX,0040
0958:0103 B241 MOV DL,41
0958:0105 B402 MOV AH,02
0958:0107 CD21 INT 21
0958:0109 49 DEC CX
0958:010A 75F9 JNZ 0105
0958:010C CD20 INT 20
Рисунок 4 – Листинг программы вывода
на дисплей 64 символа «А»
Принимая во внимание, что здесь использованы два программных прерывания, первое из которых (int 21 с инструкцией в регистре ah=02) выводит один символ в коде ASCII из регистра DL на экран, второй — (int 20) стандартно завершает программу (то есть передает управление обслуживающей системе) — можно увидеть, что программа выводит 64 (40Н) символа «А» на экран дисплея.
Команду GO можно вводить без дополнительных параметров. При этом программа начинает выполняться с адреса CS:IP. Чтобы проверить состояние регистров, воспользуемся командой REGISTER:
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=0100 NO ND EI NS NZ NA NP NC
0958:0100 B94000 MOV CX,0040
Так как в регистрах CS и IP хранится адрес начала программы, можно вводить команду GO:
-g
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Программа выполнена успешно
Перед нами ряд из 64 символов «А». Сообщение «Программа выполнена успешно» означает, что программа завершилась нормально и передала управление монитору.
Команда GO позволяет задать адрес команды, с которой можно начать выполнение программы. Таким образом, программу можно запускать не только с ее начального адреса (CS:IP). В этом случае после символа «А» набирается знак равенства «=» и адрес команды.
Если указан короткий адрес, то адрес сегмента выбирается из регистра CS.
Команда GO может служить для включения точек останова в отлаживаемую программу. Эти точки отмечают места прерывания нормально выполняющейся программы. Чтобы обозначить точку останова внутри программы, в командной строке указывается ее адрес. Если указывается короткий адрес, то адрес сегмента выбирается из регистра CS. Если нужно остановиться по адресу, сегмент которого отличается от текущего, в командной строке указывается его полный адрес.
При нормальном выполнении программы в точке останова происходит прерывание, и на экран выводится содержимое регистров и флагов состояния. Введение точек останова в программу особенно оправдано при отладке программ с разветвленной логикой.
Продолжить программу можно командой GO для прогона ее до конца или до следующей точки останова или выполнять ее в пошаговом режиме (команды TRACE или PROCESSING). Как правило, эти режимы используются всегда вместе, поэтому есть смысл рассмотреть прежде команду TRACE и PROCESSING.
1.9 Выполнение программы по шагам
Команда TRACE (t или T) осуществляет пошаговое выполнение программы в машинном коде. После выполнения каждой команды производится останов работы программы и на экран выводятся регистры и флаги состояния процессора. Полученная картинка аналогична картинке, получаемой с помощью команды REGISTER. Разница заключается только в том, что при введении TRACE перед появлением картинки, выполняется одна команда отлаживаемой программы. Проиллюстрируем работу TRACE на примере нашей программы. Если она не загpужена в память, то сделаем это по приведенному выше примеру. Чтобы узнать адpес программы, введем команду REGISTER:
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=0100 NO ND EI NS NZ NA NP NC
0958:0100 B94000 MOV CX,0040
При введении «t» выполняется команда по адресу CS:IP. После этого на экран выводятся регистры и флаги состояния:
-t
AX=0000 BX=0000 CX=0040 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=0103 NO ND EI NS NZ NA NP NC
0958:0103 B241 MOV DL,41
-t
AX=0000 BX=0000 CX=0040 DX=0041 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=0105 NO ND EI NS NZ NA NP NC
0958:0105 B402 MOV AH,02
-t
AX=0200 BX=0000 CX=0040 DX=0041 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=0107 NO ND EI NS NZ NA NP NC
0958:0107 CD21 INT 21
Далее, чтобы не уходить в длинную подпрограмму int 21, выполним эту часть нашей программы в непрерывном режиме с указанием адреса останова: 109h (на следующей команде).
A
AX=0200 BX=0000 CX=0040 DX=0041 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=0109 NO ND EI NS NZ NA NP NC
0958:0109 49 DEC CX
Мы видим, что один символ «А» вывелся на экран. Сделаем еще два шага с помощью команды TRACE:
-t
AX=0200 BX=0000 CX=003F DX=0041 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=010A NO ND EI NS NZ NA NP NC
0958:010A 75F9 JNZ 0105
-t
AX=0200 BX=0000 CX=003F DX=0041 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=0105 NO ND EI NS NZ NA NP NC
0958:0105 B402 MOV AH,02
По состояниям регистров CX и IP видно, что закончен один цикл. Теперь сделаем прогон остальных циклов до останова по адресу 10Ch:
-g10c
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AX=0200 BX=0000 CX=0000 DX=0041 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=010C NO ND EI NS ZF NA NP NC
0958:010c CD20 INT 20
На экран вывелись остальные 63 символа «А». Как следует из состояния регистров, CX=0, флаг Z взвелся, и поэтому состоялся выход из цикла.
Если бы нам захотелось досрочно выйти из цикла, достаточно было бы перед выполнением команды условного перехода (например, в конце первого цикла) самим установить Z=1:
-rf
NO ND EI NS NZ NA NP NC ‑ZF
-r
AX=0200 BX=0000 CX=003F DX=0041 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0958 ES=0958 SS=0958 CS=0958 IP=010A NO ND EI NS ZF NA NP NC
0958:010A 75F9 JNZ 0105
Теперь снова запустим программу в непрерывном режиме с этого места уже без всяких условий:
-g
Программа выполнена успешно
Программа закончила свою работу, не выведя больше ни одного символа «А».
В командной стpоке TRACE можно указать адрес выполняемой команды. В этом случае после «t» набирается знак равенства «=» и нужный адрес. Если указан короткий адрес, то адрес сегмента выбирается из регистра CS. Например:
-t=0109
Одной командой TRACE можно одновpеменно трассировать несколько команд отлаживаемой пpогpаммы. Для этого при введении «t» просто указывается их количество. После выполнения каждой команды на экране появляется картинка с содеpжимым регистров и флагов состояния. Пpи пеpеполнении экрана новые данные выводятся в нижней его части, сдвигая данные в верхней части за пределы экpана.
Чтобы остановить движение данных вдоль экрана, нажимаются клавиши Ctrl+NumLock. Чтобы возобновить движение, нажимается любая клавиша.
При нажатии Ctrl-C трассирование прекращается и на экране появляется стандартный запрос монитора:
1.9.2 Команда PROCESSING
Команда PROCESSING по существу делает то же самое, что и команда TRACE, с той лишь разницей, что она выполняет как обычные команды:
— все вызываемые подпрограммы (в том числе и программные прерывания INT);
— циклические программы на основе команд LOOP;
— строчные (цепочные) команды с заданным циклом.
Команда вызывается символом «р» или «Р». Проверьте работу этой команды на предыдущем примере (вместо TRACE), и вы убедитесь, что прерывания INT 21 и INT 20 выполняются как обычные команды. Значение этой команды трудно переоценить, особенно, если в программе использовано много программных прерываний типа INT.
1.10 Перевод мнемокода ассемблера в машинный код команд
Команду монитора ASSEMBLER предназначена для введения операторов aссемблера 80х86/80х87 непосредственно в память машины. По существу, команду ASSEMBLER можно использовать пpи составлении коротких программ на ассемблере, а также пpи внесении изменений в существующие программы. Эта команда позволяет вводить мнемокод ассемблера непосредственно в память, избавляя от необходимости транслировать (ассемблировать) программу. Пpи введении команды, необходимо набрать «а» (или «А») и адрес первой команды загpужаемой пpогpаммы. Если указан короткий адрес, то адрес сегмента выбирается из регистра CS. После введения «а» (или «А»), на экране появляется начальный адрес. Это сигнал на введение первой команды. Если команда введена без ошибок, на экран выдается адрес следующей команды и монитор опять переходит в режим ожидания. В случае ошибки монитор обозначает ее месторасположение. Если введены все команды программы, то нажимается Enter — команда ASSEMBLER заканчивает работу и возвpащает упpавление монитору.
Рассмотрим работу ASSEMBLER на примере программы, которая использовалась в предыдущих разделах (рисунок 4). На вашей машине адрес сегмента может оказаться отличным от приведенного здесь:
-a100
0958:0100 MOV CX,40
0958:010E (Здесь нажать ENTER)
Рисунок 5 – Составление программы на ассемблере
Прочитаем машинные коды, полученные в результате работы ASSEBLER:
0958:0100 B9 40 00 B2 41 B4 02 CD-21 49 75 F9 CD 20
Сверьте коды команд полученной программы с кодами программы на рисунке 4: они полностью совпадают. Здесь, в отличие от прямой загрузки в память программы в машинных кодах, операторы на языке ассемблеp, на котором составлена программа, сначала переводятся в машинный код (трансляция), и только после этого полученные машинные коды загружаются в память машины.
1.11 Идентификация файла
Команда NAME (n или N) пpисваивает имя обpабатываемому файлу. Затем этот файл загружается в память командой LOAD или записывается на диск командой WRITE. (LOAD и WRITE рассматриваются ниже).
Чтобы идентифицировать файл, наберите «n» (или «N») и спецификацию файла так, как это принято в DOSе с учетом носителя.
На лабораторном стенде имеются следующие носители:
— А: (2-х сторонний, 40 цилиндров) – флоппи-диск;
— B: (односторонний, 80-и цилиндровый) – флоппи-диск;
— D: (2-х сторонний, 80-и цилиндровый) – флоппи-диск;
Воспользуемся NAME, чтобы присвоить нашей программе имя (смотрите пункт 1.10), подразумевая (по умолчанию) текущий рабочий раздел Е:
(не более 8‑и символов в имени и не более 3‑х —в расширении имени). Или полным именем:
—
В случае успешного выполнения команды NAME увидим:
1.12 Запись данных в файл на диске
Команда WRITE (w или W) пеpеписывает на диск данные, выбиpая их из памяти. При этом в качестве спецификации создаваемого файла берется из памяти монитора последняя спецификация, созданная командой NAME.
Перед введением команды WRITE необходимо в регистровую пару BX-CX записать размер занимаемой файлом памяти в байтах (шестнадцатеричное число размером в 4 байта).
a) Загрузим программу в ассемблере по примеру, приведенному в разделе 1.10 (рисунок 1.5).
b) Запишем в регистры BX и CX значение разности между адресом первой свободной ячейки за программой 010E (рисунок 1.5) и начальным адресом программы 0100: 010E-0100=000Е и расширим разность до 4-х байт: 0000000E (BX=0000, CX=000E).
:0000 (вводит пользователь)
:000е (вводит пользователь)
c) С помощью NAME запишем спецификацию файла, учитывая, что программу будем сбрасывать на носитель Е:
d) С помощью WRITE перепишем на диск 14 байтов данных, считывая их, начиная с адреса CS:0100. Новому файлу присваивается имя «wyw_simb.com» и на экран выводится сообщение о количестве переписанных данных (в байтах).
-w
Writing 000E bytes
1.13 Загрузка файла с дисков
Команда LOAD (l или L) пpедназначена для загрузки файлов в память машины. Пpи этом спецификация загружаемого файла должна быть указана заранее с помощью команды NAME.
В командной стpоке можно указать начальный адрес, по которому загpужается файл. Если указан короткий адрес, то адрес сегмента выбирается из регистра CS. При отсутствии начального адреса, загрузка производится по адресу CS:0100.
-l
—
Можете сами убедиться с помощью инструкции U, что наша программа загружена и готова к работе в среде монитора. Помните, что LOAD пpоизводит загрузку файлов, спецификация которых записана в префиксе сегмента пpогpаммы (смещение 0000 – 00FF). Пpи введении очеpедной команды NAME, эта спецификация заменяется. Поэтому рекомендуется использовать NAME непосредственно перед загрузкой выбранного файла.
Вывод данных в порт
Микропроцессор связан с внешними устройствами через порты. Интерфейсные схемы, обслуживающие те или иные порты, выбираются с помощью дешифраторов адреса A10. A0. Поэтому каждый порт имеет свой индивидуальный адрес (аналогично байтам памяти). Каждое внешнее устройство из-за сложности управления требует обслуживания через несколько портов. В лабораторном стенде, например, клавиатура привязана к двум портам: с адресом данных 60h и адресом управления 61h; разъем принтера — к трем портам: с адресом 378h (данные), адресом 379h (состояние принтера) и адресом 37Ah (управление принтером).
Кроме интерфейсных схем, обслуживающих внешние устройства, присутствует группа схем, выполняющих функциональные задачи: отсчет интервалов времени (таймер), организация прямого доступа в память (КПДП), обработка аппаратных прерываний (KПр) и тому подобное. И каждая из них из-за высокой степени сложности также имеет несколько адресов. Например, КПДП занимает адреса с 00h по 0Fh, КПр — с 20h пo 21h, таймер — с 40h по 43h. Команда OUTPUT (о или О) осуществляет пересылку данных в выходной порт. Пpи этом в командной стpоке указывается адрес порта и значение пересылаемого байта. Параметры разделяются пробелом или запятой. Перешлем, например, значение 3Сh в порт 378h:
Появление дефиса означает, что команда вывода была выполнена.
Ввод данных из порта
Команда INPUT (i или I) считывает байт данных из указанного порта и выводит его значение на экран. Пpи этом в командной стpоке указывается адрес порта. К примеру, считаем байт из порта 21h. Его значение (21h — адрес маски прерываний) отображается на экране:
—
1.16 Вывод краткой справки по командам монитора
Таблица команд монитора представляет собой полный набор инструкций монитора (символов d, e, u, a и т.д.) с краткой их расшифровкой.
Для оперативного вывода указанной таблицы нужно ввести «?»:
D-чт.ЗУ E-зап.ОЗУ R-чт/зап рег. I-ввод O-вывод
A-ассемб. U-деасемб. Q-выход N-имя файла T,P-шаг