Материал: Лампанель

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

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

Подпрограммы – это вспомогательные алгоритмы, которые можно вызывать по имени. В языке ассемблера имя подпрограммы – это метка. Для вызова подпрограммы используется команда

call метка

Подпрограмма должна заканчиваться командой возврата из подпрограммы

ret

Подпрограммы располагаются в программе ниже основной программы, после команды stop.

Пример программы, которая использует подпрограмму divMod для деления с остатком:

ассемблер

псевдокод

mov 1234, R0

mov 10, R1

call divMod

stop

divMod:

mov R0, R2

div R1, R0

mul R0, R1

sub R1, R2

mov R2, R1

ret

R0:= 123416

R1:= 1016

вызвать divMod

стоп

R2:= R0

R0:= R0 div R1

R1:= R1 * R0

R2:= R2 – R1

R1:= R2

возврат

Чтобы при отладке выполнять по шагам не только основную программу, но и подпрограмму, при выполнении команды call нужно нажать не F8, а F7.

Работа со стеком

Стек – это структура типа LIFO (англ. Last InFirst Out, последним пришел – первым ушел). В современных компьютерах стек размещается в памяти, специальный регистр SP (англ. stack pointer) указывает на начало стека. Для работы со стеком используются всего две команды:

ассемблер

псевдокод

push R0

pop R0

сохранить R0 в стеке

«снять» данные с вершины стека в R0

Конечно, сохранять в стеке можно не только R0, но и другие регистры.

Стек используется:

  • для временного хранения данных

  • для хранения адресов возврата из подпрограмм

  • для размещения локальных переменных подпрограмм

Пример программы (обмен значений регистров R0 и R1):

ассемблер

псевдокод

push R0

push R1

pop R0

pop R1

R0 – в стек

R1 – в стек

со стека – в R0 (старое значение R1)

со стека – в R1 (старое значение R0)

Если подпрограмма использует какой-то регистр, которые не содержит исходные данные и не предназначен для записи результата, она должна сохранить его стеке при входе и восстановить старое значение из стека при выходе. Например:

ассемблер

псевдокод

proc:

push R0

...

pop R0

ret

начало подпрограммы

R0 – в стек

основное тело подпрограммы

со стека – в R0 (старое значение R0)

возврат из подпрограммы

Заметьте, что подпрограмма, приведенная в предыдущем пункте, не совсем грамотно написана – она не сохраняет значение регистра R2, хотя «портит» его во время работы.

Вызов подпрограмм из пзу

ПЗУ в данной модели компьютера – это набор подпрограмм, каждая из которых заканчивается командой ret. Всего в ПЗУ может быть до 256 подпрограмм.

ПЗУ загружается при запуске тренажёра «ЛамПанель» из файла lampanel.rom, который должен находиться в том же каталоге, что и сама программа. Это обычный текстовый файл, который можно редактировать в редакторах типа Блокнота (если, конечно, вы понимаете, что вы делаете). В настоящей версии в ПЗУ включены следующие подпрограммы:

Номер

Описание

0

Очистить все порты панели (выключить все лампочки).

1

Установить в FF16 все порты панели (включить все лампочки).

2

Записать значение R0 во все порты панели.

3

Прокрутить изображение на панели вниз.

4

Прокрутить изображение на панели вверх.

5

Вывести на панель массив данных, адрес которого находится в R0.

6

Выполнить инверсию экрана (применить NOT).

7

Операция «И» со всеми портами (R1 – маска).

8

Операция «ИЛИ» со всеми портами (R1 – маска).

9

Операция «исключающее ИЛИ» со всеми портами (R1 – маска).

A16

Логический сдвиг влево всех портов (R1 – величина сдвига).

B16

Логический сдвиг вправо всех портов (R1 – величина сдвига).

C16

Циклический сдвиг влево всех портов (R1 – величина сдвига).

D16

Циклический сдвиг вправо всех портов (R1 – величина сдвига).

E16

Арифметический сдвиг вправо всех портов (R1 – величина сдвига).

F16

Деление с остатком (R0:=R0 div R1, R1:=R0 mod R1).

1016

Вывод цифры на экран (R0 – цифра, R1 – позиция, от 0 до 2)

1116

Вывод числа из R0 на экран (R1 – система счисления, от 2 до 16).

1216

Вывод числа из R0 на экран в десятичной системе счисления.

1316

Вывод числа из R0 на экран в шестнадцатеричной системе счисления.

Просмотреть содержимое ПЗУ можно с помощью пункта меню «Программа-Просмотр ПЗУ» или кнопки на панели инструментов. Выделив какую-нибудь строчку в левой части окна, мы увидим справа текст выбранной подпрограммы и ее коды:

Для вызова подпрограмм из ПЗУ нужно использовать команду

system номер_подпрограммы

Пример программы:

ассемблер

псевдокод

system 0

system 1

mov 123, R0

system 12

system 6

mov 1, R1

system A

system B

system 13

stop

выключить панель

включить все лампочки

R0:= 123­16

вывести R0 в десятичной системе

инверсия

R1:= 1 ; величина сдвига

сдвиг экрана влево

сдвиг экрана вправо

вывести R0 в шестнадцатеричной системе

стоп

Байтовые команды

Все рассмотренные выше команды работают с 16-битными данными (словами). Часто, например, при обработке текстов, нужно использовать однобайтные данные. Для этого предназначены следующие команды, которые полностью аналогичны соответствующим командам без буквы «b» (от англ. byte) на конце:

команда

значение

movb

cmpb

shlb

shrb

sarb

rolb

rorb

rclb

rcrb

копирование байта

сравнение двух байтов

логический сдвиг влево

логический сдвиг вправо

арифметический сдвиг вправо

циклический сдвиг влево

циклический сдвиг вправо

циклический сдвиг влево через бит переноса

циклический сдвиг вправо через бит переноса

Команда movb очищает старший байт регистра, в который копируются данные. Например,

ассемблер

псевдокод

mov 1234, R0

movb 12, R0

R0:= 123416

R0:= 1216

Остальные команды никак не изменяют старший байт регистра-приемника.

Существует специальная команда для обмена старшего и младшего байтов слова:

swapb регистр

Пример программы:

ассемблер

псевдокод

mov 1234, R0

swapb R0

R0:= 123416

R0:= 341216

Работа с данными

Согласно принципу однородности памяти фон Неймана, данные размещаются в той же области памяти, что и программа (обычно сразу после команды stop).

В тренажере «ЛамПанель» данные – это 16-битные слова (вводятся как числа в шестнадцатеричной системе счисления) или символьные строки, заключенные в двойные кавычки. Для размещения данных в памяти применяется команда data. Например:

... ; основная программа

stop

ddd: ; метка начала блока данных

data 1234 ; слово 123416

data 5678 ; слово 567816

data "Ехал Грека через реку" ; строка

Для того, чтобы работать с этими данными, нужно как-то к ним обратиться. Для этого используется косвенная адресация – в регистре находятся не сами данные, а их адрес в памяти. Рассмотрим пример:

ассемблер

псевдокод

mov @ddd, R0

swapb (R0)

add 2, R0

swapb (R0)

stop

ddd:

data 1234

data 5678

R0:= адрес метки ddd

переставить байты слова под адресу ddd

увеличить адрес на 2 (байта)

переставить байты слова под адресу ddd+2

стоп

начало блока данных

здесь будет 341216

здесь будет 785616

Запись @метка означает «адрес метки». Запись (R0) означает «данные, адрес которых находится в R0» – это и есть косвенная адресация.

Косвенную адресацию можно использовать и в других командах, работающих с регистрами.

Обработка массивов

Пусть в блоке данных, который начинается на метке ddd, записан массив, который нужно обработать в цикле. В этом случае удобно использовать косвенную адресацию с автоматическим увеличением адреса. Запись «(R0)+» означает «работать с данными, адрес которых находится в R0, и после выполнения операции увеличить R0». Если команда работает со словом, R0 увеличится на 2, а если с байтом – на 1.

Пример программы:

ассемблер

псевдокод

mov @ddd, R0

mov 3, R1

loop:

swapb (R0)+

sub 1, R1

jnz loop

stop

ddd:

data 1234

data 5678

data 9ABC

R0:= адрес метки ddd

записать в R1 количество шагов цикла

начало цикла

переставить байты слова под адресу из R0

уменьшить счетчик оставшихся шагов

если счетчик не ноль, перейти в начало цикла

стоп

начало блока данных

здесь будет 341216

здесь будет 785616

здесь будет BC9A16

Пример программы обработки байтов:

ассемблер

псевдокод

mov @ddd, R0

loop:

movb (r0),r1

or 20,r1

movb r1,(r0)+

cmpb 0,(r0)

jnz loop

stop

ddd:

data "ABCD"

R0:= адрес метки ddd

начало цикла

R1:= байт слова под адресу из R0

из заглавной буквы сделать строчную

записать результат в память

сравнить код следующего байта с 0

если не ноль, перейти в начало цикла

стоп

начало блока данных

здесь будет "abcd"