Лекции Баулы (1110628), страница 3
Текст из файла (страница 3)
По своей архитектуре наша учебная машина очень похожа на первые ЭВМ, построенные в соответствии с принципами фон Неймана, например на отечественную ЭВМ СТРЕЛА, выпускавшуюся в средине прошлого века.
Примеры программ для УМ–3
Пример 1
Составим программу, которая реализует арифметический оператор присваивания
y := (x–1)2 mod (x+1)2.
Сначала необходимо решить, в каких ячейках памяти будут располагаться наши переменные x и y. Эта работа называется распределением памяти под хранение переменных. При программировании на Паскале эту работу выполняла за нас Паскаль-машина, когда видела описания переменных:
Var x,y: integer;
Теперь нам придётся распределять память самим. Сделаем естественное предположение, что наша программа будет занимать не более 100 ячеек памяти. Тогда начиная со 101 ячейки память будет свободна. Пусть для хранения значения переменной x мы выделим 101 ячейку, а переменной y – 102. Остальные переменные при необходимости будем размещать в последующих ячейках памяти. В приведенном примере нам понадобятся дополнительные (как говорят, рабочие) переменные r1 и r2, которые мы разместим в ячейках 103 и 104 соответственно.
При программировании на машинном языке, в отличие от Паскаля, у нас не будут существовать константы. Действительно, в какой бы ячейке мы не поместили значение константы, ничто не помешает нам записать в эту ячейку новое значение. Поэтому мы будем называть константами такие переменные, которые имеют начальное значение и которые мы не планируем изменять в ходе выполнения программы. Отсюда следует, что такие константы, как и переменные с начальным значением, следует располагать в тексте программы и загружать в память вместе с программой при нажатии кнопки ПУСК. Разумеется, такие переменные следует располагать в таком месте программы, чтобы устройство управления не начало бы выполнять их как команды. Чтобы избежать этого мы будем размещать их в конце программы, после команды СТОП.
Следует обратить внимание на тот факт, что изначально программист не знает, сколько ячеек в памяти будет занимать его программа. Поэтому адреса программы, ссылающиеся на константы и переменные с начальным значением, до завершения написания программы остаются незаполненными, и уже потом, разместив константы в памяти сразу же вслед за программой, следует указать их адреса в тексте программы. В приведённом примере те адреса программы, которые заполняются в последнюю очередь, будут обозначаться подчёркиванием.
Запись программы состоит из строк, каждая строка снабжается номером ячейки, куда будет помещаться это машинной слово (команда или переменная с начальным значением) при загрузке программы. Вслед за номером задаются все поля команды, затем программист может указать комментарий. Номера ячеек, кодов операций и адреса операндов будем записывать в десятичном виде, хоты первые программисты использовали для этого 8-ую или 16-ую системы счисления. Кроме того, так как числа неотличимы по внешнему виду от команд, то будем записывать их тоже в виде команд.
Текст нашей первой программы с комментариями приведён ниже.
№ | Команда | Комментарий | |||
001 | 06 | 101 | 001 | 000 | Ввод x |
2 | 12 | 103 | 101 | 009 | r1 := (x–1) |
3 | 13 | 103 | 103 | 103 | r1 := (x–1)2 |
4 | 11 | 104 | 101 | 009 | r2 := (x+1) |
5 | 13 | 104 | 104 | 104 | r2 := (x+1)2 |
6 | 24 | 102 | 103 | 104 | y := r1 mod r2 |
7 | 16 | 102 | 001 | 000 | Вывод y |
8 | 31 | 000 | 000 | 000 | Стоп |
9 | 00 | 000 | 000 | 001 | Целая константа 1 |
Теперь осталось поместить на устройство ввода два массива – саму программу (9 машинных слов) и число x (массив из одного элемента) и нажать кнопку ПУСК.
Пример 2
Составим теперь программу, реализующую условный оператор присваивания. Пусть переменная y принимает значение в зависимости от вводимой переменной x в соответствии с правилом:
В данном примере при записи программы на месте кода операции мы будем для удобства вместо числа указывать его мнемоническое обозначение. Разумеется, при реальной работе кто-то потом перед вводом программы должен будет заменить эти мнемонические обозначения на соответствующие числа.
Для определения того, является ли значение переменной x больше, меньше или равным константе 2, мы будем выполнять операцию вычитания x–2, получая в регистре w значение 0 при x=2, 1 при x<2 и 2 при x>2. При этом сам результат операции вычитания нам не нужен, но по нашему формату команд указание адреса ячейки для записи результата является обязательным. Для записи таких ненужных значений мы будем чаще всего использовать ячейку с номером 0. В соответствии с принципом однородности памяти, эта ячейка ничем не отличается от других, то есть доступна как для записи, так и для чтения данных. В некоторых реальных ЭВМ этот принцип нарушается: при считывании из этой ячейки всегда возвращался нуль, а запись в ячейку физически не осуществляется (на практике такой принцип работы удобнее).
Для хранения переменных x и y выделим ячейки 100 и 101 соответственно. Программист сам определяет порядок размещения в программе трёх ветвей нашего условного оператора присваивания. Мы будем сначала располагать вторую ветвь (x=2), затем первую (x<2), а потом третью (x>2).
№ | Команда | Комментарий | |||
001 | ВВЦ | 100 | 001 | 000 | Read(x) |
2 | СЛЦ | 101 | 100 | 011 | Y := x+2 |
3 | ВЧЦ | 000 | 100 | 011 | <000> := x–2; формирование w |
4 | УСЛ | 005 | 007 | 009 | If w=0 then goto 005 else if w=1 then goto 007 else goto 009 |
5 | ВЫЦ | 011 | 001 | 000 | Write(2) |
6 | СТОП | 000 | 000 | 000 | Конец работы |
7 | ВЫЦ | 101 | 001 | 000 | Write(y) |
8 | СТОП | 000 | 000 | 000 | Конец работы |
9 | ДЕЦ | 101 | 011 | 101 | y := 2 div y |
010 | БЕЗ | 000 | 007 | 000 | goto 007 |
1 | 00 | 000 | 000 | 002 | Целая константа 2 |
Обратите внимание, что константа 2 неотличима от команды пересылки содержимого второй ячейки памяти в нулевую ячейку, именно это и будет делаться, если это машинное слово будет выполняться УУ.
Пример 3
В качестве следующего примера напишем программу для вычисления начального отрезка гармонического ряда:
Для хранения переменных n, y и i выделим ячейки 100, 101 и 102 соответственно. В этом алгоритме мы реализуем цикл с предусловием, поэтому при вводе n<1 будет выдаваться нулевой результат.
№ | Команда | Комментарий | |||
001 | ВВЦ | 100 | 001 | 000 | Read(n) |
2 | ВЧВ | 101 | 101 | 101 | y := 0.0 |
3 | ПЕР | 102 | 000 | 013 | i := 1 |
4 | ВЧЦ | 000 | 102 | 100 | i := i–n; формирование w |
5 | УСЛ | 006 | 006 | 011 | If i>n then goto 011 |
6 | ВЕЩ | 000 | 000 | 102 | <000> := Real(i) |
7 | ДЕВ | 000 | 014 | 000 | <000> := 1.0/<000> |
8 | СЛВ | 101 | 101 | 000 | y := y+<000> |
9 | СЛЦ | 102 | 102 | 013 | i := i+1 |
010 | БЕЗ | 000 | 004 | 000 | Следующая итерация цикла |
1 | ВЫВ | 101 | 001 | 000 | Write(y) |
2 | СТОП | 000 | 000 | 000 | Стоп |
3 | 00 | 000 | 000 | 001 | Целая константа 1 |
4 | <1.0> | Вещественная константа 1.0 |
Сделаем некоторые замечания к этой программе. Так как в языке команд у нас нет команды деления целого числа на вещественное, то при вычислении величины 1.0/i нам пришлось отдельной командой преобразовать значение целой переменной i в вещественной значение. Обратите также внимание, что для нашей учебной машины мы пока не определили формат представления вещественных чисел, поэтому в ячейке с адресом 14 стоит пока условное обозначение константы 1.0, а не её машинное представление.
Пример 4
Пусть требуется написать программу для ввода массива x из 100 вещественных чисел и вычисления суммы всех элементов этого массива: