Assembler for newbies in Low level Programming Ассемблер для новичков в низкоуровневом программировании. "Тем, кто охвачен враждою и страстью, Нелегко постичь это ученье. Страсти предавшись, тьмою объятые, Они не поймут того, что тонко, Что глубоко и трудно для постиженья, Что против теченья их мысли". Винайя-питака. Значит у нас три основных сегмента, сегменты кода, данных и стека. По сути сегмент- это выделенный кусочек памяти размером в 64 кб, однако, при трансляции, часто (почти всегда если специально не укзывать обратное) данные всех трех сегментов располагаются в одном сегменте, т.к. не имеет смысла тратить память и процессорное время на то что может быть в одном сегменте. По сути для программиста это не имеет особого значения, но может в будущем пригодится. К стати, открыв программу в hex редакторе типа HVIEW ты не увидиш никакой разницы между сегментами кода, стека и данных.Разве что визуально заметиш свои команды. Единственные границы - это границы которые определяются содержанием регистров cs,ds,ss, т.е. адресные границы. Что -же такое стек, и для чего он нужен. По сути стек- лишь форма организации памяти. Это означает, что заранее известно, чтo всегда единицой данных в стеке является слово. т.е. если мы отправили в стек 3 бита к примеру, то в стек будет помещено слово с тремя установленными битами. Чаще всего стек используется для передачи параметров функциям (процедурам, это на асме одно и то-же), а в языках высокого уровня, еще и для хранения переменных, массивов и т.п.. А так-же для хранения локальных переменных (процедур, функций), о чем будет упомянуто особо. Кстати, статические данные всегда хранятся в массиве данных. Значит на адрес начала стека указывает регистр SS, на его вершину (top) указывает регистр SP. Что значит вершина стека: это адрес слова которое было последним добавлено в стек. Для работы со стеком определены несколько команд, вот две основные команды- PUSH и POP Команда PUSH помещает в стек слово. Пример: Push ax ;помещает содержимое регистра ax в стек push 77 ; помещает число 77 в стек. если потом сделать pop ax, в регистре ax будет число 0077. Т. есть pop вытаскивает из стека последнее слово, и помещает его в регистр являющийся операндом этой команды. В данном случае – в ах. Очень кстати удобно, так как часто бывает что нехватает регистров, или к примеру во время работы процедуры изменяются значения нужных нам регистров, а нам надо из сохранить. Example: error proc push dx push ax mov ah,09h mov dx,offset err int 21h pop ax pop dx ret ;возврат из процедуры err db 'ERROR!!!$' error endp теперь мы можем вызывать эту процедуру из любой точки программы, не боясь, что нужные нам значения регистров потеряются. Так-же со стеком работают команды CALL и RET. CALL - вызов процедуры, она кладет в стек текущий адрес, и делает jmp на метку процедуры. RET же берет из стека адрес и переходит на следующую команду после call. Example: data segment db 'in main program',13,10,'$' perem dw 1 dup(0) db 'in procedure',13,10,'$' data ends stk segment stack db 30 dup(0) stk ends codseg segment ASSUME CS:CODSEG,SS:STK,DS:DATA begin: mov ax,data mov ds,ax mov ax,stk mov ss,ax mov ah,09h mov dx,0 call func int 21h mov ah,4ch int 21h func proc push ax push dx mov ax,10 add ax,44 mov perem,ax mov ah,09h mov dx,offset perem int 21h pop dx pop ax ret func endp codseg ends end begin Система работы со стеком: наиболее простой аналогией является стопка блинов-FILO (First In Last Out первым пришел последним ушел), Блин который мы положили последним, первым мы и снимем. А самый первый блин будет съеден последним :) Вот, почему порядок вытаскивания из стека должен быть противоположен порядку “складывания” в него. Немного похоже на паскальские скобки begin...end, каждому begin должен соответствовать свой end. Короче: push ax push cx push bx pop bx pop cx pop ax чтобы получить правильные начальные значения регистров. Ну и напоследок небольшая програмка, иллюстрирующая свойства стека: title stack data segment db 10 dup(0) db 'BCE PAbOTAET!!!',13,10,'$' data ends stackseg segment stack db 40 dup(1) stackseg ends codesegm segment ASSUME CS:codesegm,DS:data,SS:stackseg begin: mov ax,data mov ds,ax mov ax,stackseg mov ss,ax loo: mov ax,77h push ax mov bp,sp cmp [bp],77h jl loo jg loo mov dx,10 mov ah,09h int 21h mov ah,01h int 21h mov ah,4ch int 21h codesegm ends end begin Думаю, что тут даже комментарии излишни, все просто. Регистр флагов. Используя команду cmp, а так-же условные jmp-ы (jz,jnz,jl и пр.) вы наверно могли задуматься, а откуда процессор узнает, например что 4=4 (cmp), когда выполняет команду jz. Для этих целей существует регистр флагов, в котором каждый бит выполняет какую-то функцию. 15-12 биты зарезервированы 11 OF (Overflow Flag) флаг переполнения. Поднимается(т.е. устанавливается в 1 когда результат вычисления превышает допустимые размеры. 10 DF флаг направления 9 IF (Interrupt Flag) флаг прерывания. Если он сброшен(установлен в 0) то процессор не реагирует на внешние сигналы(прерывания). 8 TF (Trace Flag) флаг трассировки. Если поднят, то после выполнения каждой операции обращается к спец. прерыванию. Используется в отладчиках. 7 SF флаг знака. Используется при работе со знаковыми числами. 6 ZF (Zero Flag) флаг нуля. Равен 1 если результат действия(сравнения к примеру) является true, и 0 если false. 5 зарезервирован 4 AF вспомогательный флаг переноса. 3 зарезервирован 2 PF флаг четности. 1 зарезервирован 0 CF флаг переноса. одно из применений – индикация того, что при выполнении ДОС-функции произошла ошибка. Вот к примеру, если при сравнении cmp ax,4 ах равен 4 то поднимается ZF, и если далее сделать JZ (Jump If Zero Прыг если Нуль, т.е. перейти, если флаг нуля поднят) то будет произведен jump, а если не равен, то он не будет произведен. Короче все условные jump-ы используют регистр флагов. Кроме jump-ов, так-же и другие команды, смотрим которые в приложении. ведущий рассылку Skif_Q. skif_q@mail.ru Последнюю версию рассылки (с исправленными ошибками), а так-же приложение, со списком функций ДОС-а, списком регистров, описанием команд, и многого другого, вы найдете на http://ass3mbler.narod.ru