Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Низкоуровневое программирование для дZeнствующих # 8


Служба Рассылок Subscribe.Ru проекта Citycat.Ru
<< ЗАБУДЬ НАДЕЖДУ, ВСЯК СЮДА ВХОДЯЩИЙ
РАССЫЛКА ДЛЯ ИЗОБРЕТАТЕЛЕЙ ФОНАРИКОВ НА СОЛНЕЧНЫХ БАТАРЕЙКАХ

НИЗКОУРОВНЕВОЕ ПРОГРАММИРОВАНИЕ
ДЛЯ ДZЕНТСВУЮЩИХ #8

ПРЕДУПРЕЖДЕНИЯ:
ПРИГЛАШАЮТСЯ:
САЙТ HI-TECH:
> В связи с присутствием в рассылке графики настоятельно рекомендуем подписываться на HTML-версию.
> В связи с затянувшимся "расширением канала" у нашего провайдера возможны задержки с выпуском рассылки.
> авторы.
> корректоры.
> художники/дизайнеры.
> владельцы сайтов, авторы рассылок (для взаимной рекламы)
> (хм) спонсоры и рекламодатели.

> архив рассылки
> коллекция инструментов
> исходные тексты
> документация
> учебники, статьи
> форум и хм... гостевая книга

 ПРОДОЛЖАЕМ ПРИНИМАТЬ ПОЗДРАВЛЕНИЯ! (УВЫ)

   10, 20, 30... после третьего десятка - и это с утра-то! - мы поняли, что наш проект перешел на качественно новый этап своего существования. На наш сайт повалил народ! Впервые за 4 месяца одиночества мы поняли, что у нас есть друзья и единомышленники...
   Сорри, но более высокопарной речи я придумать не смог. Как-то тошно было. Наверное, передZенствовал вчера. День все же - знаменательный был. Во всяком случае для нашего "авторского коллектива"... 
   150 уникальных посетителей и 1800 новых подписчиков - это круто!
   Может пообещать вам, "дорогие друзья", что мы постараемся оправдать оказанное нам доверие? А еще сказать, что вы никогда не пожалеете о том, что стали "нашими читателями"?
   Ну дык совру ж ведь! Еще как пожалеете! Горько пожалеете!! Прямо начиная с этого выпуска - пожалеете!!


 DUKE NUKEM ДОЛЖЕН УМЕРЕТЬ

   Если многочисленные компьютерные игры являются для вас одной из немногих радостей жизни - ни в коем случае не читайте этот сатанинский текст. Потому что после его прочтения вы навсегда лишитесь удовольствия убивать ваших кровожадных противников честным и благородным способом...
   Вчера, изрядно насытив свои программерские организмы программерским напитком, мы решили поразвлечься и сели играть в Дюка по сетке.
   В большинстве сетевых игр, надо сказать, есть одна досадная недоработка - невозможно использовать "халявные" коды, которые дают, например, неограниченное количество патронов или вообще делают игрока бессмертным. В некоторых играх об использовании кода тут же узнают все твои противники и начинают ругаться нехорошими словами. В некоторых играх коды вообще не предусмотрены...
   Но нам, дZенствующим программерам, поднаторевшим в святой борьбе с Матрицей, это, естественно, было фиолетово...
   Как следствие - наша битва друг с другом началась весьма необычно. Мы ВСЕ ринулись убивать друга голыми руками :). И долго не могли убить. Да чего уж там - так и не смогли на протяжении часа. А потом... потом игра, в который каждый неубиваем и обладает неограниченным боекомплектом, потеряла всякий смыл...
   Мы не использовали коды. Мы исходили из гипотетического предположения, что многочисленные циферки "в Дюке" (как-то: здоровье, количество патронов) являются целочисленными переменными и находятся в памяти компьютера, где постоянно обновляются.
   Таким образом задача "стать бессмертным" сводилась к поиску МЕСТА, где эта переменная находится. А все что нужно сделать потом - это "заморозить" значение этой переменной.
   Все более чем просто... Тем более, что для этого и существуют утилиты с громким названием "взломщики игр". Вроде где-то ;) на нашем сайте несколько подобных утилит как раз и валяются... ;)
   Вы сами разберетесь, как ими пользоваться, потому что некоторые весьма и весьма навороченые... Я же вам просто расскажу как самой простенькой из них заморозить "здоровье" в Дюке...
   Называтся эта простенткая приблуда Cheat 'O Matic и версия у нее 0.99a.
   После того, как вы ее скачали, нужно сделать вот что (по пунктам):
   1. Запустите "игру-жертву" и начните играть. Постарайтесь, чтобы кровожадные монстры не убили вас в первую же секунду. Успейте нажать на паузу или выйдите в меню!!
   Успели? Это хорошо :). Игра приостановлена и можно расслабиться... Сколько там "здоровья" было? Все 100? Замечательно!
   2. Запускаем программу Cheat 'O Matic. В самом ее верху есть ниспадающий списочек программ, которые в данный момент запущены. Наш Дюк тоже там должен быть. Вот его и выбираем ;).
   Здоровья у нас сколько? 100? Вот и вводим это значение в маленькое беленькое окошечко (есть там такое). А затем жмем на кнопку "Search".
   Внизу есть индикатор, который показывает, сколько памяти она уже просканила на предмет наличия переменной со значением 100. Естественно, сканится только та память, в которой располагается игрушка...
   Сканить может долго. Слышите, как шебуршит ваш винт? Это из-за своп-файла, который тоже как бы опративная память... (за подробностями к Micro$oft'у обращайтесь).
   Поиск закончен. Программа выдает сообщение "Change the value in the program, then enter the new value... ". Это означает, что она нашла несколько переменных, содержащих значение 100. Например, патронов у вас тоже 100 - чем не переменная?
   Короче, программа создала себе список из переменных, содержащих значение 100. Нам нужно выбрать из них ту единственную, которая отвечает за "здоровье".
   3. Переключаемся в Дюка, "слезаем с паузы" и подставляем себя под удар, в трезвом уме и здравой памяти лишая себя нескольких единиц "здоровья".
   Ранили! Здоровья стало, например, 90. Пауза!! Новое значение, которое нужно внести - 90.
   4. Вводим и жмем на "поиск". Из списка, сформированного в прошлый раз (при поиске "100") программа ищет те переменные, значение которых изменилось из 100 на 90. Опять то же самое! Нашлись несколько переменных, которые изменились со 100 на 90... А нужна только одна!
   5. Переключаемся в Дюка. Даем себя ранить. Жмем на паузу. Запоминаем, сколько у нас стало "здоровья"... 78, например, стало...
   6. Вводим новое значение, жмем на "поиск". Черт подери, опять не нашла!! То есть нашла, но несколько, а не одну!!
   Сужаем круг поиска!! Повторяем пункт 5 инструкции.
   7. Здоровья стало совсем мало. У меня - 35 только. Последний шанс!
   Дрожащей рукой вводим 35 (или сколько там у вас). Поиск... УРА!!
   НАШЛОСЬ!! Программа сообщает: "Cha-ching! Got it. Enter the value you'd like to change it to, and hit 'Set' (Code: x83E9DE22:3E0 x83E09356:78B x83DDAD36:724) ".
   8. В то же самое поле, куда мы вносили значение переменной "здоровье", напишем то, что на английском языке нам и предлагается написать: ЗНАЧЕНИЕ, НА КОТОРОМ МЫ ХОТИМ ЗАМОРОЗИТЬ нашу переменную.
   Пишем: 100. Жмем: "Set". Отмечаем: флажок Lock.
   Готово...
   9. Идем и мочим мутантов голыми руками, некоторое врямя получая от этого весьма и весьма сомнительное удовольствие.
   Поздравляю :((. Вы стали АБСОЛЮТНЫМ ИГРОКОМ!!
   Прощай, здоровье...
   


 ПРЕДЫДУЩИЕ ВЫПУСКИ РАССЫЛКИ

   Ну не то, чтобы повторение - мать учения... просто не проштудировав материал прыдыдущих номеров, вам будет весьма затруднительно "въехать" в номер текущий...
     Выпуск 2 - про систему счисления.
   Выпуск 3 - про порядок загрузки компьютера.
   Выпуск 4 - про регистры.
   Выпуск 5 - про программу в памяти.
   Выпуск 6 - про прерывания.
   Выпуск 7 - программируем и отлаживаем.
   Кстати, если у кого проблемы с инетом - обращайтесь :). Нам не западло выслать предыдущие номера мылом...
   


 ИЗВИНЕНИЯ

   Собирались мы было программу из прошлого номера менее тупорылой сделать и даже текст соответствующий написали... Но прочитали его, посмотрели на листинг и подумали, что нихрена вы не поймете :(. Пришлось все переделывать, а по сему и небольшая задержка с выпуском...
   Для вас же, непонятливых, старались! А вы этого не цените... "Где восьмой выпуск?!" - орете...
   Да вот он, перед вами! Радуйтесь!! А девятый в воскресенье будет, если мне на голову не свалится кирпич и/или очередная разновидность простудифилиса...
   Но вернемся к нашим баранам. Прежде чем мы пойдем программировать дальше, вам жизненно необходимо разобраться с двумя вещами: "циклами" и "стеком". Иначе при прочтении материалов следующего номера ваша крыша имеет все шансы выполнить недопустимую операцию и "уехать"...
   Кстати, сегодня у нас в деревне пошел снег!!


 РАЗБОРКИ СО СТЕКОМ

    [1] В средней школе, где я когда-то учился, учителем физкультуры был настоящий зверь. Помимо того, что он заставлял нас бегать-прыгать-подтягиваться, у него еще любимое наказание было - проходило оно в так называемом "зале тяжелой атлетики".
   Что такое тяжелая атлетика, вы наверняка знаете. Видели по телевизору, когда выходит на помост этакий кабан и рвет собственное здоровье (с потенцией вместе), поднимая штангу...
   Штанга - это такая "палка", по бокам которой навешиваются так называемые "блины" - круглые-плоские с дыркой посередине диски невероятной тяжести.
   Хранятся же эти диски на штырях, которые представляют собой те же "палки", но вторкнутые вертикально в пол. На них и хранились диски - один на другой положенные...
   "Наказание" заключалось вот в чем: этот придурок заставлял нерадивого ученика комплектовать штангу!! Это нефиг делать, если диски просто валяются по залу... Но когда они аккуратно сложены на штырь - это намного сложнее :(.
   Садист!! Чтобы достать со штыря диск заданной тяжести (который по-приколу обычно находился внизу) необходимо было снять со штыря все "вышележащие" диски. Достать самый нижний, а остальные снова надеть на штырь. А потом - точно так же достать диск другой "тяжести" со второго штыря... А он как-бы случайно тоже в самом низу... И так далее - до полной победы идиотизма над здравым рассудком... Не правда ли, изощренная пытка?
   По окончании школы мы с этим отморозком разобрались :) Он осознал свою ошибку и больше так не издевается...
   К чему это лирическое бредисловие? А к тому, что стек - это тоже своего рода штырь с блинами. И уж поверьте, трахаться вы с ним будете намного чаще, чем мы делали это на уроках физкультуры... С той лишь несущественной разницей, что у нас на следующий день болела спина, а у вас на следующий день будут болеть мозги...
   М-да... на "кипение возмущенного разума" уходит чертовски много энергии...

   [2] Так вот, про стек: "штырь" для блинов находится в оперативной памяти (а где ж еще?). А роль блинов выполняют хорошо знакомые нам всем регистры, вернее - их "значения".
   Правила работы с ним те же: вы можете снять только верхний блин. Чтобы самый нижний блин - вам нужно прежде снять все те, которые НАД ним.
   Очевидно, что из десяти "блинов", которые вы на "штырь" надели, первым будет сниматься последний из надетых (верхний), а последним - первый, то есть самый нижний...
   Все очень просто: "первый пришел - последним уйдешь" и наоборот "пришел последним - уйдешь первым".
   Это вам не очередь времен социализма... Это очередь "загрузки-разгрузки" стека! Привыкайте...

   [3] Для работы со стеком вам пока что необходимо знать только две команды: push и pop. Так как в качестве "блинов" у нас регистры - то соответственно необходимо после этих команд указывать и "имена собственные" помещаемых в стек значений регистров...
   Соответственно:
   push AX - ПОМЕЩАЕТ В СТЕК значение регистра AX;
   pop AX - ИЗВЛЕКАЕТ ИЗ СТЕКА значение регистра AX.
   Ну а как делать то же самое с остальными регистрами вы, наверняка уже и сами догадались...
   Очень важно помнить каким нездоровым образом "в стеке" реализована ОЧЕРЕДЬ поместить/извлечь. Помните, мы вас предупреждали, что нам нельзя верить на слово? Не верьте! А по сему - обязательно убедитесь в истинности/неистинности нашего голословного утверждения:
   Это легко можно проверить следующей программулькой:

     :0100 MOV AX,0001        ;AX = 1
     :0103 PUSH AX              ;В стек записана 1-ца
     :0104 MOV AX,0002
     :0107 PUSH AX              ;В стек записана 2-ка
     :0108 MOV AX,0003
     :010B PUSH AX              ;В стек записана 3-ка
     :010C MOV AX,0004
     :010F PUSH AX              ;В стек записана 4-ка
     :0110 MOV AX,0005
     :0113 PUSH AX              ;В стек записана 5-ка

     :0114 POP AX                
     :0115 POP AX                
     :0116 POP AX
     :0117 POP AX
     :0118 POP AX
     :0119 INT 20

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

     -r
     AX=0000 BX=0000 CX=001B DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
     DS=14DC ES=14DC SS=14DC CS=14DC IP=0100 NV UP EI PL NZ NA PO NC
     14DC:0100 B80100           MOV AX,0001
     -

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

     -t
     AX=0001 BX=0000 CX=001B DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
     DS=14DC ES=14DC SS=14DC CS=14DC IP=0103 NV UP EI PL NZ NA PO NC
    14DC:0103 50                  PUSH AX
     -

   Анализируем: AX=0001 - значит команда выполнилась правильно :). Следующая команда, по идее, должна поместить 1 в стек...

     -t
     AX=0001 BX=0000 CX=001B DX=0000 SP=FFFC BP=0000 SI=0000 DI=0000
     DS=14DC ES=14DC SS=14DC CS=14DC IP=0104 NV UP EI PL NZ NA PO NC
     14DC:0104 B80200           MOV AX,0002
     -
   
    И что? Команда выполнилась... где мы можем увидеть, что в стек действительно "ушла" единица? Увы, но здесь это не отображается :). Проверим потом. Ведь логично, что если эти значения действительно сохранились в стеке, то мы их потом без проблем оттуда извлечем? Ну то есть если найдем "там" наши 1 2 3 4 5 - значит все ок...
    А по сему - дадим проге работать дальше до адреса 114 (не включительно), не вдаваясь в подробный анализ. Че тут анализировать? Если значение регистра AX последовательно меняется от 1 до 5 - значит команда mov работает. А стек (команда push) проверим потом, как и договорились...
   Проехали до адреса 114...

     - g 114
     AX=0005 BX=0000 CX=001B DX=0000 SP=FFF4 BP=0000 SI=0000 DI=0000
     DS=14DC ES=14DC SS=14DC CS=14DC IP=0114 NV UP EI PL NZ NA PO NC
     14DC:0114 58                  POP AX
     -

   А вот теперь снова анализируем :). При следующем шаге выполнится команда, извлекающая некогда "запомненное" значение AX из стека...
   Обратите нимание, регистр IP указывает на адрес (114) выполняемой команды. Мы с вами это уже проходили, не так ли?
   Поехали дальше!!

     -t
     AX=0005 BX=0000 CX=001B DX=0000 SP=FFF6 BP=0000 SI=0000 DI=0000
     DS=14DC ES=14DC SS=14DC CS=14DC IP=0115 NV UP EI PL NZ NA PO NC
     14DC:0115 58                  POP AX
     -

   Выполнился первый POP. Готовиться выполниться второй. AX=5. То есть по сравнению с предыдущим шагом вроде ничего не изменилось... Но на самом деле это не так. AX=5 - эта пятерка "загрузилась" из стека :)). В этом вы легко убедитесь, сделав следующий шаг трассировки...

     -t
    
AX=0004 BX=0000 CX=001B DX=0000 SP=FFF8 BP=0000 SI=0000 DI=0000
     DS=14DC ES=14DC SS=14DC CS=14DC IP=0116 NV UP EI PL NZ NA PO NC
     14DC:0116 58                  POP AX
     -

   Ууупс... AX=4 :). А команда вроде та же :) - POP AX :)

     -t
     AX=0003 BX=0000 CX=001B DX=0000 SP=FFFA BP=0000 SI=0000 DI=0000
     DS=14DC ES=14DC SS=14DC CS=14DC IP=0117 NV UP EI PL NZ NA PO NC
     14DC:0117 58                  POP AX
     -

   AX=3 :)

     -t 
     AX=0002 BX=0000 CX=001B DX=0000 SP=FFFC BP=0000 SI=0000 DI=0000
     DS=14DC ES=14DC SS=14DC CS=14DC IP=0118 NV UP EI PL NZ NA PO NC
     14DC:0118 58                  POP AX
     -
   AX=2 :)

     -t    
     AX=0001 BX=0000 CX=001B DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000
     DS=14DC ES=14DC SS=14DC CS=14DC IP=0119 NV UP EI PL NZ NA PO NC
     14DC:0119 CD20              INT 20
     -

   AX=1 :) То есть нашлись-таки наши 1 2 3 4 5 :). Восстановились из стека...
   Теперь поверили?? А то!!
   
   Еще раз обращаю ваше внимание на то, что: последовательность записи (четыре PUSH'а) была 1 2 3 4 5, а вот последовательность извлечения (четыре POP'а) 5 4 3 2 1, то есть "последний пришел - первый ушел". Зарубите это себе на носу! (Как сделал это на своем перебитом носе наш школьный учитель физкультуры...).

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


 РАЗБОРКИ С ЦИКЛАМИ

   [1] Наша программа для работы со стеком линейна. А линейное программирование - это плохо.
   Но не всегда :)
   Итак давайте еще раз посмотрим на нашу программу для работы со стеком. С 100-го до 113-го адреса - у нас имеется пять почти идентичных блоков. Изменяется только значение AX, но на одно и то же число - на единицу, в большую сторону. То есть AX = предыдущее значение + 1. Это очевидно.
   Еще более очевидно, что простая команда POP AX (с 114 по 119) повторяется у нас тоже 5 раз...
   Мне почему-то сразу вспомнился анекдот о том, как два мента едут в машине и один спрашивает у другого: "Глянь, работает ли у нас мигалка на крыше". Тот высунул в голову в форточку и говорит: "Работает-неработает-работает-неработает-работает-неработает..."
   Так вот, не будем уподобляться этим нехорошим людям и сделаем нашу прогу более нормальной...
   Добьемся мы этого с помощью так называемого "цикла"...

   [2] Цикл - это... хм... не буду давать общепринятые определения... кто хочет - поищите в книжках, благо их навалом...
   Скажу только: "сесть-встать, сесть-встать, сесть-встать" - это не цикл, а вот "сесть-встать и так три раза" - уже можно считать циклом.
   Реализуется же он (цикл) при помощи регистра CX и команды LOOP следующим образом:
   Число циклов заносится в регистр CX. После этого следует простыня из команд, которые вы хотите "зациклить", т. е. выполнить энное количество раз. Заканчиваться все это должно LOOP'ом с указанием адреса "строки", с которой необходимо начать цикл (обычно это "строка", следующая сразу же после mov СХ...
   Давайте мы сначала "набьем" нелинейный вариант нашей проги, а потом разберемся, что там к чему...
   Набиваем:

      :0100 ХOR AX,AX           ; AX=0
      :0102 MOV CX,0005        ; нижеследующий до команды LOOP кусок повторить CX раз
      :0105 ADD AX,0001        ; AX=AX+1 (у нас же значение AX на 1 увеличивается...)
      :0108 PUSH AX              ; помещаем в стек
      :0109 LOOP 0105           ; конец цикла; инициируем повторение; CX = CX-1
      :010B MOV CX,0005        ; второй цикл повторить тоже 5 раз
      :010E POP AX                ; достаем из стека
      :010F LOOP 010E           ; конец цикла; повторить! ; CX=CX-1
      :0111 INT 20                 ; выход из нашей "правильной" проги...

   Наверное, вы уже поняли, что цикл повторяется до тех пор, пока CX не станет равен 0. Несмотря на то, что CX - он как бы регистр общего назначения, для "зацикливания" используется именно он :). С остальными такой фокус не проходит. Хотя, зачем это я говорю? "Специализацию" регистров мы уже затрагивали при первом знакомстве с ними...
   Протрассируйте эту программу! Искренне надеюсь, что вы поняли, о чем это я вас тут загружал...


 РАЗБОРКИ С ОПТИМИЗАЦИЕЙ

   Как я уже говорил, линейное программирование - это плохо, но не всегда...
   Сравните размеры ваших линейной и нелинейной программ. Не знаю, как у вас, но у меня линейная "весит" 27, а нелинейная - 19 байт :). Как по вашему, какая быстрее работать будет?
   "Ну естественно нелинейная, потому что она меньше!" - скажете вы и будете неправы...
   Попытайтесь оттрассировать "зацикленную". Не правда ли, она трассируется намного дольше своего линейного аналога?
   Угу... все поняли? Сам знаю, что не все :(
   Объясняю: в "зацикленной" программе "компутеру" приходится выпонять БОЛЬШЕ команд, нежели в "незацикленной"...
   Аргументирую это голословное утверждение следующей таблицей (построенной на основе трассировки):

Что делает линейная
Что делает нелинейная
  AX=1   AX=0
  помещаем в стек 1   CX=5
  AX=2   AX=AX+1=1
  помещаем в стек 2   помещаем в стек 1
  AX=3   конец цикла - переход
  помещаем в стек 3   AX=AX+1=2
  AX=4   помещаем в стек 2
  помещаем в стек 4   конец цикла - переход
  AX=5   AX=AX+1=3
  помещаем в стек 5   помещаем в стек 3
  достаем из стека 5   конец цикла - переход
  достаем из стека 4   AX=AX+1=4
  достаем из стека 3   помещаем в стек 4
  достаем из стека 2   AX=AX+1=5
  достаем из стека 1   помещаем в стек 5
  выход   CX=5
    достаем из стека 5
    конец цикла - переход
    достаем из стека 4
    конец цикла - переход
    достаем из стека 3
    конец цикла - переход
    конец цикла - переход
    достаем из стека 1
    выход

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

   Гы... как щас пор помню... был в моем турбо-си в преференсах к компилятору такой радиобуттон: оптимайзить по размеру или по скорости выполнения... Угадайте, на чем основан принцип этой оптимизации? Хм...
   Только не вздумайте писать линейные проги! Пишите "нелинейные"! Нелинейную в линейную "переоптимайзить" - как два пальца намочить! А вот наоборот - :((
   


 АНОНС!

   Что будет в следующий раз - не знаю!! Скорее всего - мне на голову упадет-таки кирпич!
   Не надо меня дергать по пустякам, а то вот сами видите, каким нервным из-за этого выпуск получился...
   Белым же по черному сказано:
"В связи с затянувшимся "расширением канала" у нашего провайдера возможны задержки с выпуском рассылки"!
   Для чего я это писал?? Думаете, чтобы место пустое забить??
   Так вот, если да, то нет!!



serrgio@gorki.unibel.by / ICQ: 63813760 / Приятного вам дZенствования!!


http://subscribe.ru/
E-mail: ask@subscribe.ru
Поиск

В избранное