Маленький человеческий компьютер

редактировать

Маленький человеческий компьютер (LMC ) - это обучающая модель компьютера, созданного доктором Стюартом Мэдником в 1965 году. LMC обычно используется для обучения студентов, потому что он моделирует простую архитектуру фон Неймана компьютер, обладающий всеми основными функциями современного компьютера. Его можно запрограммировать в машинном коде (хотя и в десятичном, а не в двоичном) или ассемблерном коде.

Модель LMC основана на концепции маленького человечка, запертого в закрытой почтовой комнате (аналог компьютера в этом сценарий). В одном конце комнаты находится 100 почтовых ящиков (память ), пронумерованных от 0 до 99, каждый из которых может содержать 3-значные инструкции или данные (от 000 до 999). Кроме того, на другом конце есть два почтовых ящика, помеченных INBOX и OUTBOX, которые используются для приема и вывода данных. В центре комнаты находится рабочая зона, содержащая простой двухфункциональный калькулятор (сложение и вычитание), известный как Accumulator, и сбрасываемый счетчик, известный как Program Counter. Счетчик программ содержит адрес следующей инструкции, которую будет выполнять Маленький Человек. Этот Программный Счетчик обычно увеличивается на 1 после выполнения каждой инструкции, что позволяет Маленькому Человеку работать с программой последовательно. Инструкции ветвления позволяют включать в программу итерации (циклы) и условные структуры программирования. Последнее достигается путем установки Программного Счетчика на непоследовательный адрес памяти, если выполняется определенное условие (обычно значение, хранящееся в аккумуляторе, равно нулю или положительно).

Как указано в архитектуре фон Неймана, каждый почтовый ящик (обозначающий уникальную ячейку памяти) содержит как инструкции, так и данные. Поэтому необходимо следить за тем, чтобы программный счетчик не достиг адреса памяти, содержащей данные, иначе Маленький Человек попытается обработать это как инструкцию. Этим можно воспользоваться, написав инструкции в почтовые ящики, которые должны интерпретироваться как код, для создания самомодифицирующегося кода. Чтобы использовать ЛКМ, пользователь загружает данные в почтовые ящики, а затем сигнализирует Маленькому Человеку начать выполнение, начиная с инструкции, хранящейся по адресу памяти 0. Сброс счетчика программы на ноль эффективно перезапускает программу, хотя и в потенциально другом состоянии.

Содержание
  • 1 Цикл выполнения
  • 2 команды
    • 2.1 Инструкции
    • 2.2 Примеры
      • 2.2.1 Использование цифровых кодов инструкций
      • 2.2.2 Использование мнемоники и меток
  • 3 метки
    • 3.1 Пример
  • 4 См. Также
  • 5 Ссылки
  • 6 Внешние ссылки
    • 6.1 Симуляторы
Цикл выполнения

Чтобы выполнить программу, человечек выполняет следующие шаги:

  1. Проверьте счетчик программ на номер почтового ящика, который содержит инструкцию программы (т. Е. Ноль в начале программы).
  2. Получить инструкцию из почтового ящика с этим номером. Каждая инструкция содержит два поля: код операции (указывающий на выполняемую операцию) и поле адреса (указывающее, где найти данные для выполнения операции).
  3. Увеличить счетчик программы (чтобы он содержал почтовый ящик номер следующей инструкции)
  4. Расшифровать инструкцию. Если инструкция использует данные, хранящиеся в другом почтовом ящике, используйте поле адреса, чтобы найти номер почтового ящика для данных, с которыми он будет работать, например 'получить данные из почтового ящика 42')
  5. Извлечь данные (из входа, накопителя или почтового ящика с адресом, определенным на шаге 4)
  6. Выполнить инструкцию на основе заданного кода операции
  7. Разветвите или сохраните результат (в выводе, накопителе или почтовом ящике с адресом, определенным на шаге 4)
  8. Вернитесь к счетчику программ, чтобы повторить цикл или остановить
Команды

Хотя LMC действительно отражает реальную работу двоичных процессоров, была выбрана простота десятичных чисел, чтобы минимизировать сложность для студентов, которым может быть неудобно работать в двоичном формате / шестнадцатеричный.

Инструкции

Некоторые симуляторы LMC программируются напрямую с использованием 3-значных числовых команд, а некоторые используют трехбуквенные мнемонические коды и метки. В любом случае набор инструкций намеренно очень ограничен (обычно около десяти инструкций) для упрощения понимания. Если LMC использует мнемонические коды и метки, то они преобразуются в 3-значные числовые инструкции при сборке программы.

В таблице ниже показан типичный набор числовых команд и эквивалентные мнемонические коды.

Инструкции
Числовой кодМнемонический кодИнструкцияОписание
1xxADDADDДобавьте значение, хранящееся в почтовом ящике xx, к любому значению, которое в данный момент находится в аккумуляторе (калькуляторе).
Примечание: содержимое почтового ящика не изменяется, и действия аккумулятора (калькулятора) не определены для инструкций добавления, которые вызывают суммы, превышающие 3 цифры. Аналогично SUBTRACT, можно установить отрицательный флаг при переполнении.
2xxSUBSUBTRACTВычтите значение, хранящееся в почтовом ящике xx, из текущего значения аккумулятор (калькулятор).
Примечание: содержимое почтового ящика не изменяется, и действия аккумулятора не определены для инструкций вычитания, которые вызывают отрицательные результаты - однако будет установлен отрицательный флаг, так что 7xx (BRZ) и 8xx (BRP) можно использовать правильно.
3xxSTASTOREСохранение содержимого аккумулятора в почтовом ящике xx (деструктивно).
Примечание: содержимое аккумулятора (калькулятора) не изменяется (неразрушающий), но содержимое почтового ящика заменяется независимо от того, что там было (разрушающее)
5xxLDAЗАГРУЗИТЬЗагрузить значение из почтового ящика xx (неразрушающий) и ввести его в аккумулятор (разрушающий).
6xxBRABRANCH (безусловный)Установить программный счетчик на заданный адрес (значение xx). То есть значение xx будет следующей выполненной инструкцией.
7xxBRZBRANCH IF ZERO (условный )Если аккумулятор (калькулятор) содержит значение 000, установите программный счетчик на значение xx. В противном случае, ничего не делать. Учитывается ли отрицательный флаг, не определено. Когда СУБТРАКТ опустошает аккумулятор, этот флаг устанавливается, после чего аккумулятор не определен, потенциально равен нулю, в результате чего поведение BRZ будет неопределенным при недостаточном заполнении. Рекомендуемое поведение будет для перехода, если аккумулятор равен нулю и отрицательный флаг не установлен.
Примечание: поскольку программа хранится в памяти, данные и программные инструкции имеют одинаковый формат адреса / расположения.
8xxBRPBRANCH IF POSITIVE (условно)Если аккумулятор (калькулятор) равен 0 или положителен, установите программный счетчик на значение xx. В противном случае ничего не делайте. Как ячейки памяти LMC может содержать только значения от 0 до 999, эта инструкция зависит исключительно от отрицательного флага, установленного недостаточным переполнением в SUBTRACT и, возможно, от переполнения в A ДД (не определено).
Примечание: поскольку программа хранится в памяти, данные и программные инструкции имеют одинаковый формат адреса / расположения.
901INPINPUTПерейдите во INBOX, получите значение от пользователя и поместите его в аккумулятор (калькулятор)
Примечание: это перезапишет любое значение, которое было в аккумуляторе (деструктивно)
902ВЫХОДВЫХОДСкопируйте значение из аккумулятора (калькулятора) в OUTBOX.
Примечание: содержимое аккумулятора не изменяется (неразрушающий).
000HLT / COBHALT / COFFEE BREAKПрекратить работу / завершить программу.
DATDATAЭто инструкция ассемблера, которая просто загружает значение в следующий доступный почтовый ящик. DAT также можно использовать вместе с метками для объявления переменных. Например, DAT 984 сохранит значение 984 в почтовом ящике по адресу инструкции DAT.

Примеры

Использование цифровых кодов инструкций

Эта программа (от инструкции 901 до инструкции 000 ) написана только с использованием числовых кодов. Программа принимает на вход два числа и выводит разницу. Обратите внимание, что выполнение начинается в почтовом ящике 00 и заканчивается в почтовом ящике 07. Недостатки программирования LMC с использованием числовых кодов команд обсуждаются ниже.

Почтовый ящикЧисловой кодОперацияКомментарии
00901ВХОДЯЩИЙ ->АККУМУЛЯТОРВВЕДИТЕ первое число, введите в калькулятор (стирая все, что там было)
01308АККУМУЛЯТОР ->ПАМЯТЬ [08]СОХРАНИТЬ текущее значение калькулятора (чтобы подготовиться к следующему шагу...)
02901ВХОДЯЩИЙ ->АККУМУЛЯТОРВВЕДИТЕ второе число, введите в калькулятор (стирая все, что там было)
03309АККУМУЛЯТОР ->ПАМЯТЬ [09]СОХРАНИТЬ текущее значение калькулятора (опять же, чтобы подготовиться к следующему шагу...)
04508ПАМЯТЬ [08] ->АККУМУЛЯТОР(Теперь, когда оба значения ВХОДА СОХРАНЕНЫ в почтовых ящиках 08 и 09...)

ЗАГРУЗИТЬ первое значение обратно в калькулятор (стирая все, что там было)

05209АККУМУЛЯТОР = АККУМУЛЯТОР - ПАМЯТЬ [09]ВЫЧИТАЙТЕ второе число из текущего значения калькулятора (которое было только что установлено на первое число)
06902АККУМУЛЯТОР ->ВЫХОДНОЙ ЯЩИКВЫВОДИТЬ результат калькулятора в OUTBOX
07000(операции не выполняются)ОСТАНОВИТЬ LMC

с помощью мнемоники и меток

ассемблер - это язык программирования низкого уровня, который использует мнемонику и метки вместо числовых кодов команд. Хотя LMC использует только ограниченный набор мнемоник, удобство использования мнемоники для каждой инструкции становится очевидным из языка ассемблера той же программы, показанной ниже - программисту больше не требуется запоминать набор анонимных цифровых кодов и теперь может программировать с набором более запоминающихся мнемонических кодов. Если мнемоника - это инструкция, которая включает в себя адрес памяти (либо инструкцию перехода, либо загрузку / сохранение данных), тогда метка используется для наименования адреса памяти.

Этот пример программы можно скомпилировать и запустить на симуляторе LMC, доступном на веб-сайте Йоркского университета (Торонто, Онтарио, Канада) или на сайте настольное приложение, написанное Майком Коли. Все эти симуляторы включают полные инструкции и примеры программ, ассемблер для преобразования кода сборки в машинный код, интерфейсы управления для выполнения и мониторинга программ, а также пошаговое подробное описание каждой инструкции LMC.
INP STA FIRST INP STA SECOND LDA FIRST SUB SECOND OUT HLT FIRST DAT SECOND DAT
Метки

Без меток программист должен вручную вычислять адреса почтовых ящиков (памяти). В примере числового кода, если новая инструкция должна быть вставлена ​​перед последней инструкцией HLT, тогда эта инструкция HLT переместится с адреса 07 на адрес 08 (маркировка адреса начинается с адреса 00). Предположим, пользователь ввел 600 в качестве первого ввода. Команда 308 будет означать, что это значение будет сохранено в ячейке адреса 08 и перезапишет команду 000 (HLT). Поскольку 600 означает «переход к адресу почтового ящика 00», программа вместо остановки могла бы застрять в бесконечном цикле.

Чтобы обойти эту трудность, большинство языков ассемблера (включая LMC) объединяют мнемонику с метками. Метка - это просто слово, которое используется либо для обозначения адреса памяти, где хранятся инструкция или данные, либо для ссылки на этот адрес в инструкции.

При ассемблировании программы:

  • Метка слева от мнемоники инструкции преобразуется в адрес памяти, в котором хранятся инструкция или данные. т.е. loopstart INP
  • Метка справа от мнемоники инструкции принимает значение адреса памяти, упомянутого выше. т.е. BRA loopstart
  • Метка в сочетании с оператором DAT работает как переменная, она маркирует адрес памяти, в котором хранятся данные. т.е. один DAT 1 или number1 DAT

В языке ассемблера пример, который использует мнемонику и метки, если новая инструкция была вставлена ​​перед последняя команда HLT, тогда адрес, помеченный как FIRST, теперь будет в ячейке памяти 09, а не 08, и команда STA FIRST будет преобразована в 309 (STA 09), а не в 308 (STA 08), когда программа будет собрана.

Метки используются для того, чтобы:

  • идентифицировать конкретную инструкцию как цель для инструкции BRANCH.
  • идентифицировать ячейку памяти как именованную переменную (используя DAT) и при необходимости загружать данные в программа во время сборки для использования в программе (это использование неочевидно, пока не будет принято во внимание, что нет способа добавить 1 к счетчику. Можно попросить пользователя ввести 1 в начале, но было бы лучше иметь это загружается во время сборки с использованием one DAT 1 )

Пример

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

INP OUT // Инициализируется output LOOP BRZ QUIT // Обозначьте этот адрес памяти как LOOP. Если значение аккумулятора равно 0, перейдите к адресу памяти, помеченному QUIT SUB ONE // Вычтите значение, хранящееся по адресу ONE, из аккумулятора OUT BRA LOOP // Перейти (безусловно) в адрес памяти, помеченный LOOP QUIT HLT // Обозначьте этот адрес памяти как QUIT ONE DAT 1 // Сохраните значение 1 в этом адресе памяти и отметьте i t ONE (объявление переменной)

Программа ниже примет пользовательский ввод, возведет его в квадрат, выведет ответ и затем повторите. Ввод нуля завершит программу.. (Примечание: ввод, который приводит к значению больше 999, будет иметь неопределенное поведение из-за 3-значного ограничения числа LMC).

START LDA ZERO // Инициализация для многократного выполнения программы STA RESULT STA COUNT INP // Пользовательский ввод BRZ END // Переход к программе END, если input = 0 STA VALUE // Сохранение ввода как VALUE LOOP LDA RESULT / / Загрузить РЕЗУЛЬТАТ ДОБАВИТЬ ЗНАЧЕНИЕ // Добавить ЗНАЧЕНИЕ, введенное пользователем, в РЕЗУЛЬТАТ STA РЕЗУЛЬТАТА // Сохранение нового РЕЗУЛЬТАТА LDA COUNT // Загрузить COUNT ДОБАВИТЬ ОДИН // Добавить ЕДИНИЦУ в COUNT STA COUNT // Сохранить новый COUNT SUB VALUE // Вычесть введенное пользователем значение VALUE из COUNT BRZ ENDLOOP // Если ноль (VALUE было добавлено к RESULT в VALUE раз), перейти к ENDLOOP BRA LOOP // Перейти к LOOP, чтобы продолжить добавление VALUE к RESULT ENDLOOP LDA RESULT / / Load RESULT OUT // Вывод RESULT BRA START // Переход к START для инициализации и получение другого ввода VALUE END HLT // HALT - был введен ноль, готово! RESULT DAT // Вычисленный результат (по умолчанию 0) COUNT DAT // Счетчик (по умолчанию 0) ONE DAT 1 // Константа, значение 1 VALUE DAT // Пользовательский ввод, значение, которое нужно возвести в квадрат (по умолчанию 0) ZERO DAT // Константа, значение 0 (по умолчанию 0)

Примечание: если после оператора DAT нет данных, то значение по умолчанию 0 сохраняется в адресе памяти.

В приведенном выше примере [BRZ ENDLOOP] зависит от неопределенного поведения, так как COUNT-VALUE может быть отрицательным, после чего значение ACCUMULATOR не определено, в результате BRZ либо ветвится, либо нет (ACCUMULATOR может быть равен нулю или обернутый). Чтобы код был совместим со спецификацией, замените:

... LDA COUNT // Загрузите COUNT ADD ONE // Добавьте ЕДИНИЦУ в COUNT STA COUNT // Сохраните новое COUNT SUB VALUE // Вычтите предоставленный пользователем ввод VALUE from COUNT BRZ ENDLOOP // Если ноль (VALUE было добавлено к RESULT в VALUE раз), перейти к ENDLOOP...

со следующей версией, которая оценивает VALUE-COUNT вместо COUNT- VALUE, убедившись, что аккумулятор никогда не переполняется:

... LDA COUNT // Загрузить COUNT ADD ONE // Добавить ЕДИНИЦУ в COUNT STA COUNT // Сохранить новое COUNT LDA VALUE // Загрузить VALUE SUB COUNT // Вычесть COUNT из введенного пользователем input VALUE BRZ ENDLOOP // Если ноль (VALUE было добавлено к RESULT в VALUE раз), перейти к ENDLOOP...

Другой пример - quine, распечатывая собственный машинный код (источник печати невозможен, потому что буквы не могут быть выведены):

ЗАГРУЗИТЬ LDA 0 // Загрузить позицию 0 в аккумулятор. Эта строка будет изменяться в каждом цикле для загрузки следующих строк в аккумулятор OUT // Вывод значения аккумулятора. Значение аккумулятора будет только что загруженной строкой SUB ONE // Вычтите 1 из значения в аккумуляторе. Это сделано для того, чтобы мы могли выполнить BRZ на следующем шаге, чтобы увидеть, находимся ли мы на последней строке в программе BRZ ONE // Если предыдущее вычитание сделало аккумулятор 0 (что означает, что у нас было значение 001 в аккумуляторе), затем перейти к позиции ONE LDA LOAD // Загрузить позицию LOAD в аккумулятор, это подготовка к увеличению цифр адреса для этой позиции ADD ONE // Увеличиваем цифры позиции для строки LOAD. Текущее значение в аккумуляторе при чтении в виде инструкции загрузит в аккумулятор следующую строку по сравнению с последней загруженной строкой STA LOAD // Сохраните вновь увеличенную строку LOAD обратно в позицию LOAD BRA LOAD // Вернитесь к начало цикла ONE DAT 1 // Переменная ONE. Если читать как инструкцию, это будет интерпретировано как HLT / COB и завершит программу.

Этот quine работает с использованием самомодифицирующегося кода. Позиция 0 увеличивается на единицу в каждой итерации, выводя код этой строки, пока код, который она выводит, не станет 1, после чего она переходит в ОДНУ позицию. Значение в позиции ONE имеет код операции 0, поэтому он интерпретируется как инструкция HALT / COB.

См. Также
Ссылки
Внешние ссылки

Симуляторы

Последняя правка сделана 2021-05-28 03:56:16
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте