машинные коды процессора intel

FE23 LOOCH DISASM


СПРАВОЧНИК ПО КОМАНДАМ
ПРОЦЕССОРОВ x86


машинные коды процессора intel. . машинные коды процессора intel фото. машинные коды процессора intel-. картинка машинные коды процессора intel. картинка . Справочник по командам процессоров x86 дает хорошее обзорное представление по основным командам этого семейства процессоров.
ГлавнаяЗагрузкаИнструкцияКоманды x86Карта сайта

машинные коды процессора intel. . машинные коды процессора intel фото. машинные коды процессора intel-. картинка машинные коды процессора intel. картинка . Справочник по командам процессоров x86 дает хорошее обзорное представление по основным командам этого семейства процессоров.
машинные коды процессора intel. . машинные коды процессора intel фото. машинные коды процессора intel-. картинка машинные коды процессора intel. картинка . Справочник по командам процессоров x86 дает хорошее обзорное представление по основным командам этого семейства процессоров.машинные коды процессора intel. . машинные коды процессора intel фото. машинные коды процессора intel-. картинка машинные коды процессора intel. картинка . Справочник по командам процессоров x86 дает хорошее обзорное представление по основным командам этого семейства процессоров.

СПРАВОЧНИК ПО КОМАНДАМ
ПРОЦЕССОРОВ x86


ГЛАВНАЯ СТРАНИЦА


Содержание


Об этом справочнике

Справочник по командам процессоров x86 дает хорошее обзорное представление по основным командам этого семейства процессоров.

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

Приветствуются любые отзывы и замечания по справочнику

Мой адрес электронной почты: looch-disasm ( @ ) ya.ru

Все страницы справочника

Сейчас на справочнике всего есть 30 страниц (больших и маленьких).

Главная страница


Основные большие таблицы


Структура машинной команды


Команды по группам


Разные заметки


Последнее обновление справочника


25.07.2011

Уже давно приготовлены основные таблицы справочника. Можно сказать, что с этих больших страниц и начинался весь справочник.

В таблицы справочника были внесены все команды процессора i486.

Затем все команды были разбиты на несколько групп, по каждой из групп должна быть своя страница. Однако, пока не все из этих страниц готовы. Некоторые из них сделаны формально. Страница якобы присутствует, но на странице есть только начало. Так пришлось сделать, чтобы в основных таблицах можно было поставить ссылку на каждую группу.

И есть еще новые идеи. На сегодня сделаны еще не все страницы справочника, которые задуманы. Так что развитие справочника еще не закончилось, будут еще новые страницы.

Добавлены страницы по структуре машинной команды и большие таблицы с кодами байта MRM и байта SIB. Вроде бы придуман неплохой способ, как сделать таблицы со всеми возможными кодами байта MRM и байта SIB.

Источник

Машинный код Intel-486 (подробно) + прочие процессоры

Интересуют Intel, 486 (прежде всего), а также RISC и AMD-64 + факультативно другие процессоры.

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Подскажите мать поддерживающую процессоры Intel Celeron D 347 и Intel Core i3-2100.
Подскажите мать поддерживающую процессоры: Intel Celeron D 347 Intel Core i3-2100 Желательно без.

Сильно ли отличаются процессоры по мощности: DualCore Intel Pentium D 820 от Intel Core 2 Duo E4500?
Подскажите пожалуйста у меня стоит DualCore Intel Pentium D 820 если поменять на Intel Core 2 Duo.

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

Процессоры Core-i5 Intel
Привет Хочу спросить про процессоры от Intel (для игрового ПК) Сore i5 8400 Сore i5 8600 Сore.

Вложения

машинные коды процессора intel. zip. машинные коды процессора intel фото. машинные коды процессора intel-zip. картинка машинные коды процессора intel. картинка zip. Справочник по командам процессоров x86 дает хорошее обзорное представление по основным командам этого семейства процессоров.OPCODES.ZIP (65.5 Кб, 44 просмотров)

Возьми, к примеру, команду DIV и распиши в каком виде ты хотел бы ее увидеть

P.S. посмотри https://www.cyberforum.ru/asse. 05284.html там есть частичный разбор опкодов, хотя лучше чем в мануалах от Intel это никто не опишет

Добавлено через 5 минут
(ремарка: ссылки форум потёр, они были на сайт x86asm, раздел coder32)

Ещё забавно, что в этой таблице поле расширения базового опкода 0F стоит ДО этого поля, хотя является не другим полем вовсе а именно этим самым базовым; а то, что указано в поле базового опкода уже должно по идее занимать соседнюю, следующую полосу.

Источник

Машинные коды процессора intel

Рустэм Галеев aka Roustem

Содержание

1. Введение в машинные коды для Win32

Мир машинных кодов для процессоров Intel IA-32 захватывающий и фантастический. Он предоставляет такие богатые возможности для творчества, что будет неудивительно, если через некоторое время станут проводить чемпионаты по спортивному программированию в машинных кодах, а лучшие творения кодеров представлять на выставках, как произведения искусства. Множество интересных находок было накоплено за прошедшие годы кодокопателями, среди которых есть как законные системные программисты, так и подпольные авторы вирусов, хакеры и кракеры.

Можно сказать, информационные технологии проходят сейчас период массового производства, как когда-то автомобильная и другие виды промышленности. Конвейер штампует однотипные универсальные изделия. Но посмотрите на исторические тенденции. Сначала автомобили собирали поштучно. Потом появился конвейер. Но сейчас самые дорогие и качественные машины опять собирают вручную! А разве механические часы исчезли с появлением электронных? Напротив, стали только качественнее и дороже. А когда их сравнивают с электронными, последние презрительно именуют «штамповкой». И как сравнить массовую бижутерию с синтетическими камнями с филигранной ювелирной работой.

Как бы то ни было, но и в компьютерной индустрии постепенно развилась особая субкультура низкоуровневого программирования. Долгое время она варилась в собственном соку, оставаясь достоянием узкого круга посвященных, интенсивно осмысливавших накопленные знания. Вероятно, был пройден некий порог, и мы вплотную приблизились к моменту, когда начинает зарождаться элитарное штучное ручное производство и в данной высокотехнологичной области. И делать это, естественно, могут лишь специалисты высочайшей квалификации, понимающие значение каждого используемого байта. Однако для дальнейшего развития в этом направлении нужно не только ознакомить более широкую аудиторию с накопленным в узких кругах опытом, но и развенчать некоторые уже устаревшие стереотипы наподобие того, что современные системы программировать на низком уровне невозможно вообще.

Пугаться этого не следует. На самом деле, для программирования под Windows требуется весьма ограниченный набор инструкций, и скоро мы сможем в этом убедиться. Мы будем изучать нужные нам инструкции по мере необходимости. А сейчас кратко рассмотрим «суть» программирования в машинных кодах, а она довольно проста.

машинные коды процессора intel. regs. машинные коды процессора intel фото. машинные коды процессора intel-regs. картинка машинные коды процессора intel. картинка regs. Справочник по командам процессоров x86 дает хорошее обзорное представление по основным командам этого семейства процессоров.

На рисунке показано взаимоотношение адресуемых частей для регистра EAX; регистры ECX, EDX и EBX имеют подобную же схему. Регистры ESP, EBP, ESI и EDI «включают» в свой состав лишь 16-разрядные SP, BP, SI, DI и не допускают обращения к отдельным байтам.

Как же узнать, к какой именно части регистра происходит обращение, тем более, если коды регистров одни и те же (как в случае EAX, AX и AL)? Эта информация заложена в саму инструкцию. Многие опкоды имеют так называемый бит w, который указывает на размер используемого регистра (или операнда в целом): если он равен 0, это байт, если 1, «полный» регистр. В 16-разрядном режиме бит w обозначает размер операнда 8 или 16 бит. Но современная Windows работает в 32-разрядном режиме, и состояние бита w обозначает размер операнда 8 или 32 бита. Обращение к 16 младшим битам регистра тоже возможно, но для этого используется другая схема с применением префиксов (об этом поговорим в другой раз).

Есть еще два регистра, с которыми придется иметь дело: это регистр флагов EFLAGS и указатель инструкций EIP. Состояние регистра флагов может меняться после каждой инструкции в зависимости от полученного результата; подробнее об этом поговорим в другой раз. Регистр EIP содержит адрес начала следующей инструкции в памяти. Его значение увеличивается каждый раз, когда из памяти извлекается для исполнения очередная инструкция, на величину размера этой инструкции.

Обрабатываемые инструкцией данные могут находиться не только в регистре, но и в памяти, а также входить в состав самой инструкции. При обращении к памяти в инструкции указывается адрес, по которому расположены данные. Рассмотрим различные способы доступа к данным на примере инструкции (а вернее, группы инструкций) перемещения данных, которыми мы будем очень активно пользоваться. На ассемблере группа данных инструкций обозначается мнемоникой MOV.

Начнем с команды, которая перемещает непосредственное значение (являющееся частью самой инструкции) в регистр общего назначения. Формат команды следующий:

Теперь эту же единицу загрузим в старший байт регистра AX (2-й байт EAX): тоже один байт (w=0), но код регистра AH уже другой (100):

Удовольствие составления различных инструкций с данным опкодом оставим вам для самостоятельных упражнений и перейдем к другой команде, которая перемещает данные между памятью и регистром EAX (AX, AL):

А теперь то же значение загрузим в регистр AL (w=0, d=0):

Архитектура IA-32 предоставляет очень богатый набор способов адресации памяти. Сейчас отметим лишь, что возможна еще и косвенная адресация, когда адрес операнда в памяти находится в регистре, а инструкция ссылается на соответствующий регистр. Для работы с такими случаями, а также для перемещения данных между регистрами используется так называемый байт способа адресации (ModR/M). Этот байт следует непосредственно за опкодом, который предполагает его использование, и содержит следующие поля:

Байт ModR/M предполагает, что имеются два операнда, причем один из них всегда находится в регистре (код которого содержится в поле REG), а второй может находиться (в зависимости от значения поля MOD) либо тоже в регистре (при MOD = 11; при этом поле R/M содержит код регистра), либо в памяти (R/M=»register or memory»). В последнем случае адрес памяти, по которому находится операнд, вычисляется следующим образом (см. табл.):

R/MMOD=00MOD=01MOD=10
000[EAX][EAX] + 1 байт смещения[EAX] + 4 байта смещения
001[ECX][ECX] + 1 байт смещения[ECX] + 4 байта смещения
010[EDX][EDX] + 1 байт смещения[EDX] + 4 байта смещения
011[EBX][EBX] + 1 байт смещения[EBX] + 4 байта смещения
100SIBSIB + 1 байт смещенияSIB + 4 байта смещения
1014 байта смещения[EBP] + 1 байт смещения[EBP] + 4 байта смещения
110[ESI][ESI] + 1 байт смещения[ESI] + 4 байта смещения
111[EDI][EDI] + 1 байт смещения[EDI] + 4 байта смещения

Если MOD=01, за байтом ModR/M следует байт, значение которого добавляется к значению соответствующего регистра и таким образом вычисляется адрес операнда. При MOD=10 за ModR/M следуют уже 4 байта; значение этого числа тоже суммируются со значением соответствующего регистра для вычисления адреса.

Присутствие байта ModR/M обычно требует также наличия битов d и w. Рассмотрим еще одну команду:

Это характерная особенность работы с машинными кодами. Подобные этим трюки могут использоваться для создания защит и антиотладочных приемов. Между тем даже ассемблер генерирует для подобных команд лишь один вид кода, тем самым значительно вас обкрадывая, не говоря уже о компиляторах с языков высокого уровня.

Только не надо пугаться и думать, что при программировании в машинных кодах все время придется делать выбор из сотен возможных вариантов. На самом деле в Win32-программировании постоянно будут встречаться одни и те же инструкции, так что мы их помимо своей воли выучим наизусть. Хотя в этой статье оказалось много разнообразного материала, вы можете считать его одой свободе и богатству выбора, которую несут с собой машинные коды. В будущих статьях мы непременно сможем убедиться, насколько простым может быть программирование под Windows в машинных кодах, особенно если вы сумели уловить логику построения инструкций.

Источник

Написание простого процессора и окружения для него

Здравствуйте! В этой статье я расскажу какие шаги нужно пройти для создания простого процессора и окружения для него.

Архитектура набора команд (ISA)

Для начала нужно определиться с тем, каким будет процессор. Важны такие параметры как:

Архитектуры процессоров можно разделить по размеру инструкций на 2 вида (на самом деле их больше, но другие варианты менее популярны):

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

Я решил сделать RISC процессор во многом похожий на MIPS.

Я это сделал по целому ряду причин:

Вот основные характеристики моего процессора:

Register type(досл. Регистровый тип) выглядит вот так:

машинные коды процессора intel. image loader. машинные коды процессора intel фото. машинные коды процессора intel-image loader. картинка машинные коды процессора intel. картинка image loader. Справочник по командам процессоров x86 дает хорошее обзорное представление по основным командам этого семейства процессоров.

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

Immediate type(досл. Немедленный тип):

машинные коды процессора intel. image loader. машинные коды процессора intel фото. машинные коды процессора intel-image loader. картинка машинные коды процессора intel. картинка image loader. Справочник по командам процессоров x86 дает хорошее обзорное представление по основным командам этого семейства процессоров.

Инструкции этого типа оперируют с двумя регистрами и числом.

OP — это номер инструкции, которую нужно выполнить (или же для указания, что эта инструкция Register type).

R0, R1, R2 — это номера регистров, которые служат операндами для инструкции.

Func — это дополнительное поле, которое служит для указания вида Register type инструкций.

Imm — это поле куда записывается то значение, которое мы хотим явно предоставить инструкции в качестве операнда.

Полный список инструкций можно посмотреть в github репозитории.

Вот лишь пару из них:

NOR это Register type инструкция, которая делает логическое ИЛИ НЕ на регистрах r1 и r2, после записывает результат в регистр r0.

Для того, чтобы использовать эту инструкцию нужно изменить поле OP на 0000 и поле Func на 0000000111 в двоичной системе счисления.

LW это Immediate type инструкция, которая загружает значение памяти по адресу r1 + n в регистр r0.

Для того, чтобы использовать эту инструкцию в свою очередь нужно изменить поле OP на 0111, а в поле IMM записать число n.

Написание кода процессора

После создания ISA можно приступить к написанию процессора.

Для этого нам нужно знание какого нибудь языка описания оборудования. Вот некоторые из них:

Я выбрал Verilog, т.к. программирование на нем было частью моего учебного курса в университете.

Для написания процессора нужно понимать логику его работы:

И так до бесконечности.

Получается нужно создать несколько модулей:

Разберем по отдельности каждый модуль.

Регистровый файл

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

В моем случае у меня 64 регистра. В один из регистров записывается результат операции над двумя другими, так что мне нужно предоставить возможность изменять только один, а получать значения из двух других.

Декодер

Декодер это тот блок, который отвечает за декодирование инструкций. Он указывает какие операции нужно выполнить АЛУ и другим блокам.

На этом этапе декодер определяет, что эта инструкция:

И передает эти сведения следующим блокам.

После управление переходит в АЛУ. В нем обычно выполняются все математические, логические операции, а также операции сравнения чисел.

То есть, если рассмотреть ту же инструкцию addi, то на этом этапе происходит сложение 0 и 20.

Другие

По мимо вышеперечисленных блоков, процессор должен уметь:

Тут и там можно увидеть как это выглядит в коде.

Ассемблер

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

Я решил реализовать его на языке программирования Си.

Так как мой процессор имеет RISC архитектуру, то для того, чтобы упростить себе жизнь, я решил спроектировать ассемблер так, чтобы в него можно было легко добавлять свои псевдоинструкции(комбинации из нескольких элементарных инструкций или из других псевдоинструкций).

Можно реализовать это с помощью структуры данных, хранящей в себе тип инструкции, ее формат, указатель на функцию, которая возвращает машинные коды инструкции, и ее название.

Обычная программа начинается с объявления сегмента.

Для нас достаточно двух сегментов .text — в котором будет храниться исходный код наших программ — и .data — в котором будет хранится наши данные и константы.

Инструкция может выглядеть вот так:

Сначала указывается название инструкции, потом операнды.

В .data же указываются объявления данных.

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

Удобно парсить (сканировать) ассемблер файл в таком виде:

Для работы ассемблеру нужно проходить исходный файл 2 раза. В первый раз он считает по каким смещениям находятся ссылки (они служат для), они обычно выглядят вот так:

А во второй проход можно уже и генерировать файл.

В дальнейшем, можно запускать выходной файл из ассемблера на нашем процессоре и оценивать результат.

Также готовый ассемблер можно использовать в Си компиляторе. Но это уже позже.

Источник

Машинные коды процессора intel

Главный герой находится в заточении. В абсолютной тьме. У него были выколоты глаза, но за год они регенерировали, и зрение постепенно к нему возвращается.
И однажды каким-то чудом в одной камере с ним оказывается загадочный Дворкин — создатель Лабиринта. Именно «чудом» — он просто появился неизвестно откуда. Он тоже находится «в заключении», но, в отличие от Корвина (главного героя), может спокойно ходить через каменные стены.
Удивленный Корвин спрашивает его:
— Как ты оказался в моей камере? Ведь здесь нет дверей.
Дворкин отвечает:
— Двери есть везде. Просто нужно знать, как в них войти.
Будем считать это эпиграфом.

1.1. Система счисления

#4. Очень наглядно это отображают обыкновенные счеты. Набранное на них число 35672 будет выглядеть. см. рисунок слева в общем.

Это (если сверху вниз считать) сколько на каждом «прутике» «костяшек» влево отодвинуто.

#5. Пальцев на руках у человека 10, поэтому и считать мы привыкли в системе счисления с основанием 10, то есть в десятичной. Если вы хорошо представляете себе счеты и немного поупражнялись в разложении чисел аналогично выражению 1, то перейти на систему счисления с основанием, отличным от привычной, особого труда для вас не составит. Нужно всего лишь представить себе счеты, на каждый прут которых нанизано не привычные 10 костяшек, а. скажем, 9 или 8, или 16, или 32, или 2 и. попробовать мысленно считать на них.

#6. Для обозначения десятичных чисел мы используем цифры от 0 до 9, для обозначения чисел в системах счисления с основанием менее 10 мы используем те же цифры:

Если же основание системы счисления больше десяти, то есть больше, чем десять привычных нам чисел, то начинают использоваться буквы английского алфавита. Например, для обозначения чисел в системе счисления с основанием 11 «как цифра» будет использоваться буква А:

Правда, при определенном основании (при каком?) буквы аглицкого алфавита закончатся.
Но нам это, пока что, глубоко фиолетово, так как работать мы будем только с тремя radix-ами: 10 (ну естественно), 16 и 2. Правда, если кто на ДВК поизучать это дело собирается, тому еще и radix 8 понадобится.

#7. Числа в любой системе счисления строятся аналогично десятичной. Только на «счетах» не с 10, а с другим количеством костяшек.
Например, когда мы пишем десятичное число 123, то имеем в виду следующее:

Если же мы используем символы 123 для представления, например, шестнадцатеричного числа, то подразумеваем следующее:

Истина где-то рядом.

#8. Трудность у вас может возникнуть при использовании символов A, B, C и т. д. Чтобы решить эту проблему раз и навсегда, необходимо назубок вызубрить ма-а-аленькую табличку «соответствия» между употребляемыми в «компьютерном деле» систем счисления:

radix 100123456789101112131415
radix 160123456789ABCDEF
radix 201101110010111011110001001101010111100110111101111

Следуя этой таблице, число 5BC в шестнадцатеричном формате «строится» так:

А теперь, если пораскинуть мозгами, с легкостью переведем 5BC из шестнадцатеричной в десятичную систему счисления:

Преобразования чисел в системы счисления с другим основанием проводятся аналогично. Счеты! Обыкновенные счеты, только с «плавающим» числом «костяшек» на каждом «прутике».

Правда, этим немножко затрудняется понимание происходящего? А ведь тоже десятичная система! И рисунок цифр как бы знакомый :-)))
Или вообще считать в 256-ричной системе счисления, используя в качестве «рисунка цифр» таблицу ASCII-символов! (По сравнению с вами, извращенцами, любой Биллгейтс будет девственником казаться!!).

#12. Теперь самая интересная часть Марлезонского балета.
Компьютер, как известно, считает только в двоичной системе счисления. Человеку привычна десятичная. Так нахрена еще и шестнадцатеричную какую-то знать нужно?
Все очень просто. В умных книжках пишут, что «шестнадцатеричная нотация является удобной формой представления двоичных чисел». Что это значит?
Переведите число A23F из шестнадцатеричной «нотации» в двоичную. (Один из возможных алгоритм приведен в п.10.). В результате длительных манипуляций у вас должно получиться 1010001000111111.
А теперь еще раз посмотрите на таблицу в п. 8. (которую вы как бы уже и выучили) и попробуйте то же самое сделать в уме :

#13. Кстати (наверняка вы это уже знаете):

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

О специализации нам пока что говорить рано, описание наподобие «регистр-указатель базы кадра стека» вам вряд ли о чем-то скажет. Поэтому для начала познакомимся только с так называемыми регистрами общего назначения (РОН), и то не со всеми, а только с четырьмя основными, которые являются своего рода «рабочими лошадками» микропроцессора.

А сейчас мы поближе посмотрим на эти «рабочие лошадки» микропроцессора.

Не правда ли, весьма похоже на то, что показывают в художественных фильмах про хакеров?

Природа не терпит пустоты. Писателей приводит в ужас чистый лист бумаги.

Весьма скоро и вы при виде «пустых» регистров будете испытывать непреодолимое наркотическое желание чем-нибудь их заполнить.

Однако прежде чем мы сделаем это в первый раз, давайте уточним тип этих «переменных».

В общем, в умных книжках рисуют вот такую вот «нездоровую» схемку 3 :

А означает она следующее.

Очевидно, что присвоить AX значение, например, 72F9h, мы можем следующими способами:

Точно так же присвоить значение 78h регистру AH можно двумя способами:

То же самое, но для регистра AL:

Тех, кого смущают числа с буквами, мы со зловредной ухмылкой отсылаем к 1.1. Система счисления :-]

AX2F4D
AHAL2F4D
Значение бита0010111101001101
Номер бита1514131211109876543210
ТетрадыСтаршая AHМладшая AHСтаршая ALМладшая AL

#4. «Принудительно» присвоить регистру значение можно при помощи той же команды «R», только с параметром «имя собственное регистра».

выбросит вам на монитор

Введите после двоеточия, например, число 123 и снова нажмите на Enter:

На дисплее опять появится приглашение «-«, на которое мы отвечаем командой «R» без параметров и таким образом вновь просматриваем значения наших регистров:

4). «Первый справа» бит мы будем называть «нулевым». Однако нам попадались руководства, в которых это же бит обозван как «первый». Можно долго обсуждать тонкости русского языка (которые, к сожалению, не всегда понимает переводчик), однако это выходит за рамки данной книги. Просто имейте это ввиду, что можете с этим столкнуться, и будьте бдительнее, читая документацию.

Например, команда (параметр L8 означает «вывести 8 байтов»):

покажет вам системную дату в правом столбце дампа.

Итак, у нас есть оперативная память, в которую загружается программа перед ее выполнением (сразу же по нажатию на Enter из Norton Commander). Операционная система, которая, собственно, и загружает программу, сообщает процессору, что надо начать обрабатывать команды, которые в памяти начинаются с такого-то адреса. И здесь первый подводный камень, вернее скала, которую трудно не заметить.

Каким образом? А очень легко! Компьютер «распознает» как выход из программы специальную последовательность байтов. Например, для исполнимых файлов типа com (именно с этим типом файлов мы будем работать на начальном этапе) достаточно последовательности CD и 20.

Пробуем-проверяем? Ну конечно же! Только для этого вам понадобится какой-нибудь шестнадцатеричный редактор, например, HexWorkshop.

Запускать это ваше первое творение лучше из Norton или Volcov Commander (все же это пока что DOS’овская программулька).

Что же она делает, эта 2-байтовая малышка? А ничего, просто этот файл обладает двумя важными свойствами:

Последнее и является единственным, что она пока что может делать (корректно выгружаться из памяти).

Вот и давайте создадим еще одну программу типа com со следующим «шестнадцатеричным содержимым»:

На высоком уровне это делает операционная система. Например, она не пытается загрузить в память для выполнения файлы с расширениями, отличными от COM, EXE и BAT (последний вообще не из этой оперы, но принцип сохраняется).

Хотя. вы всегда можете поэкспериментировать! Смените, например, у какого-нибудь текстового файла тип с TXT на COM и попробуйте его запустить на выполнение (хотя мы это делать настоятельно не рекомендуем!). В большинстве случаев ваш компьютер зависнет! Потому что:

Почти такой же эффект, но с потенциально большей разрушительной силой может получиться, если управление получит ИСПОРЧЕННЫЙ код, который вроде бы «в основном» правильный, но часть его вместо инициализации переменных и прочих подготовительных действий в лучшем случае ничего не делает, а в худшем портит другой код и данные.

#4. Еще немного идеологии. О программе, которая выполняется в памяти.

#5. Как уже говорилось в #3, одна и та же последовательность битов в памяти может быть:

Соответственно, и программа состоит из трех частей (сегментов): сегмента данных (data), сегмента кода (code) и сегмента стека (stack).

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

Посмотрите на машинные коды, и «что они делают» в #2. Немножко дополним эту «простыню». Например, командой «внести значение» 1234 последовательно в каждый из «регистров общего пользования»:

В этом вы можете убедиться, загрузив вашу программу myprg_1.com в debug (например, командной строкой

А вот дальше начинается самое интересное :)))

#7. Вот что вы должны увидеть:

Возвратившись к #2, перенесем сюда «описание» машинных команд.

Соответственно, вместо шестнадцатеричных кодов мы легко могли вводить эти команды при помощи команды «A» (однако этим мы займемся позже).

Итак, вводим «T» и жмем на Enter!

Вводим команду «T» снова:.

Вводим команду «T» снова:.

Вводим команду «T» снова:

«Прибавить содержимое AX к BX». Оно? А то!

Вводим команду «T» снова:

«Переслать содержимое BX в CX». Сделано!

Вводим команду «T» снова:

«Очистка AX»? И точно: AX=0000!

Вводим команду «T» снова. И ГРОМКО РУГАЕМСЯ!!

Для тех, кому лень продолжать жать на букву «T», введите для разнообразия команду «G» (от английского GO). На монитор должна вывалиться надпись «Нормальное завершение работы программы».

#9. Только непонятно вот, почему вдруг между int 20 (CD 20) и надписью «Нормальное завершение работы программы» куча всяких «левых» непонятных команд (в том случае, если вы и дальше производили тарассировку, а не воспользовались «халявной» командой «G»)?

А потому, дорогие наши, что вы имели счастье нарваться на прерывание (interrupt)!

Ну, посудите сами, должна же операционная система ну хоть что-нибудь делать!!

Когда же и «кем» генерируются эти «сигналы» (в смысле «прерывания»)?

Итак, я достаю свой толстый талмуд с описанием прерываний и выбираю, каким бы это прерыванием вас занять на ближайшие 1/2 часа ;).

Ну, например, вот одно симпатичное, под названием «прокрутить вверх активную страницу».

Внимательно читаем описание (и наши комментарии):

ВХОДНЫЕ ПАРАМЕТРЫ: (_2)

Далее представим входные параметры в виде таблички: (_7)

AH06hALЧисло строк
BHАтрибутBLНе имеет значения
CHСтрока (верх)CLСтолбец (верх)
DHСтрока (низ)DLСтолбец (низ)

Плюс подробнейшее толкование, что подразумевается под словом «атрибут» (регистр BH):

ВЫХОДНЫЕ ПАРАМЕТРЫ: отсутствуют (т.е. ни один регистр не меняется). (_8)

Входные строки гасятся в нижней части окна. (_9)

Совсем недавно, если бы вам показали подобное «описание», вы бы ничего в нем не поняли и ужаснулись. Теперь же, после прочтения предыдущих глав курса, в эти «таблицы» вы, более или менее, но «въехать» должны! Тем более, что сейчас я сделаю комментарии для. хм. «отстающих» учеников (внимательно смотрим на циферки в скобках):

_1. Черным по белому, в толстом талмуде, описывающем функции прерываний, написано: «Драйвер видео вызывается по команде INT 10h и выполняет все функции, относящиеся к управлению дисплеем».

И далее. «…ДЕЙСТВИЕ: после входа управление передается одной из 18 программ в соответствии с кодом функции в регистре AH. При использовании запрещенного кода функции, управление возвращается вызывающей программе.

НАЗНАЧЕНИЕ: прикладная программа может использовать INT 10h для прямого выполнения функций видео. «

Вот что из этого следует:

Нижеследующая табличка называется «Функции, реализуемые драйвером видео»:

Соответственно, если перед выполнением INT 10 в регистре AH будет значение 06h, то выполнится именно «прокрутить вверх активную страницу», а не что-то другое из «простыни» функций десятого прерывания.

Теперь читаем описание дальше (смотрим на циферки в скобках):

_2. Входные параметры? Что тут может быть непонятного? Даже запуск ракеты с атомной боеголовкой требует прежде всего указать координаты цели. Чего уж тут говорить об обыкновенной функции?

_6. Как известно из школьного курса геометрии, прямоугольник можно построить по двум точкам. Это утверждение справедливо и для окна, в котором мы желаем проскроллировать наш текст.

_7. Резюме того, что было написано выше.

_8. К примеру, попала ли наша ракета в цель или нет ;).

_9. Если бы мы использовали функцию 07h, то было бы глубокомысленно написано, что «строки гасятся в верхней части окна».

_10. Это то самое, которое в DOS по умолчанию. Т.е. белыми буквами на черном фоне. Правда, это 07h лучше все же рассматривать как 00000111b 🙂 но это уже совсем другая проблема.

#4. А теперь мы напишем программу. Ручками, без использования компилятора. Запускаем наш любимый debug.exe, вводим команду «а» и судорожно стучим по клавиатуре:

Сначала запускаем из-под Norton Commander. Затем запускаем из-под debug. Трассируем. Открываем в HEX-редакторе. Смотрим на «бессмыслицу» шестнадцатеричных циферек. Медитируем, медитируем и еще раз медитируем.

1.6. Немножко программируем и немножко отлаживаем

Давайте немножко видоизменим программу, которую мы писали в прошлый раз. Сделаем так, чтобы наше «окошко скроллинга» располагалось более или менее посередине экрана.

#2. Вот к каким умозаключениям вы должны были придти, пораскинув мозгами. За атрибут (то бишь цвет) у нас отвечает регистр BH. Он был равен 10h, а нужно на 10h больше. это, значит, 20h будет.

А еще можно одним махом в CX записать число 0510h той же командой mov.

Правда, красивые циферки-буковки? Набиваем-набиваем! Если сейчас к вам подойдут недZенствующие приятели/коллеги и посмотрят, что вы тут колупаете, то ни черта не поймут и покрутят пальцем у виска. Привыкайте к этому. Только не говорите им, что пытаетесь сейчас получить Матрицу, ПОТОМУ ЧТО это неправда. А неправда это потому, что сейчас Матрица в очередной раз обманула вас!

#3. ВСЕ ПОТОМУ, ЧТО МОЗГАМИ ДУМАТЬ НАДО, А НЕ ТОЛЬКО СЛЕПО СЛЕДОВАТЬ РУКОВОДСТВУ!

1. Что вам мешает после команды «a» указать адрес, который вы желаете переассемблировать? И благополучно заменить старую команду на новую!

А что делать, если не переассемблировать нужно, а вообще удалить?

Кстати, если процессор встретит команду NOP, то он просто побездельничает некоторое очень короткое время.

ПРОБУЙТЕ!! В конце концов, ваша прога должна принять такой вот вид:

Мы просто приведем вам последовательность действий, а вывод «зачем это нужно» и прочие возможные выводы вы уже сами делать будете. Ок?

Состояние: обнулился регистр AX (первую команду MOV AL,AL мы не видим). Процессор готовится выполнить команду MOV BH,10. Дадим ему это сделать!

Вот теперь-то мы и делаем это «на лету»:

А действительно ли сделали? Проверим!

Состояние? BH теперь равно 40h! Мы «вклинились» между строчками:

И изменили текущую цепь событий, заставив программу делать ТО, ЧТО НАМ НУЖНО! Поздравляю!

Что такое тяжелая атлетика вы наверняка знаете. Видели по телевизору, когда выходит на помост этакий здоровяк, и рвет собственное здоровье, поднимая штангу.

Это вам не очередь времен социализма. Это очередь «загрузки-разгрузки» стека!

#2. Для работы со стеком вам пока что необходимо знать только две команды: push и pop. Так как в качестве «блинов» у нас регистры, то, соответственно, необходимо после этих команд указывать и «имена собственные» помещаемых в стек значений регистров.

Ну а как делать то же самое с остальными регистрами вы, наверняка, уже и сами догадались.

С очередностью заполнения стека, наверное, все понятно :). Я много про абстрактные «блины» загружал. А вот с адреса 114 начинается извлечение из стека. В какой последовательности это делается, вы можете увидеть сами, произведя трассировку этой небольшой проги.

Анализируем. Прога еще не начала работать, готовится выполниться команда по адресу 100. Делаем ШАГ!

Проехали до адреса 114.

А вот теперь снова анализируем :). При следующем шаге выполнится команда, извлекающая некогда «запомненное» значение AX из стека.

Обратите внимание, регистр IP указывает на адрес (114) выполняемой команды. Мы с вами это уже проходили, не так ли?

AX=1 🙂 То есть нашлись-таки наши 1, 2, 3, 4, 5 :). Восстановились из стека. Теперь поверили? А то!

Медитируйте над этой темой до полного просветления! Иначе потом придется туго!

Еще более очевидно, что простая команда POP AX (с 114 по 119) повторяется у нас тоже 5 раз.

Мне почему-то сразу вспомнился анекдот о том, как два мента едут в машине, и один спрашивает у другого: «Глянь, работает ли у нас мигалка на крыше». Тот высунул в голову в форточку и говорит: «Работает-не работает-работает-не работает-работает-не работает. «

Так вот, не будем уподобляться этим нехорошим людям и сделаем нашу прогу более нормальной.

Реализуется же он (цикл), например, при помощи регистра CX и команды LOOP следующим образом.

Число циклов заносится в регистр CX. После этого следует «простыня» из команд, которые вы хотите «зациклить», т. е. выполнить энное количество раз. Заканчиваться все это должно LOOP’ом с указанием адреса «строки», с которой необходимо начать цикл (обычно это «строка», следующая сразу же после mov СХ.

Давайте мы сначала «набьем» нелинейный вариант нашей проги, а потом разберемся, что там к чему. Набиваем:

Протрассируйте эту программу! Искренне надеюсь, что вы поняли, чем это я вас тут загружал.

#2. А теперь вопрос на засыпку ;). Сколько раз выполнится следующий цикл:

Самые подозрительные могут сразу же посмотреть на этот цикл под отладчиком, и с удивлением обнаружат, что LOOP сначала уменьшает значение CX (0-1=FFFF), а потом уже проверяет, не равен ли он нулю. И с гордостью за задний ум своей головы воскликнут: FFFFh раз!

Так вот: этот ответ близок к истине, но тоже неправильный 😉

Но только вы и мне не верьте! Истинно только то утверждение, которое вы сами проверили на практике. Медитируйте!

1.9. Немножко оптимизации

Попытайтесь оттрассировать «зацикленную». Не правда ли, она трассируется намного дольше своего линейного аналога?

Угу, всё поняли? Сам знаю, что ни черта. 🙁

Объясняю: в «зацикленной» программе «компутеру» приходится выполнять БОЛЬШЕ команд, нежели в «незацикленной».

Аргументирую это голословное утверждение следующей таблицей (построенной на основе трассировки):

Ну и как по-вашему, какую из двух простыней процессор быстрее обработает? Сказать вам по секрету? А вот ничего я вам не скажу! Сами думайте! :]

Как сейчас помню, был в моем Турбо-Си в преференсах к компилятору такой радиобуттон: «оптимайзить» по размеру или по скорости выполнения. Угадайте, на чем основан принцип этой оптимизации?

1.10. Разборка с процедурами

Если вы так подумали, то оказались совершенно правы! Программу из #4 запросто можно было представить в таком вот виде:

И несмотря на то, что размер ее оказался несколько большим, она тоже будет работать правильно :).

Внимательно всмотритесь в полный текст программы и в этот выделенный кусок. И помедитируйте над ним до полного просветления текущей «обстановки».

Все более чем просто. Когда в программе встречается CALL с указанием АДРЕСА-НАЧАЛА-ПРОЦЕДУРЫ (в нашем случае это 011E), то компьютер «идет» по этому адресу и выполняет все команды, расположенные между «точкой входа» (включительно) и командой RET, то есть так называемое «тело» процедуры.

Не ругайтесь. Мы знаем, что вы ни черта не поняли. А по сему набьем в debug’е эту прогу и посмотрим, что она делает.

Те, кто читал внимательно, могут отметить, что инструкция CALL по своему действию очень похожа на инструкцию генерации прерывания INT, с той лишь разницей, что аргументом CALL является адрес процедуры, а не индекс в таблице «векторов прерываний», где и хранится адрес обработчика прерывания (той же процедуры). А для особо продвинутых отметим, что в ранних моделях процессоров от Intel при подаче запроса на обработку от внешнего устройства контроллер прерываний, помимо собственно сигнала прерывания, посылал в процессор инструкцию CALL.

#3. Кстати, вы уже поняли, почему мы называем debug «до боли любимой программой»? Нет? Неужели вы еще не полюбили это произведение программерского гения всеми фибрами своей души? Еще нет? М-да. мы в вас разочаровались.

Не правда ли, красиво получилось?

Итак, до адреса 0110 вам все должно быть понятно, мы это рассматривали. Трассируем дальше.

Мысленно мы ругаем вас нехорошими словами (ну сколько раз повторять-то можно!), а вслух скажем: Команда «T» и Enter. Команда «T» и Enter. Команда «T» и Enter…

Команда CALL 011E по адресу 0110 говорит процессору: «Дальше мы не пойдем, пока не выполним простыню, начинающуюся по адресу 011E». И далее, естественно, следует переход на этот адрес.

Входим в тело процедуры, начиная с 011E, и выполняем команды до 012D включительно.

А теперь внимательно смотрим, на какой адрес нас «перекинет» команда RET.

На 113-й? И это правильно! По 113-му адресу у нас какая команда? Да вот опять CALL 011E!

Опять процедура с адреса 011E, опять RET[URN] на строку ниже, то есть на 116.

Короче, куда бы вас не посылали всяческие «столбы с указателями», конец вашего пути только один. А плутать вокруг да около этого конца вы можете сколько вам заблагорассудится.

Кстати, именно это и является одной из многочисленных тайн программинга.

#4. Те, кто внимательно ознакомились с циклами, они и на этом не остановятся! Посмотрев на адреса 110. 119, они вообще возьмут и возомнят себя воистину крутыми парнями! Знаете, что они напишут? А вот что (предвидим!):

То бишь еще и CALL в цикл при помощи MOV CX,4 и LOOP’а «закрутят». И что? А попробуйте!

Что, не «пашет»? А что надо делать, если «не пашет, а должно бы»? Правильно! Смотреть из-под отладчика!

А теперь попробуйте выкрутиться из этой нехорошей ситуации с использованием стека :). Кстати, весьма «мозгопрочищающая» задачка :).

Соответствено, для успешного перехода необходимо указать: ПРИ КАКОМ УСЛОВИИ выполнить переход, КУДА ПЕРЕЙТИ, ну и, наконец, сам пинок под зад нужно СДЕЛАТЬ, чтобы переход «гостя» в заданном направлении все-таки «состоялся».

Например, на первом шаге можно использовать как «аптекарские весы» инструкцию CMP, которой обязательно нужно указать, ЧТО и С ЧЕМ она будет сравнивать.

В зависимости от значений регистров у нас возможны следующие состояния: «наклон влево» (AX &tt; BX), «наклон вправо» (AX > BX) и «равновесие» (AX = BX). Таким образом ВЫЧИСЛЕНИЕ УСЛОВИЯ у нас уже организовано :). Только условие не бинарное, а есть еще и «серединный вариант» (и даже несколько других!). Это нормально. Это для того сделано, чтобы мы могли выражения типа «больше-или-равно», «меньше-или-равно» да и просто «равно» в своих программах использовать.

Итак, УСЛОВИЕ есть. Теперь решаем, что нам делать при том или ином условии. Вот далеко не полный список возможных «прыг-скоков»:

Естественно, что после мнемоники («прыгнуть, если») должен стоять АДРЕС, куда нужно «прыгнуть», если условие соблюдено. Если же условие не соблюдено, то прыжок не происходит, и выполняется нижеследующая строка программы.

А о чем это мы? Ах да, переходы.

#3. Продолжим программировать, что ли? Напишем что-нибудь красивое и неизменно тупое? С использованием условных и безусловных переходов?

Поехали! Слабаем мы сейчас что-то наподобие графического редактора :)). Не верите?

Как работает последний кусок кода, обязательно проверьте под отладчиком, это полезно :).

#4. И лезем, лезем в наш горячо любимый DZEBUG, дабы набить там драгоценные строчки машинного мнемонического никому-кроме-вас-непонятного кода!

Тут один из автору вставили шпильку:

#5. А сейчас мы это все дело прокомментируем:

Правда, здорово получилось?

#1. Работать с кодом мы с вами научились. Сейчас поучимся заставить наш код обрабатывать данные.

Итак, запускаем DZEBUG и вводим следующую команду:

Которая означает: «набиваем память всяким дерьмом начиная со смещения 115».

В ответ он вам выплюнет:

Если вы вознамеритесь последовательно ввести 1,2,3,4,5, то это будет выглядеть приблизительно так:

А теперь делаем дамп памяти и смотрим, что за дрянь у нас получилась.

А ведь получилoсь же!!

#2. Мы запросто умеем «присваивать» регистру любое значение (mov AL,1C какой-нить), запросто можем «копировать» содержимое одного регистра в другой (mov AL,BL например). А сейчас мы с вами научимся при помощи той же команды MOV еще и с данными из памяти работать.

Все проще пареной репы. Если мы напишем

то в результате выполнения этой команды в регистр AL «внесутся» две шестнадцатеричные циферки (байт), которые по адресу 115 находятся. То есть в нашем случае AL станет равным 1.

А теперь посмотрите, что делает «обратная» команда:

В первой строчке мы присвоили AL значение 55, а второй строчкой «скопировали» значения регистра в байт по адресу 115. Правда, проще некуда?

Обязательно посмотрите на этот процесс под отладчиком!

#3. А еще вот какой изврат с этим можно делать:

Сие присваивает регистру AL значение байта по адресу 115 :). Ну. через посредника «BX» присваивает! Который у нас «переменная», как известно :).

А этот кусок кода у нас «записал» 1C в сегмент данных по адресу 115 :). Ну, и извращения наподобие:

Тоже весьма и весьма полезны в программерском деле :).

#4. Низкоуровневый Paint мы с вами уже писали. Сегодня напишем низкоуровневый дZенский EXCEL.

Задание простое. Есть у нас табличка типа:

Для начала мы наберем «исходные данные» и зарезервируем место (например, забьем нулями) под третий столбец, в который собираемся помещать результат.

Набиваем блок данных, начиная с адреса, например, 115:

Вот так это у меня в DZEBUG’е выглядело :). Только я еще дамп посмотрел, правильно ли я ввел:

Вроде правильно :)). Ну а программу я вот какую придумал:

Команда по адресу 106 забирает в AL цифирь из первого столбца.

Ну и ADD BX,3 для перехода на следующую строчку :).

Сделайте трассировку (внутрь INT 20 залезать не надо) и посмотрите на дамп нашего блока данных 🙂

Я и говорю: ПРОЩЕ ПАРЕНОЙ РЕПЫ!! 😉

#5. Видите? В качестве переменных «в компьютере» можно использовать не только регистры, но и «куски» памяти! А уж там вы можете клепать свои переменные в почти неограниченном количестве! Единственное, что нужно иметь ввиду: с переменными-регистрами компьютер работает намного быстрее, чем с переменными-в-памяти :).

Кстати, если вы хотите сохранить плод своих сегодняшних трудов на веник, то имейте ввиду, что вы и сегмент данных тоже должны сохранить! То есть: вам нужно сохранить весь «диапазон» от адреса 100 до 123 включительно :).

Ну и, само собой, при попытке дизассемблирования с адреса 115 у вас абракадабра пойдет. мы об этом уже говорили и упоминали один из принципов фон Неймана.

Полагаю, вы уже поняли, что значит «выучить язык ассемблера» 🙂 и теперь с удовольствием кинете грязью в того, кто скажет вам, что это сложно 😉

Да о чем это я, в общем-то? (Утомлен кофием, поэтому речь несвязна)? Просто хотел сообщить вам, что первая часть курса закончилась. Вооружившись справочником команд и прерываний, вы уже можете программировать под дос. Если вы внимательно штудировали предыдущие главы, то идеология этого дела (под дос) вам уже должна быть понятна как 2х2=100b.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *