Работа со стеком. Стековые передачи • • • PUSH op ; (E)SP - 2 (4) в зависимости от атрибута размера адреса use16 или use32, op – mem, reg, 16, 32 разряда ; op [ SS: SP (ESP) ], помещает операнд в вершину стека. Непосредств. оп. 8,16,32 бита ; 8 –ми битовый операнд расширяется до 16 (32) бит. Пр. PUSH AX push word ptr MEM push esp ; включает в стек то значение ESP (SP), которое было до выполнения команды ( для i8086 push sp – новое- (SP-2). PUSH A ; включает в стек содержимое регистров AX, CX, DX, BX, SP, BP, SI, DI PUSHAD ; “------” EAX, EBX, ……., EDI PUSHF ; SP – 2: FLAGS [SS: SP] PUSHFD ; ESP – 4 ; EFLAGS [ SS: ESP ] Выполнение команды PUSH и её разновидностей не влияет на флаги в FLAGS. Команды извлечения из стека • • • • • • • POP op ; [ SS: SP (ESP) ] op , SP (ESP) + 2 (4). op – mem, reg 16, 32 бита ; содержимое из вершины стека помещается в операнд (16, 32) , указатель стека увеличивается на 2 ( 4 ). POPA ; извлечение из стека в DI, SI, BP, SP, BX, DX, CX, AX (обратно PUSHA ) POPAD ; извлече6ние из стека в EDI, ESI, ……, ECX, EAX ( обратно PUSHAD ) POPF ; [ SS: SP(ESP) ] FLAGS , SP(ESP) +2; ( обратно ком. PUSHF ) POPFD ; [ SS: ESP] EFLAGS , ESP + 4; ( обратно ком. PUSHFD ) • Поскольку стек работает в режиме LIFO ( last input – first output ) , то при записи и выборке из стека это необходимо учитывать: • • • • • • • • P1 PROC push ax push ds ….. …… pop ds pop ax P1 ENDP Атрибутные операторы, возвращающие значение • • • • • • • • • • • • • • • • • LENGTH <имя переменной> ; возвращает число единиц переменных в конструкции DUP, если она является первым операндом после DB, DW, DD и 1 в остальных случаях. SIZE < имя переменной > ; возвращает число байт, соответствующих массиву. OFFSET < метка или переменная > ; Возвращает эффективный адрес (полное смещение в сегменте). SEG < метка или переменная > ; Возвращает соответствующий сегментный адрес. Пример: data segment para public ‘d1’ A dw 125 B dw 100 DUP (?) data ends code segment ‘c1’ ……………. mov cx, LENGTH B ; cx = 100 mov dx, SIZE B ; dx = 200 mov bx , offset B ; bx = 2 ( lea bx, B ) mov ax , seg B ; ax = data ( адрес сегмента из ds ) ……………. Процедуры • Это некоторая последовательность действий, которая может быть выделена как самостоятельная программа, и к которой можно обращаться из любого места программы любое число раз ( аналог п/п или функций в ЯВУ). • В основной программе процедуры обычно размещают либо в начале сегмента кода перед командой, с которой начинается программа ( точка входа), либо в конце сегмента команд. • Описание процедур: имя PROC < дистанция > • < тело процедуры > • имя ENDP • Имя процедуры ассемблер как метку на первую команду процедуры. Процедура может быть описана как ближняя ( NEAR ) в поле дистанции или дальняя ( FAR ). К процедурам типа NEAR можно обращаться только из того сегмента кода, где она описана, а к процедурам типа FAR из любого сегмента кода. По умолчанию процедура считается ближней ( NEAR ) . Имена и метки, описанные в процедуре не локализуются внутри её, т.е. Не должны совпадать с другими идентификаторами программы. В больших программах процедуры часто размещают в отдельных сегментах кода и к ним обращение осуществляется через полный указатель ( CS:IP(EIP) – дальний указатель FAR ). • Обращение к процедурам можно осуществлять с помощью команды безусловного перехода (JAMP near ( far)), предварительно предусмотрев передачу параметров и точку возврата в вызывающую программу. Для упрощения работы с процедурами в систему команд процессора включены команды вызова ( CALL ) возврата из процедуры ( RET ). Команды вызова и возврата процедур • • • • • • • • • • • • • • • Относятся к командам безусловной передачи управления. Вызовы выполняются так же как и командой JAMP, но вначале они запоминают текущее значение указателя команд (IP(EIP), или CS:IP(EIP) в стеке, что позволяет вернуть управление из процедуры на следующую команду после CALL. Команды возвратов осуществляют эту передачу управления, восстанавливая из стека адрес точки возврата в указателе команды. Вызовы и возвраты – средства организации процедур. Так же, как и команда переходов, вызовы и возвраты бывают двух видов: внутрисегментные и межсегментные. Форматы команд: CALL < метка > ; внутрисегментный прямой вызов, SP=SP-2; IP [SS:SP]; IP+ met IP met – смещение между меткой и командой после CALL. CALL word ptr MEM ; внутрисегментный косвенный вызов, SP=SP-2; IP [SS:SP]; в MEM эффективный адрес (смещение) к имени процедуры. [MEM] IP. CALL far <метка> ; межсегментный прямой вызов, SP=SP-2; CSSS:SP; SP=SP-2, IP SS:SP; IP offset <метка>, CS seg < метка >. CALL dword ptr MEM1 ; межсегментный косвенный вызов, SP=SP-2; CSSS:SP; SP=SP-2, IP SS:SP; IP [MEM1], CS [MEM1+2]. RET ; Внутрисегментный возврат, [SS:SP] IP, SP = SP+2. RET n ; Внутрисегментный возврат, [SS:SP] IP, SP = SP+2, SP= SP+n. RET ; межсегментный возврат, [SS:SP] IP, SP = SP+2, [SS:SP] CS, SP = SP+2. RET n ; межсегментный возврат, [SS:SP] IP, SP = SP+2; [SS:SP] CS, SP = SP+2+n. Передача параметров процедур • • • • • • • • • • • • • • • • Можно разделить на две группы: 1) В каком виде их передаём: - по значению, - по ссылке ( передача адреса) 2) Через чего передаётся , т.е. Где размещаются параметры перед вызовом процедуры: - регистры, - память. Передача параметров через регистры – самый простой способ, при этом процедура не будет тратить время на извлечение их из памяти. Пример: mov ax, A mov bx, offset mas1 call proc12 Память – чаще всего используется передача параметров через стек, особенно когда параметров много. Пример2: push A push offset mas1 call proc11 В этом случае после передачи управления процедуре proc11 в вершине стека будет находиться адрес точки возврата (значение IP), по адресу [SP+2] offset mas1, а по адресу [SP+4] значение переменной А. Локальные переменные процедур • Если по алгоритму в процедуре требуются локальные переменные, то место под них, как правило отводятся в стеке. • Пример: Пусть требуется выделить память в стеке под m переменных длиной WORD • pr1 proc • push bp • mov bp, sp • sub sp, m*2 • mov [bp-2], 100 • ……… • mov sp, bp • pop bp • ret • pr1 endp • Таким образом доступ к локальным переменным осуществляется через регистр bp. • Все действия программы, связанные с подготовкой стека для работы с процедурой называются прологом, а все действия, которые выполняются по восстановлению регистров и освобождению стека, называются эпилогом. Модульное программирование • Под модулем понимается часть программы, решающая некоторую задачу, и которая может быть оттранслирована отдельно. Частным случае модуля может служить процедура. Существует два основных способа объединения модулей: • 1. Все модули объединяются в одну программу, которая проходит этапы компиляции и редактирования связей ( т.е. имеют одинаковое имя сегмента кода и тип public). M1 M2 Компиляция MASM Редактирование LINK M.exe M3 2. Модули пишутся и компилируются раздельно. Объединяются на этапе редактирования M1 Компиляция MASM M2 Компиляция MASM M3 Компиляция MASM Редактирование LINK M.exe Структура любого модуля близка к структуре программы, то есть это последовательность • предложений, оканчивающаяся директивой END. У головной программы END с именем точки входа. Все имена в модуле локализуются. • Для связи переменных и меток из разных модулей существуют две директивы. Для указания внешних переменных и меток предназначена директива EXTRN: • Формат: EXTRN < имя >: < тип >, …, < имя > : < тип > • Где тип – BYTE, WORD, DWORD – для внешних переменных, NEAR, FAR – для меток и имен процедур, ABS – стандартная константа со значением 0. Данная директива может указываться любое число раз в любом месте модуля. • Например: EXTRN A: WORD, PR1: FAR, K: ABS • Чтобы имена и метки были доступны из модулей, где они описаны, необходимо использовать директиву PABLIC < имя >, …, < имя > • Пример: Модуль1 Моддуль2 • extrn A1: WORD, B1: FAR DATA segment • code segment A1 DW 55 • ………. DATA ends • mov ax, A1 public A1, B1 • ………. ………. • call B1 B1 proc far • …….. ………. • code ends B1 endp