Дело было так: посмотрел сисадмин на траффик, "генерируемый" нашим "почтовым роботом", и сказал: "Я убью тебя, Serrgio"!
Само собой, ДZенский напиток в очередной раз спас мою драгоценную жизнь, но спасти жизнь почтового робота-убийцы оказалось не под силу даже ему. А на живую огненную воду денег на тот момент не было...
Так вот: самая большая беда заключается в том, что пока я бегал в поисках "капусты", на сисадмина наехал шеф, вследствие чего наш сисадмин впал в неблагоприятное для решения подобных вопросов состояние духа... И сколько мы с ним не дZенствовали, мне так и не удалось убедить его не обижать наших блаженных Инетом подписчиков :((.
И знаете, что он сделал?? Убить - не убил, но придушил, надо сказать, основательно...
Итог этих всех неприятных событий таков: "робот" теперь будет рассылать не более 50 "ответных писем" в сутки. Те, у кого действительно нет Интернета - они-то рано или поздно дождутся предыдущих номеров. А вот те, у кого Интернет есть, но они все равно захотели "нахаляву" - таким ПОЗОР и мое личное дZенское презрение...
Ну не то, чтобы повторение - мать учения... просто не проштудировав материал прыдыдущих номеров, вам будет весьма затруднительно "въехать" в номер текущий...
Выпуск 2 - про систему счисления.
Выпуск 3 - про порядок загрузки компьютера.
Выпуск 4 - про регистры.
Выпуск 5 - про программу в памяти.
Выпуск 6 - про прерывания.
Выпуск 7 - программируем и отлаживаем.
Выпуск 8 - "ломаем" игры, разборки со стеком, циклами, оптимизацией...
Кстати, у кого проблемы с интернетом... хм... это только ваши проблемы и к нам по этому поводу обращаться не стоит...
После 8-го выпуска рассылки в адрес "редакции" пришел ряд гневных писем. Типа мы тут народ взлому программного обеспечения учим. Это надо же!! Игры мы тут, понимаете ли, "кракаем" :((. Противозаконно это, видите-ли :((.
Чушь!! Притом полная!! Противозаконным является всяческое копание в бинарнике с целью, например, обхода той или иной защиты от копирования. А то, что мы "делаем с играми" - с точки зрения текущего законодательства всех развитых, неразвитых и недоразвитых стран вполне безобидно. Так что даже и не делайте попытку на нас из-за этого наезжать...
Тут некоторым начинающим программерам CD 18 по неопытности понравилось :). Так вот, дрожащими руками ловите еще одну симпатичную миниатюрку (сразу предупреждаю, что программулька очень нехорошая):
:0100 CLI :0101 JMP 0100
А почему ей INT 20h не нужен - вы и сами догадаетесь, когда запустить попробуете...
На WINDOWS-2000, кстати, она работает не совсем корректно ;)
Некто Алексей в гостевой книге рассказал, как на вшивость своих коллег (мега-пупер-программеров) проверял. Вопрос простой был: перевести число из HEX'а в BIN. И что же? Имея высшее-компьютерное образование, они не смогли это сделать. Не то что в уме - даже "на листике" это у них заняло весьма длительное время. Да и ответ в конце-концов был неправильный...
Все почему-то переводили сначала их HEXа в DEC, а потом из DECа в BIN :(. А над вопросом, зачем вообще этот HEX нужен, никто и никогда, наверное, не задумывался...
А стоило бы!!
Всех, у кого подобные проблемы, отсылаю к выпуску #2 , особенно к п. 12.
И не стоит забывать прописную истину, которой нас учили еще в средней школе - ПОВТОРЕНИЕ - МАТЬ УЧЕНИЯ! (Верите?)
А еще мега-пупер-ассемблерщики наезжали. Типа лапшу мы "читателям" вешаем :). Прежде чем объяснять "политику партии" (все ж обидно, когда коллеги критикуют), мы им один простой вопрос "на крутость" задавали:
Сколько раз выполнится следующий цикл:
:0102 MOV CX,0000 :0105 ADD AX,0001 :0108 LOOP 0105
Очевидный ответ - 0 раз. В CX же 0 у нас занесен. Так вот: ответ неправильный.
Менее очевидный ответ - 1 раз! Ведь перед LOOP'ом сложение один раз выполнится-таки... Так вот: этот ответ тоже неправильный.
Самые подозрительные могут сразу же посмотреть на этот цикл под отладчиком и с удивлением обнаружат, что LOOP сначала уменьшает значение CX (0-1=FFFF), а потом уже проверяет, не равен ли он нулю... И с гордостью за задний ум своей головы воскликнут: FFFFh раз!!
Так вот: этот ответ близок к истине, но тоже неправильный ;)
Правильный ответ - цикл выполнится 10000h (65536d) раз.
Это тем более прикольно, что в некоторых умных книжках черным по белому сказано, что, "увы, максимум мы можем выполнить цикл FFFFh раз"...
Но только вы и мне не верьте! Истинно только то утверждение, которое вы сами проверили на практике...
О чем это я?? Ах да!! О "критиках"... да, в общем-то, черт с ними, с этими "критиками"... Пускай ругают, сколько хотят! У них свой путь, а у нас - свой... И если им с нами не по пути - то это только их проблемы.
Наша же дорога трудна. В ней нет "порталов", которые помогают пересекать огромные расстояния "нахаляву", нет хорошей заасфальтрованной дороги... Есть только многочисленные указатели, один из которых посылает налево, другой направо, а третий вообще на три веселых буквы...
А дорога такая: 86, 286, 386, 486, p, p2. С "привалами" на MMX, 3DFX и прочей "дури". Соответственно и "платформы" - сначала DOS и только потом WIN и WIN32. Оговорюсь сразу: к "звездам" - исключительно через "тернии". Правда, с многочисленными "тоже звездами" - но это от систематических ударов по голове...
Сказано же черным по белому на сайте "старшего брата": "Рассылка "Низкоуровневое программирование для дZeнствующих" целиком посвящена программированию на ассемблере, начиная с самых фундаментальных его основ"!
Вот мы вам и пытаемся дать ЭТО - фундаментальные основы. Ну а зачем они нужны, вы и сами знаете - чтоб крыша не поехала, чуть ветер дунет...
А ветер - скоро дунет!! Спросите у линуксоидов...
СЛЕДУЙ ЗА БЕЛЫМ КРОЛИКОМ!
[1] В #7 (п. 4.) мы сделали глупую линейную программульку, выводящую окошки. Обещал я было, что в следущем выпуске мы ее сделаем менее "тупой", да отвлекся на циклы и стек почему-то... То есть я-то знаю, ПОЧЕМУ, но вот вам об этом - не скажу! Догадайтесь сами...
Итак, поехали...
Шаг первый. Внимательно посмотрев на "линейную" прогу из седьмого номера и прочитав "условие задачи" из п.1 вы обязаны возмутиться: зачем мы использовали команду MOV, если и ежу понятно, что отличия последующего окошка от предыдущего можно выразить более лаконично: BH=BH+10, CH=CH+1, CL=CL+1, DH=DH-1, DL=DL-1 ? И не нужно напрягать мозги, подсчитывая новое значение регистра вручную...
Если вы так подумали, то оказались совершенно правы!!
Программу из #7 запросто можно было представить в таком вот виде:
:0100 XOR AL,AL ;окошко первое :0102 MOV BH,10 :0104 MOV CH,05 :0106 MOV CL,10 :0108 MOV DH,10 :010A MOV DL,3E :010C MOV AH,06 :010E INT 10 :0110 ADD BH,10 ;окошко второе :0113 ADD CH,01 :0116 ADD CL,01 :0119 SUB DH,01 :011C SUB DL,01 :011F INT 10 :0121 ADD BH,10 ;окошко третее :0124 ADD CH,01 :0127 ADD CL,01 :012A SUB DH,01 :012D SUB DL,01 :0130 INT 10 :0132 ADD BH,10 ;окошко четвертое :0135 ADD CH,01 :0138 ADD CL,01 :013B SUB DH,01 :013E SUB DL,01 :0141 INT 10 :0143 ADD BH,10 ;окошко пятое :0146 ADD CH,01 :0149 ADD CL,01 :014C SUB DH,01 :014F SUB DL,01 :0152 INT 10 :0154 INT 20 ;конец программы
И несмотря на то, что размер ее оказался несколько большим, она тоже будет работать правильно :)
Но тут любой более-менее наблюдательный программер возмутится повторно: да что это за программа такая?? В ней целых 4 раза повторяется один и тот же кусок:
:0143 ADD BH,10 :0146 ADD CH,01 :0149 ADD CL,01 :014C SUB DH,01 :014F SUB DL,01 :0152 INT 10
И знаете что?? Этот наблюдательный программер будет прав! А если он еще и выразится матом по поводу такого "неправильного" стиля программирование - то он будет прав АБСОЛЮТНО!
Внимательно всмотритесь в полный текст программы и в этот выделенный кусок... И помедитируйте над ним до полного просветления текущей "обстановки"...
[2] Итак, у нас есть ПОВТОРЯЮЩАЯСЯ ЧАСТЬ программы. А еще у нас есть пальцы, которым, как правило, лень набивать длинные "простыни" программного кода. Это одна из многочисленных причин, по которым и придумали такого зверя, как ПРОЦЕДУРУ (она же - ПОДПРОГРАММА). Остальные причины, мы рассмотрим попозже, а вот на счет "лени" поговорим прямо сейчас:
Если мы возмем наш частоповторяющийся кусок программы и допишем в ее конец команду RET, то получится у нас именно ПРОЦЕДУРА - во всей своей красе...
:011E ADD BH,10 ;"точка входа"; она же - начало "тела". :0121 ADD CH,01 :0124 ADD CL,01 :0127 SUB DH,01 :012A SUB DL,01 :012D INT 10 ;конец "тела" :012F RET
Красота ее вот в чем заключается: процедуру можно "вызвать" командой CALL :)))
Все более чем просто :). Когда в программе встречается CALL с указанием АДРЕСА-НАЧАЛА-ПРОЦЕДУРЫ (в нашем случае это 011E), то "компьютер" "идет" по этому адресу и выполняет все команды, ресположенные между "точкой входа" (включительно) и командой RET, то есть так называемое "тело" процедуры.
RET - это тоже команда, но к "телу" (адреса 11E... 12D) она не относится. Она является "ОРГАНИЗАТОРОМ" этого "тела". Процессор, встретив команду RET, "перепрыгивает" на строчку ниже "вызвавшего" данную процедуру CALL'а...
Короче: CALL XXXX - означает "выполнить процедуру, начинающуюся по адресу XXXX". А RET - означет "конец процедуры" и, соответственно, переход на строчку ниже вызвавшего его CALL'а.
Не ругайтесь. Я знаю, что вы ничерта не поняли. А по сему набьем в debug'е эту прогу и посмотрим, что она делает...
[3] Кстати, вы уже поняли, почему я называю debug "до боли любимой программой"? Нет?? Неужели вы еще не полюбили это произведение программерского гения всеми фибрами своей души?
Еще нет??
М-да... я вас разочаровался... А по сему:
"НАБИВАЕМ!" - злобно кричу я вам, брызгая слюной на свой эргономичный коврик...
:0100 XOR AL,AL ;первое окошко рисуем, как и раньше... :0102 MOV BH,10 :0104 MOV CH,05 :0106 MOV CL,10 :0108 MOV DH,10 :010A MOV DL,3E :010C MOV AH,06 :010E INT 10 :0110 CALL 011E ;четыре раза вызываем подпрограмму, начинающуюся :0113 CALL 011E ;по адресу 011E :0116 CALL 011E :0119 CALL 011E :011C INT 20 ;выход из программы... :011E ADD BH,10 ;начало процедуры :0121 ADD CH,01 :0124 ADD CL,01 :0127 SUB DH,01 :012A SUB DL,01 :012D INT 10 :012F RET ;конец процедуры
Не правда ли, красиво получилось??
А то!
Первое, что вас может смутить, это то, что команда выхода (INT 20) расположена не там, где вы привыкли, то есть не в конце программы.
Ну что я вам могу на это ответить? Концы-то - они разные бывают! Последняя строчка в листинге вовсе не означает, что последней будет выполняться именно она. И это не должно вас смущать!! А если все-же смущает - смотрим, как работает эта прога из-под отладчика...
Итак, до адреса 0110 вам все должно быть понятно. Мы это рассматривали. Трассируем дальше...
Команда CALL 011E по адресу 0110 говорит поцессору: "дальше мы не пойдем, пока не выполним простыню, начинающуюся по адресу 011E"; и далее, естественно, следует переход на этот адрес.
Входим в тело процедуры, начиная с 011E и выполняем команды до 012D включительно...
А теперь внимательно смотрим, на какой адрес нас "перекинет" команда RET.
На 113-й? И это правильно!
По 113-му адресу у нас какая команда? Да вот опять CALL 011E!
Опять процедура с адреса 011E, опять RET[URN] на строку ниже, то есть на 116...
И так далее до того момента, пока следующей строчкой не окажется INT 20 - собственно, на этом и программе конец.
Ну оно и ежу понятно, что несмотря на то, что INT 20 - не в конце программы, последним выполнится именно он.
Короче, куда бы вас не посылали всяческие "столбы с указателями", конец вашего пути только один - ГРОБ. А плутать вокруг да около этого гроба вы можете сколько вам заблагорассудится...
Кстати, именно это и является одной из многочисленных тайн структурного программинга...
Кто после этого скажет, что программисты - не дZенствующие люди??
[4] Те, кто читал прошлый номер, они и на этом не остановятся!! Посмотрев на адреса 110...119, они вообще возьмут и возомнят себя воистину крутыми парнями!
Знаете, что они напишут??
А вот что (предвижу!):
:0100 XOR AL,AL :0102 MOV BH,10 :0104 MOV CH,05 :0106 MOV CL,10 :0108 MOV DH,10 :010A MOV DL,3E :010C MOV AH,06 :010E INT 10 :0110 MOV CX,0004 :0113 CALL 011A :0116 LOOP 0113 :0118 INT 20 :011A ADD BH,10 :011D ADD CH,01 :0120 ADD CL,01 :0123 SUB DH,01 :0126 SUB DL,01 :0129 INT 10 :012B RET
То бишь еще и CALL в цикл при помощи MOV CX,4 и LOOP'а "закрутят". И что? А попробуйте!
Что, не "пашет"? А что надо делать, если "не пашет, а должно бы"? Правильно! Смотреть из-под отладчика!
Смотрим? Если посмотрите, то сразу же и "загвоздку" увидите! CX, использованный в качестве "счетчика" циклов, "перебивает" тот же CX, но используемый как "координаты верхнего левого угла окна". И что с этим делать прикажете??
Вот... вы столкнулись с одной из самых больших проблем. В компьютере есть только 4 переменные, помните я вам говорил об этом?
И теперь догадайтесь, как выкрутиться из этой нехорошей ситуации с использованием стека??
В общем, вот что: медитируйте-ка вы до следующей недели, а там посмотрим :)
Это Нового года может не быть! Следующий номер рассылки - будет! Даже если мне на голову еще один кирпич свалится... - следующий номер все равно будет!!
Может по-приколу с наступающим новым тысячелетием всех поздравить?
Не хочу и не буду!
Потому что после последнего милениума, с которым я сталкивался, меня до сих пор к окну тянет... осовободиться, понимаете ли, от излишних несовместимых компонентов... самой крутой... самой опера... ционной... системы ... тысяче... ле..... сяче... и.......
ДА НЕ НАКАРКАЕТ НАМ БИЛЛГЕЙТС БЕДЫ СВОИМ WINDOWS-MILLENIUM'ом!
Крепите дух, братья! Нас ждут страшные годы...