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

Трансляция всех модификаций команд 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 ,3 eh ; Функция закрытия файла 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. Тестирование и отладка программы Для проверки правильности работы программы-транслятора был создан специальный входной файл, содержащий максимально возможное число затрудняющих моментов: пустые строки, строки коментарии (в начале строки, после оператора и сдвинутые относительно левой границы), команды и операторы, набранные в различных регистрах.