┌──────────────────────────────────────────────────────────────┐ │ ████▓▓▓▓▒▒▒▒░░░░ АССЕМБЛЕР ДЛЯ НАЧИНАЮЩИХ ░░░░▒▒▒▒▓▓▓▓█████ │ └──────────────────────────────────────────────────────────────┘ (C) THINKER ─────────── ╔══════╗ │ СТЕК │ Это очень важное для программиста понятие, так что чи- ╚══════╝ тайте внимательно, а я постараюсь объяснить, как можно понятнее. Допустим, Вам необходимо выполнить какую-нибудь процедуру, при этом рег. 'A' там используется, значит в нем пос- ле выхода будет содержаться какое-то число, а нам нужно, чтобы после выполнения процедуры рег. 'A' и рег. 'B' содержали те-же значения, что и перед этим. Что делать? Можно сохранить акку- мулятор (так называют рег. 'A', помните?) в какой-нибудь ячейке памяти, тогда наша программка будет выглядеть так: LD A,10 ; в 'A' заносим число 10 LD B,25 ; в 'B' заносим число 25 LD (65000),A ; в ячейку памяти с адресом 65000 заносим ; значение рег. 'A' LD A,B ; в рег. 'A' заносим значение рег. 'B'. ; (Ведь в памяти мы можем оперировать только ; с рег. A!) LD (65001),A ; в ячейку пямяти с адресом 65001 ложим 'А' CALL PROGR ; переходим к подпрограмме (равносильно ; RANDOMIZE USR PROGR, где PROGR - адрес) LD A,(65001) ; заносим в 'A' значение из ячейки памяти с ; адресом 65001 LD B, A ; заносим в 'B' содержимое рег.'А' LD A, (65000) ; теперь в рег. 'B' у нас снова число 25, а ; в рег. 'A' - 10 RET ; возврат Но можно было использовать и стек. Для этого существует две мнемоники: PUSH ss и POP ss. PUSH ss сохраняет в стеке рег. пару ss, а POP ss - вытаскивает. Для того, чтобы представить, как все это работает вообразим себе некий стакан, куда падают числа. Стакан - это наш стек, a числа - это значения рег. пар. В стек можно заносить больше, чем одну рег. пару, при этом, если мы сохранили три рег. пары, для того, чтобы вытащить первую нам не- обходимо вытащить две последние. То есть, принцип "первым вошел, последним вышел" - основной принцип машинного стека. Теперь перепишем нашу программку с использованием стека: ──┐ │ Пояснения: │ ╔═══════════════╣ Сохраняем в стеке регис- LD A,10 ║ LD A,10 ║ тровую пару 'AF', ведь мы LD B,25 ║ LD B,25 ║ можем сохранять там только PUSH AF ║ PUSH AF ║ пары. Вот, что происходит в ║ PUSH BC ║ стеке. ║ CALL PROGR ║ ║ POP BC ║ AF──┐ ║ POP AF ║ │ ║ ║ ║░▒▓1▓▒░║ ║ ║ ║ RET ║ │ │ │ │ ╚═══════════════╝ │ │ │ │ │ │ │░▒▓1▓▒░│ ▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀ Теперь сохраняем в стеке 'BC': BC──┐ │ PUSH BC ║▓▒░2░▒▓║ ║ ║ │ │ │ │ Выполняем подпрограмму CALL PROGR, │ │ │▓▒░2░▒▓│ вытаскиваем из стека 'BC': │░▒▓1▓▒░│ │░▒▓1▓▒░│ ▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀ POP BC Вытаскиваем из стека 'AF': ┌──AF │ POP AF ║ ║ ║▓▒░2░▒▓║ │ │ │ │ ┌──AF │▓▒░2░▒▓│ │ │ │ │░▒▓1▓▒░│ │░▒▓1▓▒░│ ║ ║ ║░▒▓1▓▒░║ ▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀ │ │ │ │ │ │ │ │ Обратите внимание, как работает │░▒▓1▓▒░│ │ │ принцип "LIFO" - "Last Input - First ▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀ Output ( "Последним пришел - первым вышел"). RET ╔═══════╗ │CALL ss│ Этими мнемониками мы уже пользовались раньше, но не │ RET │ вникали в сущность работы. Вот что происходит в ╚═══════╝ продессе выполнения этих команд: CALL - в стек заносится адрес следующей за CALL команды, по- том выполняется переход на адрес ss. Команда CALL может быть условной, при этом условие определяется так-же, как и в JP (JR) (CALL Z,ss ; CALL NZ,ss). RET - из стека извлекается число и происходит переход на этот адрес. Команда RET может быть условной, при этом условие определяется так-же как в JP (JR) и CALL (RET Z ; RET NZ). Из этого следует: для того, чтобы нормально возвратиться из подпрограммы, необходимо перед выходом сделать стек таким, каким он был при выполнении CALL. Например, нам необходимо занести в рег. 'B' число "3", а с помощью подпрограммы TEST, в рег. 'C' число, которое содержится в ячейке памяти 23560, при этом рег. 'A' должен остаться без изменений: ┌──────ВЕРНО:───────┐ ┌-----------------НЕВЕРНО:---------------┐ │ LD B,3 │ | LD B,3 | LD B,3 | │ CALL TEST │ | CALL TEST | CALL TEST | │ RET │ | RET | RET | │TEST PUSH AF │ |TEST PUSH AF |TEST LD A,(23560)| │ LD A,(23560)│ | LD A,(23560)| LD C,A | │ LD C,A │ | LD C,A | POP AF | │ POP AF │ | RET | RET | │ RET │ └----------------------------------------┘ └───────────────────┘ Данный пример наглядно показывает: чтобы операция со стеком имела смысл, положенные на стек значения следует впоследствии оттуда забрать. Т.е. каждая команда PUSH должна иметь соответст- вующую мнемонику POP и наоборот. Присмотритесь внимательно к обоим примерам, там где неправильно - отсутствует либо PUSH либо POP. Поэтому такие команды очень часто называют парными. ╔═══════╗ │ INC s │ Операция инкремента. Увеличивает рег. s или рег. пару ╚═══════╝ s на единицу.1 (s-A,B,C,D,E,HL,BC,DE и еще некоторые, о которых вам знать пока необязательно). ╔═══════╗ │ DEC s │ Операция декремента. Уменьшает рег. s или рег. пару s ╚═══════╝ на единицу. ╔═══════╗ │ LDI │ Очень полезная мнемоника процессора. После получения ╚═══════╝ этой команды компьютер действует по следующему алго- ритму: 1) Заносится число из памяти с адресом 'HL' в ячейку памяти с адресом 'DE' 2) Увеличивается 'HL' на 1 [ (HL) -> (DE) ] 3) Увеличивается 'DE' на 1 [ HL+1 -> HL ] 4) Уменьшается 'BC' на 1 [ DE+1 -> DE ] 5) Если BC=0, устанавлива- [ BC-1 -> BC ] ется флаг P/V, иначе он < BC=0 ? > сброшен ДА │ НЕТ ┌─────────┴──────────┐ [ (f) P/V=0 ] [ (f) P/V=1 ] ╔═══════╗ │ LDD │ Тоже самое, что и LDI, только в пунктах 2) и 3) рег. ╚═══════╝ пары не увеличиеаются, а уменьшаются. ╔═══════╗ │ LDIR │ Еще более полезная команда - пересылка блока памяти с ╚═══════╝ увеличением адресов. Вот ее алгоритм: 1) Заносится число из памяти с адресом 'HL' в ячейку памяти с адресом 'DE'. 2) Увеличивается 'HL' на 1 ┌────────┐ 3) Увеличивается 'DE' на 1 [ (HL) -> (DE) ] │ 4) Уменьшается 'BC' на 1 [ HL+1 -> HL ] │ 5) Если BC<>0, следует переход [ DE+1 -> DE ] │ на пункт 1 [ BC-1 -> BC ] │ < BC=0? > │ ├───НЕТ──┘ Д│А ┌-----------------------------┐ │ |В сущности LDIR соответствует| КОНЕЦ | следующей программке: | | | Проиллюстрируем практическое | LABEL LDI | применение LDIR. Например, нам | JR PE,LABEL | нужно вывести на экран область | RET | памяти длиной 6144 байт, └-----------------------------┘ начиная с 0: LD DE,0 ; ОТКУДА LD HL,16384 ; КУДА LD BC,6144 ; СКОЛЬКО LDIR RET Теперь предлагаю Вашему вниманию маленькую несложную програм- мку, действие которой, Вы увидите на экране после нажатия клавиш <6> или <7>. Постарайтесь понять, что она делает, объясните каждую мнемонику, потом перепишите ее сами, по памяти, составляя заново. IM 0 ; включаем опрос клавиатуры, устанав- EI ; ливаем режим, разрешаем прерывания. LD HL,5000 PUSH HL OPROS LD A,(23560) CP "6" JR Z,LEFT CP "7" JR Z,RIGHT CP " " JR Z,EXIT JR OPROS EXIT POP HL RET LEFT POP HL INC HL PUSH HL CALL PRINT JR OPROS RIGHT POP HL DEC HL PUSH HL CALL PRINT JR OPROS PRINT LD DE,#5800 LDIR RET ________________________________________________________________ Ну что-ж, я думаю на сегодня достаточно. Увидимся в следующем номере. Если при изучении данного материала у Вас возникнут ка- кие-либо вопросы, обращайтесь в редакцию "Полесья" либо найдите другой способ со мной связаться. До новых встреч.