201212160530


Микропроцессоры.

Сроки и содержание обучения (содержание учебной работы по неделям: тема(ы), форма обучения (лекция, семинар, групповая работа, дискуссия, практическая работа), рассматриваемые понятия, теории, подходы и т.д.), самостоятельная работа по освоению предмета.
 
------+-----+----------------------------------+---------------
Неделя|Часов|Описание темы                     |Самостоятельная
No    |(ч)  |                                  |домашняя работа
------+-----+----------------------------------+---------------
      |     |Контроллеры - что это такое и что |
 1    |  2  |у него внутри. Гарвард против фон |
      |     |Неймана. Синхронные (x86) и       |
      |     |асинхронные (ARM) процессоры. SoC |
      |     |(System on Chip) и микроконтроллер|
------+-----+----------------------------------+---------------
      |     |Контроллер Siemens Logo! Главный  |
 2    |  2  |цикл программы. Языки обучения    |
      |     |контроллера:                      |
      |     |  - лестничные диаграммы          |
      |     |    (релейная логика)             |
      |     |  - логические схемы              |
      |     |    (дигитальная техника)         |
      |     |  - макроассемблер                |
      |     |    (программирование)            |
------+-----+----------------------------------+---------------
      |     |Иммитация работы логических схем: |
 3    |  2  |элементы AND, NAND, OR, NOR, XOR, | 
      |     |NOT, D-ячейка памяти. Генератор   |
      |     |прямоугольных импульсов, UDF-цепи.|
      |     |Блоки симулятора Siemens Logo!:   |
      |     |Input, Output, Flag, Status (0, 1)|
------+-----+----------------------------------+---------------
 4    |  2  |Схема двоичный счетчик            | 
------+-----+----------------------------------+---------------
 5    |  2  |Схема управления шаговым          |
      |     |двигателем                        | 
------+-----+----------------------------------+---------------
 6    |  2  |Схема "Knightrider"               |
------+-----+----------------------------------+---------------
 7    |  2  |Таймеры. Схемы с таймерами.       | 
------+-----+----------------------------------+---------------
 8    |  2  |Контрольная работа.               |
      |     |Цифровая схема.                   |
------+-----+----------------------------------+---------------
 9    |  2  |Аналоговые вход, выход и флажок.  |
------+-----+----------------------------------+---------------
10    |  2  |Компаратор, усилитель. Схемы      | 
------+-----+----------------------------------+---------------
11    |  2  |Устройства автоматики.            |
      |     |PI-контроллер. Схемы.             |
------+-----+----------------------------------+---------------
12    |  2  |Элемент: Мат. инструкция. Схемы.  |
------+-----+----------------------------------+---------------
13    |  2  |Элемент: Сторожевой пес (watchdog)|
------+-----+----------------------------------+---------------
14    |  2  |Элемент: Аналоговый фильтр. Схемы.|
------+-----+----------------------------------+---------------
15    |  2  |Другие элементы.                  |
------+-----+----------------------------------+---------------
16    |  2  |Комбинированные схемы.            |
------+-----+----------------------------------+---------------
17    |  2  |Контрольная работа.               |
      |     |Комбинированная схема.            |
------+-----+----------------------------------+---------------
18    |  2  |Итоги. Зачет.                     |
------+-----+----------------------------------+---------------


1. Контроллеры - что это такое и что у него внутри. 
   
Очень давно, на планете были времена, когда самым сложным техническим устройством считались часы (с шестеренками). Примерно в это же время родилась идея учить сложным вещам исходя из простых: добываем руду, плавим металл, точим шестеренки, через год - другой часы готовы. Постепенно мир обогащался все более сложными вещами. Помнится в начале тысячелетия еще удавалось пробежаться по цепочке математика -> физика -> прикладная физика -> электротехника -> электроника -> автоматика -> дигитальная техника -> микропроцессоры -> основы программирования -> основы баз данных -> программирование I -> программирование II -> объектно-ориентированное программирование.
 
Но еще задолго до механических часов на Земле жили животные (и человек), которых нужно было уметь лечить. Пройти по цепочке от атома к мозгу невозможно хотя бы по двум причинам:

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


Алгоритмы.

Один из основных принципов работы компьютера - последовательное выполнение инструкций. Например

  0 сделай зарядку
  1 позавтракай
  2 иди в школу
  
Последовательное выполнение инструкций можно изменить с помощью перехода к метке:

  0 сделай зарядку
  1 позавтракай
  2 иди в школу
  3 иди к пункту 5
  4 иди на дискотеку
  5 выучи уроки
  7 ложись спать
  8 иди к пункту 1
  
Последовавший приведенному алгоритму очень скоро будет плохо пахнуть, потому что он не умывается. Кроме того, пункт 4 для него недосягаем.

Почему то со временем, переход к пункту 4 признали примитивным стилем, а инструкцию типа "иди ..." (goto) сдали в музей. Переход к пункту 0 заменили словом цикл (loop, do, while, for) Со временем и эти слова постигла участь goto, настала эра моды на итераторы, рекурсию, генераторы (yield) и тому подобные вещи. Впрочем, это уже совсем другая история. 


Гарвард против фон Неймана. 

Напишем другую инструкцию.

  0 Установи на входах и выходах значения по-умолчанию.
  1 Прочти и запомни число А
  2 Прочти и запомни число Б
  3 Сложи А и Б
  4 Напечатай сумму
  5 Иди к пункту 1
  
Человеку быстро надоест подобное упражнение, поэтому такая задача лучше подходит компьютеру. Если машина запоминает числа и инструкцию в разных "мозгах", то мы имеем дело с Гарвардской машиной, если в одном "мозге", то это машина фон Неймана. 


Синхронные (x86) и асинхронные (ARM) процессоры. 

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

Роботизированная пчела оборудована камерой наблюдения, устройством передачи данных 3G, и подчиняется командам внешнего мозга (бортовой компьютер). По команде некоторой программы, в точно определенное время, она летит фотографировать некоторое помещение с выбитым стеклом в окне. (асинхронная схема).

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

SoC (System on Chip) и микроконтроллер.

Как правило неймановские компьютеры сложнее, так как благодаря общей памяти кода и данных, на них можно решать больше разных задач, например программировать Гарвардские машины. Если почти все компоненты машины Неймана могут быть найдены на одном и том же чипе - говорят о SoC компьютере. Если Гарвардская машина бесконечно прокручивает такой алгоритм (Главный цикл программы контроллера):
 
  0 Установи на входах и выходах значения по-умолчанию.
  1 читай состояние входов
  2 обрабатывай полученные данные
  3 выводи результаты обработки на выходы
  4 иди к 1
  
то речь идет о контроллере. Если все схемы упрятаны в один чип, говорят о микроконтроллере.

Далее будем изучать контроллер Siemens Logo! и программировать его с помощью x86 компьютера (Google поиск: Windows, Linux, MacOSX, Intel, AMD)

----------

2. Контроллер Siemens Logo! 

Главный цикл программы. Язык обучения контроллера.

В инструкции из пяти пунктов, приведенной выше, у контроллера можно видоизменять только пункт 2. Если это делается с помощью словесного описания - говорят о программировании контроллера на некотором языке программирования. Обычно это Assembly, C, Java. 


Язык обучения контроллера: лестничные диаграммы/релейная логика
                           
В пятидесятые годы двадцатого века, людей знавших хоть какой- нибудь язык программирования было мало. Однако было много специалистов, умевших соединять реле в релейные схемы. Задача перевода такой схемы в инструкцию для контроллера оказалась под силу компьютерам того времени. По традиции, работа реле подробно изучается по сей день. 


Язык обучения контроллера: логические схемы/дигитальная техника

В шестидесятые годы двадцатого века, людей знавших хоть какой- нибудь язык программирования было мало. Однако было много специалистов, умевших соединять логические элементы в цифровые схемы. Задача перевода такой схемы в инструкцию для контроллера оказалась под силу компьютерам того времени. По традиции, работа логических элементов подробно изучается по сей день.
 
Воспользуемся этой оплошностью (традицией) для программирования контроллера Siemens Logo!

----------

3. Иммитация (симуляция) работы логических схем: 

Элементы AND,NAND,OR,NOR,XOR,NOT,D-ячейка памяти. UDF-цепи. 

Генератор прямоугольных импульсов.

Блоки симулятора Siemens Logo!: Input, Output, Flag, Status.

Чтобы снизить расходы по переводу текста на разные языки, перейдем на всем понятный язык картинок. (Допускается использование общепринятых математических символов и выражений, пиктограмм (всем понятные обозначения типа "места для детей и инвалидов), латынь использовать будем разве что для пословиц и поговорок: alen novi nostra ales - что означает: "... если один человек построил, другой завсегда разобрать может" к/ф. Формула любви, 1984, 39-я минута).

----------

Где взять ?

Google:

  siemens logo comfort download
  
LOGO! Demo software , upgrades/updates, drivers ... - Siemens

  http://www.automation.siemens.com/mcms/programmable-logic-controller/en/logic-module-logo/demo-software/pages/default.aspx

This can be done via the internet directly from within LOGO! Soft Comfort, downloading and installing the required files or purchasing the upgrade on a CD.

----------

Приложение А

Программирование микропроцессоров.

К. Нелус                                             2003.04.09

В этой статье мы рассмотрим базовые понятия и методы программирования микропроцессоров на примере упрощенного процессора ( в дальнейшем я буду называть его "Простой Машиной фон Неймана", сокращенно ПМН. 


1. ПМН взглядом электроника.

          - Мы разработали новый процессор !
            - И сколько корпусов он занимает ?
          - Всего три.
            - Посмотреть бы !
          - Так пятиэтажные здания стоящие прямо перед нами ...
            и есть корпуса, где расположен наш процессор.

Чтобы разобраться, как работает ПМН, необходимо понимание следующих разделов курса "Цифровые устройства":

  - Комбинационные схемы.
  - Синхронный D-триггер со сбросом.
  - Мультиплексоры и демультиплексоры.
  - Параллельный сумматор.
  - Параллельный преобразователь в дополнительный код.
  - Кольцевой счетчик.


1.1 Функциональные блоки.

Процессор состоит из следующих функциональных блоков:

  - Регистр данных (DATA).
  - Схема хранения и декодирования команды.
  - Схема указателя команды.
  - Тактовый генератор.
  - Модуль адресного пространства.


1.1.1 Регистр данных.

Представляет собой параллельный 8-разрядный регистр со сбросом.

Упражнение. В симуляторе Circuitmaker Student построить названную схему


1.1.2 Схема хранения и декодирования команды.

Схема включает в себя:

  - Одноразрядный регистр кода операции.
  - 7-разрядный регистр адреса.
  - Демультиплексор 1 х 2

  Упражнение. В симуляторе Circuitmaker Student построить названную схему. В качестве подсказки использовать схему, изображаемую при загрузке симулятора ПМН Chip.exe. Добавить схему сброса.


1.1.3 Схема указателя команды.

Схема состоит из
 
  - Счетчика команд (PCOUNTER),
  - Схемы, называемой SKIP3NZ,
  - Мультиплексора  выбора команды 120 х 8,
  - Модуль адресного пространства.


1.1.3.1 Счетчик команд.

Представляет собой 7-разрядный двоичный счетчик со схемами установки начального значения и сброса. Сброс устанавливает на счетчик число 8 (а не 0).

  Упражнение. В симуляторе Circuitmaker Student построить названную схему.


1.1.3.2 Схема SKIP3NZ

Схема  имеет 7-разрядный вход А, 8-разрядный вход Б и 7 разрядный выход С. Составляющие элементы схемы:

  - 7-разрядный параллельный сумматор.
  - 8-ми входовый элемент ИЛИ.

Если число на входе Б равно нулю, то С = А. Если число на входе Б не равно нулю, то С = А + 3.

  Упражнение. В симуляторе Circuitmaker Student построить названную схему.


1.1.4 Тактовый генератор.

Тактовый генератор представляет собой 3-х разрядный кольцевой счетчик, подключенный к выходу генератора прямоугольных импульсов. Одна команда ПМН выполняется за 3 такта генератора.

  1. Выборка и запись команды в схему хранения и декодирования 
     команды.
  2. Увеличение значения в счетчике команд на единицу.
  3. Исполнение декодированной команды.

  Упражнение. В симуляторе Circuitmaker Student построить названную схему.


1.1.5 Модуль адресного пространства.

Модуль включает в себя:

  - Демультиплексор записи 1 х 128.
  - Мультиплексор чтения 128 х 8.
  - Набор из 127 8-разрядных параллельных регистров со сбросом.
  - Арифметическо-логическое устройство (АЛУ).
  - Порты чтения и записи.


1.1.5.1 АЛУ

АЛУ является комбинационной схемой.

Составляющие АЛУ:

  - Параллельный 8-разрядный сумматор (ADD - add).
  - Параллельный 8-разрядный преобразователь в дополнительный 
    код (NEG - negate).
  - 8-ми разрядная схема "логическое И" (AND - and).
  - 8-ми разрядная схема "логическое НЕ" (COM - complement to 
    one).
  - Cхема сдвига вправо на 1 бит (LSR - logical shift right).

  Упражнение. В симуляторе Circuitmaker Student построить названные схемы.


1.1.5.2 Порты

Порты представляют собой соединения с внешними выводами ПМН.
      
  +---------------+-------------------+-----------+
  |   D0          |         CPU       |   Q0      |
  |   D1          |                   |   Q1      |
  |   D2          |                   |   Q2      |
  |   D3          |                   |   Q3      |
  |   D4          |                   |   Q4      |
  |   D5          |                   |   Q5      |
  |   D6          |                   |   Q6      |
  |   D7          |                   |   Q7      |
  +---------------+                   |           |
  |   RESET (0..7)|                   |           |
  |   RESET ALL   |                   |           |
  +---------------+-------------------+-----------+

Рис. Условное обозначение ПМН.


1.2 Соединения.

Входы всех регистров, находящихся в адресном пространстве, а также входы установки начального состояния счетчика PCOUNTER, подключены к выходам регистра DATA с помощью 8-ми разрядной шины. Выбор адреса записи осуществляется демультиплексором записи, выходы которого подключены к синхровходам адресуемых регистров. При этом счетчик команд имеет адрес 0.
 
Выходы первых 8 адресных регистров подключены следующим образом

  - Выход счетчика 0 - на вход А схемы SKIP3NZ
  - Выход регистра 1 - на вход Б схемы SKIP3NZ
  - Выход регистра 2 - на вход А схемы ADD
  - Выход регистра 3 - на вход Б схемы ADD, вход схемы NEG
  - Выход регистра 4 - на вход А схемы AND
  - Выход регистра 5 - на вход Б схемы AND, вход схемы COM
  - Выход регистра 6 - на вход схемы LSR
  - Выход регистра 7 - к выводам Q0..Q7 ПМН, представляющим собой единственный выходной порт микропроцессора.

Выходы адресных регистров 8..127 подключены к соответствующим входам мультиплексора чтения. 

Входы 0..7 мультиплексора чтения распределены следующим образом

  - Входы 0 - подключены на массу.
  - Входы 1 - бит 0 к выходу переноса схемы ADD, биты 1..7 на массу.
  - Вход  2 - к выходу схемы ADD.
  - Вход  3 - к выходу схемы NEG.
  - Вход  4 - к выходу схемы AND.
  - Вход  5 - к выходу схемы COM.
  - Вход  6 - к выходу схемы LSR.
  - Вход  7 - к выводам D0..D7 ПМН, представляющим собой единственный входной порт микропроцессора.

Схема ПМН видна при запуске симулятора Chip. Цепи сброса, для простоты, не показаны. 

  Упражнение. Запустить симулятор Chip. Ознакомиться с кнопками управления

  Стрелки   - выбор адреса (единицы по вертикали, x16 по горизонтали).
  0..9,a..f - ввод 16-го числа в выбранный стрелками адрес.
  F2        - сохранение регистров 8-127 в файл chip.bin
  F3        - загрузка регистров 8-127 из файла chip.bin
  F4        - выполнить один такт генератора (микрошаг).
  F5        - выполнить одну команду ПМН (шаг = 3 микрошага).
  F6        - сбросить счетчик команд.
  F7        - сбросить все регистры (очистка)
  qwrtyuio  - установить бит (76543210) на входах ПМН.


2. ПМН взглядом программиста.

                             - Операция "Ы".
                             - Почему "Ы" ?
                             - Чтоб никто не догадался...

Необходимо понимание следующих разделов из курса "Цифровые устройства":

  - Бит, байт, слово.
  - Что такое алгебра.
  - Двоичная и 16-ая арифметика.
  - Алгебра Буля.


2.1 Формат команды

Команда ПМН представляет собой 8-разрядное двоичное число и состоит из двух полей:

  - поля операции.
  - поля адреса.
  
  +---------------+-----------------------------------------+
  |  Биты:     7  |  6     5     4     3     2     1     0  |
  +---------------+-----------------------------------------+
  |  код операции |              А Д Р Е С                  |
  +---------------+-----------------------------------------+


2.2 Набор команд.

Состоит из 2х команд:

  - ЧТЕНИЕ - чтение данных по адресу АДРЕС в регистр DATA.
  - ЗАПИСЬ - запись данных из регистра DATA в адресуемый регистр с адресом АДРЕС.


2.2.1 Команда ЧТЕНИЕ.

  Код операции       0
  Адрес              0..7fh
  Формула            Команда = Адрес
  Пример             00000101 - чтение данных по адресу 5.  (05h)


2.2.2 Команда ЗАПИСЬ.

  Код операции       1
  Адрес              0..7fh
  Формула            Команда = Адрес + 80h
  Пример             10000111 - запись данных по адресу 7.  (87h)


2.3 Пример простой программы.

- Постановка задачи

Создать код программы работающей по следующему алгоритму (последовательности действий).

  Пункт  Действие

  1.     Прочесть число, заданное состоянием входов D0..D7 ПМН. 
  2.     Получить дополнительный код этого числа.
  3.     Результат представить на выходах Q0..Q7 ПМН.
  4.     Перейти к пункту 1.

Решение

  Пункт  Адрес   Код     Пояснение

  1.     08h     07h     Читать с входного порта
  2.     09h     83h     Записать на вход схемы NEG
  2.     0ah     03h     Читать результат с выхода схемы NEG
  3.     0bh     87h     Записать в выходной порт
  4.     0ch     0eh     Читать число по адресу 0eh
  4.     0dh     80h     Записать в счетчик команд.
  4.     0eh     08h     Число 8 (адрес начала кода).

Ответ

07 83 03 87 0e 80 08

  Упражнение. Ввести и выполнить на симуляторе Chip рассмотренную программу.


2.4 Типы данных.

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

  - Тип "int":    8 бит образуют знаковое целое в диапазоне 
                  -128..127

  - Битовое поле: 8 бит рассматриваются независимо друг от 
                  друга, образуя таким образом 8 булевых 
                  переменных (ложь:0, истина:1).

  - Тип "bool":   ложь: 00000000, истина 00000001. Все другие 
                  значения являются недопустимыми для типа 
                  "bool".

  - Тип "float":  Запись разбита на 2 поля:
                  - биты 7..5: степень 10-ти в дополнительном 
                    коде. Числа в диапазоне -4..3.
                  - биты 4..0: знаковое целое в дополнительном 
                    коде в диапазоне -16 .. 15.
                  Служит для организации приближенных 
                  вычислений. Числа с плавающей точкой в 
                  диапазоне: +/-(0.0001 .. 15000).

  - Адрес:        8 бит образуют беззнаковое целое число в 
                  диапазоне 0..255, служащее для представления 
                  адреса.

  - Код команды:  Формат представлен в п. 2.1.


3. Операции

Все рассматриваемые ниже типы данных допускают операцию присвоения "=" (Присвоить левой переменной значение выражения справа от знака =).

Примеры

  y = 3           y равен трем.
  y = y + 1       теперь y равен четырем.   


3.1  Алгебра битовых полей.

Определим операции:

  Обозначение  Наименование                Пример

  ~            Побитное инвертирование     ~1ah      = 0e5h
  &            Побитное И                  55h & 0ch =  40h
  |            Побитное ИЛИ                55h & 0ch =  5dh
  ^            Побитное исключающее ИЛИ    55h ^ 0ch =  59h
  >>           Сдвиг вправо                55h >> 1  =  2ah
  <<           Сдвиг влево                 55h << 1  = 0aah

В ПМН встроены только три операции: "&" (AND), "~" (COM) и ">> 1" (LSR). Обратите внимание, что операция сдвига вправо реализована как унарная операция сдвига на 1 бит.

Остальные операции можно создать программно.

  Упражнения. Вычислить следующие выражения:

   (~27h) + (1 << 2)
   25 ^ 11
   ~(-1)
   ~(-0)
   (7 & 4) >> 2 | 0ch


3.2  Алгебра целых знаковых чисел с фиксированной разрядностью. Тип "int".

На целых числах в диапазоне -128..127 производятся операции:

   +, -, *, /, унарный -

Поскольку рассматриваемые объекты ( переменные и операции) должны образовывать алгебру, то операции должны быть переопределены, по сравнению со знакомыми нам арифметическими операциями. 

В ПМН встроены только две операции: сложение (ADD) и унарный минус (NEG). Остальные операции можно создать программно.

Пример 1. Переопределение операции сложения

  (a + b) & 0ffh, (a + b) & 100h
   
Операция сложения становится векторной, имея результатами сумму и бит переноса.

Пример 2. Вычитание

Пусть числа заданы на входах сумматора 2 и 3, а результат ожидаем на выходе сумматора. Учтем, что: 

  a - b = a + (-b). 

Тогда код операции вычитания: 03 83

Упражнение. На базе рассмотренных операций написать выражение для вычисления произведения двух целых чисел в диапазоне 0 <= a < 16, 0 <= b < 16.


3.3  Тип "bool". Алгебра Буля над переменными типа "bool".

Несмотря на то, что одна переменная типа bool занимает байт, допустимыми являются только два значения:

  - ложь    00000000
  - истина  00000001

В ПМН встроены только одна операция: И. Остальные операции можно создать программно.

Пример. Отрицание.

Обозначим операцию отрицания над переменной типа bool как "!". Тогда:

  ! a = (~a) & 01h

Пример. ИЛИ.

  a | b = !((!a) & (!b)) 
        = (~((~a) & 1) & ((~b) & 1)) & 1 =
        = (~(~a & ~b)) & 1
       
  Упражнение. Вычислить выражение ((!a) & (!b)) для всех допустимых значений a и b.
            
            
3.4  Операции, приводящие к преобразованию типа.

Наиболее распространенный класс операций, тип результата которых отличается от типа операндов - это операции сравнения. Так при сравнении двух чисел типа "int" - мы получим результат типа "bool". Действительно, результат сравнения может быть либо истинным, либо ложным. 

  Обозначение     Наименование                    Пример

    <             Меньше                          a <  2
    >             Больше                          1 >  5
    <=            Меньше или равно                a <= a
    >=            Больше или равно                a >= b
    ==            Равно                           a == 0
    !=            Не равно                        b != 1

Введем также явное преобразования переменных типа int в тип bool. Любое не нулевое значение преобразуется в 00000001 (истина), нулевое не изменяется 00000000 (ложь).

  Обозначение     Наименование                    Пример

    @             tobool                          @3ah = 1

Операция @ может быть создана на основе имеющихся операций.

  @a = ((a>>7)|(a>>6)|(a>>5)|(a>>4)|(a>>3)|(a>>2)|(a>>1)|a)&01h

В ПМН имеется встроенная операция @, неявно преобразующее данные на входе Б схемы SKIP3NZ ( подключен к выходу регистра с адресом 1).

Примеры

  Операция       Результат

  02h >  03h     00h
  02h <= 03h     01h

  Упражнение. Вычислить выражение (2 > (3 >> 1)) + 5


3.5  Адресная арифметика. Тернарная операция "?:".

Введем операцию над тремя аргументами:  

  y = a ? b : c

Смысл операции следующий: Ecли а истинно то y = b иначе y = c. Ясно, что операция осмысленна если y, b, c одинакового (любого из рассмотренных) типа. Переменная a должна быть типа bool. Указанная операция может быть создана на базе рассмотренных операций:

  a ? b : c = ((-a) & b) | ((~(-a)) & c)

  Упражнение. Вычислить выражение 

  (((9 > (4 << 1 )) - 1) ? 12 : 8) * 3


3.6 Сложные операции.

Обозначение     Наименование         Смысл                    

  a && b        Логическое И         ( @a) & ( @b)
  a || b        Логическое ИЛИ       ( @a) | ( @b)

  Упражнение. Пусть a = 7, b = 2. Вычислить выражение: a && b. Вычислить выражение: a &  b.


3.7  Операции как функции.

Операцию можно записывать как функцию. Так, запись 

  y = summa( a, b) 
  
равноценна более привычной нам записи y = a + b. Разумеется функция summa должна выполнять сложение по тем же правилам, что и операция +.

Пример. Преобразование int к bool.

   y = tobool( a)


3.8  Мнемокод.

Изменим порядок записи вычисления функции:

   y = summa ( a, b)

На:

   summa y, a, b

Это типичная запись команды в языке программирования Ассемблер. Слово обозначавшее имя функции (summa) теперь называется мнемокодом. Программирование в мнемокодах более удобно по сравнению с программированием в машинных кодах, так как человек запоминает слова легче чем числа.

Перевод программы написанной в мнемокодах в машинный код называется трансляцией и осуществляется программой ассемблера (в нашем случае это fasm.сom для среды DOS, или fasm32.exe для Windows 95,98,NT,2000,XP)


3.9  Метки и переменные.

Для хранения переменной нужно зарезервировать память. В языке ассемблер резервирование памяти производится с помощью псевдооперации "db" (data byte). Адресу зарезервированной ячейки дается имя - говорят, что адрес помечается. 

Метка адреса ячейки памяти с данными и есть имя переменной. Один адрес может иметь несколько меток.

Пример.

     age: db    20h   ; переменная "аge" длиной в 1 байт хранит 
                      ; значение 20h
  q: xyz: db    ?     ; переменная имеет 2 имени "q" и "xyz". 
                      ; Значение неопределено
   start: summa y,a,b ; команда "summa" расположена по адресу 
                      ; "start"

Замечание к примеру: Символ ";" используется для обозначения начала комментария. Комментарий заканчивается в конце строки.

Значение хранимое по адресу, вычисляется взятием имени метки в квадратные скобки [].

Пример:

Пусть по адресу 25h расположена переменная "а":

  а: db 77h

тогда a есть 25, но [a] есть 77h


3.10 Директивы.

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


3.10.1 Директива EQU.

Формат директивы понятен из примеров.

   CTO      equ 100
   TRUE     equ 1
   FALSE    equ 0

   int      equ db
   address  equ db
   bool     equ db
   code     equ db

   a:       int  CTO
   b:       bool FALSE


3.10.2 Директива ORG.

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

Формат директивы понятен из примера.

     ORG 18h
  a: int 00h  ; а = 18h, [a] = 00h


3.11 Программирование линейных алгоритмов.

Если команды процессора выполняются последовательно в порядке возрастания адресов, то говорят о программе с линейным алгоритмом.

Пример. Вычислить 1 + 2 + 3

  int equ db

         summa y, [c00h], [c00h]
         summa y, y, [c01h]
         summa y, y, [c02h]
         summa y, y, [c03h]

  c00h:  int   00
  c01h:  int   01
  c02h:  int   02
  c03h:  int   03
  y:     int   ?


3.12 Понятие макроподстановки.

ПМН имеет всего две команды ЧТЕНИЕ и ЗАПИСЬ. Но даже эти команды не знакомы ассемблеру. Для того, чтобы транслятор понимал новые команды их нужно создать с помощью макроподстановки. Создадим команды read и write. Вспомним, как создается код команд чтения:
 
  00h + адрес, и записи: 80h + адрес (смотри пп. 2.2.1, 2.2.2).

  macro read x
  {
        db x
  }

  macro write x
  {
        db 80h + x
  }

Теперь строку:

  read 02h

Транслятор заменит на " db 02", которую он знает как транслировать, поскольку псевдокоманда db встроена в fasm. Команды read и write называют макрокомандами.

Макрокоманды могут состоять из нескольких команд.

Новые макрокоманды могут создаваться на базе уже созданных макрокоманд.

Пример. Операция y = x.

  macro copy y, x
  {
      read   x
      write  y
  }

Пример. Операция y = y + a

  macro add y, a
  {
      copy 2, y
      copy 3, a
      copy y, 2
  }



3.13 Операция записи в счетчик. Фиксированные (безусловные) переходы.

Для изменения последовательности выполнения команд, можно использовать команду записи адреса следующей команды в счетчик. Предположим, что следующей мы хотим выполнить команду по адресу 66h. Для этого следует записать в счетчик адрес 66h:

  address equ     db

  macro   goto  a
  {                   ; слово local обозначает, что имя метки  
                      ; x известно только внутри описания 
          local   x   ; макрокоманды. (особенность ассемблера 
                      ; fasm)
          read    x
          write   0
  x:      address a    
  }

Теперь, достаточно использовать макрокоманду goto

  goto 66h

Пример. Окончание программы "Стенка"

       ...
  wall:  goto wall

Пример. Бесконечный цикл.

  start: ...
         ...
         goto start

Переходы к жестко указанному адресу называются безусловными. 


3.14 Вычисляемые (условные) переходы. Программирование ветвлений.

Мы можем предварительно вычислить адрес перехода. Если адрес вычисляется с применением тернарной (с тремя операндами) операции "?:", то такой переход называется условным. 

Напишем макрокоманду gotoc (goto calculated) осуществляющую переход по адресу, хранимому в переменной a.

  macro gotoc a
  {
      read    a
      write   0
  }

Другой способ осуществления условного перехода основан на использовании регистра 01h для записи результата вычисления условия.

Примеры применения условных переходов можно найти в тексте макрокоманд if и ifn (см. приложение А).


3.15 Стек.

Стек является аналогом рожка автомата. Мы имеем доступ только к последнему вложенному объекту (патрону). Для работы со стеком используются команды

  - push (записать в стек)
  - pop  (прочитать из стека)

В адресуемой памяти текущее положения стека определяется с помощью переменной, называемой указателем стека.

Пример. Организация стека в ПМН.

  address  equ      db   ; введем тип "адрес"
  code     equ      db   ; введем тип "код"
  
     c01:  address  01h  ; константа для увеличения указателя 
                         ; стека (+1).
    c0ff:  address 0ffh  ; константа для уменшения указателя 
                         ; стека (-1).
     c80:  address  80h  ; константа для динамического 
                         ; формирования команды записи.
                         
  stack_pointer:  address  70h  ;указатель стека
  
  macro copypv a, x      ; записать значение переменной x в 
  {                      ; адрес, хранимый в переменной a 
        local addr       ; (суффикс pv читается: pointer, value 
                         ; и служит для напоминания типов 
                         ; аргументов команды)
                         
        copy  3,   c80   ; записать значение 80h на вход Б 
                         ; сумматора.
        сopy  2,   a     ; записать значение переменной а на 
                         ; вход сумматора А
        сopy  addr,a     ; создать команду "записать в адрес 
                         ; записанный в а".
        
        read  x          ; читаем из х
  addr: code  ?          ; пишем в вычисленный адрес [a]. 
  }                      ; Команда (код 80h + [a]) создается 
                         ; динамически (на лету).
  
  macro copyvp x, a      ; записать в переменную x значение 
  {                      ; переменной, расположенной по адресу,
        local addr       ; хранимому в переменной a.
  
        сopy  addr,a     ; создать команду чтения 
  addr: code  ?          ; читаем из вычисленного адреса [a]. 
                         ; Команда (код 00h + [a]) создается 
                         ; динамически (на лету).
        write x;
  }
  
  macro push z
  {
      copypv stack_pointer, z
      ; stack_pointer = stack_pointer + 1
      add   stack_pointer, c01 
  }
  
  macro pop z
  {
      copyvp z, stack_pointer
      ; stack_pointer = stack_pointer - 1
      add    stack_pointer, c0ff 
  }


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

Иногда бывает выгодно иметь возможность совершить безусловный переход к некоторому адресу (называемый вызовом подпрограммы, call), выполнить код (называемый подпрограммой), а затем вернуться к оператору, следующему за переходом (называется возврат - ret).

Напишем макрокоманды вызова и возврата.

  macro call  sname
  {
      local   a
      local   v
      
      push    v
      goto    sname
  v:  address a
  a:  
  }
  
  macro ret
  {
  ra: address 00h
      pop     ra
      write   00h, ra
  }
  

4. Структурное программирование.

                  Дилемма курицы и яйца делает предпочтительной 
                  такую иерархию, в которой круг является более 
                  фундаментальным понятием, чем прямая.


4.1 Блок. Соглашение об именах (Naming convention).

Выделим некоторый фрагмент программы метками, состоящими из слов begin, end и имени этого фрагмента. Такой фрагмент будем считать блоком

    begin_name:
        ...
    end_name:

4.2  Оператор if

If - Then - Else декомпозиция на два If - Then.
                            
        |                           |         +-------+
        |                           |         |       |            
       / \                         / \        |      / \           
     /     \  0                  /     \  0   |    /     \  0      
    <  a ?  >--------+          <  a ?  >---+ |   <  !a ? >---+    
     \     /         |           \     /    | |    \     /    |    
       \ /           |      =>     \ /      | |      \ /      |    
        | 1          |              | 1     | |       | 1     |    
  +-----+-----+ +----+-----+  +-----+-----+ | | +-----+-----+ |    
  | then block| |else block|  | then block| | | | else block| |    
  +-----+-----+ +----+-----+  +-----+-----+ | | +-----+-----+ |    
        |            |              |       | |       |       |    
        +------------+              +-------+-+       +-------+    
        |                                             |            
                            
  if a                        if a
      begin                       begin
         ...                          ...
      end             ==>         end
  else                        if !a
      begin                       begin
          ...                         ...
      end                         end

4.3 Циклы. Цикл while.

         |                           |        
         +----<--+                   +----<--+
        / \      |               +---+---+   |
      /     \    |               | block |   |
   +-<  a ?  >   |               +---+---+   |
   |  \     /    |                   |       |
   |    \ /      |                  / \      |
   |     | 1     |                /     \    |
   | +---+---+   |             +-<  a ?  >   |
   | | block |   |             |  \     /    |
   | +---+---+   |             |    \ /      |
   v     |       |             v     | 1     |
   |     +---->--+             |     +---->--+
   |                           |              

   while a                     do 
       begin                      begin
           ...                        ...
       end                        end
                               while a

              
4.4 Переход к ассемблеру.

   if a                         begin:
       begin                        ifn a, end
           ...          ==>             ...
       end                      end:    
   
   while a                      begin:
       begin                        ifn a, end
           ...          ==>             ...
       end                          jmp begin
                                end:

   do                           begin:
       begin                        ...
           ...          ==>         ifn a, end
       end                          jmp begin
   while a                      end:


Список литературы:

  1. Thomasz Grysztar. Flat assembler programmers manual.
  2. Kernigan, Ritchie. The C programming language.

Дополнительные учебные материалы:

  1. Симулятор электронных схем "Circuitmaker Student 6". Доступен на сайте http://www.circuitmaker.com
  2. Макроассемблер "fasm 1.45". Доступен на сайте http://fasm.sourceforge.net
  3. Симулятор ПМН "chip 2003.04.04". Доступен на сайте http://nelus.tpt.edu.ee

----------

Приложение Б. Заголовочный файл настройки flat assembler на систему команд ПМН (chiphead.asm).

  ; CHIP MACROASSEMBELER HEADER.           
  ; Written by Kirill Nelus. 2003.04.04.
  ; Compile with FASM by Thomasz Grysztar.
  
  ; read only registers
  
         OUT_ZERO        equ    0
         OUT_CARRY       equ    1
         OUT_ADD         equ    2
         OUT_NEG         equ    3
         OUT_AND         equ    4
         OUT_COM         equ    5
         OUT_LSR         equ    6
  
  ; write only registers
  
         IN_PCOUNTER     equ    0
         IN_SKIP3NZ      equ    1
         IN0_ADD         equ    2
         IN1_ADD         equ    3
         IN_NEG          equ    3
         IN0_AND         equ    4
         IN1_AND         equ    5
         IN_COM          equ    5
         IN_LSR          equ    6
         PORT            equ    7
  
  macro  read  a
  {
         db    a
  }
  
  macro  write  a
  {
         db     80h + a
  }
  
  macro  copy   a, b
  {
         read   b
         write  a
  }
  
  macro  add    a, b
  {
         copy   IN0_ADD, a
         copy   IN1_ADD, b
         copy   a,       OUT_ADD
  }
  
  macro  neg    a
  {
         copy   IN_NEG,  a
         copy   a,       OUT_NEG
  }
  
  macro  sub    a, b
  {
         copy   IN_NEG,  b
         copy   IN1_ADD, OUT_NEG       
         copy   IN0_ADD, a
         copy   a,       OUT_ADD
  }
  
  macro  and    a, b
  {
         copy   IN0_AND, a
         copy   IN1_AND, b
         copy   a,       OUT_AND
  }
  
  macro  com    a
  {
         copy   IN_COM,  a
         copy   a,       OUT_COM
  }
  
  macro  or    a, b
  {
         copy   IN_COM,  a
         copy   a,       OUT_COM
         copy   IN_COM,  b
         copy   IN1_AND, OUT_COM
         copy   IN0_AND, a
         copy   IN_COM,  OUT_AND
         copy   a,       OUT_COM
  }
  
  macro  jmp    a
  {
         local  f
         copy   IN_PCOUNTER, f
   f:    db     a
  }
  
  macro  goto   a
  {
         local  f
         copy   IN_PCOUNTER, f
   f:    db     a
  }
  
  macro  clrskp
  {
         copy   IN_SKIP3NZ, OUT_ZERO
  }
  
  macro  ifn    a, go
  {
         copy   IN_SKIP3NZ, a
         goto   go
         clrskp
  }
  
  macro  if     a, go
  {
         local  f
         ifn    a, f
         goto   go
   f:
  }

----------

Приложение В. Симулятор ПМН на Borland C v.5.5.

  #include <io.h>
  #include <fcntl.h>
  #include <sys\stat.h>
  #include <conio.h>

  #define vkF1     0x13b
  #define vkF2     0x13c
  #define vkF3     0x13d
  #define vkF4     0x13e
  #define vkF5     0x13f
  #define vkF6     0x140
  #define vkF7     0x141
  #define vkF8     0x142
  #define vkF9     0x143
  #define vkF10    0x144

  #define vkRIGHT  0x14b
  #define vkLEFT   0x14d
  #define vkUP     0x148
  #define vkDOWN   0x150

  #define vk0      0x30
  #define vk1      0x31
  #define vk2      0x32
  #define vk3      0x33
  #define vk4      0x34
  #define vk5      0x35
  #define vk6      0x36
  #define vk7      0x37
  #define vk8      0x38
  #define vk9      0x39

  #define vka      0x61
  #define vkb      0x62
  #define vkc      0x63
  #define vkd      0x64
  #define vke      0x65
  #define vkf      0x66
  #define vkg      0x67
  #define vkh      0x68
  #define vki      0x69
  #define vkj      0x6a
  #define vkk      0x6b
  #define vkl      0x6c
  #define vkm      0x6d
  #define vkn      0x6e
  #define vko      0x6f
  #define vkp      0x70
  #define vkq      0x71
  #define vkr      0x72
  #define vks      0x73
  #define vkt      0x74
  #define vku      0x75
  #define vkv      0x76
  #define vkw      0x77
  #define vkx      0x78
  #define vky      0x79
  #define vkz      0x7a

  #define vkA      0x41
  #define vkB      0x42
  #define vkC      0x43
  #define vkD      0x44
  #define vkE      0x45
  #define vkF      0x46
  #define vkG      0x47
  #define vkH      0x48
  #define vkI      0x49
  #define vkJ      0x4a
  #define vkK      0x4b
  #define vkL      0x4c
  #define vkM      0x4d
  #define vkN      0x4e
  #define vkO      0x4f
  #define vkP      0x50
  #define vkQ      0x51
  #define vkR      0x52
  #define vkS      0x53
  #define vkT      0x54
  #define vkU      0x55
  #define vkV      0x56
  #define vkW      0x57
  #define vkX      0x58
  #define vkY      0x59
  #define vkZ      0x5a

  #define DATAPORT    0x378

  #define BACKGROUND  LIGHTGRAY  //BLACK
  #define INVERTEDBG  BLACK      //LIGHTGRAY
  #define COUNTERTXT  YELLOW     //LIGHTRED
  #define INOUTPORTS  LIGHTRED   //YELLOW
  #define HEXCONSOLE  BLUE       //LIGHTGREEN
  #define WIRES       BLACK      //LIGHTBLUE
  #define CONSOLETXT  LIGHTGREEN //WHITE
  #define INVERTEDTXT LIGHTRED   //LIGHTRED

  unsigned char GEN, IN, RAM[ 128], ADDRESS, DATA;

  void draw_CHIP( void);
  void show( void);
  long navigate( void);

  unsigned long uchar2bin( unsigned char x)
  {
      unsigned long i;
      unsigned long y = 0;

      for (i=0; i<8; i++)
          y = y + (((x & (1 << i)) ? 1L : 0L) << (i * 4));
      return y;
  }

  unsigned char SKIP3NZ( void)
  {
      return 0x7f & (RAM[1] == 0 ? RAM[ 0] : RAM[0] + 3);
  }

  unsigned char CARRY( void)
  {
      return ((int)RAM[ 2] + (int)RAM[3] > 255) ? 1 : 0;
  }

  unsigned char ADD( void)
  {
      return RAM[2] + RAM[3];
  }

  unsigned char NEG( void)
  {
      return -RAM[3];
  }

  unsigned char AND( void)
  {
      return RAM[4] & RAM[5];
  }

   unsigned char COM( void)
  {
      return ~RAM[5];
  }

  unsigned char LSR( void)
  {
      return RAM[6] >> 1;
  }

  unsigned char RAMOUT( long addr)
  {
      unsigned char y;

      switch (addr)
      {
         case 0:  y = 0x00;       break;
         case 1:  y = CARRY();    break;
         case 2:  y = ADD();      break;
         case 3:  y = NEG();      break;
         case 4:  y = AND();      break;
         case 5:  y = COM();      break;
         case 6:  y = LSR();      break;
         case 7:  y = IN;         break;
         default: y = RAM[ addr]; break;
      }
      return y;
  }

  void show_DATA( void)
  {
      textcolor( CONSOLETXT); textbackground( INVERTEDBG); 
      gotoxy( 8, 5);
      cprintf("%08lx", uchar2bin( DATA));
  }

  void show_ADDR( void)
  {
      textcolor( CONSOLETXT); textbackground( INVERTEDBG); 
      gotoxy( 9, 9);
      cprintf("%07lx", uchar2bin( 0x7f & ADDRESS));
  }

  void show_RW( void)
  {
      textcolor( CONSOLETXT); textbackground( INVERTEDBG); 
      gotoxy( 8,16);
      cprintf("%01x", ( 0x80 & ADDRESS) >> 7);
  }

  void show_GEN( void)
  {
      textcolor( INVERTEDTXT); textbackground( INVERTEDBG);
      gotoxy( 8,43); cprintf("%01x", (GEN & 4) >> 2);
      gotoxy( 8,44); cprintf("%01x", (GEN & 2) >> 1);
      gotoxy( 8,45); cprintf("%01x", (GEN & 1));
  }

  void show_PC( void)
  {
      textcolor( COUNTERTXT); textbackground( INVERTEDBG); 
      gotoxy( 39, 6);
      cprintf("%07lx", uchar2bin( RAM[ 0]));
  }

  void show_REG( long n)
  {
      if (n==7)
      {
          textcolor( INOUTPORTS); textbackground( INVERTEDBG);
      }
      else
      {
          textcolor( CONSOLETXT); textbackground( INVERTEDBG);
      }
      gotoxy( 38, 6 + n * 3);
      cprintf("%08lx", uchar2bin( RAM[ n]));
  }

  void show_SKIP2NZ( void)
  {
      textcolor( COUNTERTXT); textbackground( INVERTEDBG); 
      gotoxy( 52, 6);
      cprintf("%07lx", uchar2bin( SKIP3NZ()));
  }

  void show_ADD( void)
  {
      textcolor( CONSOLETXT); textbackground( INVERTEDBG);
      gotoxy( 63, 11); cprintf("%01x", uchar2bin( CARRY()));
      gotoxy( 56, 12); cprintf("%08lx", uchar2bin( ADD()));
  }

  void show_NEG( void)
  {
      textcolor( CONSOLETXT); textbackground( INVERTEDBG); 
      gotoxy( 56, 15);
      cprintf("%08lx", uchar2bin( NEG()));
  }

  void show_AND( void)
  {
      textcolor( CONSOLETXT); textbackground( INVERTEDBG); 
      gotoxy( 56, 18);
      cprintf("%08lx", uchar2bin( AND()));
  }

  void show_COM( void)
  {
      textcolor( CONSOLETXT); textbackground( INVERTEDBG); 
      gotoxy( 56, 21);
      cprintf("%08lx", uchar2bin( COM()));
  }

  void show_LSR( void)
  {
      textcolor( CONSOLETXT); textbackground( INVERTEDBG); 
      gotoxy( 56, 24);
      cprintf("%08lx", uchar2bin( LSR()));
  }

  void show_IN( void)
  {
      textcolor( INOUTPORTS); textbackground( INVERTEDBG); 
      gotoxy( 56, 27);
      cprintf("%08lx", uchar2bin( IN));
  }

  void show_RAMBYTE( long addr)
  {
      long  i,j;

      i = addr / 16;
      j = addr % 16;

      if (addr == SKIP3NZ())
      {
          textcolor( INVERTEDTXT); textbackground( INVERTEDBG);
      }
      else
      {
          textcolor( HEXCONSOLE); textbackground( BACKGROUND);
      }

      gotoxy(40 + i * 3, 31 + j);
      cprintf( "%02x", RAM[ addr]);
      gotoxy(40 + i * 3, 31 + j);
  }

  void show_RAM( void)
  {
      long  i,j;

      textcolor( HEXCONSOLE); textbackground( BACKGROUND);
      for( i=127; i>=0; i--) show_RAMBYTE( i);
  }

  void draw_CHIP( void)
  {
      window( 1, 1, 80, 50);
      clrscr();
      textcolor(WIRES); textbackground( BACKGROUND); 
      gotoxy(1,1);

      cprintf(
      "DR<============================================================================+"
      "                                  +----------+                     +--------+  |"
      "    +----------+               c1-|+|PCOUNTER|                  AB=|ADDR|MUX|  |"
      "DR==|D| DATA   |=>DW           DW=|D|        |    +-------+        |----|   |  |"
      "cc--> |76543210|     +-------+    | |        |====|SKIP3NZ|=>AC    |DATA|   |  |"
      "    +----------+  AB=|A|DM| 0|----> | 6543210|    |6543210|        |    |   |  |"
      "    +----------+     | |  |  |    +----------+    |       |     00-|0   |   |  |"
      "mx==|D| ADDRESS|     | |  |  | DW=|D|        |====|       |        |    |   |  |"
      "c0--> | 6543210|=>AB | |  | 1|----> |76543210|    +-------+----+   |    |   |  |"
      "    +----------+     |_|  |  |    +----------+    |       carry|---|1   |   |  |"
      "cc-----<-----------+ | |  |  | DW=|D|        |====|ADD        0|   |    |   |  |"
      "          +------+ | | |  | 2|----> |76543210| +==|    76543210|===|2   |   |  |"
      "c2----->--|D|DM|0|-+ | |  |  |    +----------+ |  +------------+   |    |   |  |"
      "    +---+ |-|  | |   | |  |  | DW=|D|        |=+==|NEG         |   |    |   |  |"
      "m7--|D|W| | |  | |   | |  | 3|----> |76543210|    |    76543210|===|3   |   |  |"
      "c0--> |0|-|A|  |1|---|D|  |  |    +----------+    +------------+   |    |   |  |"
      "    +---+ +------+   | |  |  | DW=|D|        |====|AND         |   |    |   |==+"
      "                     | |  | 4|----> |76543210| +==|    76543210|===|4   |   |   "
      "    F1 - Help        | |  |  |    +----------+ |  +------------+   |    |   |   "
      "    F2 - Save        | |  |  | DW=|D|        |=+==|COM         |   |    |   |   "
      "    F3 - Load        | |  | 5|----> |76543210|    |    76543210|===|5   |   |   "
      "                     | |  |  |    +----------+    +------------+   |    |   |   "
      "    Arrows           | |  |  | DW=|D|        |====|LSR         |   |    |   |   "
      "                     | |  | 6|----> |76543210|    3    76543210|===|6   |   |   "
      "    F4 - Microstep   | |  |  |    +----------+    +------------+   |    |   |   "
      "    F5 - Step        | |  |  | DW=|D|OUT     |    3IN  qwrtyuio|   |    |   |   "
      "                     | |  | 7|----> |76543210|    3    76543210|===|7   |   |   "
      "    F6 - Reset       | |  |  |    +----------+    +------------+   |    |   |   "
      "    F7 - Clear       | |  |  |         00 10 20 30 40 50 60 70     |    |   |   "
      "                     | |  |  |    +---------------------------+ +==|8-7f|   |   "
      "    F10  Exit        | |  | 8|----> |0|00 00 00 00 00 00 00 00| |  +--------+   "
      "                     | |  |  |    | |1|00 00 00 00 00 00 00 00| |               "
      "                     | |  | 9|----> |2|00 00 00 00 00 00 00 00| |  +--------+   "
      "                     | |  |  |    | |3|00 00 00 00 00 00 00 00| |  |DATA|MUX|   "
      "                     | |  |  |....| |4|00 00 00 00 00 00 00 00| |  |    |   |   "
      "                     | |  |  |    | |5|00 00 00 00 00 00 00 00|=+==|    |   |   "
      "                     | |  |  |    | |6|00 00 00 00 00 00 00 00|    |    |D0 |   "
      "                     | |  |ff|----> |7|00 00 00 00 00 00 00 00|    |    |...|=mx"
      "                     +-------+    | |8|00 00 00 00 00 00 00 00|    |    |D6 |   "
      "  +-----+                         | |9|00 00 00 00 00 00 00 00|    |    |   |   "
      "  |CLOCK|                         |_|A|00 00 00 00 00 00 00 00|    |    |   |   "
      "  |-----|                         | |B|00 00 00 00 00 00 00 00|    |    |   |   "
      "  |2|  0|--[c2]--->               | |C|00 00 00 00 00 00 00 00|    |    |   |   "
      "  |1|  1|--[c1]--->            DW=|D|D|00 00 00 00 00 00 00 00|    |    |D7 |-m7"
      "  |0|  2|--[c0]--->               | |E|00 00 00 00 00 00 00 00|    +-----   |   "
      "  +-----+                         | |F|00 00 00 00 00 00 00 00| AC=|ADDR|   |   "
      "                                  +---------------------------+    +--------+   "
      "                                                                                "
      "AB-address bus; AC-next cmd addr; DR-data read; DW-data write; c0,c1,c2,cc-clock"
      );

      textcolor( GREEN); 
      textbackground( INVERTEDBG); 
      gotoxy( 1, 50); 
      cprintf(" SIMPLE VON NEUMANN MICROCONTROLLER SIMULATOR.   By Kirill Nelus.  v.2008.03.03");
  }

  void show( void)
  {
      long  i;

      window( 1, 1, 80, 50);

      show_DATA();
      show_ADDR();
      show_RW();
      show_GEN();

      show_PC();
      for (i=1; i<=7; i++) show_REG( i);

      show_SKIP2NZ();
      show_ADD();
      show_NEG();
      show_AND();
      show_COM();
      show_LSR();
      show_IN();

      show_RAM();
  }

  long navigate( void)
  {
      long c = getch();
      if (c==0) c = getch() + 0x100;
      return c;
  }

  void clean( void)
  {
      long i;

      IN = DATA = ADDRESS = 0;

      GEN = 1;
      /* set PCOUNTER to first regular RAM location */
      RAM[ 0] = 8;
      for (i = 1; i<128; i++) RAM[ i] = 0;
  }

  void reset( void)
  {
      long i;

      IN = DATA = ADDRESS = 0;

      GEN     = 1;
      RAM[ 0] = 8;
      for (i=1; i<8; i++) RAM[ i] = 0;
  }

  long generator( void)
  {
      if (GEN & 1) ADDRESS = RAM[ SKIP3NZ()];
      if (GEN & 2)
      { 
         RAM[ 0]++; if (RAM[ 0] >= 128) RAM[ 0] = 8; 
      }
      if (GEN & 4)
         if ( ADDRESS & 0x80)
         {
            switch ( 0x7f & ADDRESS)
            {
               case 0x00: RAM[ 0x7f & ADDRESS] = 0x7f & DATA; 
                          break;
               case 0x07: outportb( DATAPORT, DATA);
               default  : RAM[ 0x7f & ADDRESS] = DATA;
            }
         }
         else
            DATA = RAMOUT( 0x7f & ADDRESS);
            
      GEN = GEN << 1;
      if (GEN > 4) GEN = 1;

      show();

      return GEN;
  }

  void load( void)
  {
      int           h, i;
      unsigned char c;

      h = open("CHIP.BIN", O_RDONLY | O_BINARY, S_IREAD);

      lseek(h, 0L, SEEK_SET);

      i = 8;
      while (!eof (h))
      {
          read(h, &c, 1);
          RAM[ i++] = c;
      }

      close(h);
  }

  void save( void)
  {
      int           h;

      h = open("CHIP.BIN", O_CREAT | O_BINARY | 
                           O_RDWR, S_IREAD | S_IWRITE);

      write(h, RAM+8, 120);
  }

  void main()
  {
      long  i;
      long  x, y;

      textmode( C80);
      textmode( C4350);

      clean();

      draw_CHIP();

      show();

      x = y = 0;

      do {
        i = navigate();

        switch (i)
        {
           case vkRIGHT: x = x - 1; break;
           case vkLEFT:  x = x + 1; break;
           case vkUP:    y = y - 1; break;
           case vkDOWN:  y = y + 1; break;

           case vkF2:    save(); break;
           case vkF3:    clean();
                         load(); break;


           case vkF4:    generator(); break;
           case vkF5:    if (GEN==1) generator();
                         if (GEN==2) generator();
                         if (GEN==4) generator(); break;
           case vkF6:    reset();     break;
           case vkF7:    clean();     break;

           case vk0:
           case vk1: 
           case vk2: 
           case vk3: 
           case vk4: 
           case vk5: 
           case vk6:
           case vk7: 
           case vk8: 
           case vk9: RAM[ x*16 + y] = (RAM[ x*16 + y] << 4) + 
                                      i - vk0; break;
           case vka: 
           case vkb: 
           case vkc: 
           case vkd:
           case vke: 
           case vkf: RAM[ x*16 + y] = (RAM[ x*16 + y] << 4) + 
                                      i - vka + 10; break;
           case vkA: 
           case vkB:
           case vkC: 
           case vkD:
           case vkE: 
           case vkF: RAM[ x*16 + y] = (RAM[ x*16 + y] << 4) + 
                                      i - vkA + 10; break;
           case vkq: 
           case vkQ: IN = IN ^ 0x80; show_IN(); break;
           case vkw: 
           case vkW: IN = IN ^ 0x40; show_IN(); break;
           case vkr:
           case vkR: IN = IN ^ 0x20; show_IN(); break;
           case vkt: 
           case vkT: IN = IN ^ 0x10; show_IN(); break;
           case vky: 
           case vkY: IN = IN ^ 0x08; show_IN(); break;
           case vku:
           case vkU: IN = IN ^ 0x04; show_IN(); break;
           case vki: 
           case vkI: IN = IN ^ 0x02; show_IN(); break;
           case vko:
           case vkO: IN = IN ^ 0x01; show_IN(); break;
        }

        if (x <  0) x =  0;
        if (x >  7) x =  7;
        if (y <  0) y =  0;
        if (y > 15) y = 15;

        RAM[ 0] = 0x7f & RAM[ 0];

        show();
        show_RAMBYTE( x*16 + y);


      } while ( i!=vkF10);

      textcolor( LIGHTGRAY);
      textbackground( BLACK);
      normvideo();
      clrscr();
  }
