201304190915


Контроллер на базе симулятора AQA( упрощенного процессора PIC)

  Условимся: PORTA - цифровые входы (SWITCHES).
             PORTB - цифровые выходы (LEDS).
             PORTС - аналоговый выход (DAC).
           
Программная часть будет состоять из трех частей.

  ; 1. Настройки транслятора (пусто).
  ;    Настройки симулятора - комментарии с указаниями.
   
  ; Connect SWITCHES to PORTA.
  ; Connect LEDS to PORTB
  ; Connect DAC to PORTC.
   
  ; 2. Настройки микроконтроллера.

    MOVW  0xFF     ; all inputs
    MOVWR TRISA   
    MOVWR PORTA    ; turn on pull-up resistors
    
    MOVW  0x00     ; all outputs (LEDS)
    MOVWR TRISB   
    MOVWR PORTB    ; clean output
    
    MOVW  0x00     ; all outputs (DAC inputs)
    MOVWR TRISC   
    MOVWR PORTC    ; clean output
   
  ; 3. Главный цикл контроллера.

    MAINLOOP:

    ; === start here ===

       MOVRW PORTA
       MOVWR PORTB
       MOVWR PORTC

    ; === end here ===

       JMP MAINLOOP
   
Таким образом, выполнение поставленной задачи сводится к изменению части программы между ; === start here === и ; === end here ===.

Эту часть можно создать

  - С помощью языка релейной логики
  - С помощью языка электронных схем
  - На одном из языков программирования, таких как Assembly, BASIC, C.
  
Перевод в требуемый код (в нашем случае это AQA Assembly source) осуществляет транслятор.

Для выполнения работы потребуется умение работы с компьютером, общее для популярных нынче сред Windows, MacOSX, iOS, Androild, Linux. Как правило, это умение работать с подходящим browser'ом и работа в командной строке (реже).


Задачи.

  1.  Дана релейно-контактная схема.

           im1         q1
      +---| |---+-----( )
      |         |
      |    i3   | 
      +---|/|---+
      |
      |    i1    i2    im1
      +---| |---| |---(/)
      
    Построить код главного цикла контроллера.
  
  2.  Дана цифровая схема
  
           +-----+
    a0 ----+   1 |    +-----+  --- b7 .. b4
           |     o----+   & |
    a1 ----+     |    |     +----- b3 .. b0
           +-----+    |     |
    a2 ---------------o     |
                      +------
                    
  Построить код главного цикла контроллера.
  
  3. Дано описание

    b0 =  ! a2 | ( a0 ^ a1 ) 
    b1 = b0   b2 = b0   b3 = b0   b4 = 0   b5 = 0   b6 = o0   b7 = o0

    Построить код главного цикла контроллера.
  
------------------------------------------------------------------------

Для того, чтобы прочесть формулу в задаче 3 (инфиксная запись) нужно знать больше, чем для постановки задачи 2 (цифровая схема), а именно:

  - таблицу обозначений операций ( ! не, | или, & и, () приоритет, = присвоить ..)
  
  - таблицу приоритетов операций
  
  - таблицу преобразования типов при совершении операций над данными, например -

  +--------+-----------------+-----------------+----------+-----------+
  | класс  | операнд 1       | операнд 2       | операция | результат |
  | опера- +-----------------+-----------------+----------+-----------+
  | ций    | байт, целое     | байт, целое     |          | байт  бит |
  +--------+-----------------+-----------------+----------+-----------+
  |        |  58             |     унарная     | ! не     | 0         |
  | логич. |  1              |  2              | | или    | 3         |
  |        |  3              |  4              | & и      | 0         |
  |        |  5              |  6              | ^ xor    | 3         |
  +--------+-----------------+-----------------+----------+-----------+
  |        |  7              |  8              | +        | f         |
  | арифм. |  8              |  9              | -        | ff        |
  |        |  a              |  b              | *        | 6e        |
  |        |  27             |  5              | /        | 5  2      |
  |        |  28             |  6              | %        | 4         |
  +--------+-----------------+-----------------+----------+-----------+
  |        |  58             |     унарная     | ~ не     | a7        |
  | биты   |  12             |  1              | >>       | 9         |
  |        |  12             |  1              | <<       | 24        |
  +--------+-----------------+-----------------+----------+-----------+
  |        |  12             |  10             | ==       |         0 |
  | сравн. |  12             |  10             | >=       |         1 |
  |        |  12             |  10             | <=       |         0 |
  |        |  12             |  10             | >        |         1 |
  |        |  12             |  10             | <        |         0 |
  +--------+-----------------+-----------------+----------+-----------+

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

  a3:        db    0        ; зарезервируем место для переменной a3
      ...
  Atoa3:     movrw PORTA    ; Читаем байт в порту A
             andw  0x08     ; бит 3 (7654[3]210) равен 1 ?
             jpz   seta3to0 ;
             movw  0x01     ; Установим в аккумуляторе (WORK) единицу
             movwr a3       ; и поместим ее в ячейку a3
             ret            ; выходим
  seta3to0:  movw  0x00     ; Установим в аккумуляторе (WORK) ноль
             movwr a3       ; и поместим его в ячейку a3
             ret            ; выходим
             
Осталось сказать, что следует проделать указанную процедуру для оставшихся семи битов и главное - все это легко сделать автоматически в программе-трансляторе.

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

  W = sin( x)   ==>   T = x
                      call sin

Теперь проще сделать операцию с битами - например w0 = w0 & t0 превратится в следующий код AQA.

  WeqWandT:    andw   0x01
               jpz    w0was0
               movrw  T
               andw   0x01
               ret
  w0was0:      movw   0x00
               ret

Для вас будет достаточно писать код между следующими комментариями:

  ; === start here ===
  ...
  ; === end here ===
  
Такой стиль называется RAD (rapid  application  development) а шаблон для вписывания кода (наподобие картинок "раскрась сам") называется wizard code. Получается, что даже в одиночку мы работаем в команде с волшебником (wizard) и роботом (computer). Удается сделать больше, чем, скажем 10 лет назад, потому  что мы доверяем тому, что созданные программы и железо работают, и не нужно создавать их заново.

Примеры программ для кода из приложения А.

Пример 1. Деление WORK на 2.
  
  ; === start here ===
  
  ; Logical shift WORK 1 bit right:
  
               call   lsrw

  ; === end here ===
  
Пример 2. PORTB = PORTA.4 & PORTA.0

  ; === start here ===
  
               call   splitW
               
               movrw  a4     ; T = a4
               movwr  T      ; --||--
               movrw  a0     ; W = a0
               
               call   WeqWandT

  ; === end here ===
  
Пример 3. PORTB = PORTA.4 | PORTA.0

  По сравнению с примером 2 нужно поменять вызов WeqWandT на WeqWorT.

Пример 4. PORTB = PORTA.4 ^ PORTA.0

  По сравнению с примером 2 нужно поменять вызов WeqWorT на WeqWxorT.
  
  
Весенняя рыбалка*
-----------------

При невозможности передать аргумент в подпрограмму очень полезным механизмом работы с памятью оказывается "стек". Запоминаем адрес указателя стека SP (для AQA 0xEF) и при желании положить на стек делаем MOV в ячейку [SP]: movwr 0xEF, после чего модифицируем команду с помощью эксплойта, обнаруженного в публикации "Микропроцессоры (level 1)". Наблюдаем заполнение стека на следующем примере:
 
  0000            movw    0xAA
  0001            call    push
  0002            call    push
  0003            call    push
  0004    push:   movwr   0xEF
  0005            dec     0x04
  0006            ret         

При втором вызове push указатель стека будет сдвинут на -1, то есть реально в памяти будет находится команда movrw 0xEE. Чтение из стека (процесс обратный записи) производится командой pop. Логичным было бы предположить, что промах с командой DEC не случаен, и поискать червяка в команде INC. И в самом деле, с ее помощью удается увеличить SP в команде MOVRW, но почему-то одновременно уменьшается на единицу код операции, что приводит к останову процессора. Нужен кряк (патч двоичного кода). 


Компилятор выражений
--------------------

        Языком танца индийская девушка рассказывала древнюю легенду. 
        Незная хинди, я сделал ошибочный вывод о том, что с ней 
        возможно говорить движениями. Поистинне, cлепая танцовщица была 
        живым воплощением реальности словосочетания Write Only..
         
Схема из примера 2 может быть легко преобразована в инфиксное выражение

  b0 = !(a0 | a1) & !a2
  
но делать этого не нужно, потому что в итоге, машинный код ближе к RPN (Reverse Polish Notation) Яна Лукашевича:

  movrw   a0
  movwr   T          ; T = a0
  movrw   a1         ; W = a1
  call    WeqWorT    ; w0 = w0 | t0
  xorw    0x01       ; w0 = !w0
  movwr   T          ; T  = W
  movrw   a2         ; W  = a2
  xorw    0x01       ; w0 = !w0
  call    WeqWandT   ; w0 = w0 & T
               
То есть:

  a0 a1 | ! a2 ! => b0
  
Абзац который легче всего найти в этом тексте по словам "раскрась сам" в значительной степени объясняет нам, почему сегодня невыгодно брать язык C для изготовления компилятора выражений - значительно лучше подойдут PHP, Python, Ruby. Выбираем язык С. Помимо того, что это cool (англ. "прикольно"), причин несколько:

  1. Есть готовый исходник на сайте:
  
        http://en.wikipedia.org/wiki/Shunting-yard_algorithm#The_algorithm_in_detail
        
     который очень легко приспособить под нашу задачу.
     
  2. Мы уже программировали на С микроконтроллеры, теперь займемся трансляторами.

------------------------------------------------------------------------

  Приложение А. Контроллер из симулятора AQA. Добавилось две секции "Переменные" (в начале) и "Функции" (в конце).

  ; Connect SWITCHES to PORTA.
  ; Connect LEDS to PORTB
  ; Connect DAC to PORTC.
   
               jmp    SETUP
             
  a7:          db     0x00        
  a6:          db     0x00        
  a5:          db     0x00        
  a4:          db     0x00        
  a3:          db     0x00        
  a2:          db     0x00        
  a1:          db     0x00        
  a0:          db     0x00        
  b7:          db     0x00        
  b6:          db     0x00
  b5:          db     0x00
  b4:          db     0x00
  b3:          db     0x00
  b2:          db     0x00
  b1:          db     0x00
  b0:          db     0x00
  
  T:           db     0x00
             
  SETUP:     
               movw  0xFF
               movwr TRISA
               movwr PORTA
               
               movw  0x00
               movwr TRISB   
               movwr PORTB
               
               movw  0x00
               movwr TRISC   
               movwr PORTC
   
  MAINLOOP:

               movrw  PORTA

  ; === start here ===

               call   splitW
               
               movrw  a4     ; T = a4
               movwr  T      ; --||--
               movrw  a0     ; W = a0
               
               call   WeqWxorT

  ; === end here ===
               
               movwr  PORTB  ; PORTB = W

               jmp MAINLOOP

  ; Both W, Т must contain 00h or 01h
  ; -[ W = W ^ T ]--------------------
  
  WeqWxorT:    andw   0x01
               jpz    xorWwas0
               movrw  T
               xorw   0x01
               ret
  xorWwas0:    movrw  T
               xorw   0x00
               ret
               
  ; Both W, Т must contain 00h or 01h
  ; -[ W = W | T ]--------------------
  
  WeqWorT:     andw   0x01
               jpz    orWwas0
               ret
  orWwas0:     movrw  T
               ret
  
  ; Both W, Т must contain 00h or 01h
  ; -[ W = W & T ]--------------------
  
  WeqWandT:    andw   0x01
               jpz    andWwas0
               movrw  T
               andw   0x01
               ret
  andWwas0:    movw   0x00
               ret
  
  ; -[ W = W >> 1 ]-------------------
  
  lsrw:        movwr  T
               andw   0x02
               jpz    w1was0
               movrw  T
               orw    0x01
               jmp    tw2
  w1was0:      movrw  T
               andw   0xfe
  tw2:         movwr  T
               andw   0x04
               jpz    w2was0
               movrw  T
               orw    0x02
               jmp    tw3
  w2was0:      movrw  T
               andw   0xfd
  tw3:         movwr  T
               andw   0x08
               jpz    w3was0
               movrw  T
               orw    0x04
               jmp    tw4
  w3was0:      movrw  T
               andw   0xfb
  tw4:         movwr  T
               andw   0x10
               jpz    w4was0
               movrw  T
               orw    0x08
               jmp    tw5
  w4was0:      movrw  T
               andw   0xf7
  tw5:         movwr  T
               andw   0x20
               jpz    w5was0
               movrw  T
               orw    0x10
               jmp    tw6
  w5was0:      movrw  T
               andw   0xef
  tw6:         movwr  T
               andw   0x40
               jpz    w6was0
               movrw  T
               orw    0x20
               jmp    tw7
  w6was0:      movrw  T
               andw   0xdf
  tw7:         movwr  T
               andw   0x80
               jpz    w7was0
               movrw  T
               orw    0x40
               jmp    tw8
  w7was0:      movrw  T
               andw   0xbf
               jmp    tw8
  tw8:         movwr  T
               andw   0x7f
               ret
               
  ; -[ A : { a7, a6, ... a0 } ]-------
                
  splitW:      call   Atoa7
               call   Atoa6
               call   Atoa5
               call   Atoa4
               call   Atoa3
               call   Atoa2
               call   Atoa1
               call   Atoa0
               ret

  ; -[ a7 = A.7 ]---------------------

  Atoa7:       movrw  PORTA    
               andw   0x80     
               jpz    seta7to0 
               movw   0x01     
               movwr  a7       
               ret             
  seta7to0:    movw   0x00     
               movwr  a7       
               ret             
                    
  ; -[ a6 = A.6 ]---------------------

  Atoa6:       movrw  PORTA    
               andw   0x40     
               jpz    seta6to0 
               movw   0x01     
               movwr  a6       
               ret             
  seta6to0:    movw   0x00     
               movwr  a6       
               ret             
                    
  ; -[ a5 = A.5 ]---------------------

  Atoa5:       movrw  PORTA    
               andw   0x20     
               jpz    seta5to0 
               movw   0x01     
               movwr  a5       
               ret             
  seta5to0:    movw   0x00     
               movwr  a5       
               ret             
                    
  ; -[ a4 = A.4 ]---------------------

  Atoa4:       movrw  PORTA    
               andw   0x10     
               jpz    seta4to0 
               movw   0x01     
               movwr  a4       
               ret             
  seta4to0:    movw   0x00     
               movwr  a4       
               ret             
                    
  ; -[ a3 = A.3 ]---------------------

  Atoa3:       movrw  PORTA    
               andw   0x08     
               jpz    seta3to0 
               movw   0x01     
               movwr  a3       
               ret             
  seta3to0:    movw   0x00     
               movwr  a3       
               ret             
                    
  ; -[ a2 = A.2 ]---------------------

  Atoa2:       movrw  PORTA    
               andw   0x04     
               jpz    seta2to0 
               movw   0x01     
               movwr  a2       
               ret             
  seta2to0:    movw   0x00     
               movwr  a2       
               ret             
                    
  ; -[ a1 = A.1 ]---------------------

  Atoa1:       movrw  PORTA    
               andw   0x02     
               jpz    seta1to0 
               movw   0x01     
               movwr  a1       
               ret             
  seta1to0:    movw   0x00     
               movwr  a1       
               ret             
                    
  ; -[ a0 = A.0 ]---------------------

  Atoa0:       movrw  PORTA    
               andw   0x01     
               jpz    seta0to0 
               movw   0x01     
               movwr  a0       
               ret             
  seta0to0:    movw   0x00     
               movwr  a0       
               ret             

----------

  Приложение Б. Конвертер инфиксных выражений в RPN.

  /* 
    На базе http://en.wikipedia.org/wiki/Shunting-yard_algorithm
    gcc toRPN.c -o toRPN
   */

  #include <wchar.h>
  #include <string.h>
  #include <stdio.h>
  #define bool int
  #define false 0
  #define true 1
   
  /*
    Precedence   Operator   Associativity
    -------------------------------------------- 
    9           !           RL
    8           ~           RL
    7           + -         LR
    6           «  »        LR
    5           <  >  =     LR
    4           &           LR
    3           ^           LR
    2           |           LR
    1           ?           LR
  */

  int op_preced(const wchar_t c)
  {
    switch(c)    {
      case L'!':
        return 9;
      case L'~':
        return 8;
      case L'+':  case '-':
        return 7;
      case L'«':  case L'»': /* <<  >>    */
        return 6;
      case L'<':  case L'>': case L'=':
        return 5;
      case L'&':
        return 4;
      case L'^':
        return 3;
      case L'|':
        return 2;
      case L'?':            /* ?:        */
        return 1;
    }
    return 0;
  }
   
  bool op_left_assoc(const wchar_t c)
  {
    switch(c)    {
      case L'«': case L'»': 
      case L'<': case L'>': case L'=':
      case L'+': case L'-':
      case L'&': case L'^': case L'|': 
      case L'?':
        return true;
      case L'!': case L'~':
        return false;
    }
    return false;
  }
   
  unsigned int op_arg_count(const wchar_t c)
  {
    switch(c)  {
      case L'?':
        return 3;
      case L'«': case L'»': 
      case L'<': case L'>': case L'=':
      case L'+': case L'-':
      case L'&': case L'^': case L'|':
        return 2;
      case L'!': case L'~':
        return 1;
      default:
        return c - L'A';
    }
    return 0;
  }
   
  #define is_operator(c)  (c == L'!' || c == L'~' || c == L'«' ||      \
                           c == L'»' || c == L'<' || c == L'>' ||      \
                           c == L'=' || c == L'+' || c == L'-' ||      \
                           c == L'&' || c == L'^' || c == L'|' ||      \
                           c == L'?')
                           
  #define is_ident(c)     ((c >= L'0' && c <= L'9') ||                 \
                           (c >= L'a' && c <= L'z'))
   
  bool shunting_yard(const wchar_t *input, wchar_t *output)
  {
    const wchar_t *strpos = input, *strend = input + wcslen(input);
    wchar_t c, *outpos = output;
   
    wchar_t      stack[32];     
    unsigned int sl = 0;
    wchar_t      sc;        
   
    while(strpos < strend)   {
      c = *strpos;
      if(c != ' ')    {
        if(is_ident(c))  {
          *outpos = c; ++outpos;
        }
        else if(c == ',')   {
          bool pe = false;
          while(sl > 0)   {
            sc = stack[sl - 1];
            if(sc == '(')  {
              pe = true;
              break;
            }
            else  {
              *outpos = sc; 
              ++outpos;
              sl--;
            }
          }
          if(!pe)   {
            printf("%ls\n",
                   L"Error: separator or parentheses mismatched");
            return false;
          }
        }
        else if(is_operator(c))  {
          while(sl > 0)    {
            sc = stack[sl - 1];
            if(is_operator(sc) &&
              ((op_left_assoc(c) && (op_preced(c) <= op_preced(sc))) ||
                 (op_preced(c) < op_preced(sc))))   {
              *outpos = sc; 
              ++outpos;
              sl--;
            }
            else   {
              break;
            }
          }
          stack[sl] = c;
          ++sl;
        }
        else if(c == L'(')   {
          stack[sl] = c;
          ++sl;
        }
        else if(c == L')')    {
          bool pe = false;
          while(sl > 0)     {
            sc = stack[sl - 1];
            if(sc == L'(')    {
              pe = true;
              break;
            }
            else  {
              *outpos = sc; 
              ++outpos;
              sl--;
            }
          }
          if(!pe)  {
            printf("%ls\n", L"Error: parentheses mismatched");
            return false;
          }
          sl--;
          if(sl > 0)   {
            sc = stack[sl - 1];
          }
        }
        else  {
          printf("%ls %lc\n", L"Unknown token", c);
          return false;
        }
      }
      ++strpos;
    }
    while(sl > 0)  {
      sc = stack[sl - 1];
      if(sc == L'(' || sc == L')')   {
        printf("%ls\n", L"Error: parentheses mismatched");
        return false;
      }
      *outpos = sc; 
      ++outpos;
      --sl;
    }
    *outpos = 0;
    return true;
  }
   
  bool execution_order(const wchar_t *input) {
    printf("%ls\n", L"order:");
    const wchar_t *strpos = input, *strend = input + wcslen(input);
    char c, res[4];
    unsigned int sl = 0, sc, stack[32], rn = 0;
    while(strpos < strend) {
      c = *strpos;
      if(is_ident(c)) {
        stack[sl] = c;
        ++sl;
      }
      else if(is_operator(c)) {
        sprintf(res, "_%02d", rn);
        printf("%s = ", res);
        ++rn;
        unsigned int nargs = op_arg_count(c);
        if(sl < nargs) {
          return false;
        }
        if(nargs == 1) {
          sc = stack[sl - 1];
          sl--;
          printf("%lc %ls;\n", c, &sc);
        }
        else {
          sc = stack[sl - 2];
          printf("%ls %lc ", &sc, c);
          sc = stack[sl - 1];
          sl--;sl--;
          printf("%ls;\n",&sc);
        }
      stack[sl] = *(unsigned int*)res;
      ++sl;
      }
      ++strpos;
    }
    if(sl == 1) {
      sc = stack[sl - 1];
      sl--;
      printf("%ls %ls\n", &sc, L"is a result");
      return true;
    }
    return false;
  }
   
  int main() {
    const wchar_t *input = L"~(f - b) & !(c + d) | e";
    wchar_t output[128];
    printf("%ls %ls\n", L"input:", input);
    if(shunting_yard(input, output)) {
      printf("%ls %ls\n", L"output:", output);
      if(!execution_order(output))
        printf("\n%ls\n", L"Invalid input");
    }
    return 0;
  }

----------

  Приложение В. Калькулятор RPN

  /*
    На базе http://www.math.bas.bg/bantchev/place/rpn/rpn.c.html
    
    gcc rpncalc.c -o rpncalc
  */
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <ctype.h>
  #include <setjmp.h>

  char *pb, *p;
  jmp_buf env;

  int eval()  {
    int x, qry, ya, nop;  
    char *q1, *q2;
    if (isspace(*--p))  {
      while (p>pb && isspace(*p))  --p;
      if (p>pb)  switch (*p)  {
          case '~': return ~ eval();
          case '}': return eval() >> 1;
          case '{': return eval() << 1;
          
          case '!': return ! eval();
          case '&': return eval() & eval();
          case '|': return eval() | eval();
          case '^': return eval() ^ eval();
          
          case '+': return eval() + eval();
          case '*': return eval() * eval();
          case '-': return eval() - eval();
          case '/': return eval() / eval();
          case '%': return eval() % eval();
          
          case '>': return eval() > eval();
          case '<': return eval() < eval();
          case '=': return eval() == eval();

          case '?': /* attention ! lazy evaluation */
                    qry = eval(); ya = eval(); nop = eval();
                    return qry ? ya : nop;
          
          default : q1 = p+1;
                    while (!isspace(*--p))  ;
                    if (x=strtod(++p,&q2),q2==q1) return x;
      }
    }
    longjmp(env,0);
  }

  int main()  {
    int sz,c,n;
    int x;
    pb = (char*)malloc(sz=4);
    *pb = ' ';
    p = pb+1;
    while ((c=getchar())!=EOF)
      if (c!='\n')  {
        n = p-pb;
        if (n+2==sz)  pb = (char*)realloc(pb,sz*=2);
        p = pb+n;
        *p++ = c;
      }  else  {
        while (--p>pb && isspace(*p))  ;
        if (p++>pb)  {
          *p++ = ' ';
          if (!setjmp(env))  {
            x = eval();
            while (--p>pb && isspace(*p))  ;
            if (p>pb)  longjmp(env,0);
            printf("%d\n",x);
          }
          else  printf("error\n");
          p = pb+1;
        }
      }
    return 0;
  }

----------

  Приложение Г. Тест-контейнер самодельного пятиэтажного стека

  ; custom stack (5 floors)

            jmp    start

  stk0:     db     0x0
  stk1:     db     0x0
  stk2:     db     0x0
  stk3:     db     0x0
  stk4:     db     0x0
  stkt:     db     0x0
  stkp:     db     stk4
  
   start:   movw   0x0
            movwr  portb
            call   wait
      
            call   initstk
            movw   0x1
            movwr  portb
            call   pushw
            movw   0x2
            movwr  portb
            call   pushw
            movw   0x3
            movwr  portb
            call   pushw
            movw   0x4
            movwr  portb
            call   pushw
            movw   0x5
            movwr  portb
            call   pushw
            
            movw   0xff
            movwr  portb
            call   wait
            
            call   popw
            movwr  portb
            call   popw
            movwr  portb
            call   popw
            movwr  portb
            call   popw
            movwr  portb
            call   popw
            movwr  portb
            
            jmp    start
            
     wait:  nop
            ret
  
  initstk:  movw   stk4
            movwr  stkp
            ret
            
    pushw:  movwr  stkt
            movrw  stkp
            xorw   stk4
            jpz    wpos4
            movrw  stkp
            xorw   stk3
            jpz    wpos3
            movrw  stkp
            xorw   stk2
            jpz    wpos2
            movrw  stkp
            xorw   stk1
            jpz    wpos1
            movrw  stkp
            xorw   stk0
            jpz    wpos0
            ret
    wpos4:  movrw  stkt
            movwr  stk4
            dec    stkp
            ret
    wpos3:  movrw  stkt
            movwr  stk3
            dec    stkp
            ret
    wpos2:  movrw  stkt
            movwr  stk2
            dec    stkp
            ret
    wpos1:  movrw  stkt
            movwr  stk1
            dec    stkp
            ret
    wpos0:  movrw  stkt
            movwr  stk0
            dec    stkp
            ret

     popw:  inc    stkp
            movrw  stkp
            xorw   stk4
            jpz    rpos4
            movrw  stkp
            xorw   stk3
            jpz    rpos3
            movrw  stkp
            xorw   stk2
            jpz    rpos2
            movrw  stkp
            xorw   stk1
            jpz    rpos1
            movrw  stkp
            xorw   stk0
            jpz    rpos0
            ret
    rpos4:  movrw  stk4
            ret
    rpos3:  movrw  stk3
            ret
    rpos2:  movrw  stk2
            ret
    rpos1:  movrw  stk1
            ret
    rpos0:  movrw  stk0
            ret

----------

  Приложение Д. Тест-контейнер схемы отображающей двоичный код набранный переключателями в порту A на семисегментом индикаторе подключенном к порту B.

            jmp    SETUP

    T:      db     0x00
    TT:     db     0x00

    SETUP:  movw   0xff
            movwr  TRISA
            movwr  PORTA
            movw   0x00
            movwr  TRISB
            movwr  PORTB  

    LOOP:   movrw  PORTA
            movwr  TT
            call   RCC
            movwr  PORTB

            movrw  TT
            call   LCC
            movwr  PORTB
            jmp    LOOP

    RCC:    movwr  T
            andw   0x08
            jpz    rc0xxx
            jmp    rc1xxx
            
    ; ------------------

    rc0xxx: movrw  T
            andw   0x04
            jpz    rc00xx
            jmp    rc01xx
            
    rc1xxx: movrw  T
            andw   0x04
            jpz    rc10xx
            jmp    rc11xx
            
    ; ------------------
          
    rc00xx: movrw  T
            andw   0x02
            jpz    rc000x
            jmp    rc001x

    rc01xx: movrw  T
            andw   0x02
            jpz    rc010x
            jmp    rc011x
            
    rc10xx: movrw  T
            andw   0x02
            jpz    rc100x
            jmp    rc101x
            
    rc11xx: movrw  T
            andw   0x02
            jpz    rc110x
            jmp    rc111x

    ; ------------------
            
    rc000x: movrw  T
            andw   0x01
            jpz    rc0000
            jmp    rc0001

    rc001x: movrw  T
            andw   0x01
            jpz    rc0010
            jmp    rc0011

    rc010x: movrw  T
            andw   0x01
            jpz    rc0100
            jmp    rc0101

    rc011x: movrw  T
            andw   0x01
            jpz    rc0110
            jmp    rc0111

    rc100x: movrw  T
            andw   0x01
            jpz    rc1000
            jmp    rc1001

    rc101x: movrw  T
            andw   0x01
            jpz    rc1010
            jmp    rc1011

    rc110x: movrw  T
            andw   0x01
            jpz    rc1100
            jmp    rc1101

    rc111x: movrw  T
            andw   0x01
            jpz    rc1110
            jmp    rc1111

    ; ------------------

    rc0000: movw   0x7d
            ret        
                       
    rc0001: movw   0x05
            ret        
                       
    rc0010: movw   0x5b
            ret        

    rc0011: movw   0x4f
            ret        

    rc0100: movw   0x27
            ret        

    rc0101: movw   0x6e
            ret        

    rc0110: movw   0x7e
            ret        

    rc0111: movw   0x45
            ret        

    rc1000: movw   0x7f
            ret        

    rc1001: movw   0x6f
            ret        

    rc1010: movw   0x77
            ret        

    rc1011: movw   0x3e
            ret        

    rc1100: movw   0x1a
            ret        

    rc1101: movw   0x1f
            ret        

    rc1110: movw   0x7a
            ret        

    rc1111: movw   0x72
            ret        


    LCC:    movwr  T
            andw   0x80
            jpz    lc0xxx
            jmp    lc1xxx
            
    ; ------------------

    lc0xxx: movrw  T
            andw   0x40
            jpz    lc00xx
            jmp    lc01xx
            
    lc1xxx: movrw  T
            andw   0x40
            jpz    lc10xx
            jmp    lc11xx
            
    ; ------------------
          
    lc00xx: movrw  T
            andw   0x20
            jpz    lc000x
            jmp    lc001x

    lc01xx: movrw  T
            andw   0x20
            jpz    lc010x
            jmp    lc011x
            
    lc10xx: movrw  T
            andw   0x20
            jpz    lc100x
            jmp    lc101x
            
    lc11xx: movrw  T
            andw   0x20
            jpz    lc110x
            jmp    lc111x

    ; ------------------
            
    lc000x: movrw  T
            andw   0x10
            jpz    lc0000
            jmp    lc0001

    lc001x: movrw  T
            andw   0x10
            jpz    lc0010
            jmp    lc0011

    lc010x: movrw  T
            andw   0x10
            jpz    lc0100
            jmp    lc0101

    lc011x: movrw  T
            andw   0x10
            jpz    lc0110
            jmp    lc0111

    lc100x: movrw  T
            andw   0x10
            jpz    lc1000
            jmp    lc1001

    lc101x: movrw  T
            andw   0x10
            jpz    lc1010
            jmp    lc1011

    lc110x: movrw  T
            andw   0x10
            jpz    lc1100
            jmp    lc1101

    lc111x: movrw  T
            andw   0x10
            jpz    lc1110
            jmp    lc1111

    ; ------------------

    lc0000: movw   0xfd
            ret        
                       
    lc0001: movw   0x85
            ret        
                       
    lc0010: movw   0xdb
            ret        

    lc0011: movw   0xcf
            ret        

    lc0100: movw   0xa7
            ret        

    lc0101: movw   0xee
            ret        

    lc0110: movw   0xfe
            ret        

    lc0111: movw   0xc5
            ret        

    lc1000: movw   0xff
            ret        

    lc1001: movw   0xef
            ret        

    lc1010: movw   0xf7
            ret        

    lc1011: movw   0xbe
            ret        

    lc1100: movw   0x9a
            ret        

    lc1101: movw   0x9f
            ret        

    lc1110: movw   0xfa
            ret        

    lc1111: movw   0xf2
            ret        
