Воробьёвы

(-:

К 27 ПОДПИСЧИКАМ

К сожалению, нашу рассылку еще не перевели в "серебрянную" категорию :( Однако заявка отправлена и мы с нетерпением ждем, когда администрация Subscribe.Ru соизволит ее рассмотреть...

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

Сегодня же начинается самое интересное - мы начинаем программировать!

2.3. ПРОГРАММА В ПАМЯТИ... РАБОТАЕТ??

[1] Любая программа выполняется построчно. То есть пока не выполнилась текущая "строка" программы, следующая не выполнится. Совсем другой вопрос, какая "строка" будет выполнена после "текущей" (здесь мы имеем дело со всевозможными логическими "ветвлениями", "циклами" и т. д.) или же строчку из КАКОЙ программы процессор выполнит следующей, а какая - будет ждать своей очереди (так называемая "многозадачность").

Многозадачность оставим пока что в покое. А вот с однозадачностью попробуем разобраться.

Итак, у нас есть ОПЕРАТИВНАЯ ПАМЯТЬ, в которую загружается программа перед ее выполнением (сразу же по нажатию на Enter из Norton Commander'а). Процессор и иже с ними смекают, что надо начать обрабатывать команды, которые в памяти начинаются с такого-то адреса. И здесь первый подводный камень, вернее - скала, которую сложно не заметить:

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

Каким образом? А очень легко!! Компьютер "разспознает" как выход из программы последовательность битов CD 20 (внимание: это работает только для "исполнимых файлов" тима COM).

Пробуем?? (А то!)

Вам понадобится какой-нибудь шестнадцатеричный редактор для этого: например, Hex Workshop 2.54 (есть на нашем сайте). Все очень просто: создайте новый файл, далее по меню: Edit > Insert > (HEX, любое число байт). А потом пишем "CD 20" и сохраняем как my_proga.com. Если вы позпботились о том, чтобы после CD 20 небыло никаких прочих шестнадцатеричных циферок, то исполнимая программа будет весить только 2 байта.

Запускать это ваше первое творение лучше из Norton или Volcov Commander'a (все же это досовская пока что программулька). Причем из этих двух оболочек мы настоятельно рекомендуем использовать именно Volcov Commander - в отличие от Norton'а он могет "на лету" выгружать резиденты из памяти, что нам вскоре очень понадобится...

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

  1. Это - программа.
  2. Эта программа - с корректным выходом.

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

Еще к вопросу о выгружаемости: если вы после CD 20 напишите всякой лабуды, она все равно будет проигнорирована. Дело до их выполнения просто-напросто не дойдет. Другое дело - если вы напишите лабуду ДО...

[2] Честно говоря, опасно при низкоуровневом программировании лабуду писать. Можно невзначай и винт отформатировать :))). Поэтому лабуду писать не будем, вернее - будем, но не лабуду...

Итак, продолжим наше извращение. Познакомимся еще с некоторыми "машинными командами" (в нашем случае - последовательностями шестнадцатеричных циферек).

        B82301 - Внести значение 0123h в AX;
        052500 - Прибавить значение 0025h к AX;
        8BD8    - Переслать содержимое AX в BX;
        03D8    - Прибавить содержимое AX к BX;
        8BCB    - Переслать содержимое BX в CX;
        31C0    - Очистка AX;
        CD20    - Конец программы. Передача управления операционной системе.

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

Если вы все ввели правильно, то прога у вас без проблем запустится, а операционная система не заругается... Правда, визуально (в смысле на мониторе) вы ее работу так и не заметите, но поверьте на слово, она работает!! В этом вы еще убедитесь, когда посмотрите на ее работу ИЗНУТРИ - не различающими цветов глазами копмьютера... Только сначала исчо немного теории...

[3] В Hex Workshop'е, в том окошке, где вы вводите машинные команды, одна вещь должна быть вам до боли знакома. Это колонка символов справа от вводимых шестнадцатеричных кодов. Здесь "непечатные" символы заменены на точки, а вот если вы просмотрите их по кнопке F3 (а затем F4 для переключения в HEX) в NC, то там не заменены...

Наиболее пытливые умы могут сравнить, как выглядет в таком режиме текстовый "досовский" файл и наш исполнимый COM-овский...

Теперь поговорим о втором подводном камне :). Один из принципов фон Неймана гласит приблизительно так: машине пофиг целевое назначение данных... Одна и та же цепочка битов может быть и МАШИННЫМИ КОМАНДАМИ и СИМВОЛАМИ, выраженными в виде кодов (есть такая "таблица символов ASCII", наверняка вы ее знаете).

Что из этого следует? А то, что компьютеру нужно УКАЗЫВАТЬ, что подразумевается под той или иной "простыней" из битов: ДАННЫЕ или КОД (или ЕЩЕ ЧТО-НИБУДЬ)...

На ВЫСОКОМ уровне это делает операционная система. Например, она не пытается загрузить в память ДЛЯ ВЫПОЛНЕНИЯ файлы с расширениями, отличными от COM, EXE и BAT (последний вообще не из это оперы)...

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

1. Он пытается интерпретировать ДАННЫЕ как КОД. Соответственно, в процессор "попадает" всякая ерунда.

2. Врядли он натолкнется на последовательность CD 20 вашем тексте :). Даже в том случае, если этот ТИПА КОД выполнется "успешно" - ваша ТИПА ПРОГРАММА не возвратит управление операционной системе, а пойдет выполняться хлам, содержащийся в оперативной памяти. Как-то: остатки ранее выполненных программ, куски чьих-то данных, интерпретированные как код... и прочая многочисленная ерунда...

Как вам тяжело въехать в смысл повествования, состоящего из кусков различных книг, так и компьютеру тяжело понять подобную "мешанину". С той лишь разницей, что любую "неинтересную книгу" вы можете использовать в качестве туалетной бумаги, а вот "компутер" подобного права выбора лишен... он ДОЛЖЕН в это "въезжать"... его процессор начинает перегреваться, а мозги кипят и вытекают через низкоуровневые порты ввода-вывода (командами in и out соответственно... ).

[4] Еще немного идеологии. Про ПРОГРАММУ, которая выполняется в ПАМЯТИ...

Сколько бы не было "мозгов" в вашей навороченной тачке, ЛЮБАЯ программа выполняется в 640 килобайтах "нижней" памяти. Если отнять от этой цифры "резидентную часть" операционной системы, многочисленные драйвера и т. д., то оставшееся и есть ОБЪЕМ ПАМЯТИ, в котором выполняется ваша программа. А остальные мегабайты - это место для хранения промежуточных данных, переменных среды и т. д., т. е. место для хранения ПРЕДЫДУЩЕГО (перед тем, как вы запустили программу) СОСТОЯНИЯ вашего компьютера.

Не верите? И правильно делаете!! Не верьте, пока не проверите сами...

Существуют различные "структуры" файлов, пригодных для интерпретации их содержимого как КОДА. Это исполнимые файлы типа COM, EXE, оверлеи, библиотеки подкачки и многочисленные прочие... Естественно, они не вмещаются в эти жалкие 640. И уж тем более - в 64 килобайта одного сегмента.

Все "подводные камни" низкоуровневого программирования IBM-совместимых компьютеров (у MAC-ов легче - там "единое адресное пространство") связаны прежде всего с "сегментной" адресацией памяти. Компьютер должен помнить, КАКАЯ часть программы ГДЕ в оперативной памяти находится. Не правда ли, ужасно?

[5] Есть такая замечательная игра: "ЧТО, ГДЕ, КОГДА". Так вот, в модификацию этой игры под названием "ЧТО, ГДЕ, С КЕМ" компьютер играет постоянно: как с сам с собой, так и с бедным программером.

"Общение" между "компьютерными" устройствами происходит на трех логических уровнях:

КОД. То есть ЧТО компьютеру нужно делать.

ДАННЫЕ. Все, зачем нужен код - это ОБРАБОТАТЬ те или иные ДАННЫЕ.

СТЕК. Где расположены данные? Где расположен код? В какое место оперативной памяти помещать промежуточный результат? И так далее...

Соответственно, и программа состоит из трех частей (сегментов): сегмента ДАННЫХ (DATA), сегмента КОДА (CODE) и сегмента СТЕКА (STACK)...

И все эти СОСТОЯНИЯ и ИЗМЕНЕНИЯ (читай СЕГМЕНТЫ) компьютер должен отслеживать! Ужас!!

А работа со стеком вообще дурдомом кажется, не так ли?

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

Первые программы, которые мы написали, имеют тип COM. Именно этот тип (а не EXE) был выбран по нескольким причинам:

1. COM-программа "сует в память" свою абсолютно идентичную копию. Никаких подгрузок и подкачек.

2. Она размещается в пределах одного сегмента (соответственно и ограничение в размере - 64 k).

3. Начало кода автоматически инициализируется по смещению 100h (т. е. его не надо УКАЗЫВАТЬ).

А еще мы пока что постараемся не использовать никаких ДАННЫХ. Будем просто работать с КОДОМ до полного прояснения ситуации...

ВЫ ГОТОВЫ ПОИМЕТЬ МАТРИЦУ?? Если да - тогда поехали!!

[6] Помните, как в конце фильма Matrix Нео в конце концов увидел ее - черно-зеленую "матрицу"?? Сейчас с вами произойдет нечто подобное!!

Посмотрите на машинные коды и "что они делают" в п.2. Немножко дополним эту простыню:

Например, командой "внести значение" 1234 последовательно во каждый из "регистров общего пользования":

       B83412 - AX=1234
       BB3412 - BX=1234
       B93412 - CX=1234
       BA3412 - DX=1234

Наиболее наблюдательные должны для себя отметить: первый байт - это команда "переместить в регистр", а второй и третий - само число, только байты как-бы наоборот (об этом мы говорили в выпуске "про регистры").

Однако никто не пишет программы в шестнадцатеричных редакторах! НИКТО! Это большая глупость! Единственное, зачем мы Вам про это рассказываем - это чтобы вы поняли, что могут означать загадочные пары шестнадцатеричных циферек в ДАМПЕ (дамп памяти мы в прошлых выпусках рассколупывали)...

Нет необходимости заучивать, что B8 - это "переместить в регистр AX", BB - "переместить в регистр BX" и так далее...

Все намного проще!

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

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

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

      11B7:0100 B82301     MOV AX,0123   ; Внести значение 0123h в AX
      11B7:0103 052500     ADD  AX,0025   ; Прибавить значение 0025h к AX
      11B7:0106 8BD8        MOV  BX,AX     ; Переслать содержимое AX в BX
      11B7:0108 03D8        ADD  BX,AX     ;  Прибавить содержимое AX к BX
      11B7:010A 8BCB        MOV CX,BX      ; Переслать содержимое BX в CX
      11B7:010C 31C0        XOR  AX,AX     ; Очистка AX
      11B7:010E CD20        INT   20          ; Конец программы

и т. д. ... ^^^ этих комментариев не видно...

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

Эти mov, add, xor, int - так называемые "мнемонические команды" (более-менее понятные человеку), на основе которых формируется (это debug делает) "машинный код". Не правда ли, так намного легче?

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

[8] А теперь мы выполним нашу программу ПОШАГОВО - произведем так называемую "трассировку" при помощи команды "t".

Итак, вводим "t" и жмем на Enter!

Вот что мы видим:

      AX=0123 BX=0000 CX=0010 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
      DS=11B7 ES=11B7 SS=11B7 CS=11B7 IP=0103 NV UP EI PL NZ NA PO NC 
      11B7:0103 052500 ADD AX,0025 

Смотрим на значение AX вспоминаем "описание": "внести значение 0123h в AX". Внесли? И правда! А в самом низу - код и мнемоника команды, которая будет выполняться следующей...

Вводим команду "t" снова...

 
      AX=0148 BX=0000 CX=0010 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
      DS=11B7 ES=11B7 SS=11B7 CS=11B7 IP=0106 NV UP EI PL NZ NA PE NC 
      11B7:0106 8BD8 MOV BX,AX 

AX=0148 "Прибавить значение 0025h к AX" сделали? Сделали!!

Вводим команду "t" снова...

      AX=0148 BX=0148 CX=0010 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
      DS=11B7 ES=11B7 SS=11B7 CS=11B7 IP=0108 NV UP EI PL NZ NA PE NC 
      11B7:0108 03D8 ADD BX,AX 
 

AX=0148=BX "Переслать содержимое AX в BX" сделали? Сделали!!С

Вводим команду "t" снова...

      AX=0148 BX=0290 CX=0010 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
      DS=11B7 ES=11B7 SS=11B7 CS=11B7 IP=010A NV UP EI PL NZ AC PE NC 
      11B7:010A 8BCB MOV CX,BX 

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

Вводим команду "t" снова...

   
      AX=0148 BX=0290 CX=0290 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
      DS=11B7 ES=11B7    SS=11B7 CS=11B7 IP=010C NV UP EI PL NZ AC PE NC 
      11B7:010C 31C0 XOR AX,AX 

"Переслать содержимое BX в CX" сделано!

Вводим команду "t" снова...

      AX=0000 BX=0290 CX=0290 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
      DS=11B7 ES=11B7    SS=11B7 CS=11B7 IP=010E NV UP EI PL ZR NA PE NC 
      11B7:010E CD20 INT 20 

"Очистка AX"? И точно AX=0000!

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

Потому что, по идее, сейчас наша программа должна была завершиться - у нас же там код выхода прописан, а она куда лезет? NOP-ы какие-то (если продолжать команду "t" вводить) CALL 1085 (да вы продолжайте "трассировку", продолжайте!)

Для тех, кому лень продолжать жать на букву "t", введите для разнообразия команду "g" от агцицкого GO. На монитор должна вывалиться надпись "Нормальное завершение работы программы".

"Уф, - должны сказать вы - Работает!"

А то!

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

А потому, дорогие наши, что вы имели несчастье нарваться на ПРЕРЫВАНИЕ (INTERRUPT)!

Понимете ли, завершить программу - дело непростое :). Нужно восстановить первоначальное значение регистров, восстановить переменные среды и кучу всего! Знаете, как это сложно?

Однако эта процедура насколько сложная, настолько и ТИПИЧНАЯ для исполнимых программ. А по сему разработчики операционной системы решили избавить программеров от необходимости делать это ручками и включили эту стандартную процедуру в ядро операционной системы. И сказали: да будешь ты (процедура обработки прерывания) вызываться как int 20 и будешь ты обеспечивать корректную передачу управления из выполняемой программы - назад в ядро. И стало так...

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

АНОНС СЛЕДУЮЩЕГО ВЫПУСКА

Ну что, загрузили мы вас сегодня??

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

Так что в следующий раз - про прерывания!!

PS: Yoshi, ведь ты ждал именно этого, да??

ОБРАЩЕНИЕ К ПОДПИСЧИКАМ

Господа 27! Особо настаивать не буду... но в ваших же интересах (а может быть и не в ваших интересах) кликнуть сюда, и прислать нам мыл (пустой или же с какими-нить замечаниями/предложениями). Все, кто подписался на рассылку, пока она была в категории "для каждого", считаются... считаются... короче - нам нужно ваше мыло, а там как хотите...

Сия "просьба" действительна вплоть до следующего выпуска рассылки...