Ты говоришь на ассемблере?
(часть третья)
Приветствую всех, кто хочет чего-то большего, чем просто играть, и ищет другие способы самореализации. Знать, как выполнить ту или иную задачу, действительно приятно. А если нас за это ещё и похвалят…
Но перейдём к делу!
Постараюсь объяснить, что такое «стек», как он работает и как им пользоваться.
В словаре польского языка под этим словом можно прочитать: «много предметов, сложенных один на другой; большое количество чего-либо…». В компьютере стек устроен похожим образом. Он служит для сохранения значений регистров.
Стек, конечно же, находится в оперативной памяти RAM, а конкретное место в ней задаёт специальный 16-битный регистр SP. Инструкция BASIC CLEAR адрес устанавливает основание стека. На ассемблере это может выглядеть так:
1 LD SP,адрес ; установка стека с указанного адреса
2 LD SP,HL ; установка основания стека
3 LD SP,IX ; по значению
4 LD SP,IY ; соответствующего регистра
5 LD SP,(адрес) ; установка стека по значению двух
; ячеек памяти
Но не спешите! Появилось немало новых понятий. Для начала, пожалуй, объясню, что такое «основание стека». Это его самая глубокая точка. Этот адрес означает, что регистры будут сохраняться в стеке, начиная с указанного адреса и двигаясь назад — к началу памяти.
Команда LD SP,HL — это загрузка, то есть копирование значения регистров HL в SP.
То же самое делают команды LD SP,IX и LD SP,IY. Стоит знать, что существуют такие регистры, как IX и IY. В разных умных книгах их называют индексными регистрами. Но ими мы займёмся в другой раз.
Пятая команда в нашем списке, LD SP,(адрес), означает установку основания стека по значению двух ячеек памяти. Первая ячейка — так называемая младшая, вторая — старшая. Старшая задаёт область памяти с точностью до 256 байт и называется страницей, а младшая указывает, о каком из этих 256 байтов идёт речь.
Например, адрес 16384 в записи «младший — старший» будет выглядеть так:
адрес = 0
адрес+1 = 64, то есть:
256*(адрес+1)+(адрес) = 256*64+0 = 16384
А теперь, наконец, о том, как сохранить регистр с помощью стека. К сожалению, сохранить один регистр нельзя. Это обязательно должна быть пара регистров.
Основная инструкция:
PUSH пара регистров
То есть, если процессор, например, встретит команду
PUSH HL
он выполнит следующие действия:
DEC SP
LD (SP),H ; старший
DEC SP
LD (SP),L ; младший
Разумеется, извлечение регистров происходит в обратном порядке. Основная инструкция для этого:
POP пара регистров
Например:
POP HL
это приведёт к следующим действиям процессора:
LD L,(SP) ; младший
INC SP
LD H,(SP) ; старший
INC SP
Выполнение инструкции PUSH HL не обязательно должно сопровождаться инструкцией POP HL. Значение регистров можно извлечь из стека в любую пару регистров, например DE.
PUSH HL
POP DE
Процессор не знает, сохраняли мы значение HL или DE. Поэтому, если мы сохранили больше одной пары и хотим после извлечения получить в них те же значения, инструкции POP должны идти в обратном порядке.
PUSH HL
PUSH DE
.
. ; программа
.
POP DE
POP HL
Но это ещё не все инструкции, использующие стек. Существует, среди прочего, инструкция
CALL адрес
для вызова подпрограмм. Это аналог команды BASIC GO SUB: встретив RETURN, программа возвращается в основную часть. Если говорить проще: CALL помещает в стек адрес следующей за ней инструкции, которую предстоит выполнить, а RET извлекает этот адрес и переходит по нему.
40000 CALL 40100 ; DEC SP
LD (SP),40003
40003 .
.
.
40100 RET ; JP (SP) INC SP
Стек очень полезен, однако ошибки при написании процедур часто возникают из-за неправильного его использования. Так случается даже у самых опытных программистов. Мой первый совет: следите за тем, чтобы количество команд PUSH и POP совпадало. Иначе, например, если забыть POP, а затем выполнить RET, процессор перейдёт по адресу, который не был извлечён из стека вместе с парой регистров. Чаще всего это приводит к «зависанию» программы или перезагрузке системы.
Теперь я покажу короткую программку, которая должна проиллюстрировать сказанное выше. Но для полного понимания нам ещё нужно узнать о команде ADD HL,DE, которая здесь встречается. Она прибавляет значение DE к HL. Подробнее об арифметике Z-80 мы поговорим в следующей части цикла.
10 ORG 40000 ; показ экрана
20 TLO EQU 127 ; цвет фона
30 KOLOR EQU 7 ; цвет экрана
40 OPOZN EQU 2 ; задержка
Постоянные значения задаются с помощью директивы EQU.
50 LD HL,22528 ; закраска
60 LD DE,22529 ; экрана
70 LD BC,767 ; цветом
80 LD (HL),TLO ; фона
90 LDIR
100 START LD DE,32
110 LD HL,22528
120 LD B,8
130 PETLA PUSH BC
140 PUSH HL
150 LD B,OPOZN
160 SLOW HALT
170 DJNZ SLOW
180 LD B,3
190 SUPEL CALL LOOK ; переход к
подпрограмме
200 INC H ; увеличение
пары регистров HL на 256 за счёт
увеличения только старшего байта
пары HL — переход к следующей трети
210 DJNZ SUPEL
220 POP HL
230 POP BC
240 ADD HL,DE ; переход
на строку ниже
250 DJNZ PETLA
260 RET ; выход из процедуры
270 LOOK PUSH HL ; подпрограмма
280 PUSH BC ; рисования полосы
290 LD B,32
300 LOOP LD (HL),KOLOR
310 INC HL
320 DJNZ LOOP
330 POP BC
340 POP HL
350 RET ; выход из подпрограммы
P. S. Если мои рассуждения и наблюдения изложены слишком сложно, прошу написать мне об этом и связаться со мной.
SCALMA
Поделитесь вашим мнением о статье