Трансляция всех модификаций команд add и cmp

                               Введение

         Для написания пользователем программ на различных языках программи-

    рования необходимо иметь средства их перевода в машинный язык.  Это  вы-

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

    рами.  Трансляторы для микроЭВМ преобразуют исходную программу на  одном

    из языков программирования в некоторую стандартную  форму,    называемую

    объектной программой. Существует три вида трансляторов: ассемблеры, ком-

    пиляторы и интерпретаторы.

         Транслятор, преобразующий программу,  написанную на языке ассембле-

    ра, в машинный код,  называется ассемблером.  При написании программы на

    языке ассемблера программист использует мнемонические обозначения машин-

    ных команд и адресов (имена и метки).  В процессе  трансляции  ассемблер

    заменяет все мнемонические обозначения (коды команд и имена) их двоичны-

    ми кодами.  Для сокращения текста при повторении идентичных частей прог-

    раммы в отдельные языки ассемблера введены средства написания и обработ-

    ки макрокоманд. Это позволяет программисту определить некоторую последо-

    вательность команд как макроопределение, которое обрабатывается макроас-

    семблером (макропроцессором). Последний представляет тексты макроопреде-

    лений с соответствующими  фактическими  параметрами  макровызова  вместо

    макрокоманд в программе.

         Простейший ассемблер является однопроходным и преобразует  исходную

    программу за один просмотр. Но при этом возникают трудности, связанные с

    вычислением адресов имен,  которые определены позже.  Этого можно  избе-

    жать, потребовав, чтобы все имена данных были объявлены заранее, а неоп-

    ределенные адреса заносились в таблицу,  в которую вводятся ссылки  впе-

    ред. Эта таблица либо используется для модификации команды, либо загруз-

    чиком, который может формировать данный адрес во время загрузки.  Однако

    это не всегда удобно,  поэтому большинство ассемблеров выполняют  в  два

    прохода.

         Основная идея двухпроходного ассемблера проста.  На первом  проходе

    все символы (имена, метки) собираются в таблицу символов с соответствую-

    щими значениями адресов,  на втором генерируется машинная  программа  на

    основании построенной на первом проходе таблицы символов.

         Если язык ассемблера включает средства макрорасширений,  то макроп-

    роцессор реализуется различными способами.  Он может быть добавлен к ас-

    семблеру как препроцессор для выполнения просмотра исходного текста  пе-

    ред первым проходом ассемблера.  В результате  препроцессирования  полу-

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

    тексты макроопределений, если они есть в исходной программе,  сохраняют-

    ся, а вместо макровызовов подставляются ассемблерные команды из макрооп-

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

    ассемблера, что сокращает время трансляции, но удлиняет текст ассемблера.

;█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█

;█ -----    ТРАНСЛЯЦИЯ ВСЕХ МОДИФИКАЦИЙ КОМАНДЫ ADD       ----- █

;█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█

      .MODEL TINY

      .CODE

      JUMPS

      ORG 100h

begin:

      jmp   general

;┌──────────────────────────────────────┐

;│ Макрос вывода на экран строки stirng │

;└──────────────────────────────────────┘

Wrt   macro string

      mov   ah,9              ; Функция вывода строки

      lea   dx,string         ; Загружаем адрес строки

      int   21h               ; Вызов прерывания DOS

endm

;┌──────────────────────────────┐

;│ Основная процедура программы │

;└──────────────────────────────┘

general     proc  near

      push  cs                ; Все

      push  cs                ;   сегменты

      push  cs                ;     приравниваем

      pop   ds                ;     к сегменту

      pop   es                ;           кода

      pop   ss                ; (необходимо для строковых операций)

      Wrt   about             ; Вывод информации о программе

; Начинаем разбор строки параметров

      mov   cl,ds:[80h]       ; Получаем длину строки параметров

      sub   ch,ch             ; Обнуляем ch

      cmp   cx,2              ; Смотрим длину строки параметров

      jb    source_error            ; Если она меньше двух - нет вх.файла

      mov   di,82h            ; Заносим в DI адрес начала поиска

      mov   path1,di          ; Запоминаем адрес входного файла

      mov   al,' '            ; Искать будем до первого пробела

      repne scasb             ; Запускаем цикл поиска

      jne   dest_error        ; Если не нашли значет нет вых. файла

      mov   byte ptr [di-1],0 ; Приводим к формату ASCIIZ

      mov   path2,di          ; Запоминаем адрес выходного файла

      mov   cl,ds:[80h]       ; Заносим в cl длину строки поиска

      sub   ch,ch             ; Обнуляем ch

      mov   al,13             ; Искать будем до символа CR

      repne scasb             ; Запуск цикла поиска

      jne   dest_error        ; Если не нашли значет нет вых. файла

      mov   byte ptr [di-1],0 ; Приводим к формату ASCIIZ

      call  Fopen             ; Открываем входной файл

      jc    open_source_error ; Если ошибка, выдаем сообщение

      call  Fcreate                 ; Создаем выходной файл

      jc    create_dest_error ; Если ошибка, выдаем сообщение

gen1: call  Fread             ; Считываем очередную строку из файла

      jc    read_error        ; Переход если ошибка

      cmp   EOF,1             ; Смотрим признак конца файла

      jne   gen2              ; Если не конец, продолжаем

      cmp   si,0              ; Проверяем длину строки

      jne   gen2              ; Если не 0 символов продолжаем

      jmp   read_error        ; Иначе выводим сообщение об ошибке

gen2: inc   string                  ; Увеличиваем счетчик строк

      mov   strlen,si         ; Запоминаем длину строки

      call  DelSpc                  ; Вызываем процедуру удаления пробелов

      cmp   strlen,2          ; Если длина строки 2 символа, то

      je    gen1              ; переходим на чтение следующей

      lea   di,buffer         ; Загружаем адрес строки

      cmp   byte ptr [di],';' ; Если первый символ строки ; то

      je    gen1              ; переходим на чтение следующей

      lea   si,command        ; Будем искать строку ADD

      mov   cx,3              ; Длина искомой строки 3 символа

      repe  cmpsb             ; Начинаем поиск

      je    gen3              ; Переход, если ADD найдена

      wrt   error06                 ; Вывод сообщения об ошибке

      call  WrtStr                  ; Вывод ошбочной строки

      mov   was_error,1       ; Устанавливаем флаг ошибки

      jmp   gen1              ; Переход на чтение следующей строки

gen3: call  Coding

      cmp   was_error,0

      jne   gen1

      call  Fwrite

      jnc   gen1

      ret

gen4: mov   bx,dest                 ; Если трансляция проведена успешно

      call  Fclose                  ; Закрываем выходной файл

      Wrt   mes2              ; Вывод сообщения о завершении

      Wrt   mes3              ; Вывод сообщения о строках

      call  WrtStr                  ; Вывод числа обработаных строк

      Wrt   mes1              ; Вывод CR,LF

      ret                     ; ВЫХОД В DOS

; Далее располагаются метки перехода по различным ошибкам

source_error:

      Wrt   error01

      ret

dest_error:

      Wrt   error02

      ret

open_source_error:

      Wrt   error03

      ret

create_dest_error:

      Wrt   error04

      ret

read_error:

      cmp   EOF,1             ; Смотрим был ли конец файла

      je    skip_err_message  ; Если да, то это не ошибка

      Wrt   error05                 ; Иначе выводим сообщение об ошибке

      mov   was_error,1       ; Устанавливаем флаг ошибки

skip_err_message:

      mov   bx,source         ; Заносим дескриптор выходного файла

      call  Fclose                  ; Закрываем выходной файл

      cmp   was_error,0       ; Смотрим флаг ошибки

      je    gen4              ; Если ошибок нет, то переход

      call  Fdel              ; Иначе удаляем выходной файл

      Wrt   mes1              ; Вывод CR,LF

      Wrt   bell              ; Вывод сигнала

      ret

general     endp

;┌──────────────────────────────────────────────────────┐

;│  Переводит значение регистра AX в десятичный формат     │

;└──────────────────────────────────────────────────────┘

Decimal     proc  near

      call  clear             ; Очищаем строку вывода

      mov   cx,0010                 ; Заносим в CX делитель

      lea   si,outstr+4       ; Указатель на конец строки

b20:  cmp   ax,0010                 ; Сравниваем AX с 10

      jb    short b30         ; Переход, если меньше

      xor   dx,dx             ; Обнуляем DX

      div   cx                ; Делем AX на CX

      or    dl,30h                  ; Добавляем 48, чтобы получить 1

      mov   [si],dl                 ; Заносим в выходную строку

      dec   si                ; Движемся влево

      jmp   short b20         ; Переход на следующий символ

b30:  or    al,30h                  ; Добавляем 48, чтобы получить 1

      mov   [si],al                 ; Заносим в выходную строку

      ret

; Вспомогательная процедура. Очищает строку outstr (забивает нулями)

clear proc  near

      lea   si,outstr         ; Загружаем адрес выходной строки

      mov   cl,48             ; 48 - символ нуля

      mov   [si],cl                 ; Забиваем

      mov   [si+1],cl         ;    нулями

      mov   [si+2],cl         ;       все

      mov   [si+3],cl         ;         символы

      mov   [si+4],cl         ;            строки

      ret                     ; Выход

clear endp

outstr      db    '     $'

Decimal     endp

;┌──────────────────────────────┐

;│ Процедура вывода числа строк │

;└──────────────────────────────┘

WrtStr      proc  near

      xor   ax,ax             ; Обнуление регистра AX

      mov   al,string         ; В AL число обработанных строк

      call  Decimal                 ; Преобразуем в десятичный формат

      Wrt   outstr                  ; Выводим на экран

      ret                     ; Выход

WrtStr      endp

;┌─────────────────────────────┐

;│ Процедура удаления пробелов │

;└─────────────────────────────┘

DelSpc      proc  near

      cld                     ; Устанавливаем флаг направления

      lea   si,buffer         ; Заносим в SI адрес буфера

      mov   di,si             ; Дублируем в DI

      mov   cx,strlen         ; В CX длина считанной строки

      xor   bx,bx             ; Обнуляем BX

      mov   dl,1              ; Условие цикла

del1: lodsb                   ; Загружаем в AL один байт

      cmp   al,65             ; A?

      jb    del2              ; Переход если меньше

      cmp   al,90             ; Z?

      ja    del2              ; Переход если больше

      add   al,32             ; Иначе перевод F-f

      xor   dl,dl             ; Обнуляем DL

      jmp   del4              ; Переход на запись символа

del2: cmp   al,' '                  ; Пробел?

      je    del3              ; Если да, то переход

      xor   dl,dl             ; Обнуляем DL

      jmp   del4              ; Переход на запись символа

del3: cmp   dl,1              ; DL=1?

      je    del5              ; Если да, то переход

      mov   dl,1              ; Заносим в DL 1

del4: stosb                   ; Запись символа в строку

      inc   bx                ; Увеличиваем счетчик символов

del5: loop  del1              ; Цикл

      mov   strlen,bx         ; Заносим длину преобразованной строки

      ret                     ; Выход

DelSpc      endp

;┌──────────────────────────┐

;│ Процедура открытия файла │

;└──────────────────────────┘

Fopen proc  near

      mov   ah,3dh            ; Функция открытия файла

      xor   al,al             ; Открываем на чтение (AL=0)

      mov   dx,path1          ; Адрес ASCIIZ строки файла

      int   21h               ; Вызов прерывания DOS

      jnc   ok1               ; Переход, если открытие успешно

      stc                     ; Установка флага переноса

      ret                     ; Выход

ok1:  mov   source,ax         ; Запоминаем дескриптор файла

      ret                     ; Выход

Fopen endp

;┌──────────────────────────┐

;│ Процедура создания файла │

;└──────────────────────────┘

Fcreate     proc  near

      mov   ah,3ch            ; Функция создания файла

      xor   cx,cx             ; Заносим атрибут файла в CX

      mov   dx,path2          ; Адрес ASCIIZ строки файла

      int   21h               ; Вызов прерывания DOS

      jnc   ok2               ; Переход, если файл создан успешно

      mov   ah,3ch                  ; Если выходной файл не задан

      xor   cx,cx             ; Устанавливаем по умолчанию

      lea   dx,default        ; Имя OUT.COM

      int   21h               ; Пытаемся создать файл

      jnc   ok2               ; Если получилось - на выход

      stc                     ; Установка флага переноса

      ret                     ; Выход

ok2:  mov   dest,ax                 ; Запоминаем дескриптор файла

      ret                     ; Выход

default     db    'OUT.COM',0

Fcreate     endp

;┌──────────────────────────┐

;│ Процедура закрытия файла │

;└──────────────────────────┘

Fclose      proc  near

      mov   ah,3eh            ; Функция закрытия файла

      int   21h               ; Вызов прерывания DOS

      jnc   ok3               ; Переход, если файл успешно закрыт

      Wrt   error05           ; Вывод сообщения об ошибке

ok3:  ret                     ; Выход

Fclose      endp

;┌────────────────────────┐

;│ Процедура чтения файла │

;└────────────────────────┘

Fread proc  near

      lea   dx,buffer         ; Загружаем адрес буфера

      mov   di,dx             ; Запоминаем адрес начала буфера в DI

      xor   si,si             ; Обнуляем SI

      mov   bx,source         ; Заносим в BX дескриптор файла

      mov   cx,1              ; Число символов для чтения

frd1: mov   ah,3fh                  ; Функция чтения из файла

      int   21h               ; Вызов прерывания DOS

      jnc   frd2              ; Переход, если нет ошибки

      wrt   error05                 ; Вывод сообщения об ошибке

      stc                     ; Устанавливаем флаг переноса

      ret                     ; Выход

frd2: cmp   ax,0              ; Если AX=0, то достигнут конец файла

      je    end1              ; переход на установку флага EOF

      inc   si                ; Увеличиваем счетчик

      cmp   byte ptr [di],10  ; Смотрим не пустая ли строка

      je    frd3              ; Если да, то переход

      inc   dx                ; Увеличиваем адрес буфера

      inc   di                ; Увеличиваем указатель начала буфера

      jmp   frd1              ; Чтение следующего символа

end1: mov   EOF,1             ; Устанавливаем флаг конца файла

frd3: clc                     ; Сброс флага переноса

      ret                     ; Выход

Fread endp

;┌─────────────────────────┐

;│ Процедура записи в файл │

;└─────────────────────────┘

Fwrite      proc  near

      mov   bx,dest                 ; Заносим в BX дескриптор

      lea   dx,cod                  ; Заносим в DX записываемый код

      mov   cx,2              ; Число байт для записи

      mov   ah,40h                  ; Функция записи в файл

      int   21h               ; Вызов прерывания DOS

      jnc   ok4               ; Переход, если нет ошибки

      Wrt   error10                 ; Вывод сообщения об ошибке

ok4:  ret                     ; Выход

Fwrite      endp

;┌──────────────────────────┐

;│ Процедура удаления файла │

;└──────────────────────────┘

Fdel  proc  near

      mov   dx,path2          ; Путь к удаляемому файлу

      mov   ah,41h                  ; Функция удаления файла

      int   21h               ; Вызов прерывания DOS

      jnc   ok5               ; Переход, если нет ошибки

      lea   dx,defaul2        ; Пробуем удалить OUT.COM

      mov   ah,41h                  ; Функция удаления файла

      int   21h               ; Вызов прерывания DOS

      jnc   ok5               ; Переход, если нет ошибки

      Wrt   error11                 ; Вывод сообщения об ошибке

ok5:  ret                     ; Выход

defaul2     db    'OUT.COM',0

Fdel  endp

;┌────────────────────────────────┐

;│ Процедура кодирования операции │

;└────────────────────────────────┘

Coding      proc  near

      inc   di                ; Увеличиваем указатель в массиве

      mov   si,di             ; Заносим в SI адрес первого операнда

      mov   cod,0             ; Обнулаяем код

      mov   numbop,0          ; Номер операнда - первый

      mov   al,','                  ; Будем искать разделитель операндов

      mov   cx,strlen         ; В CX заносим длину строки

      sub   cx,4              ; Вычитаем длину ADD+пробел

      repne scasb             ; Начало поиска

      je    cod1              ; Переход, если разделитель найден

      Wrt   error07                 ; Выдаем сообщение об ошибке

      call  WrtStr                  ; Выводим адрес ошибочной строки

      mov   was_error,1       ; Устанавливаем флаг ошибки

      ret                     ; Выход

cod1: mov   second,di         ; В second адрес второго операнда

      dec   di                ; Смещаем указатель

      dec   di                ; на конец первого операнда

cod2: mov   dx,di             ; Запоминаем адрес конца первого оп.

      sub   dx,si             ; Выч. из адреса конца адрес начала

      inc   dx                ; Увеличиваем на 1

      cmp   dx,2              ; Смотрим длину первого операнда

      je    cod3              ; Если она равна 2, операнд - регистр

      ja    cod8              ; Если больше, операнд - память

      Wrt   error08                 ; Выдаем сообщение об ошибке

      call  WrtStr                  ; Выводим адрес ошибочной строки

      mov   was_error,1       ; Устанавливаем флаг ошибки

      ret                     ; Выход

; --------------------------------------; операнд - регистр

cod3: mov   dx,16             ; Всего 16 регистров. DX - счетчик

      lea   di,Rtable         ; Загружаем адрес таблицы регистров

newreg:     mov   cx,2              ; Сравниваем два символа

      rep   cmpsb             ; Начало сравнения

      je    regfound          ; Переход, если регистр найден

      inc   di                ; Увеличиваем смещение в таблице на 1

      inc   di                ; Увеличиваем смещение в таблице на 1

      dec   si                ; Указатель на начало перв. операнда

      dec   dx                ; Уменьшаем счетчик на 1

      jnz   newreg                  ; Если еще не все регистры, переход

      Wrt   error08                 ; Если регистр не найден - ошибка

      call  WrtStr                  ; Выводим адрес ошибочной строки

      mov   was_error,1       ; Устанавливаем флаг ошибки

      ret                     ; Выход

regfound:

      mov   ah,[di]                 ; Запоминаем код регистра

      cmp   numbop,0          ; Если это первый операнд

      je    cod7              ; То переход на установку d-бита

      cmp   typeop,1          ; Иначе это второй операнд

      je    cod4              ; Переход, если первый оп. имеет тип m

      xor   al,al             ; Иначе оба операнда регистры

      mov   bx,cod                  ; Заносим в BX код операции

      mov   bh,ah             ; Заносим в BH код регистра

      and   bx,0101h          ; Сравниваем w-биты

      cmp   bh,bl             ; Обоих операндов

      je    cod5              ; Если они равны, то переход

      Wrt   error09                 ; Иначе ошибка - несоответствие типов

      call  WrtStr                  ; Выводим адрес ошибочной строки

      mov   was_error,1       ; Устанавливаем флаг ошибки

      ret                     ; Выход

cod4: mov   al,ah             ; Заносим в AL код команды

      and   al,01h                  ; Устанавливаем в 1 w-бит

      and   ah,00111000b            ; Установка битов в поле reg

      jmp   cod6              ; Переход на следующий операнд

cod5: mov   cl,3              ; Будем сдвигать на 3 бита вправо

      shr   ah,cl             ; Сдвигаем код регистра в поле r/m

      or    ah,0c0h                 ; Установка поля mod в 11

                             ; (т.е. биты r/m определяют регистр)

cod6: or    cod,ax                  ; Накладка установленных битов на код

      inc   numbop                  ; Берем второй операнд

      mov   typeop,0          ; Устанавливаем тип операнда r

      jmp   cod11             ; Переход далее

cod7: mov   al,02h                  ; Установка бита направления (d-бит)

      or    al,ah             ; Накладываем на код

      and   ax,0011100000000011b    ; Установка битов в поле reg и r/m

      mov   cod,ax                  ; Заносим полученное в готовый код

      inc   numbop                  ; Увеличиваем номер операнда

      mov   typeop,0          ; Устанавливаем тип операнда r

      jmp   short cod11       ; Переход далее

;---------------------------------------; операнд - память

cod8: cmp   numbop,0          ; Первый операнд?

      je    cod9              ; Если да - переход

      cmp   typeop,1          ; Тип операнда m?

      jne   cod9              ; Если нет - переход

      Wrt   error10                 ; Иначе ошибка: оба операнда - память

      call  WrtStr                  ; Выводим номер ошибочной строки

      mov   was_error,1       ; Устанавливаем флаг ошибки

      ret                     ; Выход

cod9: cmp   byte ptr [si],'[' ; Операнд начинается со скобки?

      jne   cod10             ; Если нет - переход на ошибку

      cmp   byte ptr [di],']' ; Операнд заканчивается скобкой?

      jne   cod10             ; Если нет - переход на ошибку

      inc   si                ; Пропускаем скобку

      dec   di                ; Отодвигаем указатель назад от скобки

      mov   dx,di             ; Заносим в DX конец операнда

      sub   dx,si             ; Вычитаем из конца начало строки

      inc   dx                ; Увеличиваем на 1

                             ; Т.о. в DX - длина операнда

; --------- Далее идет установка полей mod и r/m ---------------------------

      mov   bx,7              ; Всего 7 типов адресации памяти

      lea   di,ExTabl         ; Адрес таблицы способов адресации

rm1:  push  si                ; Запоминаем адрес начала операнда

      mov   cl,[di]                 ; Заносим в CL длину операнда

      xor   ch,ch             ; Обнуляем CH

      inc   di                ; Двигаем указатель в ExTabl

      cmp   cx,dx             ; Сравниваем длины операндов

      jne   rm2               ; Переход если не равны

      rep   cmpsb             ; Иначе сравниваем на соответствие

      je    rm3               ; Переход если равны

rm2:  inc   di                ; Двигаем указатель в ExTabl на 1

      add   di,cx             ; Теперь сдвигаем его на 5

      pop   si                ; Восст. адрес начала операнда

      dec   bx                ; Уменьшаем счетчик вариантов

      jnz   rm1               ; Если он не равен нулю - переход

      Wrt   error08                 ; Если счетчик равен 0, а соответствий

      call  WrtStr                  ; не найдено, значит ошибка в операнде

      mov   was_error,1       ; Устанавливаем флаг ошибки

      ret                     ; Выход

rm3:  pop   si                ; Восст. адрес начала операнда

      mov   ah,[di]                 ; Заносим в AH код операции

      xor   al,al             ; Обнуляем AL

      or    cod,ax                  ; Накладываем на код код операции

      inc   numbop                  ; Увеличиваем номер операнда

      mov   typeop,1          ; Устанавливаем тип операнда m

      jmp   cod11             ; Переход дальше

cod10:

      Wrt   error08                 ; Выводим сообщение об ошибке

      call  WrtStr                  ; Выводим ошибочную строку

      mov   was_error,1       ; Устанавливаем флаг ошибки

      ret

cod11:      cmp   numbop,1          ; Это второй операнд?

      je    cod12             ; Если да - переход

      ret                     ; Иначе выход

cod12:      mov   di,second         ; Заносим в DI адрес второго операнда

      mov   si,di             ; А также и в SI

      mov   cx,strlen         ; Заносим в CX длину операнда

      sub   cx,4              ; Уменьшаем на 4

cod13:      mov   al,[di]                 ; Смотрим содержимое операнда

      cmp   al,' '                  ; Пробел?

      je    cod14             ; Если да, то переход

      cmp   al,';'                  ; Коментарий?

      je    cod14             ; Если да, то переход

      cmp   al,13             ; Новая строка?

      je    cod14             ; Если да, то переход

      inc   di                ; Увеличиваем адрес

      loop  cod13             ; Повторяем цикл

cod14:      dec   di                ; Уменьшаем адрес

      jmp   cod2              ; Переход

Coding      endp

about db    13,10,'Транслятор команды ADD. (c) 1997, БГУИР, гр.410703, '

      db    'Валасевич Сергей.',13,10,'$'

error01     db    'Формат запуска: ADD исходный_файл выходной_файл',13,10,'$'

error02     db    13,10,'Не задан выходной файл',13,10,7,'$'

error03     db    13,10,'Ошибка открытия входного файла',13,10,7,'$'

error04     db    13,10,'Ошибка создания выходного файла',13,10,7,'$'

error05     db    13,10,'Ошибка чтения входного файла',13,10,7,'$'

error06     db    13,10,'Неизвестная команда в строке: $'

error07     db    13,10,'Отсутствует второй операнд в строке: $'

error08     db    13,10,'Неверный операнд в строке: $'

error09     db    13,10,'Несоответствие типов операндов в строке $'

error10     db    13,10,'Ошибка записи в выходной файл',13,10,7,'$'

error11     db    13,10,'Ошибка удаления выходного файла',13,10,7,'$'

mes1  db    13,10,'$'

mes2  db    13,10,'Трансляция завершена успешно$'

mes3  db    13,10,'Обработано строк: $'

bell  db    7,'$'

buffer  db  1024 dup (32)           ; Буфер для считывания данных

command     db    'add'             ; Транслируемая команда

path1 dw    0                 ; Адрес строки входного файла

path2 dw    0                 ; Адрес строки выходного файла

source      dw    0                 ; Обработчик входного файла

dest  dw    0                 ; Обработчик выходного файла

                             ; 00reg00w

Rtable      db    'ax',1                  ; 00000001b

      db    'bx',25                 ; 00011001b

      db    'cx',9                  ; 00001001b

      db    'dx',17                 ; 00010001b

      db    'si',49                 ; 00110001b

      db    'di',57                 ; 00111001b

      db    'bp',41                 ; 00101001b

      db    'sp',33                 ; 00100001b

      db    'al',0                  ; 00000000b

      db    'ah',32                 ; 00100000b

      db    'bl',24                 ; 00011000b

      db    'bh',56                 ; 00111000b

      db    'cl',8                  ; 00001000b

      db    'ch',40                 ; 00101000b

      db    'dl',16                 ; 00010000b

      db    'dh',48                 ; 00110000b

                             ;mod000r/m

ExTabl      db    5,'bx+si',0       ; 00000000b

      db    5,'bx+di',1       ; 00000001b

      db    5,'bp+si',2       ; 00000010b

      db    5,'bp+di',3       ; 00000011b

      db    2,'si',4          ; 00000100b

      db    2,'di',5          ; 00000101b

      db    2,'bx',7          ; 00000111b

string      db    0                 ; Номер текущей строки

strlen      dw    0                 ; Длина текущей строки

EOF   db    0     ; Флаг конца файла (1-конец достигнут, 2-нет)

was_error db      0     ; Флаг ошибки последней операции (1-была, 2-нет)

numbop      db    0     ; Номер операнда (0-первый, 1-второй)

typeop      db    0     ; Тип операнда (0-регистр, 1-память)

cod   dw    0     ; Записываемый код

second      dw    0     ; Адрес начала второго операнда

      end   begin

;╔══════════════════════════════════════════════════════════════════╗

;║              ТРАНСЛЯЦИЯ ВСЕХ МОДИФИКАЦИЙ КОМАНДЫ CMP             ║

;╚══════════════════════════════════════════════════════════════════╝

        .MODEL TINY

        .CODE

        JUMPS

        ORG 100h

begin:

        jmp     general

Wrt     macro   string

        mov     ah,9                    ; Функция вывода строки

        lea     dx,string               ; Загружаем адрес строки

        int     21h                     ; Вызов прерывания DOS

endm

;┌──────────────────────────────┐

;│ Основная процедура программы │

;└──────────────────────────────┘

general proc    near

        push    cs                      ; Все сегментры к сегменту кода

        push    cs

        push    cs

        pop     ds

        pop     es

        pop     ss                      ; (необходимо для строковых операций)

        Wrt     autor                   ; Вывод информации о программе

; Начинаем разбор строки параметров

        mov     cl,ds:[80h]             ; Получаем длину строки параметров

        sub     ch,ch                   ; Обнуляем ch

        cmp     cx,2                    ; Смотрим длину строки параметров

        jb      source_error            ; Если она меньше двух - нет вх.файла

        mov     di,82h                  ; Заносим в DI адрес начала поиска

        mov     Addr1,di                ; Запоминаем адрес входного файла

        mov     al,' '                  ; Искать будем до первого пробела

        repne   scasb                   ; Запускаем цикл поиска

        jne     dest_error              ; Если не нашли значет нет вых. файла

        mov     byte ptr [di-1],0       ; Приводим к формату ASCIIZ

        mov     Addr2,di                ; Запоминаем адрес выходного файла

        mov     cl,ds:[80h]             ; Заносим в cl длину строки поиска

        sub     ch,ch                   ; Обнуляем ch

        mov     al,13                   ; Искать будем до символа CR

        repne   scasb                   ; Запуск цикла поиска

        jne     dest_error              ; Если не нашли значет нет вых. файла

        mov     byte ptr [di-1],0       ; Приводим к формату ASCIIZ

        call    Fopen                   ; Открываем входной файл

        jc      open_source_error       ; Если ошибка, выдаем сообщение

        call    Fcreate                 ; Создаем выходной файл

        jc      create_dest_error       ; Если ошибка, выдаем сообщение

gen1:   call    Fread                   ; Считываем очередную строку из файла

        jc      read_error              ; Переход если ошибка

        cmp     flag_eof,1              ; Смотрим признак конца файла

        jne     gen2                    ; Если не конец, продолжаем

        cmp     si,0                    ; Проверяем длину строки

        jne     gen2                    ; Если не 0 символов продолжаем

        jmp     read_error              ; Иначе выводим сообщение об ошибке

gen2:   inc     string                  ; Увеличиваем счетчик строк

        mov     strlen,si               ; Запоминаем длину строки

        call    DelSpc                  ; Вызываем процедуру удаления пробелов

        cmp     strlen,2                ; Если длина строки 2 символа, то

        je      gen1                    ; переходим на чтение следующей

        lea     di,buffer               ; Загружаем адрес строки

        cmp     byte ptr [di],';'       ; Если первый символ строки ; то

        je      gen1                    ; переходим на чтение следующей

        lea     si,command              ; Будем искать строку CMP

        mov     cx,3                    ; Длина искомой строки 3 символа

        repe    cmpsb                   ; Начинаем поиск

        je      gen3                    ; Переход, если CMP найдена

        wrt     error06                 ; Вывод сообщения об ошибке

        call    WrtStr                  ; Вывод ошбочной строки

        mov     flag_err,1              ; Устанавливаем флаг ошибки

        jmp     gen1                    ; Переход на чтение следующей строки

gen3:   call    Coding

        cmp     flag_err,0

        jne     gen1

        call    Fwrite

        jnc     gen1

        ret

gen4:   mov     bx,dest                 ; Если трансляция проведена успешно

        call    Fclose                  ; Закрываем выходной файл

        Wrt     m2                      ; Вывод сообщения о завершении

        Wrt     m3                      ; Вывод сообщения о строках

        call    WrtStr                  ; Вывод числа обработаных строк

        Wrt     m1                      ; Вывод CR,LF

        ret                             ; ВЫХОД В DOS

; Далее располагаются метки перехода по различным ошибкам

source_error:

        Wrt     error01

        ret

dest_error:

        Wrt     error02

        ret

open_source_error:

        Wrt     error03

        ret

create_dest_error:

        Wrt     error04

        ret

read_error:

        cmp     flag_eof,1              ; Смотрим был ли конец файла

        je      skip_err_message        ; Если да, то это не ошибка

        Wrt     error05                 ; Иначе выводим сообщение об ошибке

        mov     flag_err,1              ; Устанавливаем флаг ошибки

skip_err_message:

        mov     bx,source               ; Заносим дескриптор выходного файла

        call    Fclose                  ; Закрываем выходной файл

        cmp     flag_err,0              ; Смотрим флаг ошибки

        je      gen4                    ; Если ошибок нет, то переход

        call    Fdel                    ; Иначе удаляем выходной файл

        Wrt     m1                      ; Вывод CR,LF

        Wrt     bell                    ; Вывод сигнала

        ret

general endp

;┌──────────────────────────────────────────────────────┐

;│  Переводит значение регистра AX в десятичный формат  │

;└──────────────────────────────────────────────────────┘

Decimal proc    near

        call    clear                   ; Очищаем строку вывода

        mov     cx,0010                 ; Заносим в CX делитель

        lea     si,outstr+4             ; Указатель на конец строки

b20:    cmp     ax,0010                 ; Сравниваем AX с 10

        jb      short   b30             ; Переход, если меньше

        xor     dx,dx                   ; Обнуляем DX

        div     cx                      ; Делем AX на CX

        or      dl,30h                  ; Добавляем 48, чтобы получить 1

        mov     [si],dl                 ; Заносим в выходную строку

        dec     si                      ; Движемся влево

        jmp     short   b20             ; Переход на следующий символ

b30:    or      al,30h                  ; Добавляем 48, чтобы получить 1

        mov     [si],al                 ; Заносим в выходную строку

        ret

; Вспомогательная процедура. Очищает строку outstr (забивает нулями)

clear   proc    near

        lea     si,outstr               ; Загружаем адрес выходной строки

        mov     cl,48                   ; 48 - символ нуля

        mov     [si],cl                 ; Забиваем нулями символы

        mov     [si+1],cl

        mov     [si+2],cl

        mov     [si+3],cl

        mov     [si+4],cl

        ret                             ; Выход

clear   endp

outstr  db      '     $'

Decimal endp

;┌──────────────────────────────┐

;│ Процедура вывода числа строк │

;└──────────────────────────────┘

WrtStr  proc    near

        xor     ax,ax                   ; Обнуление регистра AX

        mov     al,string               ; В AL число обработанных строк

        call    Decimal                 ; Преобразуем в десятичный формат

        Wrt     outstr                  ; Выводим на экран

        ret                             ; Выход

WrtStr  endp

;┌─────────────────────────────┐

;│ Процедура удаления пробелов │

;└─────────────────────────────┘

DelSpc  proc    near

        cld                             ; Устанавливаем флаг направления

        lea     si,buffer               ; Заносим в SI адрес буфера

        mov     di,si                   ; Дублируем в DI

        mov     cx,strlen               ; В CX длина считанной строки

        xor     bx,bx                   ; Обнуляем BX

        mov     dl,1                    ; Условие цикла

del1:   lodsb                           ; Загружаем в AL один байт

        cmp     al,65                   ; A?

        jb      del2                    ; Переход если меньше

        cmp     al,90                   ; Z?

        ja      del2                    ; Переход если больше

        add     al,32                   ; Иначе перевод F-f

        xor     dl,dl                   ; Обнуляем DL

        jmp     del4                    ; Переход на запись символа

del2:   cmp     al,' '                  ; Пробел?

        je      del3                    ; Если да, то переход

        xor     dl,dl                   ; Обнуляем DL

        jmp     del4                    ; Переход на запись символа

del3:   cmp     dl,1                    ; DL=1?

        je      del5                    ; Если да, то переход

        mov     dl,1                    ; Заносим в DL 1

del4:   stosb                           ; Запись символа в строку

        inc     bx                      ; Увеличиваем счетчик символов

del5:   loop    del1                    ; Цикл

        mov     strlen,bx               ; Заносим длину преобразованной строки

        ret                             ; Выход

DelSpc  endp

;┌──────────────────────────┐

;│ Процедура открытия файла │

;└──────────────────────────┘

Fopen   proc    near

        mov     ah,3dh                  ; Функция открытия файла

        xor     al,al                   ; Открываем на чтение (AL=0)

        mov     dx,Addr1                ; Адрес ASCIIZ строки файла

        int     21h                     ; Вызов прерывания DOS

        jnc     ok1                     ; Переход, если открытие успешно

        stc                             ; Установка флага переноса

        ret                             ; Выход

ok1:    mov     source,ax               ; Запоминаем дескриптор файла

        ret                             ; Выход

Fopen   endp

;┌──────────────────────────┐

;│ Процедура создания файла │

;└──────────────────────────┘

Fcreate proc    near

        mov     ah,3ch                  ; Функция создания файла

        xor     cx,cx                   ; Заносим атрибут файла в CX

        mov     dx,Addr2                ; Адрес ASCIIZ строки файла

        int     21h                     ; Вызов прерывания DOS

        jnc     ok2                     ; Переход, если файл создан успешно

        mov     ah,3ch                  ; Если выходной файл не задан

        xor     cx,cx                   ; Устанавливаем по умолчанию

        lea     dx,default              ; Имя OUT.COM

        int     21h                     ; Пытаемся создать файл

        jnc     ok2                     ; Если получилось - на выход

        stc                             ; Установка флага переноса

        ret                             ; Выход

ok2:    mov     dest,ax                 ; Запоминаем дескриптор файла

        ret                             ; Выход

default db      'OUT.COM',0

Fcreate endp

;┌──────────────────────────┐

;│ Процедура закрытия файла │

;└──────────────────────────┘

Fclose  proc    near

        mov     ah,3eh                  ; Функция закрытия файла

        int     21h                     ; Вызов прерывания DOS

        jnc     ok3                     ; Переход, если файл успешно закрыт

        Wrt     error05                 ; Вывод сообщения об ошибке

ok3:    ret                             ; Выход

Fclose  endp

;┌────────────────────────┐

;│ Процедура чтения файла │

;└────────────────────────┘

Fread   proc    near

        lea     dx,buffer               ; Загружаем адрес буфера

        mov     di,dx                   ; Запоминаем адрес начала буфера в DI

        xor     si,si                   ; Обнуляем SI

        mov     bx,source               ; Заносим в BX дескриптор файла

        mov     cx,1                    ; Число символов для чтения

frd1:   mov     ah,3fh                  ; Функция чтения из файла

        int     21h                     ; Вызов прерывания DOS

        jnc     frd2                    ; Переход, если нет ошибки

        wrt     error05                 ; Вывод сообщения об ошибке

        stc                             ; Устанавливаем флаг переноса

        ret                             ; Выход

frd2:   cmp     ax,0                    ; Если AX=0, то достигнут конец файла

        je      end1                    ; переход на установку флага flag_eof

        inc     si                      ; Увеличиваем счетчик

        cmp     byte ptr [di],10        ; Смотрим не пустая ли строка

        je      frd3                    ; Если да, то переход

        inc     dx                      ; Увеличиваем адрес буфера

        inc     di                      ; Увеличиваем указатель начала буфера

        jmp     frd1                    ; Чтение следующего символа

end1:   mov     flag_eof,1              ; Устанавливаем флаг конца файла

frd3:   clc                             ; Сброс флага переноса

        ret                             ; Выход

Fread   endp

;┌─────────────────────────┐

;│ Процедура записи в файл │

;└─────────────────────────┘

Fwrite  proc    near

        mov     ax,cmp_

        or      al,38h

        mov     cmp_,ax

        mov     bx,dest                 ; Заносим в BX дескриптор

        lea     dx,cmp_                 ; Заносим в DX записываемый код

        mov     cx,2                    ; Число байт для записи

        mov     ah,40h                  ; Функция записи в файл

        int     21h                     ; Вызов прерывания DOS

        jnc     ok4                     ; Переход, если нет ошибки

        Wrt     error10                 ; Вывод сообщения об ошибке

ok4:    ret                             ; Выход

Fwrite  endp

;┌──────────────────────────┐

;│ Процедура удаления файла │

;└──────────────────────────┘

Fdel    proc    near

        mov     dx,Addr2                ; Путь к удаляемому файлу

        mov     ah,41h                  ; Функция удаления файла

        int     21h                     ; Вызов прерывания DOS

        jnc     ok5                     ; Переход, если нет ошибки

        lea     dx,defaul2              ; Пробуем удалить OUT.COM

        mov     ah,41h                  ; Функция удаления файла

        int     21h                     ; Вызов прерывания DOS

        jnc     ok5                     ; Переход, если нет ошибки

        Wrt     error11                 ; Вывод сообщения об ошибке

ok5:    ret                             ; Выход

defaul2 db      'OUT.COM',0

Fdel    endp

;┌────────────────────────────────┐

;│ Процедура кодирования операции │

;└────────────────────────────────┘

Coding  proc    near

        inc     di                      ; Увеличиваем указатель в массиве

        mov     si,di                   ; Заносим в SI адрес первого операнда

        mov     cmp_,0                  ; Обнулаяем код

        mov     numbop,0                ; Номер операнда - первый

        mov     al,','                  ; Будем искать разделитель операндов

        mov     cx,strlen               ; В CX заносим длину строки

        sub     cx,4                    ; Вычитаем длину CMP+пробел

        repne   scasb                   ; Начало поиска

        je      cmp_1                   ; Переход, если разделитель найден

        Wrt     error07                 ; Выдаем сообщение об ошибке

        call    WrtStr                  ; Выводим адрес ошибочной строки

        mov     flag_err,1              ; Устанавливаем флаг ошибки

        ret                             ; Выход

cmp_1:   mov     addr2,di               ; В addr2 адрес второго операнда

        dec     di                      ; Смещаем указатель

        dec     di                      ; на конец первого операнда

cmp_2:   mov     dx,di                  ; Запоминаем адрес конца первого оп.

        sub     dx,si                   ; Выч. из адреса конца адрес начала

        inc     dx                      ; Увеличиваем на 1

        cmp     dx,2                    ; Смотрим длину первого операнда

        je      cmp_3                   ; Если она равна 2, операнд - регистр

        ja      cmp_8                   ; Если больше, операнд - память

        Wrt     error08                 ; Выдаем сообщение об ошибке

        call    WrtStr                  ; Выводим адрес ошибочной строки

        mov     flag_err,1              ; Устанавливаем флаг ошибки

        ret                             ; Выход

; --------------------------------------; операнд - регистр

cmp_3:   mov     dx,16                  ; Всего 16 регистров. DX - счетчик

        lea     di,RegTabl              ; Загружаем адрес таблицы регистров

newreg: mov     cx,2                    ; Сравниваем два символа

        rep     cmpsb                   ; Начало сравнения

        je      regfound                ; Переход, если регистр найден

        inc     di                      ; Увеличиваем смещение в таблице на 1

        inc     di                      ; Увеличиваем смещение в таблице на 1

        dec     si                      ; Указатель на начало перв. операнда

        dec     dx                      ; Уменьшаем счетчик на 1

        jnz     newreg                  ; Если еще не все регистры, переход

        Wrt     error08                 ; Если регистр не найден - ошибка

        call    WrtStr                  ; Выводим адрес ошибочной строки

        mov     flag_err,1              ; Устанавливаем флаг ошибки

        ret                             ; Выход

regfound:

        mov     ah,[di]                 ; Запоминаем код регистра

        cmp     numbop,0                ; Если это первый операнд

        je      cmp_7                   ; То переход на установку d-бита

        cmp     typeop,1                ; Иначе это второй операнд

        je      cmp_4                   ; Переход, если первый оп. имеет тип m

        xor     al,al                   ; Иначе оба операнда регистры

        mov     bx,cmp_                 ; Заносим в BX код операции

        mov     bh,ah                   ; Заносим в BH код регистра

        and     bx,0101h                ; Сравниваем w-биты

        cmp     bh,bl                   ; Обоих операндов

        je      cmp_5                   ; Если они равны, то переход

        Wrt     error09                 ; Иначе ошибка - несоответствие типов

        call    WrtStr                  ; Выводим адрес ошибочной строки

        mov     flag_err,1              ; Устанавливаем флаг ошибки

        ret                             ; Выход

cmp_4:   mov     al,ah                  ; Заносим в AL код команды

        and     al,01h                  ; Устанавливаем в 1 w-бит

        and     ah,00111000b            ; Установка битов в поле reg

        jmp     cmp_6                   ; Переход на следующий операнд

cmp_5:   mov     cl,3                   ; Будем сдвигать на 3 бита вправо

        shr     ah,cl                   ; Сдвигаем код регистра в поле r/m

        or      ah,0c0h                 ; Установка поля mod в 11

                                        ; (т.е. биты r/m определяют регистр)

cmp_6:   or      cmp_,ax                ; Накладка установленных битов на код

        inc     numbop                  ; Берем второй операнд

        mov     typeop,0                ; Устанавливаем тип операнда r

        jmp     cmp_11                  ; Переход далее

cmp_7:   mov     al,02h                 ; Установка бита направления (d-бит)

        or      al,ah                   ; Накладываем на код

        and     ax,0011100000000011b    ; Установка битов в поле reg и r/m

        mov     cmp_,ax                 ; Заносим полученное в готовый код

        inc     numbop                  ; Увеличиваем номер операнда

        mov     typeop,0                ; Устанавливаем тип операнда r

        jmp     short cmp_11            ; Переход далее

;---------------------------------------; операнд - память

cmp_8:   cmp     numbop,0               ; Первый операнд?

        je      cmp_9                   ; Если да - переход

        cmp     typeop,1                ; Тип операнда m?

        jne     cmp_9                   ; Если нет - переход

        Wrt     error10                 ; Иначе ошибка: оба операнда - память

        call    WrtStr                  ; Выводим номер ошибочной строки

        mov     flag_err,1              ; Устанавливаем флаг ошибки

        ret                             ; Выход

cmp_9:   cmp     byte ptr [si],'['      ; Операнд начинается со скобки?

        jne     cmp_10                  ; Если нет - переход на ошибку

        cmp     byte ptr [di],']'       ; Операнд заканчивается скобкой?

        jne     cmp_10                  ; Если нет - переход на ошибку

        inc     si                      ; Пропускаем скобку

        dec     di                      ; Отодвигаем указатель назад от скобки

        mov     dx,di                   ; Заносим в DX конец операнда

        sub     dx,si                   ; Вычитаем из конца начало строки

        inc     dx                      ; Увеличиваем на 1

                                        ; Т.о. в DX - длина операнда

; --------- Далее идет установка полей mod и r/m ---------------------------

        mov     bx,7                    ; Всего 7 типов адресации памяти

        lea     di,MemTabl              ; Адрес таблицы способов адресации

rm1:    push    si                      ; Запоминаем адрес начала операнда

        mov     cl,[di]                 ; Заносим в CL длину операнда

        xor     ch,ch                   ; Обнуляем CH

        inc     di                      ; Двигаем указатель в MemTab

        cmp     cx,dx                   ; Сравниваем длины операндов

        jne     rm2                     ; Переход если не равны

        rep     cmpsb                   ; Иначе сравниваем на соответствие

        je      rm3                     ; Переход если равны

rm2:    inc     di                      ; Двигаем указатель в MemTabl на 1

        add     di,cx                   ; Теперь сдвигаем его на 5

        pop     si                      ; Восст. адрес начала операнда

        dec     bx                      ; Уменьшаем счетчик вариантов

        jnz     rm1                     ; Если он не равен нулю - переход

        Wrt     error08                 ; Если счетчик равен 0, а соответствий

        call    WrtStr                  ; не найдено, значит ошибка в операнде

        mov     flag_err,1              ; Устанавливаем флаг ошибки

        ret                             ; Выход

cmp_11:  cmp     numbop,1

        je      cmp_12

        ret

rm3:    pop     si                      ; Восст. адрес начала операнда

        mov     typeop,1                ; Устанавливаем тип операнда m

        jmp     cmp_11                  ; Переход дальше

cmp_10:

        Wrt     error08                 ; Выводим сообщение об ошибке

        call    WrtStr                  ; Выводим ошибочную строку

        mov     flag_err,1              ; Устанавливаем флаг ошибки

        ret

cmp_12:  mov     di,addr2               ; Заносим в DI адрес второго операнда

        mov     si,di                   ; А также и в SI

        mov     cx,strlen               ; Заносим в CX длину операнда

        sub     cx,4                    ; Уменьшаем на 4

cmp_13:  mov     al,[di]                ; Смотрим содержимое операнда

        cmp     al,' '                  ; Пробел?

        je      cmp_14                  ; Если да, то переход

        cmp     al,';'                  ; Коментарий?

        je      cmp_14                  ; Если да, то переход

        cmp     al,13                   ; Новая строка?

        je      cmp_14                  ; Если да, то переход

        inc     di                      ; Увеличиваем адрес

        loop    cmp_13                  ; Повторяем цикл

cmp_14:  dec     di                     ; Уменьшаем адрес

        jmp     cmp_2                   ; Переход

Coding  endp

Autor   db      13,10,'Транслятор команды CMP. (c) 1997, БГУИР, гр.410703, '

        db      'Коровинский Сергей.',13,10,'$'

error01 db      'Формат запуска: CMP исходный_файл выходной_файл',13,10,'$'

error02 db      13,10,'Не задан выходной файл',13,10,7,'$'

error03 db      13,10,'Ошибка открытия входного файла',13,10,7,'$'

error04 db      13,10,'Ошибка создания выходного файла',13,10,7,'$'

error05 db      13,10,'Ошибка чтения входного файла',13,10,7,'$'

error06 db      13,10,'Неизвестная команда в строке: $'

error07 db      13,10,'Отсутствует второй операнд в строке: $'

error08 db      13,10,'Неверный операнд в строке: $'

error09 db      13,10,'Несоответствие типов операндов в строке $'

error10 db      13,10,'Ошибка записи в выходной файл',13,10,7,'$'

error11 db      13,10,'Ошибка удаления выходного файла',13,10,7,'$'

m1      db      13,10,'$'

m2      db      13,10,'Трансляция завершена успешно$'

m3      db      13,10,'Обработано строк: $'

bell    db      7,'$'

buffer  db      1024 dup (32)           ; Буфер для считывания данных

command db      'cmp'                   ; Транслируемая команда

Addr1   dw      0                       ; Адрес строки входного файла

Addr2   dw      0                       ; Адрес строки выходного файла

source  dw      0                       ; Обработчик входного файла

dest    dw      0                       ; Обработчик выходного файла

                                        ; 00reg00w

RegTabl db      'ax',1                  ; 00000001b

        db      'bx',25                 ; 00011001b

        db      'cx',9                  ; 00001001b

        db      'dx',17                 ; 00010001b

        db      'si',49                 ; 00110001b

        db      'di',57                 ; 00111001b

        db      'bp',41                 ; 00101001b

        db      'sp',33                 ; 00100001b

        db      'al',0                  ; 00000000b

        db      'ah',32                 ; 00100000b

        db      'bl',24                 ; 00011000b

        db      'bh',56                 ; 00111000b

        db      'cl',8                  ; 00001000b

        db      'ch',40                 ; 00101000b

        db      'dl',16                 ; 00010000b

        db      'dh',48                 ; 00110000b

                                        ; mod000r/m

MemTabl db      5,'bx+si',0             ; 00000000b

        db      5,'bx+di',1             ; 00000001b

        db      5,'bp+si',2             ; 00000010b

        db      5,'bp+di',3             ; 00000011b

        db      2,'si',4                ; 00000100b

        db      2,'di',5                ; 00000101b

        db      2,'bx',7                ; 00000111b

string  db      0                       ; Номер текущей строки

strlen  dw      0                       ; Длина текущей строки

flag_eof db     0       ; Флаг конца файла (1-конец достигнут, 2-нет)

flag_err db     0       ; Флаг ошибки последней операции (1-была, 2-нет)

numbop  db      0       ; Номер операнда (0-первый, 1-второй)

typeop  db      0       ; Тип операнда (0-регистр, 1-память)

cmp_    dw      0       ; Записываемый код

addr2   dw      0       ; Адрес начала второго операнда

        end     begin

     1. Анализ технического задания

         В данном курсовом проекте необходимо разработать программу на  язы-

    ке Ассемблер для МП Intel 8086 реализующую функции транслятора всех  мо-

    дификаций команды ADD. Программа должна правильно распознавать тип адре-

    сации (например регистр-регистр, регистр-память,  память-регистр), иметь

    подпрограммы обработки ошибок трансляции,  выводить информацию о процес-

    се трансляции и ошибках на экран дисплея.  Команды для трансляции должны

    быть оформлены в виде текстового файла,  находящегося в одном каталоге с

    программой-транслятором.  При анализе команд транслятором  также  должны

    правильно обрабатываться:

        - пустые строки

        - строки с начальными и конечными пробелами

        - строки-комментарии

        - прописные и заглавные буквы

        - ошибочные операторы

         Результатом работы программы-транслятора должен быть выходной файл,

    содержащий машинные коды оттранслированных команд.  Входной  и  выходной

    файлы должны задаваться в командной строке запуска  программы-транслято-

    ра в виде параметров.

         Для разработки транслятора понадобятся некоторые сведения о  струк-

    туре команды ADD, обозначении регистров и способах адресации.

    Объектный код команды ADD имеет следующий вид:

    000000 d w mod reg r/m

    ───┬── ┬ ┬ ─┬─ ─┬─ ─┬─

       │   │ │  │   │   └──── Указатель регистр/память (3 бита)

       │   │ │  │   └──────── Тип регистра (3 бита)

       │   │ │  └──────────── Тип адресации регистр/память (2 бита)

       │   │ └─────────────── Размер регистра (0-байт, 1-слово)

       │   └───────────────── Бит направления передачи

       └───────────────────── Код операции

    Регистры микропроцессора Intel 8086 кодируются следующим образом:

            ┌──────────────┬────────────┬───────────┐

            │ Код регистра │    w=0     │    w=1    │

            ├──────────────┼────────────┼───────────┤

            │     000      │    AL      │    AX     │

            │     001      │    CL      │    CX     │

            │     010      │    DL      │    DX     │

            │     011      │    BL      │    BX     │

            │     100      │    AH      │    SP     │

            │     101      │    CH      │    BP     │

            │     110      │    DH      │    SI     │

            │     111      │    BH      │    DI     │

            └──────────────┴────────────┴───────────┘

    Биты MOD могут принимать следующие значения:

         00 - биты r/m дают абсолютный адрес, байт смещения

              (относительный адрес) отсутствует

         01 - биты r/m дают абсолютный адрес памяти и имеется

              один байт смещения

         10 - биты r/m дают абсолютный адрес и имеется два

              байта смещения

         11 - биты r/m определяют регистр. Бит w (в байте кода

              операции) определяет ссылку на восьми- или

              шестнадцатибитовый регистр

    Биты R/M определяют способ адресации следующим образом:

            ┌─────┬────────┐

            │ R/M │ mod=00 │

            ├─────┼────────┤

            │ 000 │ BX+SI  │

            │ 001 │ BX+DI  │

            │ 010 │ BP+SI  │

            │ 011 │ BP+DI  │

            │ 100 │  SI    │

            │ 101 │  DI    │

            │ 110 │ Direct │

            │ 111 │  BX    │

            └─────┴────────┘

            2. Разработка алгоритма

         Разработаем алгоритм для программы-транслятора.  После  запуска  на

    выполнение программа проверяет параметры командной строки.  В  командной

    строке должны быть заданы входной и выходной файлы для трансляции.   При

    отсутствии входного файла выдается сообщение об ошибке и  программа  за-

    канчивает свою работу. При отсутствии выходного файла по умолчанию уста-

    навливается выходной файл с именем OUT.COM.

         Далее идет открытие входного файла на чтение и выходного на запись.

    Из входного файла считывается одна строка.  Если это пустая  строка  или

    строка-комментарий,  то она игнорируется и программа считывает следующую

    строку. Если в строке содержаться начальные или конечные пробелы, то они

    удаляются. Затем вся строка преобразуется к нижнему регистру.

         Запускается поиск строки 'add'.  Если строка найдена  анализируются

    первый и второй операнды. Проверяется тип адресации,  анализируются биты

    mod, r/m, w, d, reg.  В соответствии с этим кодируется машинная команда,

    осуществляющая данную операцию.  Если в обоих операндах не было  ошибок,

    то машинная команда записывается в выходной файл. Затем считывается сле-

    дующая строка и так далее, пока не достигнут конец входного файла.  Пос-

    ле достижения конца файла смотрим на наличие ошибок трансляции. Если они

    обнаружены, то выходной файл удаляется. Если нет закрываем входной и вы-

    ходной файлы и выдаем сообщение об успешной трансляции.

      3. Разработка структуры данных

         При работе программа-транслятор использует следующие переменные:

    ┌────────────┬────────────────────────────────────────────────────────┐

    │ Переменная │          Описание (содержание) переменной              │

    ├────────────┼────────────────────────────────────────────────────────┤

    │  аbout     │ Содержит информацию о программе и ее авторе            │

    │  error01   │ "Формат запуска: ADD исходный_файл выходной_файл"      │

    │  error02   │ "Не задан выходной файл"                               │

    │  error03   │ "Ошибка открытия входного файла"                       │

    │  error04   │ "Ошибка создания выходного файла"                      │

    │  error05   │ "Ошибка чтения входного файла"                         │

    │  error06   │ "Неизвестная команда в строке"                         │

    │  error07   │ "Отсутствует второй операнд в строке"                  │

    │  error08   │ "Неверный операнд в строке"                            │

    │  error09   │ "Несоответствие типов операндов в строке"              │

    │  error10   │ "Ошибка записи в выходной файл"                        │

    │  error11   │ "Ошибка удаления выходного файла"                      │

    │  mes1      │ Содержит символы перевод строки, возврат каретки       │

    │  mes2      │ "Трансляция завершена успешно"                         │

    │  mes3      │ "Обработано строк"                                     │

    │  bell      │ Содержит управляющий символ N7 (звуковой сигнал)       │

    │  buffer    │ В этот буфер заносится информация из входного файла    │

    │  command   │ Содержит транслируемую команду (ADD)                   │

    │  path1     │ Полный путь и имя входного файла                       │

    │  path2     │ Полный путь и имя выходного файла                      │

    │  source    │ Обработчик (дескриптор) входного файла                 │

    │  dest      │ Обработчик (дескриптор) выходного файла                │

    │  Rtable    │ Таблица мнемонических обозначений регистров и их коды  │

    │  ExTabl    │ Тоже самое, но при адресации памяти                    │

    │  string    │ Номер текущей строки                                   │

    │  strlen    │ Длина текущей строки                                   │

    │  EOF       │ Флаг конца файла (1-конец достигнут, 2-нет)            │

    │  was_error │ Флаг ошибки последней операции (1-была, 2-нет)         │

    │  numbop    │ Номер операнда (0-первый, 1-второй)                    │

    │  typeop    │ Тип операнда (0-регистр, 1-память)                     │

    │  cod       │ Записываемый код                                       │

    │  second    │ Адрес начала второго операнда                          │

    │            │                                                        │

    └────────────┴────────────────────────────────────────────────────────┘

         Процедуры, использующиеся в данной программе:

    ┌────────────┬────────────────────────────────────────────────────────┐

    │ Название   │ Описание процедуры                                     │

    ├────────────┼────────────────────────────────────────────────────────┤

    │ Wrt        │ Макрос вывода на экран строки stirng                   │

    │ General    │ Основная процедура программы                           │

    │ Decimal    │ Переводит значение регистра AX в десятичный формат     │

    │ WrtStr     │ Процедура вывода числа строк                           │

    │ DelSpc     │ Процедура удаления пробелов                            │

    │ Fopen      │ Процедура открытия файла                               │

    │ Fcreate    │ Процедура создания файла                               │

    │ Fclose     │ Процедура закрытия файла                               │

    │ Fread      │ Процедура чтения файла                                 │

    │ Fwrite     │ Процедура записи в файл                                │

    │ Fdel       │ Процедура удаления файла                               │

    │ Coding     │ Процедура кодирования операции                         │

    │            │                                                        │

    └────────────┴────────────────────────────────────────────────────────┘

      4. Кодирование алгоритма

         Запишем разработанный нами алгоритм в мнемокоде на языке Ассемблер.

         Основным прерыванием,  используемым в программе,  является прерыва-

    ния INT 21H (DOS Functions).  В программе использованы следующие функции

    данного прерывания:

         AH=9 - Вывод текстовой строки на экран дисплея

         AH=3Dh - Открытие файла

         AH=3Ch - Создание файла

         AH=3Eh - Закрытие файла

         AH=3Fh - Чтение из файла

         AH=40h - Запись в файл

         AH=41h - Удаление файла

       Далее располагается текст программы-транслятора на языке Ассемблер

      5. Тестирование и отладка

              программы

         Для проверки правильности работы программы-транслятора  был  создан

    специальный входной файл, содержащий максимально возможное число затруд-

    няющих моментов: пустые строки, строки коментарии (в начале строки, пос-

    ле оператора и сдвинутые относительно левой границы), команды и операто-

    ры,  набранные в различных регистрах.  Содержимое этого файла  приведено

    ниже.

        ; Тестовый файл для проверки трансляции команды ADD

    add ax,bx       ; Команда, набранная маленькими буквами, адресация r,r

    ADD [BP+SI],BX  ; Команда, набранная большими буквами, адресация m,r

    add [DI],CX     ; Сочетание больших и маленьких букв, адресация m,r

    ; Комментарий

            ; Сдвинутый комментарий

    add dx,[bp+di]      ; Адресация r,m

    ; Конец тестового файла

                         Заключение

         Итогом данной курсовой работы стала программа-транслятор, реализую-

    щая функцию трансляции команды МП Intel 8086 ADD.    Программа  соответ-

    ствует всем требованиям,  которые были поставлены в пункте 1,  обладает

    высоким быстродействием,  не требовательна к объему свободной  памяти  и

    дисковым ресурсам. В программе учтены все возможные ситуации,  в которых

    может возникнуть ошибка и предприняты попытки по устранению  большинства

    из них.

         В процессе выполнения курсового проекта мною были более широко изу-

    чены функции прерывания DOS 21h и  приобретены  навыки  написания  прог-

    рамм-трансляторов.

                Литература

    1. Абель П. Язык Ассемблера для IBM PC и программирования

       М.; Высшая Школа, 1992г.

    2. Соловьев Г.H. Операционная система ЭВМ, Высшая школа, 1989г

    3. Краковяк С.  Основы организации и функционирования операционной

       системы ЭВМ

    4. Вишняков В.А., Петровский А.А. Системное обеспечение микроЭВМ

    5. Hортон П.  Персональный компьютер фирмы IBM и операционная система

       MS-DOS

    6. Финогенов К.Г. Самоучитель по системным функциям MS-DOS

                                                                                                                                                                                                     


[1]L*.FRM*.MAC
<

Б