В мир информатики # 81 (16—30 ноября).
Моделирование

Моделирование работы ЭВМ с помощью программы
Microsoft Excel

Окончание. Начало см. “В мир информатики” № 80 (“Информатика” № 21/2006)

Е.А. Еремин,
г. Пермь

Перейдем к непосредственному моделированию описанной в первой части статьи учебной ЭВМ “Кроха”.

Прежде всего подчеркнем, что для реализации даже простейшей автоматически работающей модели наличие внутреннего языка программирования является обязательным; в частности, язык VBA (Visual Basic for Application) в Excel содержит более чем достаточное для наших целей количество разнообразных средств.

И еще одно важное для понимания дальнейшего замечание. Хотя оригинальная “Кроха” для наглядности описывается в двоичной системе (см. [1]), при моделировании в Excel удобнее воспользоваться восьмеричной; в последнем случае команда представляет собой четыре восьмеричных цифры, причем каждая из них соответствует отдельной части команды (см. структуру трехадресной команды в первой части статьи). С точки зрения простоты представления команд это обстоятельство необычайно удобно.

Реализацию “Крохи” в клетках электронной таблицы начнем с планирования назначения ячеек. Предусмотрев место под заголовки и тексты пояснений, разместим ячейки памяти нашей ЭВМ в блоке ячеек B4:E11. Количество строк очевидным образом равняется числу ячеек, причем любая из них делится на четыре части: код операции и три адреса. Таким образом, при наборе команды каждая из четырех цифр заносится в отдельную клетку, как это сделано на рис. 1.

Рядом с “ОЗУ” в группе клеток F4:G6 разместим дисплей, отображающий содержимое адресов ОЗУ в восьмеричной и десятичной системах счисления. Наконец, в строках 13–15 расположим описанные ранее регистры АЛУ и УУ. Все остальные строки есть просто справочный материал.

Картину завершают две кнопки управления — Start и Step, первая из которых служит для запуска исполнения всей программы, а вторая — только одной ее очередной команды. Последняя возможность позволяет производить пошаговое выполнение программы, что особенно удобно при отладке.

Рис. 1. Общий вид ЭВМ “Кроха” в электронной таблице

После выделения цветом фона ячеек, соответствующих различным устройствам машины, и нанесения необходимых подписей перейдем к написанию программы. Ее назначение состоит в том, чтобы реализовать исполнение основного алгоритма “Крохи”. Полный листинг программы приводится ниже.

Rem "Расположение" устройств машины в таблице

Rem ОЗУ

Const Mem_cell As Integer = 4

Rem Счетчик команд

Const IP_cell As Integer = 13

Rem Регистр команд

Const RK_cell As Integer = IP_cell + 1

Rem Сумматор

Const SM_cell As Integer = IP_cell + 2

Rem Дисплей

Const DI_cell As Integer = Mem_cell

Rem Регистр команд

Const Left_cell As Integer = 2

Rem Сообщение об ошибке

Const Msg_cell As Integer = DI_cell + 3

Const Msg_left As Integer = Left_cell + 4

Rem Переменные

Dim doing As Boolean 'True - программа

выполняется, False - стоп

Rem **ВСПОМОГАТЕЛЬНЫЕ ПРОЦЕДУРЫ И ФУНКЦИИ**

Function Oct4(n) As String

Rem Функция возвращает 4 восьмеричные цифры

Rem числа n (например, "0032")

Oct4 = Right$("000" + Oct$(n), 4)

End Function

Function Get_memory_digit(adr, n) As Integer

Rem Возвращает из заданной ячейки ОЗУ

Rem заданную восьмеричную цифру

Rem Цифры, как и ячейки ОЗУ, нумеруются с 0 (до 3)

Get_memory_digit = Cells(Mem_cell + adr, Left_cell + n)

End Function

Function Get_memory_cell(adr) As Integer

Rem Преобразует ячейку ОЗУ в десятичное число

Rem из восьмеричной системы счисления

Dim i, m, r As Integer

r = 0: m = 1

For i = 3 To 0 Step - 1

r = r + m * Get_memory_digit(adr, i)

m = m * 8

Next i

Get_memory_cell = r

End Function

Sub Put_memory_cell(adr, d$)

Rem Функция Записывает в ОЗУ по адресу adr

Rem четыре восьмеричные цифры из строки d$

Dim i As Integer

Rem Запишем в ОЗУ отдельные цифры

For i = 0 To 3

Cells(Mem_cell + adr, Left_cell + i) = Mid$(d$, i + 1, 1)

Next i

End Sub

Sub Copy_Cells(d, s)

Rem Процедура копирует четыре восьмеричные цифры

Dim i As Integer

For i = 0 To 3

Cells(d, Left_cell + i) = Cells(s, Left_cell + i)

Next i

End Sub

Sub Avost(m$) 'Аварийный останов

doing = False

'Вывод сообщения

Cells(Msg_cell, Msg_left) = m$

End Sub

Rem ** ОСНОВНЫЕ ПРОЦЕДУРЫ И ФУНКЦИИ **

Sub Do_command()

Rem Процедура выполняет одну очередную команду

Dim i, a, d, IP, KOP, OP1, OP2, RE As Integer, s As String

Rem Очистка ошибки

Cells(Msg_cell, Msg_left) = ""

Rem Извлечение команды из ОЗУ в РК

IP = Cells(IP_cell, Left_cell)

Rem Копирование команды из ОЗУ в РК

Call Copy_Cells(RK_cell, Mem_cell + IP)

Rem Увеличение СК

i = Cells(IP_cell, Left_cell): i = i + 1

If i > 7 Then i = 0

Cells(IP_cell, Left_cell) = i

Rem Операция

KOP = Cells(RK_cell, Left_cell)

OP1 = Get_memory_cell(Cells(RK_cell, Left_cell + 1))

OP2 = Get_memory_cell(Cells(RK_cell, Left_cell + 2))

On KOP + 1 GoSub c_mov, c_add, c_div, c_sub, c_beq, c_mul, c_bgt, c_hlt

If (KOP > 3) And (KOP <> 5) Then Exit Sub 'нет записи результата

Rem Проверка и запись результата

GoSub Overflow 'Проверить переполнение

Rem Занести результат в десятичном виде

Cells(SM_cell, Left_cell + 3) = RE

Rem Перевести результат в восьмеричную с.с.

s = oct4(RE)

Rem адрес для записи из РК (Aдрес 3)

a = Cells(RK_cell, Left_cell + 3)

Rem Запись результата в ОЗУ

Call Put_memory_cell(a, s)

Rem 'Скопировать число из ОЗУ в сумматор

Call Copy_Cells(SM_cell, Mem_cell + a)

Exit Sub

overflow: 'Проверка переполнения (результат < 4096)

If RE > 4095 Then

RE = 4095

Call Avost("> 4095")

End If

Return

Rem Система команд

c_mov: RE = OP1: Return

c_add: RE = OP1 + OP2: Return

c_sub: RE = Abs(OP1 - OP2): Return

c_mul: RE = OP1 * OP2: Return

c_div:

If OP2 <> 0 Then

RE = Int(OP1 / OP2)

Else: Call Avost("/ 0")

End If

Return

c_beq: If OP1 = OP2 Then

Cells(IP_cell, 2) = Cells(RK_cell, 5)

End If

Return

c_bgt: If OP1 > OP2 Then

Cells(IP_cell, 2) = Cells(RK_cell, 5)

End If

Return

c_hlt: doing = False

For i = 0 To 2 'вывод трех чисел

REM Адрес ОЗУ (берем из РК)

a = Cells(RK_cell, 3 + i)

REM Число

d = Get_memory_cell(a)

REM Десятичный вывод

Cells(DI_cell + i, Left_cell + 5) = d

REM Восьмеричный вывод

Cells(DI_cell + i, Left_cell + 4) = oct4(d)

Next i

Return

End Sub

Sub Do_program()

REM Процедура выполняет всю программу

REM Очистка СК

Cells(IP_cell, Left_cell) = 0

doing = True

Do

Do_command

Loop Until Not doing

End Sub

Объясним основные моменты “устройства” приведенной программы.

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

Далее в листинге следуют вспомогательные процедуры и функции:

· функция Oct4 преобразует к стандартному виду произвольное восьмеричное число, добавляя перед сохранением в ОЗУ необходимое количество незначащих нулей слева;

· функция Get_memory_digit возвращает из заданной ячейки ОЗУ заданную восьмеричную цифру;

· функция Get_memory_cell, используя предыдущую функцию, считывает из ОЗУ десятичное число; фактически она производит перевод числа из восьмеричной системы счисления в десятичную;

· процедура Put_memory_cell заносит в клетки таблицы, относящиеся к заданной ячейке ОЗУ, восьмеричное число из строковой переменной;

· процедура Copy_cells копирует четырехзначное восьмеричное число, которое занимает четыре смежные горизонтальные ячейки, из одной строки таблицы в другую;

· наконец, процедура Avost обслуживает аварийный останов в случаях деления на ноль или переполнения.

Собственно логика функционирования учебной ЭВМ “Кроха” сосредоточена в двух основных процедурах, каждая из которых связана со своей кнопкой. Первая — Do_command — выполняет одну очередную команду (процедура вызывается по щелчку на кнопке Step). Вторая — Do_program — организует полное выполнение программы, производя все подготовительные действия и циклически вызывая предыдущую процедуру. Прекращение выполнения обеспечивается установкой у переменной doing значения False. Данное действие обеспечивается внутри процедуры do_command по команде “Стоп” (в программе — по метке c_hlt) или в случаях аварийных ситуаций типа деления на ноль.

С точки зрения целей нашего моделирования следует подчеркнуть, что процедура do_command отчетливо повторяет основной алгоритм работы процессора, описанный в первой части статьи. Используя имеющиеся в тексте программы комментарии, легко проследить основные этапы выполнения команды: считывание очередной инструкции, увеличение счетчика команд и выполнение выбранной в регистр команд операции. Последний этап, в свою очередь, делится на извлечение адресов Aдрес 1 и Aдрес 23, их обработке согласно коду операции и, если выбрана одна из арифметических инструкций или перепись, записи полученного результата. Команды перехода и остановки имеют определенную специфику выполнения: первые проверяют требуемое условие и в случае его выполнения заносят в счетчик величину Aдрес 3 команды, а инструкция останова обслуживает вывод всех трех своих адресов на дисплей. Таким образом, все разнообразие поведения при выполнении различных команд “Крохи” обеспечивается оператором выбора On KOP + 1 GoSub …, где переменная KOP есть не что иное, как код исполняемой инструкции.

Учитывая подробные комментарии, читателям нетрудно будет разобраться в приведенном листинге.

Остается только ввести полученную программу. Для этого сначала создадим самый первый макрос, воспользовавшись меню Сервис | Макрос | Макросы… После этого внимательным образом наберем весь текст программы (напомним читателям, что в любой момент можно вернуться в редактор языка Visual Basic, нажимая сочетание клавиш Alt и F11).

Наконец, последнее, что нам предстоит сделать, — это связать управляющие кнопки с соответствующими процедурами (макросами). Учебная ЭВМ “Кроха” готова!

Для тестирования введем программу вычисления факториала, которая была разобрана ранее (перед вводом полезно еще раз внимательно рассмотреть расположение информации в ячейках таблицы на рис. 1). Нажмем кнопку Start и убедимся в том, что получился правильный результат.

Итак, пользуясь табличным процессором Microsoft Excel, мы смоделировали работу ЭВМ. Остается внимательно пронаблюдать за тем, как “Кроха” выполняет программу, обратив внимание на следующие моменты:

· каким образом выполняется линейный участок программы, команды которого расположены в памяти последовательно (практическая реализация принципа программного управления);

· как реализуется условный переход, благодаря которому возможны разветвляющиеся и циклические программы;

· любая инструкция ЭВМ есть указание на действия, которые необходимо сделать над указанной в ней информацией; команды максимально универсальны и их набор ограничен;

· обращение к информации в ОЗУ происходит по адресам (принцип адресности);

· программы и данные хранятся в общей памяти и с точки зрения хранения не имеют принципиальных различий (принцип хранимой программы);

· диапазон допустимых данных в машине ограничен; он определяется разрядностью, при превышении которой происходит особая ситуация — переполнение.

Таким образом, мы видим, что изучение описанной в статье задачи позволяет проиллюстрировать наиболее фундаментальные принципы устройства современных компьютеров. Учитывая простоту (даже, можно сказать, примитивность) модели “Кроха”, такой результат для некоторых читателей может оказаться неочевидным.

Литература

1. Основы информатики и вычислительной техники: Пробное учебное пособие для средних учебных заведений. / А.Г. Гейн, В.Г. Житомирский, Е.В. Линецкий и др. Свердловск: Изд-во Уральского ун-та, 1989.