машинный код для чайников

Машинный код для чайников

Многие любители не испытывают серьезных трудностей в овладении БЕЙСИКом. Для этого достаточно немного практики. Но рано или поздно они приходят к барьеру «машинного кода». Как это ни печально, но некоторые так перед ним и останавливаются. Это ни в коей мере не связано с отсутствием желания или способностей, просто многие не знают, с чего начать. Если в БЕЙСИКе можно начинать с чего угодно (при ошибке компьютер сам Вас поправит), то здесь Вы оказываетесь с процессором один на один, и такой метод проб и ошибок не срабатывает.

Итак, давайте напишем первую программу в машинном коде. Прежде всего, выделим для нее область памяти. Если Вы читали нашу книгу «Большие возможности Вашего «ZX-Spectrum`а», то знаете, что для БЕЙСИКа в оперативной памяти компьютера отведена область памяти, начинающаяся с адреса, на который указывает системная переменная PROG и заканчивается адресом, на который указывает системная переменная RAMTOP. Предположим, что Вы хотите записать программу в машинных кодах, начиная с адреса 30000. Дайте команду CLEAR 29999. Эта команда установит RAMTOP в 29999 и Ваша программа будет защищена от возможной порчи из БЕЙСИКа. Даже если Вы дадите команду NEW, области памяти, находящиеся выше RAMTOP, не будут поражены.

Теперь дайте две прямые команды одну за другой:

Если все, что Вы здесь прочитали, Вам понятно, то Вы уже поняли, как составляются программы в машинных кодах. Можно, конечно, возразить, что пользы от такой программы не очень много, но сейчас не в этом суть. Важно, чтобы Вы поняли, что некая последовательность чисел может быть последовательностью команд для процессора Z-80.

Теперь давайте вернемся к нашей первой программе и попробуем ее несколько развить, чтобы она все же что-то делала. Процессор Z-80 имеет несколько регистров, у которых есть имена – «А», «В», «С» и т.д. Каждый из них может содержать одно какое-либо целое число от 0 до 255 (т.е. один байт).

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

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

Источник

Секреты машинного кода

В одной из прошлых статей мы поговорили о языках программирования высокого уровня. Сегодня мы опустимся гораздо ниже — в самые недра программных систем и попробуем разобраться в том, на каком «языке» говорит с пользователями и программистами сама ЭВМ.

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

Цифровая электроника работает с двумя типами электрических сигналов, или, если точнее, с двумя «состояниями» — высоким уровнем сигнала в его источнике либо передатчике и низким его уровнем. Высокий уровень сигнала — это «ноль», низкий — «единица». Это наиболее надежная схема передачи сигналов, которая дает стабильный результат. Какие бы помехи не возникали на пути сигнала, мы всегда поймем, где «единица», а где — «ноль».

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

Как работает «двоичный код»

С помощью этого кода можно легко перевести десятичное число в понятное компьютеру двоичное. Возьмем число (например, 11001). Пронумеруем его с конца, причем, нумерацию начнем с нуля. Порядковый номер каждой цифры станет степенью, в которую мы возведем основание системы счисления (2), затем перемножим результат с цифрой. Нужно провести эту операцию с каждой цифрой двоичного числа, затем суммировать результат:

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

В результатах, снизу вверх, читаем наше число.

Таким образом считать довольно легко, причем, даже на «пальцах». У нас ведь на руке пять пальцев? Если каждому присвоить единицу или ноль (ноль – палец согнут, единица – выпрямлен) и прикинуть значение основания системы, возведенной в нужную степень, то можно считать от нуля и до 1023. Правда, в случае с числом 25 жест руки будет немного неприличным.

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

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

В остальном, операция выполняется также.

Как считает компьютер?

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

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

Теперь к числу прибавляем единицу:

Для чего же вся эта малопонятная морока? – спросит читатель. Для того, чтобы ЭВМ смогла выполнять операции с числами.

Знаковые разряды в этом случае просто не учитываются.

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

Вычитание работает по тому же принципу, умножение — путем последовательного сложения (я очень сильно упрощаю но принцип именно таков). Что касается операции деления, то она реализуется путем неоднократного сложения делимого с дополнительным кодом делителя до получения необходимого результата.

Подробно эти действия описывать не буду. Расскажу лишь про арифметический сдвиг. Это ещё одна «хитрость», которая позволяет упростить вычисления. Если сдвинуть двоичное число вправо или влево, то оно меняется, согласно определенным правилам.

Сдвиг вправо (слева добавляем единицу, последний разряд убираем):

Ещё один сдвиг вправо:

С дополнительным кодом — ещё интереснее. Сдвиг числа влево дает умножение на 2, вправо – деление на 2. Это очень удобно для умножения и деления целых чисел на числа, равные степени 2 (2, 4, 8, 16, 32, 64). Существует множество вариантов сдвига — арифметический (мы его выполнили), циклический, логический и т.д. Каждый выполняет свою задачу.

Добавлю, что всё, о чем я говорил — это «азы» компьютерных вычислений. Когда-то они выполнялись компьютером именно так. Сегодня они ведутся уже на ином уровне. Персональный компьютер выполняет расчеты при помощи встроенного в ядро основного ЦПУ математического сопроцессора, где каждая операция выполняется одной командой, что значительно ускоряет работу. Математический сопроцессор оптимизирован именно для вычислений, эти функции заложены в него на аппаратном уровне. Но описание работы сопроцессора — это тема для отдельной статьи или даже целой книги.

Кодируем… двоичный код

Сейчас мы подошли к самому главному моменту: мы уже знаем, что компьютер выполняет операции с числами в двоичном коде. Причем, это единственный пока вариант для ЭВМ и не такой уж сложный, если вдуматься, для нашего с вами понимания. Остается выяснить, как же объяснить компьютеру, что он должен делать с числом (числами), чтобы получить результат.

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

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

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

В принципе, кодирование информации происходит в компьютере неоднократно. Это циклический процесс, реализованный и на программном и на аппаратном уровнях. Начинается он со скан-кода (в IBM-совместимых компьютерах), с помощью которого драйвер клавиатуры распознает нажатия и отпускания клавиш.

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

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

Ассемблеры — аппаратно-ориентированные языки, они привязаны к архитектуре процессора, поэтому, их синтаксис для разных процессоров будет различен. Но их объединяет один принцип. Команды процессора, которые представляют собой просто числа, в ассемблерах представлены как «мнемонические» символы. Чтобы не расшифровывать это понятие, я просто продемонстрирую несколько небольших программ.

Вот как будет выглядеть программа на ассемблере 8086 (x86-совместимая), которая выводит на консоль Hello World:

Кроме уже знакомых нам команд здесь есть и другие. 02h — вывод символа, dl — первый символ, Inc dl – следующий символ, 08h — ввод без отображения (чтобы не закрылась программа), 13,10 — возврат каретки, перевод строки, сх — счетчик повторений. Loop — метка, которая «выбрасывает» цикл к началу.Собственно, далеко не каждый, даже опытный программист, сможет работать с ассемблерами. Для этого нужно очень хорошо знать архитектуру процессора. Для промышленного программирования гораздо удобнее пользоваться языками высокого уровня, о которых мы говорили в прошлый раз.

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

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

Источник

Машинный код для чайников

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

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 из шестнадцатеричной в десятичную систему счисления:

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

Правда, этим немножко затрудняется понимание происходящего? А ведь тоже десятичная система! И рисунок цифр как бы знакомый машинный код для чайников. smile3. машинный код для чайников фото. машинный код для чайников-smile3. картинка машинный код для чайников. картинка smile3. Многие любители не испытывают серьезных трудностей в овладении БЕЙСИКом. Для этого достаточно немного практики. Но рано или поздно они приходят к барьеру «машинного кода». Как это ни печально, но некоторые так перед ним и останавливаются. Это ни в коей мере не связано с отсутствием желания или способностей, просто многие не знают, с чего начать. Если в БЕЙСИКе можно начинать с чего угодно (при ошибке компьютер сам Вас поправит), то здесь Вы оказываетесь с процессором один на один, и такой метод проб и ошибок не срабатывает.))
Или вообще считать в 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’овская программулька).

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

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

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

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

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

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

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

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

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

А вот дальше начинается самое интересное машинный код для чайников. smile3. машинный код для чайников фото. машинный код для чайников-smile3. картинка машинный код для чайников. картинка smile3. Многие любители не испытывают серьезных трудностей в овладении БЕЙСИКом. Для этого достаточно немного практики. Но рано или поздно они приходят к барьеру «машинного кода». Как это ни печально, но некоторые так перед ним и останавливаются. Это ни в коей мере не связано с отсутствием желания или способностей, просто многие не знают, с чего начать. Если в БЕЙСИКе можно начинать с чего угодно (при ошибке компьютер сам Вас поправит), то здесь Вы оказываетесь с процессором один на один, и такой метод проб и ошибок не срабатывает.))

#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 часа ;).

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

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

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

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

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

Источник

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

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