| На ассемблере ты можешь хранить переменные двумя способами: в регистрах и в памяти. С регистрами все понятно. А вот с памятью у тебя могут возникнуть проблемы. Тут есть подводные камни. Читай дальше, и ты узнаешь два способа размещения переменных, которыми пользоваться нельзя, и три — которыми можно. Также ты узнаешь, какие бывают режимы адресации и как это знание поможет тебе кодить на ассемблере более эффективно. 
 
 После первых двух уроков ты умеешь пользоваться 16-битными регистрами процессора и их 8-битными половинками. Это хорошее начало. Но! Программируя на ассемблере, очень часто сталкиваешься с тем, что нужно намного больше переменных, чем может поместиться в регистры процессора. Часть переменных приходится хранить в памяти. Сейчас расскажу, как это делать. На примере программы, которая ищет простые числа. 
 Предыдущие статьи
 
 Погружение в assembler
 Делаем первые шаги в освоении асма
 Осваиваем арифметические инструкции
 
 Где данные хранить можно, а где нельзя
 
 Перед тем как размещать в памяти переменные, разберись, куда их можно всовывать, а куда нельзя. Потому что здесь ассемблер тебя никак не контролирует. Ты волен размещать переменные всюду, где только захочешь. Ассемблер все безропотно скомпилирует, а процессор все прилежно выполнит. Вот только ответственности за последствия они не несут. Вся ответственность целиком лежит на тебе. Но ты не пугайся! Просто постарайся запомнить раз и навсегда два способа размещения переменных, которыми пользоваться нельзя, и три — которыми можно. Сначала — как нельзя. 
 
 Из двух предыдущих уроков ты уже знаешь, что первые 0x100байтов любой программы, скомпилированной РІ файл СЃ расширением.com, зарезервированы операционной системой. Всовывать сюда свои переменные точно не стоит.
 Процессор 8088 не отличает данные от кода. Если ты напишешь на ассемблере вот такой код, процессор радостно выполнит не только строчки с инструкциями, но и строчки с данными — ничуть не переживая о том, что от выполнения строчек с данными получается в лучшем случае абракадабра, а в худшем программа рушится или застревает в бесконечном цикле.
 
 
 Никогда так не делай! Когда резервируешь какую-то область памяти под переменную, обязательно проследи, чтобы эта область памяти была за пределами потока выполнения. Где же такую область найти? Ведь ассемблерные инструкции, по которым процессор идет, теоретически могут размещаться в любой ячейке памяти. 
 
 Есть по крайней мере три участка, куда процессор никогда не заглядывает. Вот там и храни свои переменные: 
 
 после инструкции int 0x20, которую РјС‹ ставим РІ конце программы, чтобы вернуться РІ командную строку РћРЎ;
 сразу после безусловного перехода jmp;
 сразу после инструкции возврата ret.
   Пишем подпрограмму: печать числа РЅР° экране
 
 Р’ начале статьи РѕР± арифметических функциях РјС‹ написали библиотеку library.asmс двумя функциями:display_letter(выводит Р±СѓРєРІСѓ РЅР° экран) Рёread_keyboard(считывает символ с клавиатуры). Сейчас для вывода простых чисел на экран нам нужна более продвинутая функция вывода, которая выводит не одну букву или цифру, а полноценное число. Давай напишем такую функцию (добавь ее в конец файлаlibrary.asm). 
 
  
 
 Как РѕРЅР° работает? Берет РёР· AXчисло, которое надо вывести на экран. Рекурсивно делит его на 10. После каждого деления сохраняет остаток на стеке. Доделившись до нуля, начинает выходить из рекурсии. Перед каждым выходом снимает со стека очередной остаток и выводит его на экран. Уловил мысль? 
 
 На всякий случай, если ты с ходу не понял, как работает display_number, РІРѕС‚ тебе три примера. 
 
 Допустим, AX = 4. Тогда после деления на 10 вAXбудет 0, Рё поэтомуdisplay_numberне зайдет в рекурсию. Просто выведет остаток, то есть четверку, и всё. 
 
 Если AX = 15, то после деления РЅР° 10 РІAXбудет единица. И поэтому подпрограмма залезет в рекурсию. Покажет там единицу, затем выйдет из внутреннего вызова в основной и там напечатает цифру 5. 
 
 Если ты так до конца и не понял, то сделай вот что. Помести в AXчисло побольше, скажем4527, и поработай в роли процессора: пройди мысленно по всем строкам программы. При этом отмечай в блокноте — в обычном бумажном блокноте, не на компьютере — каждый свой шаг. Когда в очередной раз заходишь рекурсивно вdisplay_number, отступай РІ блокноте РЅР° РѕРґРёРЅ СЃРёРјРІРѕР» вправо РѕС‚ начала строки. Рђ РєРѕРіРґР° выходишь РёР· рекурсии (инструкцияret), отступай на один символ влево. 
 
 И еще: имей в виду, что после того, как display_numberвыполнится, РІAXуже не будет того значения, которое ты туда поместил перед тем, как вызвать подпрограмму. 
 Пишем программу для поиска простых чисел
 
 Два предварительных шага сделаны: ты уяснил, где переменные размещать можно, а где нельзя, и ты написал функцию печати десятичного числа. Теперь давай пощупаем всю эту теорию руками. Напишем с тобой программу, которая ищет простые числа. 
 
 Напомню, простые числа — это такие, которые делятся только на единицу и на себя. Если у них есть другие делители, то такие числа называются составными. Для поиска простых чисел существует целая куча алгоритмов. Мы воспользуемся одним из них — решетом Эратосфена. В чем суть алгоритма? Он постепенно отфильтровывает все числа за исключением простых. Начиная с числа 2Рё заканчиваяn. Числоnзадаешь ты. Как только алгоритм натыкается РЅР° очередное простое числоa, он пробегает по всему списку до конца (доn) Рё вычеркивает РёР· него РІСЃРµ числа, которые делятся РЅР°a. В Википедии есть наглядная анимированная гифка. Посмотри ее, и сразу поймешь, как работает решето Эратосфена. 
 
 
 Что здесь происходит? 
 
 Начинаем с двойки.
 Смотрим: очередное число aпомечено как составное? Да — идем РЅР° шаг 5.
 Если РЅРµ помечено (РЅРµ вычеркнуто), значит, a— простое.
 Пробегаем по всему списку и вычеркиваем все числа, которые делятся на a.
 Р?нкрементируем текущее число.
 Повторяем шаги 2–5, пока не достигли n.
 Каким образом будем бегать по списку и вычеркивать оттуда составные числа? Сначала нам этот список надо создать! Причем в регистры его точно втиснуть не получится. Нам потребуется битовый массив размером n. РџРѕ биту РЅР° каждое число РѕС‚2доn(или вполовину меньше, если РјС‹ оптимизируем алгоритм так, чтобы РѕРЅ РЅРµ видел четные числа; это ты можешь сделать РІ качестве домашнего задания). Соответственно, чем большеn, до которого ты хочешь найти простое число, тем вместительней должен быть массив. 
 
 Все понятно? Давай закодим! 
 Перейти обратно к новости
 |