Лабораторная работа № 24.


Подпрограмма и стек

Цель работы: исследование особенностей напаси и обращения к подпрограммам; изучение методов использования стека при создании программ.

Краткие сведения из теории.

Память микро-ЭВМ, построенной на основе МП 8086, может иметь не более 65 536 однобайтных ячеек. Учитывая ограниченные возможности памяти при разработке программ, нужно стараться сделать их как можно короче. С этой целью часть программы, которая неоднократно повторяется, или программа, которая часто используется, могут быть оформлены и виде подпрограмм — последовательностей команд, выполнение которых может быть вызвано из любого места программы любое количество раз. Процесс передачи управления к подпрограмме называется ее вызовом. Данные и адреса, требуемые для работы подпрограммы, называются входными параметрами. Результаты работы подпрограммы, передаваемые по окончании ее работы в основную программу, называются выходными параметрами.

Автоматическое сохранение и восстановление адреса основной программы при выполнении подпрограмм позволяет сделать подпрограммы вложенными, т. е. осуществить вызов одной подпрограммы из другой. Уровень вложенности для данной микро-ЭВМ определяется лишь размером стека.

Все операции со стеком должны быть сбалансированы, т. е. каждая подпрограмма должна содержать равное количество команд PUSH<R> и POP<R> и оканчиваться командой RET. В противном случае выполнение команды RET в конце подпрограммы приведет к записи в программный счетчик случайного числа из стека. Адрес возврата в основную программу будет потерян и нарушится последовательность ее выполнения.

 

Процедуры

Процедура - часть кода, который может называться от вашей программы, чтобы делать некоторую определенную задачу. Процедуры делают программу более структурной и более легким, чтобы понять. Вообще процедура возвращается тому же самому пункту от того, где это называлось.

Синтаксис для декларации процедуры:

name PROC
; here goes the code
; of the procedure ...
RET
name ENDP

 

name - Является именем процедуры, то же самое имя должно быть в вершине и основании, это используется, чтобы проверить правильное закрытие процедур.

Вероятно, Вы уже знаете, что команда RET используется, чтобы возвратиться операционной системе. Та же самая команда используется, чтобы возвратиться от процедуры (фактически операционная система видит вашу программу как специальная процедура).

 

PROC и ENDP - директивы компилятора, так что они не собраны в любой реальный машинный код. Компилятор только помнит адрес процедуры.

Команда CALLиспользуется, чтобы вызвать процедуру.

 

Имеется пример:

ORG 100h CALL m1MOV AX, 2 RET ; return to operating system.m1 PROCMOV BX, 5RET ; return to caller.m1 ENDP

END

 

Вышеупомянутая процедура m1 запросов примера, делает MOV BX, 5, и возвращается следующей команде после CALL: MOV АХ, 2.

 

Имеются несколько способов передать параметры для процедуры, самый легкий способ передавать параметры - используя регистраторов, имеется другой пример процедуры, которая получает два параметра в AL и регистраторах BL, умножает эти параметры и возвращает результат в регистре АХ:

ORG 100h MOV AL, 1MOV BL, 2 CALL m2CALL m2CALL m2CALL m2 RET ; return to operating system. m2 PROCMUL BL ; AX = AL * BL.RET ; return to caller.m2 ENDP

END

В вышеупомянутом значении примера регистра AL - модификация, каждый раз процедура называется, BL неизменными пребыванием регистра, так что этот алгоритм вычисляет 2 во власти 4,

Так что заключительный результат в регистре AХ 16 (или 10h).

 

Здесь идет другой пример,

Это использует процедуру, чтобы печатать Привет Мир! Сообщение:

ORG 100h LEA SI, msg ; load address of msg to SI. CALL print_me RET ; return to operating system.; ==========================================================; this procedure prints a string, the string should be null; terminated (have zero in the end), the string address should be in SI register:print_me PROC next_char: CMP b.[SI], 0 ; check for zero to stop JE stop ; MOV AL, [SI] ; next get ASCII char. MOV AH, 0Eh ; teletype function number. INT 10h ; using interrupt to print a char in AL. ADD SI, 1 ; advance index of string array. JMP next_char ; go back, and type another char. stop:RET ; return to caller.print_me ENDP; ==========================================================msg DB 'Hello World!', 0 ; null terminated string.

END

 

" B. " - приставка прежде [SI] означает, что мы должны сравнить байты, не слова. Когда Вы должны сравниться, слова добавляют "w". Приставка вместо этого. Когда один из сравненных операндов - регистр, это не требуется, потому что компилятор знает размер о каждом регистре.

 

Стек

Стек - область памяти для хранения временных данных. Стек используется командой CALL, чтобы сохранить адрес возврата для процедуры, команда RET получает это значение от стека и возвращает этому смещение. Весьма та же самая вещь случается, когда команда INT вызывает прерывание, это хранит в регистре флажка стека, доле кода и смещении. IRET команда используется, чтобы возвратиться от запроса прерывания.

Мы можем также использовать стек, чтобы сохранить любые другие данные,

Имеются две команды, которые работают со стеком:

PUSH (Поместить) - хранит 16 разрядных значения в стеке.

POP (Выталкивать) - получает 16 разрядных значения от стека.

 

Синтаксис для команды PUSH:

PUSH REG
PUSH SREG
PUSH memory
PUSH immediate

REG: AX, BX, CX, DX, DI, SI, BP, SP.
SREG: DS, ES, SS, CS.
memory: [BX], [BX+SI+7], 16 bit variable, etc...
immediate: 5, -24, 3Fh, 10001101b, etc...

 

Синтаксис для команды POP:

POP REG
POP SREG
POP memory

REG: AX, BX, CX, DX, DI, SI, BP, SP.
SREG: DS, ES, SS, (except CS).
memory: [BX], [BX+SI+7], 16 bit variable, etc...

 

Примечания:

· PUSHи POPработу с 16 разрядными значениями только!

· Обратите внимание: PUSH непосредственные работы только микропроцессором 80186 и позже!

 

Стек использует алгоритмLIFO(Последний пришел - первым вышел), это означает, что, если мы помещаем эти значения один за другим в стек:

1, 2, 3, 4, 5

Первое значение, что мы войдем в выталкивание, будет 5, тогда 4, 3, 2, и только тогда 1.

Очень важно делать равный число, PUSHи POP, иначе стек, возможно разрушенный, и будет невозможно возвратиться к операционной системе. Поскольку Вы уже знаете, что мы используем команду RET, чтобы возвратиться к операционной системе, так когда запуски программы имеется адрес возврата в стеке (вообще это 0000h).

Команда PUSH И POP особенно полезна, потому что мы не имеем слишком много регистров, чтобы работать с, так имеется уловка:

· Храните первоначальное значение регистра в стеке (использование PUSH).

· Используйте регистр для любой цели.

· Восстановите первоначальное значение регистра от стека (использование POP).

Имеется пример:

ORG 100h MOV AX, 1234hPUSH AX ; store value of AX in stack. MOV AX, 5678h ; modify the AX value.POP AX ; restore the original value of AX. RET

END

 

Другое использование стека - для обмена значений,

Имеется пример:

ORG 100h MOV AX, 1212h ; store 1212h in AX.MOV BX, 3434h ; store 3434h in BX PUSH AX ; store value of AX in stack.PUSH BX ; store value of BX in stack. POP AX ; set AX to original value of BX.POP BX ; set BX to original value of AX. RET

END

Обмен случается, потому что стек использует алгоритм LIFO(Последний пришел - первым вышел), так, когда мы помещаем 1212hи затем 3434h, на выталкивании мы сначала станемся 3434hи только после того, как это 1212h.

 

Область памяти стека установлен регистрSS (регистр стека), и регистрSP (Указатель стека). Вообще операционная система устанавливает значения этих регистров в начале программы.

 

Команда "PUSH source" делает следующее:

· Вычтите 2 от регистра SP.

· Пишите значение источника к адресу SS:SP.

 

Команда "POP destination" делает следующее:

· Пишите значение в адресе SS:SP адресату.

· Добавьте 2 к регистру SP.

 

Текущий адрес, направленный SS:SP называется вершиной стека.

Поскольку COM регистр стека файлов - вообще регистр кода, и указатель стека установлен в значение 0FFFEh. В адресе SS:0FFFEh сохранил адрес возврата для команды RET, которая выполнена в конце программы.

 

Вы можете визуально видеть операцию стека, нажимая по кнопке [Stack] на окне эмулятора. Вершина стека отмечена признаком "<".

Программа:

; This sample shows how the

; stack works. Click "Stack"

; button in emulator to see

; the contents of the stack.

 

#MAKE_COM#

 

; This code does nothing

; useful, except printing

; "Hi" in the end.

 

; COM file is loaded at 100h

; prefix:

ORG 100h

 

MOV AX, 1234h

PUSH AX

 

MOV DX, 5678h

PUSH DX

 

POP BX

POP CX

 

; function call pushes

; IP value of the next

; instruction:

 

CALL tfunc

 

MOV AX, 7890h

PUSH AX

POP BX

 

; A typical use of

; stack is to set segment

; registers.

; set DS to video

; memory segment:

 

MOV AX, 0B800h

PUSH AX

POP DS

 

; print "Hi":

MOV [170h], 'H'

MOV [172h], 'i'

 

; color attribute for 'H'

MOV [171h], 11001110b

 

; color attribute for 'i'

MOV [173h], 10011110b

 

RET

 

; the test procedure:

 

tfunc PROC NEAR

 

XOR BX, BX

XOR CX, CX

 

; here we "pop" the IP

; value:

 

RET

tfunc ENDP

 

END

 

Как правило, в начале каждой программы сохраняют в стеке содержимое всех задействованных при ее выполнении регистров с помощью команд PUSH<R> . В конце подпрограммы восстановление содержимого регистров осуществляется с помощью команд POP<R> и в обратной последовательности по отношению к их записи в стек. Обычно в виде подпрограмм записываются многократно используемые фрагменты программ, например подпрограмма выдачи звукового сигнала, подпрограмма обслуживания клавиатуры и дисплея и т.д.


 


Рис. 8.7. Алгоритм простой подпрограммы временной задержки

Рис. 8.8. Схема формирования звуковых сигналов в микро-ЭВМ

Рис. 8.9. Алгоритм подпрограммы выдачи звукового сигнала

Алгоритм работы простой подпрограммы временной задержки приведен на рис. 8.7. Общее время задержки вычисляется по формуле TD = t1 + (t2 + t3 + t4)N1+ t5, где N1 — число, первоначально записанное в счетчике. В качестве счетчика выбран регистр В, в который записывается число N1 из регистра С.

Команда NOP нужна для увеличения времени выполнения цикла, а следовательно, и обшей задержки. Вместо команды NOP может быть записана любая последовательность команд, выполнение которых не изменяет содержимого регистров микропроцессора. Время записи числа N1 в регистр В и возврата из подпрограммы t1 + t5 фиксировано и в цикл не входит. Минимальная задержка для приведенной подпрограммы определяется при N1 = 0,1 и равна t1 + t2 + t3 + t4+ t5. Максимальная задержка имеет место при N1 = 00 и вычисляется по формуле TDмах= t1 + (t2 + t3 + t4) 256+ t5.

Подпрограмма DLY (подпрограмма 8.10) представляет подпрограмму временной задержки, записанную в соответствии с алгоритмом, представленным на рис. 8.7. Рассмотрим пример использования подпрограммы временной задержки при организации звуковых сигналов в микро-ЭВМ. В микро-ЭВМ звуковые сигналы могут формироваться простейшей схемой (рис. 8.8), на вход которой со звуковой частотой записываются по очереди «0» и «1». Будем считать, что устройство формирования звуковых сигналов имеет адрес 80. Схема алгоритма работы подпрограммы генерации звуковых колебаний (подпрограмма 8.11) приведена на рис. 8.9.

Подпрограмма 8.10

Адрес Машинный Метка Мнемокод Комментарий

код

0900 41 MOV В, С записать из регистра С в регистр В

901 00 DLY NOP нет операции

902 05 DСR В уменьшить число в регистре В на 1

0903 С2 0109 JNZ DLY если число, записанное в регистре В. не равно нулю, то идти на DLY

0906 С9 RЕT

Подпрограмма 8.11.

Адрес Машинный Метка Мнемокс.:; Комментарий

код

0А00 AF ВРР ХRA А очистить аккумулятор

0А01 D3 80 OUT BP записать 00 в выходное устройство

0А03 CD 0009 CALL DLY вызнать подпрограмму задержки

0А06 2F СМА записать код ГГ в аккумулятор

0А07 D3 80 OUT ВР записать код ГГ к выходное устройство

0А09 CD 0009 CALL DLY вызвать подпрограмму задержки

0A0C С9 RET

Программа MAIN (программа 8.12) представляет программу генерации сигналов с частотой, задаваемой числом с входного регистра.

Программа 8.12

Адрес Машинный .Метка Мнемокод Комментарий

код

0800 DB 20 MAIN IN 20 записать число из входного регистра в аккумулятор

0802 4F MOV С, А записать число в регистр С

080:1 CD 000А CALL ВРР вызвать подпрограмму ВРР

0806 СЗ 0008 JMP MAIN продолжать

При использовании подпрограмм за счет изменения входных параметров можно влиять на конечный результат выполнения подпрограмм. Рассмотрим это на примере подпрограммы регулируемой временной задержки (подпрограмма 8.13).

Программа состоит из двух подпрограмм: DELB (адрес 0430) и DELA (адрес 0429). Подпрограмма DELB осуществляет регулируемую временную задержку, и входным параметром ее является двухбайтное число, записанное в паре регистров (В, С). Это число и определяет длительность задержки в миллисекундах. Частным случаем подпрограммы DELB является подпрограмма DELA, осуществляющая фиксированную задержку в 1 мс и не имеющая входных параметров. Таким образом, если необходимо иметь в программе фиксированную задержку в 1 мс, то можно обращаться с помощью команды CALL к подпрограмме DELA. При необходимости получения определенной заданной временной задержки в программе необходимо записать соответствующее число в регистры В, С, а затем вызвать подпрограмму DELB.

Подпрограмма 8.13

Адрес Машинный Метка Мнемокод Комментарий

код

0429 С5 DELA PUSH В сохранить содержимое регистров В и С в стеке

042А 01 0100 LXI В, 0001 установить длительность

042D С3 3104 JMP DEL1 1 мс

0430 С5 DELB PUSH В сохранить содержимое регистров В и С в стеке

0431 F5 DELI PUSH PSW сохранить содержимое PSW в стеке

0432 AF XRA А очистить аккумулятор

0433 D5 PUSH D сохранить содержимое регистров D и Е в стеке

0434 16 67 DEL 2 MVID, TIME загрузить счетчик

0436 15 DEL3 DCR D 1 мс задержки

0437 С2 3604 INZ DEL3 если не 0, продолжать

043A OB DCX В уменьшить содержимое счетчика длительности на 1

043В B8 СМР В

043С С2 3404 JNZ DEL 2

043F В9 СМР С

0440 С2 3404 JNZ DEL 2 если не 0, продолжать

0443 D1 POP D восстановить содержимое регистров D, Е

444 F1 POP PSW то же, PSW

445 C1 POP В то же, В, С

446 С9 RET

 

; Этот образец показывает использование функции таймера (INT 15-ый / 86-ой)

; Этот код печатает некоторые случайные работы с 1 секундной задержкой.

 

#make_COM#

ORG 100h

 

; set the segment register,

; just in case:

MOV AX, CS

MOV DS, AX

 

next_char:

CMP count, 0

JZ stop

 

; print char:

MOV AL, c1

MOV AH, 0Eh

INT 10h

 

; next ascii char:

INC c1

DEC count

 

; set interval (1 million

; microseconds - 1 second):

MOV CX, 0Fh

MOV DX, 4240h

MOV AH, 86h

INT 15h

 

; stop any error:

JC stop

JMP next_char

 

stop:

RET

count DB 10

c1 DB 'A'

END


 

Лекция 44



Дата добавления: 2016-07-05; просмотров: 2160;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

Поделитесь с друзьями:

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.049 сек.