Данному образовательному сайту пришлось несколько раз менять свое имя. С 2022 года доступ к нему обеспечивается по URL
emc.orgfree.com

emc.km.ru (2001-2007) ==> educomp.org.ru (2007-2011) ==> educomp.runnet.ru (2011-2021) ==> emc.orgfree.com (2022-...)
Более подробно об истории сайта можно прочитать здесь.


Учебные модели компьютера



Модели (software):

"Е14" (parallel !!!)
"S9PU" (parallel)

Модели (hardware):






Награды сайта
Награды сайта 2005

Реализация циклов с переменным содержимым
при помощи соподпрограмм

Данный материал родился после прочтения в "ИНФО" статьи Ю.Гиматова [1]. Статья содержит целый ряд интересных идей и примеров, хотя некотрые из них применимы не всегда. В частности, самомодификация кода программ действительно встречается на практике редко, но отнюдь не по причине "недогадливости" программистов. Можно привести ряд серьезных возражений против такого приема: принципиальная невозможность работы формирующей себя описанным способом программы в ПЗУ, нарушение процесса проверки с помощью контрольной суммы, получение при определенных условиях неидентичных копий на магнитных носителях для одной и той же программы, наконец, значительное ухудшение "читаемости" программы, что в большинстве случаев нежелательно. Впрочем, приводимые аргументы направлены не столько на критику изложенных в [1] идей, сколько против их абсолютизации.

Легко получить компромиссное решение, которое, сохранив идею самомодифицирования, свободно от перечисленных недостатков. Для этого формировать изменяющуюся часть кода программы следует не внутри нее самой, а в некоторой рабочей области ОЗУ в виде подпрограммы, которая в дальнейшем вызывается обычным образом. Идея эта достаточно проста и в более детальном обсуждении не нуждается.

Существует и другой интересный способ реализации самомодифицирующейся программы: он далеко не так очевиден, но компактен и логически настолько красив, что, по-моему, вполне заслуживает отдельного рассмотрения на страницах журнала. Для реализации цикла, в теле которого при различных проходах выполняются различные действия, использована идея соподпрограмм. Подчеркну, что такая программная конструкция существует только для процессоров с системой команд типа "PDP-11" с их развитой системой адресации: в "INTEL"-процессорах и тем более в языках высокого уровня такая возможность не предусмотрена!

Что такое соподпрограмма? Это особый тип организации программы, когда две ее части ПО ОЧЕРЕДИ вызывают одна другую (см., например, [2]). Как известно, в классическом случае основная программа вызывает подпрограмму, но не наоборот. Поскольку обе соподпрограммы функционально равноправны, то инструкция возврата RET в них не используется: вместо нее одна соподпрограмма просто вызывает другую и наоборот. Любители аналогий здесь могут представить себе двух баскетболистов, перекидывающих друг другу мяч, но при этом по неизвестной причине всегда может бежать только спортсмен, владеющий мячом (другой же замирает в точке передачи мяча).

Инструкция вызова соподпрограммы имеет вид

JSR PC, @(SP)+
и кодируется всегда восьмеричным словом 004736. При выполнении этой команды процессор сначала извлекает из стека адрес возврата в альтернативную соподпрограмму (о чем говорит запись операнда в виде @(SP)+ ) и сохраняет в своей внутренней памяти. Затем, как и при обычном вызове подпрограммы, в стек аппаратно заностится адрес следующей за JSR PC инструкции и осуществляется переход по считанному ранее адресу. Нетрудно заметить, что рассматриваемая модификация команды отличается от "классического" применения инструкции JSR только тем, что адрес начала подпрограммы берется из стека. Такой прием как раз и позволяет передать управление по адресу альтернативной соподпрограммы и сохранить в стеке адрес для последующего возврата оттуда. Для обратного перехода в исходную соподпрограмму достаточно проделать в точности те же самые действия, т.е. снова выполнить ту же инструкцию процессора.

Естественно, что для обеспечения нормальной взаимосвязи соподпрограмм необходимо перед их использованием входной адрес второй соподпрограммы занести в стек. После окончания работы с соподпрограммами ненужный "этаж" стека следует освободить.

Описанная программная конструкция применяется довольно редко и кажется достаточно экзотической. Тем не менее, на ее основе удается очень легко реализовать цикл с модифицируемыми командами: тело цикло в этом случае служит одной соподпрограммой, а в роли другой выступают подставляемые в него по очереди команды, разделенные инструкциями JSR PC, @(SP)+ .

Для иллюстрации рассмотрим следующую наглядную задачу, аналогичную примеру 2 в статье [1]. Пусть нам необходимо обойти все точки расположенного на экране прямоугольника и закрасить их. Сделаем это, однако, не традиционным построчным сканированием, а более эффектным обходом по "закручивающейся спирали", т.е. сначала нарисуем внешнюю границу прямоугольника, затем границу прямоугольника, оставшегося незакрашенным и т.д.

Обозначим координаты левого верхнего угла прямоугольника X1 и Y1, а правого нижнего - X2 и Y2. Тогда

X2 = X1 + DX, Y2 = Y1 + DY,
где DX и DY - длины сторон прямоугольника. Входные параметры X1 и X2 поместим в R1 и R2, а DX и DY - в R4 и R5 соответтвенно. При работе программы в R3 будет храниться адрес небольшого массива из четырех чисел: X1, Y1, X2, Y2. Чтобы не использовать конкретные адреса памяти, массив разместим в стековой области (правда, задавать его при этом придется в обратном порядке!). Регистр R4 будет служить счетчиком точек.

Текст программы на ассемблере "БК-0010" и результирующие коды с подробными комментарями приведены в приложении. Основная часть программы состоит из подготовительных вычислений и короткого основного цикла, начинающегося после метки L2. Для каждой точки вызывается стандартная процедура закраски EMT 30 (подробности ее работы описаны, например, в [3]) и происходит обращение к соподпрограмме NEXTXY, возвращающей в R1 и R2 координаты следующей точки спирали. NEXTXY, пожалуй, является наиболее интересной частью программы. Первоначально вход в нее соответствует метке M2, адрес которой предусмотрительно заранее был занесен в стек. Пока текущее значение X не совпадет с X2, новая точка получается увеличением на единицу абсциссы, хранящейся в регистре R1. Когда X=X2, вход в соподпрограмму перемещается на метку M4, что при последующих обращениях приводит к сохранению прежнего значения X и инкрементированию Y вплоть до точки Y=Y2. Аналогично происходит движение и по двум оставшимся сторонам прямоугольника, с той лишь разницей, что координаты при этом уменьшаются. После того, как нарисован полный прямоугольник, его вершины сдвигаются "внутрь" и процедура повторяется.

Корректность окончания процесса существенно зависит от соотношения сторон прямоугольника. Если DY > DX, то в случае нечетного количества точек по координате X остается незакрашенная вертикальная линия и общий алгоритм закраски нарушается. Чтобы исправить положение, в конце NEXTXY ставится специальная проверка, которая в случае X1=X2 гарантирует нормальное завершение.

Соподпрограмма NEXTXY может быть использована для смещения по спирали и в более сложных случаях, причем ее вызов можно помещать в нескольких местах программы. С ее помощью, например, легко определить координаты точки, расположенной на середине спирали и т.п.

Приводимая программа является полностью переносимой, т.е. может работать без изменений при загрузке с произвольного адреса ОЗУ. Для заданных в ее тексте параметров, на экране рисуется прямоугольник с вершинами в точках (8,8) и (64,128). Разумеется, заменив константы в первых четырех командах, размеры и местоположение изображения легко изменить.

Видеоэффект закраски по спирали любопытен и сам по себе, так что его можно использовать как элемент более сложных программ, в том числе и на Бейсике.


Л И Т Е Р А Т У Р А
  1. Ю.Гиматов. Самомодифицирующиеся программы.- ИНФО, 1992, N2, с.91-94
  2. В.Лин. PDP-11 и VAX-11. Архитектура ЭВМ и программирование на языке ассемблера.- М: Радио и связь, 1989.- 320 с. (см. с. 101-102)
  3. Ю.Зальцман. Архитектура и ассемблер БК. ИНФО, 1991, N4, с.53-58


Текст программы на ассемблере:

;Задать параметры

     MOV  #10,    R1   ;  012 701   10  X = X1
     MOV  #10,    R2   ;  012 702   10  Y = Y1
     MOV #100,    R4   ;  012 704  100  DX
     MOV #200,    R5   ;  012 705  200  DY

;Вычислить координаты вершин и поместить в стек

     MOV   R2,  -(SP)  ;  010 246
     ADD   R5,   (SP)  ;  060 516       Y2 = Y1 + DY
     MOV   R1,  -(SP)  ;  010 146
     ADD   R4,   (SP)  ;  060 416       X2 = X1 + DX
     MOV   R2,  -(SP)  ;  010 246       Y1
     MOV   R1,  -(SP)  ;  010 146       X1
     MOV   SP,    R3   ;  010 603       адрес таблицы в R3

;Вычислить количество точек и поместить в R4

     INC   R4          ;  005 204       (на отрезке К единиц
     INC   R5          ;  005 205        находится К+1 точка)
     MOV   R4,    R0   ;  010 400
     CLR   R4          ;  005 004
L1:  ADD   R5,    R4   ;  060 504       R5 * R4 ==> R4
     SOB   R0,    L1   ;  077 002

;Определить адрес входа в соподпрограмму NEXTXY и записать в стек

     MOV   PC,    R0   ;  010 700       адрес метки M2
     ADD  #30,    R0   ;  062 700   30  (вход в соп/п NEXTXY)
     MOV   R0,  -(SP)  ;  010 046       в стек

;Цикл вывода точек на экран

L2:  MOV   #1,    R1   ;  012 701    1  зажечь точку экрана с
     EMT   30          ;  104 030       координатами в R1 и R2
     JSR   PC,  @(SP)+ ;  004 736       вызов соп/п NEXTXY
     SOB   R4,    L2   ;  077 405
     ADD  #12,    SP   ;  062 706   12  восстановить SP
     HALT              ;  000 000       останов

;                  Соподпрограмма NEXTXY
;Выдает координаты следующей точки на спирали

M1:  JSR   PC,  @(SP)+ ;  004 736       вызов соп/п осн. цикла
;первый вход
M2:  INC   R1          ;  005 201       X = X + 1
     CMP   R1,  4(R3)  ;  020 163    4  сравнить X и X2
     BNE   M1          ;  001 373       цикл до X2

M3:  JSR   PC,  @(SP)+ ;  004 736
M4:  INC   R2          ;  005 202       движение от Y1 к Y2
     CMP   R2,  6(R3)  ;  020 263    6
     BNE   M3          ;  001 373

M5:  JSR   PC,  @(SP)+ ;  004 736
     DEC   R1          ;  005 301       движение от X2 к X1
     CMP   R1,   (R3)  ;  020 113
     BNE   M5          ;  001 374

M6:  JSR   PC,  @(SP)+ ;  004 736
     DEC   R2          ;  005 302       движение от Y2 к Y1
     CMP   R2,  2(R3)  ;  020 263    2
     BNE   M6          ;  001 373

     MOV   R3,    R0   ;  010 300       коррекция вершин
     INC  (R0)+        ;  005 220
     INC  (R0)+        ;  005 220       прямоугольника
     DEC  (R0)+        ;  005 320
     DEC  (R0)+        ;  005 320       и переход к левой
     INC   R1          ;  005 201       вершине следующего
     INC   R2          ;  005 202       прямоугольника
     CMP (R3),  4(R3)  ;  021 363    4  X1=X2: осталась незакра-
     BEQ   M4          ;  001 751       шена вертикальная линия
     BR    M1          ;  000 742


© Е.А.Еремин, 1994
Статья:
Еремин Е.А. Реализация циклов с переменным содержимым при помощи соподпрограмм. Персональный компьютер БК-0010 - БК-0011М (приложение к журналу "Информатика и образование"), 1994, N 4, с.67-70.


Автор сайта - Евгений Александрович Еремин (Пермский государственный педагогический университет). e_eremin@yahoo.com


Free Web Hosting