Моделирование вычислительной системы. Ассемблерная часть
Пояснительная
записка к курсовому проекту
По
дисциплине “Программирование”
На
тему: ”Моделирование вычислительной системы. Ассемблерная часть”
Содержание
Введение
.
Постановка задачи
.
Анализ набора команд
.
Выбор формата ассемблерной команды
.
Описание входных и выходных данных
.
Анализ возможных ошибок
.
Разработка программы
.
Описание программы
.1
Класс Asm
.2
Класс SourceLine
7.3
Класс
InfoBag
7.4
Класс
ListingLine
7.5
Класс
BinCom
.6
Класс DiagLine
.
Отладка и тестирование программы
.1
Исходные данные
.2
Файл листинга
.3
Файл диагностики
.4
Бинарный файл
Заключение
Список
литературы
Приложение
Введение
Ассемблер (от англ. assembler - сборщик) -
компьютерная программа, компилятор исходного текста программы, написанной на
языке ассемблера, в программу на машинном языке.
Как и сам язык (ассемблера), ассемблеры, как
правило, специфичны для конкретной архитектуры, операционной системы и варианта
синтаксиса языка. Вместе с тем существуют мультиплатформенные или вовсе
универсальные (точнее, ограниченно-универсальные, потому что на языке низкого
уровня нельзя написать аппаратно-независимые программы) ассемблеры, которые
могут работать на разных платформах и операционных системах. Среди последних
можно также выделить группу кросс-ассемблеров, способных собирать машинный код
и исполняемые модули (файлы) для других архитектур и ОС.
Ассемблирование может быть не первым и не
последним этапом на пути получения исполнимого модуля программы. Так, многие
компиляторы с языков программирования высокого уровня выдают результат в виде
программы на языке ассемблера, которую в дальнейшем обрабатывает ассемблер.
Также результатом ассемблирования может быть не исполнимый, а объектный модуль,
содержащий разрозненные блоки машинного кода и данных программы, из которого
(или из нескольких объектных модулей) в дальнейшем с помощью
программы-компоновщика может быть скомпонован исполнимый файл.
В данном курсовом проекте необходимо разработать
транслятор упрощенного языка ассемблера. К варианту задания прилагается таблица
команд ассемблера и структура ЭВМ.
1. Постановка задачи
Структура процессора
Объем оперативной памяти . 256 байт. Буферные
регистры a и b предназначены для хранения операндов операций. Рабочий регистр
Рез используется как регистр результата, полученного при обработке данных в
АЛУ.
Коды команд:
Команды имеют длину один или два байта. Первый
байт определяет код команды в соответствии с таблицей команд (биты 0-3).
Следующие 4 бита предназначены для указания номера регистра, коммутируемого к
буферному регистру а.
Второй байт используется для задания адреса
памяти, где находится второй операнд. Если в качестве второго операнда
используется регистр, то его номер хранится в младших 4-х битах второго байта
команды, а старшие 4 бита не используются.
Код
|
Мнемокод
и операнды
|
Описание
|
0000
|
ILR
регистр
|
Rn:=Rn+флаг
переполнения
|
0001
|
NOTR
регистр
|
Rn:=
Rn +флаг переполнения
|
0010
|
ADR
регистр,память
|
Rn:=Rn+Память[Адрес]+
флаг переполнения
|
0011
|
CLR
регистр
|
Rn:=0
|
0100
|
ONR
регистр, адрес
|
Rn:=
Rn ∨Память[Адрес]
|
0101
|
ANR
регистр, адрес
|
Rn:=
Rn ∧Память[Адрес]
|
0110
|
XNR
регистр, адрес
|
Rn:=
Rn⊕Память[Адрес]
|
0111
|
JZ
адрес
|
Переход,
если результат равен 0
|
1001
|
JL адрес
|
Переход,
если результат меньше 0
|
1010
|
JMP
адрес
|
Безусловный
переход
|
1011
|
CLF
|
Регистр
флагов := 0
|
1100
|
DOP
регистр
|
Перевод
содержимого Rn в дополнительный код
|
1101
|
LOAD
регистр, адрес
|
Rn:=
Память[адрес]
|
1110
|
SAVE
регистр, адрес
|
Память[адрес]:=
Rn
|
1111
|
CMP
регистр, адрес
|
Сравнение
содержимого Rn и Память[адрес]. Установка флагов.
|
Задание на курсовой проект состоит в написании
учебного ассемблера с заданной таблицей команд.
Задачей ассемблера является преобразование файла
содержащего программу на языке ассемблера в мнемокодах в файл, содержащий
объектный двоичный код. Также необходимо в процессе трансляции создавать файл
листинга для отображения результатов в удобной для пользователя форме и файл
диагностики, указывающий на ошибки команд.
2. Анализ набора команд
Данные в курсовом проекте команды можно
подразделить на группы: арифметические команды, логические, команды загрузки,
перехода, установки и команды обработки программы.
Арифметические
|
Код
|
Мнемокод
и операнды
|
Описание
|
0000
|
ILR регистр
|
Rn:=Rn+флаг
переполнения
|
0001
|
NOTR регистр
|
Rn:=
Rn +флаг переполнения
|
0010
|
ADR регистр,память
|
Rn:=Rn+Память[Адрес]+
флаг переполнения
|
Установка
|
Код
|
Мнемокод
и операнды
|
Описание
|
0011
|
CLR регистр
|
Rn:=0
|
1011
|
CLF
|
Регистр
флагов := 0
|
Переход
|
Код
|
Мнемокод
и операнды
|
Описание
|
0111
|
JZ адрес
|
Переход,
если результат равен 0
|
1001
|
JL адрес
|
Переход,
если результат меньше 0
|
1010
|
JMP адрес
|
Безусловный
переход
|
Загрузка
|
Код
|
Мнемокод
и операнды
|
Описание
|
1101
|
LOAD регистр,адрес
|
Rn:=
Память[адрес]
|
1110
|
SAVE регистр,адрес
|
Память[адрес]:=
Rn
|
Логические
|
Код
|
Мнемокод
и операнды
|
Описание
|
0100
|
ONR регистр,адрес
|
Rn:=
Rn ∨Память[Адрес]
|
0101
|
ANR регистр,адрес
|
Rn:=
Rn ∧Память[Адрес]
|
0110
|
XNR регистр,адрес
|
Rn:=
Rn⊕Память[Адрес]
|
Команды имеют длину один или два байта. Первый
байт определяет код команды в соответствии с таблицей команд (старшие 4 бита),
а также номер регистра-операнда (младшие 4 бита), указывающего на один из РОПов
процессора.
Второй байт определяет адрес операнда в памяти,
или задает константу.
Программа должна выполнять преобразование
текстового файла в двоичный код (т.е. код команды и код адреса нахождения
операнда).
Обработка
программы
|
Код
|
Мнемокод
и операнды
|
Описание
|
1100
|
DOP
регистр
|
Перевод
содержимого Rn в дополнительный код
|
1111
|
CMP
регистр, адрес
|
Сравнение
содержимого Rn и Память[адрес]. Установка флагов.
|
3. Выбор формата ассемблерной команды
Возможные типы строк:
X - строка не
определена
С - строка-комментарий
O - строка содержит ORG
команду
D - строка содержит
переменную или константу
E - строка,
содержащая оператор завершения работы END
P - строка содержит
команду микропроцессора
Положим тип строки X.
Если длина массива слов исходной строки равна нулю или "", или
начинается с символа ';', то тип строки - C.
Если строка начинается словом "DATA",
то тип строки - D. Если
первое слово строки - "ORG"
- тип строки - О, если "END"
- E. Если в таблице
команд находится мнемоника, эквивалентная первому слову в строке, то тип
команды устанавливается равным P.
И номер строки в таблице команд, где обнаружилось соответствие, запоминается
для дальнейшего определения типа операнда.
Методика определения типа операнда
При решении данной задачи имеем строку,
содержащую мнемонику, эквивалентную одной из мнемоник в таблице команд.
Следовательно, известен тип строки - Р и номер строки в таблице команд. А зная
строку таблицы команд, можно из неё извлечь тип операнда - строка таблицы
команд её содержит в четвёртом слове.
Алгоритм формирования инфопакета
В общем случае, формирование инфопакета - это
создание объекта, поля которого - строки листинга, диагностики, бинарная часть
- формируются вызовом конструкторов классов ListingLine,
DiagLine, BinaryBin
с параметрами. Но поскольку существуют нюансы при формировании инфопакета для
разных типов строк, то следует описать алгоритм создания инфопакета для каждого
типа строки.
Тип строки С (метод parseComment)
В случае строки-комментария необходимо
сформировать только строку листинга, которая будет содержать исходную строку,
за исключением первого символа - признака комментария. Для этого конструктору
класса ListingLine
передаётся в качестве параметра номер строки и исходная строка преобразованная
методом substring
для отделения первого символа. Остальные значения параметров -1 означают
отсутствие соответствующих элементов строки листинга. Строка диагностики и
бинарная части (поля объекта InfoBag)
устанавливаются равными null.
Тип строки О (метод setOrg)
Для строк типа О необходимо формировать строки
листинга и диагностики. ORG
команда не является командой микропроцессора. Она считается инструкцией
ассемблеру. Поэтому бинарная часть инфопакета создается равной 1 байту. Строку
диагностики для строк типа О необходимо формировать потому, что ORG
команда содержит операнд, в котором возможна ошибка.
Для формирования строки листинга необходимо
решить задачу поиска адреса из операнда. Если результат будет положительным ( в
прямом смысле этого слова, если результат будет значением адреса), то строка
листинга в данном случае формируется следующим образом: номер строки, значение
операнда, исходная строка. В противном случае, если результат решения задачи
поиска отрицательный(тип ошибки), то строка листинга формируется как номер
строки + символ ошибки "-" + исходная строка. Строка диагностики
формируется следующим образом: номер строки, исходная строка, символ перехода
на новую строку и краткое описание типа ошибки.
Тип строки D
(метод setData)
Строки, хранящие данные, должны формировать
"строку" бинарного файла и листинг. В случае ошибки в записи
операнда, так же должен формироваться файл диагностики. Значение константы в
данной задаче находится так же, как и адрес - решается задача поиска(выделения)
адреса из операнда. Строка листинга формируется следующим образом: номер
строки, первый байт, второй байт, исходная строка. Конструктору бинарной части
инфопакета передается следующая информация: длина команды - 2, адрес команды,
первый байт, второй байт. В случае ошибки в адресе операнда (поиск адреса в
операнде выдал отрицательный результат), формируется следующая строка листинга:
номер строки, символ ошибки, исходная строка. Бинарная часть инфопакета
устанавливается равной null.
Тип строки E
(метод setEnd)
Строкой типа E
может являться одна единственная команда - команда END.
Она не является командой микропроцессора, но бинарная часть будет равна 1 байту
- FF. Команда END
не содержит операндов, поэтому формирования строки диагностики на этом этапе
так же происходить не будет. Строка листинга формируется просто как номер
строки + исходная строка.
Тип строки P,
тип операнда Z (метод parseZ)
1
байт
|
2
байт
|
Мнемокод
и операнд
|
1011RRRR
|
----
|
CLF
|
Команды типа Р с типом операнда Z
- это однобайтовые команды, старшие четыре разряда содержат код команды,
остальные разряды нулевые.
Значение первого байта равно двоичному коду
мнемокоманды, смещённому на четыре разряда влево.
Двоичный код мнемокоманды берётся из
соответствующей строки таблицы команд. Соответствие устанавливается ранее, при
поиске соответствия первого слова строки мнемокоду команды из таблицы команд.
Строка листинга: номер строки, адрес команды,
первый байт, исходная строка. Бинарная часть: длина команды - 1, признак
команды, первый байт.
Формирование строки диагностики на данном этапе
не нужно, поскольку операнд в командах типа Z
отсутствует. Команды типа Р с типом операнда R -
это однобайтовые команды, старшие четыре разряда которого - код команды,
младшие четыре разряда байта - регистр.
Тип строки P,
тип операнда R (метод parseR)
1
байт
|
2
байт
|
Мнемокод
и операнд
|
0000RRRR
|
----
|
ILR
регистр
|
0001RRRR
|
----
|
NOTR
регистр
|
0011RRRR
|
----
|
CLR
регистр
|
1100RRRR
|
----
|
DOP
регистр
|
Байт-код, соответствующий мнемонике в строке и
взятый из таблицы команд в соответствующей строке сдвигается на четыре разряда
влево, а младшие четыре разряда заполняются регистром. Таким образом
формируется байт команды.
Строка листинга: номер строки, адрес команды,
байт команды, исходная строка. Бинарная часть: длина команды - 1, признак команды,
байт команды.
В случае отрицательного значения операнда
(ошибка в записи операнда), строка листинга формируется следующим образом:
номер строки, адрес команды, символ ошибки, исходная строка. Строка
диагностики: адрес команды, исходная строка, символ перехода на новую строку,
краткое описание типа ошибки.
Тип строки P,
тип операнда RА (метод parseRА)
1
байт
|
2
байт
|
Мнемокод
и операнд
|
0010RRRR
|
AAAAAAAA
|
ADR
регистр, память
|
0100RRRR
|
AAAAAAAA
|
ONR
регистр, адрес
|
0101RRRR
|
AAAAAAAA
|
ANR
регистр, адрес
|
0110RRRR
|
AAAAAAAA
|
XNR
регистр, адрес
|
1101RRRR
|
AAAAAAAAA
|
LOAD
регистр, адрес
|
1110RRRR
|
ААААААААА
|
SAVE
регистр, адрес
|
1111RRRR
|
ААААААААА
|
CMP
регистр, адрес
|
Команды типа Р с типом операнда RА
- двухбайтовые команды. Поскольку длина самой команды - четыре бита, а длина
операнда - четыре бита, то необходимо использовать минимум два байта для записи
такой команды. Распределим информацию в байтах таким образом: старшие четыре
разряда первого байта - бинарный код команды, младшие четыре разряда первого
байта - первый операнд, второй байт - операнд адрес.
Первые байт формируется точно так же, как и в
случае типа операнда R.
Значение второго байта заполняется адресом. Строка листинга: номер строки,
адрес команды, первый байт, второй байт, исходная строка. Бинарная
"строка": длина команды - 2, признак команды, первый байт, второй
байт.
Для данного типа строки возможны две ошибки -
ошибка в первом операнде и ошибка во втором операнде. Если значение операнда
отрицательно, формируется строка диагностики: "Первый"/"Второй"
+" операнд:" + краткое описание типа ошибки.
Тип строки P,
тип операнда A (метод parseA)
1
байт
|
2
байт
|
Мнемокод
и операнд
|
0111----
|
AAAAAAAA
|
JZ
адрес
|
1001----
|
AAAAAAAA
|
JL
адрес
|
1010----
|
AAAAAAAA
|
JMP
адрес
|
Строки типа Р с типом операнда A
являются двухбайтовыми командами. Старшие четыре разряда, как и в остальных
командах типа Р, содержат байт-код команды, младшие четыре разряда будут
являться нулями. Остальные восемь разрядов операнда адреса будут храниться во
втором байте.
Первый байт формируется как и в предыдущих
случаях - поразрядным сдвигом байт-кода команды на четыре. Строка листинга
формируется следующим образом: номер строки, адрес команды, первый байт, второй
байт, исходная строка. Бинарная "строка": Длина команды - 2, первый байт,
второй байт.
В случае, если допущена ошибка в записи
операнда, формируется строка листинга: номер строки, признак ошибки и исходная
строка. Строка диагностики: номер строки, исходная строка, символ перехода на
новую и краткое описание типа ошибки. Бинарная часть инфопакета - null.
Алгоритм поиска(выделения) адреса
Задача поиска адреса должна давать следующие
результаты: если операнд написан верно - его значение, если не верно - код
ошибки (отрицательное число).
Методу в качестве параметров передаётся
слово-операнд из исходной строки и шаблон записи адреса. Если переданное слово
пусто (null, или длина
слова равна 0), то результат -3(операнд отсутствует). Если слово не
удовлетворяет шаблону, то результат равен -1 (номер ошибки, соответствующий
неверной мнемонике). Если слово удовлетворяет шаблону, производится
идентификация кода - считывание признака системы счисления. Если на этапе
преобразования возникнет исключение (Exception),
то результатом будет -2(неверно задан регист(адрес)). Если ошибки не возникло,
метод возвращает значение операнда.
Алгоритм поиска(выделения) регистра
Задача аналогичная задаче поиска адреса за
исключением положения, касающегося непосредственно поиска значения регистра.
Значения регистров лежат в интервале от 0 до 15. из исходной строки выделяется
непосредственно значение регистра, и если он задан числом - возвращается это
число. Принцип формирования ошибок типа -1 и -3 аналогичен принципу в задаче
поиска адреса. Формирование ошибки типа два происходит в случае, если операнд
не является числом 0-15.
Попробуем описать алгоритм работы программы
опустившись до уровня решения подзадач.
Первым шагом алгоритма является создание потоков
считывания и записи. Далее считывается строка если она не равна нулю,
выполняется её обработка: решается задача определения типа строки. Если тип
строки C, O,
D, E
- решается задача формирования инфопакета для соответствующего типа строки.
Если тип строки P - находится
тип операнда команды и решается соответствующая задача формирования инфопакета.
Сформированные строки инфопакета выводятся в соответствующие файлы. Считывается
следующая строка. И так до тех пор, пока не будет обработана строка типа E
или строка не будет пустой (конец текстового файла). Потоки считывания и записи
закрываются.
4. Описание входных и выходных данных
Входные данные:
Текстовый файл, содержащий программу на
ассемблере. Команды в программе имеют длину 1 или 2 байта. Биты 4-7 первого
байта содержат код команды в соответствии с таблицей команд. Биты 0-3 и второй
байт используются для задания адреса памяти, где находится константа или
операнд.
Все операции производятся над операндами,
находящимися в памяти или стеке. Результат может быть сохранен в памяти или
стеке. Каждая строка должна содержать не более одной команды. Мнемокод
отделяется от операнда последовательностью пробелов. Таблица допустимых команд:
Код
|
Мнемокод
и операнды
|
0000
|
ILR
регистр
|
0001
|
NOTR
регистр
|
0010
|
ADR
регистр,память
|
0011
|
CLR
регистр
|
0100
|
ONR
регистр, адрес
|
0101
|
ANR
регистр, адрес
|
0110
|
XNR
регистр, адрес
|
0111
|
JZ
адрес
|
1001
|
JL адрес
|
1010
|
JMP
адрес
|
1011
|
CLF
|
1100
|
DOP
регистр
|
1101
|
LOADрегистр,адрес
|
1110
|
SAVE
регистр,адрес
|
1111
|
CMP
регистр, адрес
|
В программе имеет значение регистр символа - он
обязательно должен быть верхним.
Программа должна выполнять преобразование
текстового файла в двоичный код (т.е. код команды и код адреса нахождения
операнда).
Выходные данные:
При выполнении программы формируются 3 файла:
. Бинарный файл, который получается в
результате трансляции команд данного текстового файла;
. Текстовый файл - файл-листинг, который
содержит исходный текст, его кодировку и результаты трансляции.
. Файл диагностики. В этот файл выводятся
сообщения об ошибках трансляции:
ошибка в написании имени команды
ошибка в написании операндов
отсутствие операндов
При выявлении ошибки формирование объектного
файла невозможно и выполнение программы прекращается, а в файле диагностики
выводится описание ошибки.
5. Анализ возможных ошибок
В результате ассемблирования может возникнуть
ряд ошибок:
Неверные разделители(Отличный от запятой при
разделении операндов, от пробела или Таb,
при разделении мнемоники и операнда или операндов)
Неверная мнемоника команды, или её отсутствие
Несуществующий адрес
Отсутствие операнда у команды
Неверное написание операнда
Комментарии не отделены
6. Разработка программы
Программа имеет некоторые ограничения:-в каждой
строке записывается только одна команда процессора;
команда содержит мнемонику операции, за
мнемоникой следуют операнды;
после операндов можно расположить комментарий
если операнды есть, между мнемоникой и
операндами должен быть по крайней мере один пробел;
комментарий начинается знаком «;», или
отделяется пробелом;
7. Описание программы
.1 Класс Asm
Является главным классом программы. Содержит
инициализацию таблицы команд процессора, записи операндов-регистров и
операндов-адресов, констант, потоков считывания и записи, переменной адреса
команды acom. Имеет
главный метод main, который
нужно выполнить, чтобы запустить программу.
В качестве потока считывания исходного текста src.txt
используется класс BufferedReader
(FileReader). Для потоков
записи файлов листинга и диагностики используется класс PrintWriter.
Для формирования бинарного файла используется класс RandomAccessFile.
Массив String[]
PRCOMTAB формируется в
соответствии с таблицей команд процессора. Каждому элементу массива
соответствует команда из таблицы команд. Команды не повторяются, каждая команда
имеет уникальный мнемокод и двоичный код. Формат строки массива PRCOMTAB:
мнемокод, двоичный код, длина команды, тип команды, тип операнда. Каждый
элемент в строке должен быть отделён от соседнего пробелом.
Инициализация шаблонов записи операндов-адресов
и операндов-регистров. Операнд адрес - это слово, которое в случае двоичного
кода может иметь не более 8-ми битов. Может содержать любые символы
шестнадцатеричной системы счисления (0-15) и обязательно заканчиваться на один
из символов h,H.
Операнд-регистр - длиной не менее и не более
трех символов .Первый символ - признак регистра, второй и третий символ - номер
регистра. Для 16 регистров достаточно двух символов, чтобы указать каждый
регистр. Регистр должен удовлетворять такому регулярному выражению:
"^[Rr](([0-9])|([1][0-5]))$". Это значит что регистр должен
начинаться с символов R
или r и заканчиваться
цифрой от 0 до 15.
Метод main(String[]
args) Является главным
исполняемым методом. Создаёт потоки считывания и записи, производит считывание
строк, вызывает метод обработки строки parseLine
и выполняет вывод инфопакета, сформированного в процессе обработки строки, в
файлы листинга и диагностики. После считывания строки, формирования инфопакета
и вывода строки диагностики, в зависимости от типа строки lineType,
выполняет следующие действия: если тип строки О (ORG-адрес)
- устанавливает значение acom,
равное значению адреса, хранящемуся в сформированном инфопакете, в его бинарной
части; если тип команды D
(DATA) - записывает в
бинарный файл адрес команды, выводит значение первого и второго байта,
увеличивает acom на bincom.len
(длину команды); P ( команда
процессора) - выводит адрес команды, значение первого байта, и если команда
двухбайтовая - выводит второй байт. Весь текст метода, начиная с считывания
строки заключен в блок try,
и в случае возникновения исключения, все потоки будут закрыты, а выполнение
программы остановлено.
7.2 Класс SourceLine
ассемблер транслятор двоичный код
Содержит основные методы обработки исходной
строки: parseLine, parseComment, setOrg, setData, setEnd, parseRА, parseA,
parseR, parseZ; а так же
вспомогательные: setAddrValue,
setRegValue, findCom.
Основные методы в качестве результата возвращающают инфопакет, вспомогательные
- формируют результаты вычисления подфункций.
Класс имеет следующие поля:
String
sourceLine - исходная строка,
формируется конструктором - считывается из файла Src.txt.
String
myLine - исходная строка
без пробелов слева и справа.
String[] words
- части строки - слова.
int lineNumber -
номер строки.
char lineType -
тип строки.
int
nProc - номер команды в
таблице команд микроЭВМ
Метод parseComment(String
line)
В качестве параметра методу передаётся исходная
строка. Метод формирует инфопакет, бинарная и диагностическая части которого
равну null. Строка
листинга формируется следующим образом - конструктору ListingLine
передается следующий набор параметров : (-1, -1, -1, -1, " ", line.substring(1,line.length())).
Последний параметр - это строка без первого символа, т.е. без символа
";".
В качестве результата метод возвращает
сформированный инфопакет.
Метод parseLine(String[]
wo)
Метод определяет тип строки (X,C,O,D,E,P).
Переменной lineType,
в результате выполнения метода присваивается одно из этих значений в
зависимости от содержания исходной строки.
В случае, если строка имеет тип P,
переменной kod присваивается
значение двоичного кода этой команды и в переменную format
записывается 4-ое поле PRCOMTAB
- формат команды (RA,R,A,Z).
Каждому формату команды соответствует свой метод обработки строки. Для строки RA
будет выполняться метод parserRA,
для R - parseR.
Для данной структуры метод parseRA
не нужен. Но т.к. PRCOMTAB
содержит команды формата A,
необходимо определить метод parseA().
В случае, если тип строки X,
формируется инфопакет ошибки.
Метод
findCom(String w, String[] com) является
вспомогательным служебным методом. Методу в качестве параметров передаётся
строка w (первое
"слово" исходной строки) и массив строк com
(таблица мнемоник команд). Прямым поиском сравнивается w
и элементы массива com.
В качестве результата метод возвращает значение номера элемента массива com,
которому соответствует w.
Если такого соответствия не обнаружено, метод возвращает -1.
Метод setRegValue(String
w, Pattern
pr)
Так же является вспомогательным методом.
Методу в качестве параметра передается
"слово" w, содержащее
операнд регистр, вида R1
или r14. Метод проверяет
длину строки w на наличие в нём
символов (операнда). Если символов нет, переменной val
присваивается значение -3, и при дальнейшей проверке этой переменной
формируется инфопакет ошибки. Если строка w
не удовлетворяет правилам записи строки, хранящиеся в Pattern
pr, значение val
устанавливается равным -2, что означает ошибку с кодом -2, т.е. неверная
мнемоника.
Если значение слова больше нуля, и слово
удовлетворяет правилам записи операнда-регистра, выполняется следующее: в
переменную val
записывается значение w,
переведённое из той системы счисления, которое указано конечным символом слова w.
Метод возвращает значение val.Если
меньше возвращает -3.
Метод
setAddrValue(String w, Pattern pr) повторяет
метод
setRegValue.
Метод setOrg(String[]
ops)
В качестве параметра методу передается массив
слов исходной строки. Метод формирует инфопакет. Строка листинга инфопакета
формируется конструктором ListingLine
с набором параметров (lineNumber,
Asm.acom,
av, -1," ",
sourceLine). Строка бинарного
файла создается c 1 байтом.
Метод setData(String[]
ops)
В качестве параметра методу передается массив
слов исходной строки. Метод формирует инфопакет. Строка листинга инфопакета
формируется конструктором ListingLine
с набором параметров ( lineNumber, Asm.acom, av, -1, " ", sourceLine
). Строка бинарного файла создается c
1 байтом. Метод setEnd( ) возвращает инфопакет со строкой листинга, строкой
бинарного файла с 1 байтом. Метод parseZ(String[]
ops, int bk) Метод возвращает инфопакет, содержащий
информацию об обработке команды типа Z
(команда типа Z - строка, не
содержащая операндов).
В случае адекватных данных метод присваивает
значению k, значение
двоичного представления команды, которое передаётся ему в качестве параметра bk.
Метод выполняет операцию поразрядного сдвига на четыре влево.
Метод
parseR(String[] ops, int bk)
Почти аналогичен методу parseZ,
за одним исключением. После формирования переменной k,
равной bk и операции
сдвига влево на четыре разряда, к переменной применяется операция порязрядного
сложения переменной rv,
значение которой устанавливается методом setRegValue(ops[1],
Asm.pReg).
Конструктору бинарной строки передает один,
первый байт.
Метод
parseA(String[] ops, int bk)
Обрабатывает строку типа A.
Строка типа А содержит команду операнд-адрес. Метод формирует переменную k
так же, как и метод parseZ,
только значением второго байта берет av,
предварительно сформированное методом setAddrValue(ops[1],
Asm.pAddr).
Команды типа A - двухбайтовые.
Метод
parseRA(String[] ops, int bk)
Метод формирует первый байт так же, как и метод parseR,
а значение второго байта устанавливается равным av
= setAddrValue(ops[2],Asm.pAddr).
Получается, в командах типа RA
старшие четыре бита - команда, затем младшие четыре разряда первого байта - первый
регистр, а значение второго байта - адрес.
В качестве результата возвращается
сформированный инфопакет.
7.3 Класс InfoBag
Класс, отвечающий за создание и формирование
пакета как объекта. Класс имеет следующие поля: char
lineType, ListingLine
listline DiagLine diagline, BinCom bincom. Поля
этого класса являются объектами классов ListingLine,
DiagLine, BinCom.
Класс имеет
конструктор
с
параметрами
InfoBag (ListingLine listline, DiagLine diagline, BinCom bincom). Передаваемые
объекты конструктор соответственно записывает в поля объекта, тем самым
формируя инфопакет с заданными в параметрах данными.
7.4 Класс ListingLine
Класс отвечает за строку листинга инфопакета.
Класс содержит следующие поля: String
lineNo - номер строки, String
comAddress - адрес размещения
команды, String
byteOne - первый байт
комады, String
byteTwo - второй байт
команды, errMark
- метка ошибки, String
srcLine - копия исходной
строки.
Имеет конструктор без параметров, который
инициализирует объект с пустыми значениями полей класса.
Так же
имеет
конструктор
с
параметрами
ListingLine( int lno, int ad, int k1, int k2, String err, String line). Полю
lineNo присваивается
значение lno, с
использованием формата: действительное число с четырьмя знаками. Если параметр ad<0,
полю comAddress
присваивается значение " ххх ". В противном случае в переменную comAddress
записывается отформатированное значение ad.
Далее, если значение переменных k1
и k2 больше нуля, то
переменные byteOne
и byteTwo заполняются этими
значениями соответственно. Если длина какого-то параметра будет меньше нуля, то
соответственное поле заполнится “xx
” строкой. Значение поля errMark
устанавливается равным первому символу строки err,
передаваемой в качестве параметра. Переменной srcLine
присваивается
значение переменной line.
Класс содержит перегруженный метод toString,
который формирует и возвращает String-представление
объекта класса ListingLine.
Метод возвращает строку, состоящую из последовательно соединенных полей
объекта.
7.5 Класс BinCom
Класс отвечает за формирование байтов бинарного
файла. Класс имеет поля int
len - длина команды в
байтах, int
addr - адрес первого
байта команды в памяти, byte1,
byte2 - первый и второй
байты команды, int
p - признак команды,
или директива
Класс содержит конструктор без параметров BinCom()
и конструктор с параметрами (int
l, int
pd, int
a, byte
b1, byte
b2). Параметры
последнего конструктора записываются в соответствующие поля объекта, тем самым
создавая объект с заданными параметрами.
7.6 Класс DiagLine
Класс, описывающий строку диагностики
инфопакета.
Класс инициализирует статический массив
строк-ошибок ErrorMess[].
Имеет одно единственное поле: String
Line.
В классе существует два конструктора - пустой DiagLine()
и с параметрами DiagLine
(int ln,
String msg).
Передаваемые параметры в соответствии с форматом ("%03d",
ln) записываются в
поле line.
В классе определён перегруженный метод toString,
возвращающий String-представление
объекта класса DiagLine.
В качестве результата метод возвращает строку line.
8. Отладка и тестирования программы
В качестве тестовых примеров будет предложена
совокупность команд - текстовый файл, содержащий программу на ассемблере -
которая содержит допустимые записи каждой команды таблицы ассемблера, а так же
некоторые заведомо неверные записи команд. Ошибка так же будет находиться во
всевозможных местах - мнемонике команды, операнде-регистре, операнде-адресе,
операнде константе. Тестовая программа не является реально решаемой задачей.
Она содержит совокупность всевозможных записей команд для отладки программы.
8.1 Исходные данные ( файл Src.txt)
ORG 10H;R0,25H;R0,26H;R10,27H;
NOTR R15,29H;один два три31H;один два три; ORG
25H;30H;31H;0H;245H;24;R0,45H;5,39H;R15,45;
8.2 Файл
листинга
(List.txt)
Листинг
1 010 хх хх ORG 10H;
2 010 d0 25 LOAD R0,25H;
3 012 20 26 ADR R0,26H;
4 014 ea 27 SAVE R10,27H;
5 016 1f хх
NOTR R15,29H;один два
три
6 017 70 31 JZ 31H;один два три
7 019 b0 хх CLF ;
8 025 хх
хх
ORG 25H;
9 025 30 хх
DATA 30H;
10 026 31 хх
DATA 31H;
11 027 00 хх
DATA 0H;
13 ххх
хх
хх
-DaTA 245H;
14 ххх
хх
хх
-DATA 24;
15 ххх
хх
хх
-LOD R0,45H;
16 ххх
хх
хх
-ADR 5,39H;
17 ххх
хх
хх
-SAVE R15,45
19 ххх ff хх END;
.3 Файл диагностики(Diag.txt)
Диагностика
DaTA 245H;
неверная мнемоника
DATA 24;
неправильно задан регистр(адрес)
LOD R0,45H;
неверная мнемоника
ADR 5,39H;
Первый операнд: неправильно задан регистр(адрес)
SAVE R15,45
Второй операнд: неправильно задан регистр(адрес)
.4 Файл с объектным двоичным кодом(binary.bin)
Данная последовательность команд проверяет
правильность работы всех ветвей программы, связанных с обработкой правильных
команд (методы parseLine,
parseR, parseZ,
parseRA, parseA,
setAddrValue, setRegValue,
findCom класса SourceLine,
а так же классы InfoBag,
ListingLine, DiagLine,
BinCom и Asm).
Заключение
В ходе курсовой работы были освоены навыки
работы с ассемблером, а так же укреплены усвоенные навыки программирования.
Была разработана программа-ассемблер, которая переводит мнемокод в объектный
код.
Список литературы
1.
Ноутон П. Java 2: [пер. с англ.]/П.Ноутон,
Г.Шилдт. - СПб.: БХВ-Петербург, 2014.
- 1072 с.
Приложение
Текст программы
Класс
Asmjava.io.*;java.util.regex.*;class Asm {
static int acom = 0; //адрес
команды
static BufferedReader src = null; //поток
для
исходной
программы
static PrintWriter list = null; //поток
для
листинга
static PrintWriter diag = null; //поток
для
диагностики
static RandomAccessFile bin = null;
//поток
для
двоичных
команд
static Pattern pAddr; //скомпилированное
рег.выр.
для
адреса
static Pattern pReg; //скомпилированное рег.выр.
для регистра
static String[ ] PRCOMTAB = { //команды
микроЭВМ
"ILR 0000 1 P R",
"NOTR 0001 1 P R",
"ADR 0010 2 P RA",
"CLR 0011 1 P R",
"ONR 0100 2 P RA",
"ANR 0101 2 P RA",
"XNR 0110 2 P RA",
"JZ 0111 2 P A",
"JL 1001 2 P A",
"JMP 1010 2 P A",
"CLF 1011 1 P Z",
"DOP 1100 1 P R",
"LOAD 1101 2 P RA",
"SAVE 1110 2 P RA",
"CMP 1111 2 P RA",
"ORG XXXX X O ",
"DATA XXXX 1 D",
"END XXXX X E"
};
static String[ ] prCom; //мнемоника
команд
микроЭВМ
-1-й
столбец
табл.
1
static { //статический
инициализатор
таблицы
prCom
pAddr = Pattern.compile (
"^[0-9a-fA-F]{1,2}[hH]$" );
pReg = Pattern.compile (
"^[Rr](([0-9])|([1][0-5]))$" );
prCom = new String[PRCOMTAB.length];
for (int i = 0; i <
PRCOMTAB.length; i++) {
prCom[i] = PRCOMTAB[i].substring(0,
PRCOMTAB [ i ].indexOf(' '));
}
}
public static void main(String[ ]
args) throws IOException {
String srcFile = "Src.txt"; //исходная
программа
String listFile =
"List.txt"; //листинг
String binFile =
"binary.bin"; //двоичный
код
String diagFile =
"Diag.txt"; //диагностика
String line; //строка
исходного
файла
int num = 0; //номер
строки
файла
SourceLine sl;
ListingLine ll;
DiagLine dl;
BinCom bc;
//действия по открытию файлов
try { //если файлы нельзя открыть - исключение и
окончание работы
src = new BufferedReader(new
FileReader(srcFile));
list = new PrintWriter(listFile);
list.println("Листинг");
diag = new PrintWriter(diagFile);
diag.println("Диагностика");
bin = new RandomAccessFile(binFile,
"rw");
ll = new ListingLine();
dl = new DiagLine();
bc = new BinCom();
InfoBag infobag=null;
while (( line = src.readLine()) !=
null ) { num++; //прочли строку
sl = new SourceLine ( line, num );
try {
infobag = sl.parseLine(sl.words); //обработка
строки
list.println (infobag.listline);
if (infobag.diagline != null )
diag.println(infobag.diagline); (
infobag.lineType ) {
case 'O': Asm.acom =
infobag.bincom.addr;
bin.writeByte ( infobag.bincom.p );
bin.writeByte ( infobag.bincom.addr
); break;
case 'D': bin.writeByte (
infobag.bincom.p );
bin.writeByte
(infobag.bincom.byte1);
Asm.acom+= infobag.bincom.len;
break;
case 'P': bin.writeByte (
infobag.bincom.p );
bin.writeByte ( infobag.bincom.byte1
);
if (infobag.bincom.len == 2) { //если требуется
второй байт
bin.writeByte ( infobag.bincom.p );
bin.writeByte ( infobag.bincom.byte2
);
}
Asm.acom+= infobag.bincom.len;
break;
case 'E': bin.writeByte (
infobag.bincom.p );
default:
}
}
catch (Exception e) { }
}//while
}//try
finally { //закрытие
потоков
if ( src != null) { src.close ( ); }
if ( list != null) { list.close ( );
}
if ( diag != null) { diag.close ( );
}
if (bin !=null) {bin.close( ); }
}//finally
}
}
Класс
SourceLinejava.io.*;java.util.*;java.lang.*;java.util.regex.*;class SourceLine
{ // Класс : строка исходной программы
String
sourceLine; //исходная
строка, заполняется конструктором
String
myLine; //исходная строка
без пробелов слева и справа
String[ ] words;
//части строки - слова
int
lineNumber; //номер строки
char
lineType; //C-комментарий,
A,O,D,E-команды
ассемблера,
//P-команда
процессора, X - неизвестный тип
int
nAsm; //номер команды в
таблице команд ассемблера
int
nProc; //номер команды в
таблице команд микроЭВМ
public SourceLine(String l, int n) {
// конструктор
sourceLine = new String(l); //
Задается сама строка
lineNumber = n; // и ее номер,
myLine = sourceLine.trim(); //
удаляются пробелы слева и справа,
words = myLine.split("(\\s+)|[,;]");
// строка разбивается на слова
}
public InfoBag parseLine(String[ ]
wo) { //wo - массив слов строки
InfoBag info = null; DiagLine dil =
null; ListingLine lil = null;
lineType
= 'X'; //а вдруг
определить не удастся
String[ ] ww
= null; //для частей из
таблицы команд
if ( wo.length == 0) lineType =
'C';//пустая строка
if ( wo [ 0 ].equals("") )
lineType = 'C';
if ( wo [ 0 ].charAt ( 0 ) == ';')
lineType = 'C';
else if ((nProc = findCom(wo[0],
Asm.prCom)) >= 0) //поиск команды в та
{ ww = Asm.PRCOMTAB [ nProc ].split
( "\\s+" ) ;
lineType
= ww [ 3 ].charAt
( 0 );//поле с номером 3 в PRCOMTAB
- тип
}
switch (lineType)
{//обработка команды согласно ее типу
case 'C': info = parseComment ();
info.lineType = 'C'; break;
case 'O': info = setOrg ( wo );
info.lineType = 'O'; break;
case 'D': info = setData( wo );
info.lineType = 'D'; break;
case 'E': info = setEnd( wo );
info.lineType = 'E'; break;
case 'P': //команды микроЭВМ
String kod = ww [ 1 ]; //поле 1 в
PRCOMTAB - код команды
int binaryKod = Integer.parseInt (
kod, 2 ); //получен код операц
String
format = ww
[ 4 ]; //выбор поля 4 - формата команды
if (format.equals ("RA" ))
info = parseRA ( wo, binaryKod );
if (format.equals ( "Z" ))
info = parseZ ( wo, binaryKod);
if (format.equals ( "A" ))
info = parseA ( wo, binaryKod);
if (format.equals ( "R" ))
info = parseR ( wo, binaryKod);
info.lineType = 'P'; break; //строка
- команда микроЭВМ
default: {
dil = new DiagLine(lineNumber,
sourceLine + "\n " + DiagLine.ErrorMess[1]);
lil = new ListingLine(lineNumber,
-1, -1, -1,"-", sourceLine);
info = new InfoBag(lil,dil,null);
info.lineType = 'X';
}
}
return info;
}InfoBag parseComment(){
return new InfoBag (
new ListingLine ( lineNumber, -1,
-1, -1, " ", sourceLine), null, null );
}int findCom ( String w, String [ ]
com ) { //прямой поиск w в com
for ( int i = 0; i < com.length;
i++ ) {
if ( w.equals( com [ i ] ) )
return i;
}
return -1;
}
private InfoBag setOrg ( String[ ]
ops) { //параметр - массив слов строки
DiagLine dil = null;
ListingLine lil = null;
BinCom bic = null;
int av = -3;
if (ops.length > 1) av =
setAddrValue ( ops[1], Asm.pAddr );
if (av>= 0) {
lil = new ListingLine ( lineNumber,
av, -1, -1," ", sourceLine);
bic = new BinCom(1, 1,
av,(byte)0,(byte)0);
}
else {
lil = new ListingLine( lineNumber,
-1, -1, -1, "-", sourceLine);
dil = new DiagLine( lineNumber,
sourceLine + "\n " + DiagLine.ErrorMess[-av]);
}
return new InfoBag ( lil, dil, bic);
}InfoBag setData(String[ ] ops){
DiagLine dil = null;
ListingLine lil = null;
BinCom bic = null;
int av = -3;
if (ops.length > 1) av =
setAddrValue ( ops[ 1 ], Asm.pAddr );
if (av>= 0) {
lil = new ListingLine ( lineNumber,
Asm.acom, av, -1, " ", sourceLine );
bic = new BinCom (1, 2, Asm.acom,
(byte) av, (byte) 0);
}
else {
lil = new ListingLine ( lineNumber,
-1, -1, -1, "-", sourceLine);
dil = new DiagLine ( lineNumber,
sourceLine + "\n " + DiagLine.ErrorMess[-av]);
}
return new InfoBag ( lil, dil,
bic);InfoBag setEnd(String [] ops) {
DiagLine dil = null;
ListingLine lil = null;
BinCom bic = null;
int k=(7<<5)|(7<<2)|3;
return new InfoBag ( lil = new
ListingLine ( lineNumber, -1, k, -1, " ", sourceLine),
null,
bic = new BinCom (1, k, Asm.acom,
(byte)0, (byte) 0) );
}InfoBag parseRA(String [ ] ops, int
bk ) {//формат RA - регистр, адрес
int k = bk; int rv, av;
DiagLine dil = null;
ListingLine lil = null;
BinCom bic = null;
if ( ops.length > 1) { rv =
setRegValue(ops[1], Asm.pReg);
}
else { rv = -3; } //операнда нет
k = (bk << 4) | ( rv | 0 );
if (ops.length > 2) { av =
setAddrValue(ops[2],Asm.pAddr);
}
else { av = -3; } //операнда нет
if ((rv>= 0) && (av>=
0)) {
lil = new ListingLine ( lineNumber,
Asm.acom, k, av, " ",sourceLine);
bic = new BinCom ( 2, 2, Asm.acom,
(byte)k, (byte)av);
}
else { //ошибка
в 1-м или 2-м операнде
String xx = new String();
String newString=new String();
if (rv<0) {
xx+= new String("Первый
операнд: " + DiagLine.ErrorMess[-rv]);
newString="\n";//есть
первая строка
}
if (av<0) {
xx+=newString;
xx+= new String("Второй
операнд: " + DiagLine.ErrorMess[-av]);
}
lil = new ListingLine( lineNumber,
-1, -1, -1, "-",sourceLine);
dil = new DiagLine( lineNumber,
sourceLine + "\n" + xx);
}
return new InfoBag( lil, dil, bic);
}InfoBag parseA(String [ ] ops, int
bk ) {//формат A - адрес
int k = bk; int av;
DiagLine dil = null;
ListingLine lil = null;
BinCom bic = null;
if ( ops.length > 1) { av =
setAddrValue(ops[1], Asm.pAddr);
}
else { av
= -3; } //операнда нет
k = (bk
<< 4);
if (av>= 0) {
lil = new ListingLine ( lineNumber,
Asm.acom, k, av, " ",sourceLine);
bic = new BinCom ( 2, 2, Asm.acom,
(byte)k, (byte)av);
}
else { //ошибка
в 1-м или 2-м операнде
String xx = new String();String
newString=new String();
xx+= new String ("Oперанд:
" + DiagLine.ErrorMess [ -av ] );
lil = new ListingLine( lineNumber,
-1, -1, -1, "-",sourceLine);
dil = new DiagLine( lineNumber,
sourceLine + "\n" + xx);
}
return new InfoBag( lil, dil, bic);
}InfoBag parseZ (String[ ] ops, int
bk ) {//формат Z - без операндов
DiagLine dil = null;
ListingLine lil = null;
BinCom bic = null;
int k = (bk << 4); // 4 бита -
код операции,
lil = new ListingLine(lineNumber,
Asm.acom, k, -1, " ",sourceLine);
bic=new BinCom ( 1, 2, Asm.acom, (
byte ) k,(byte)0);
return new InfoBag ( lil, dil, bic
);
}InfoBag parseR ( String [ ] ops,
int bk ) { //формат R
int k=bk; int rv;
DiagLine dil = null;
ListingLine lil = null;
BinCom bic = null;
if (ops.length > 1) { rv =
setRegValue(ops[1], Asm.pReg);
}
else { rv = -3;}
k = (bk << 4)|(rv | 0);
if ( rv>=
0 ) { // ошибок в операнде нет
lil = new ListingLine(lineNumber,
Asm.acom, k, -1, " ",sourceLine);
bic=new BinCom ( 1, 2, Asm.acom, (
byte ) k, ( byte )0);
}
else { // в операнде есть ошибки
String xx =new String ("Первый
операнд: "+DiagLine.ErrorMess [-rv] );
lil = new ListingLine ( lineNumber,
-1, -1, -1, "-",sourceLine );
dil = new DiagLine ( lineNumber,
sourceLine + "\n" + xx ) ;
}
return new InfoBag ( lil, dil, bic
);
}int setRegValue(String w,Pattern
pr) {// w - слово, содержащее операнд-регистр
if (( w == null ) || ( w.length()==0
)) { val= -3;
}
else {
if ( pr.matcher(w).matches( ) ) {
val = Integer.parseInt (
w.replaceAll("[Rr]",""));
val = (val | 0);
}
else { val = -2; }
}
return val;
}int setAddrValue( String w,Pattern
pr) {
int val = 0;
if ((w == null) || (w.length() ==
0)) { val= -3;
}
else {
if ( pr.matcher(w).matches ( ) ) {
val = Integer.parseInt ( w.substring
( 0, w.length( ) - 1), 16);
}
else { val= -2; }
}
return val;
}
}
Класс InfoBag
public class InfoBag { //информационный пакет
char lineType; //тип строки в исходном тексте
ListingLine listline; //строка листинга
DiagLine diagline; //строка диагностики
BinCom bincom; //двоичный код, соответствующий
строке
public InfoBag (ListingLine
listline, DiagLine diagline ,BinCom bincom) {
this.listline = listline;
this.diagline = diagline;
this.bincom = bincom;
}
}
Класс
ListingLineclass ListingLine {
String lineNo; //номер
строки
String comAddress; //адрес размещения кода
команды
String byteOne; //первый байт команды
String byteTwo; //второй байт команды
String errMark; // ‘’-‘’ отметка строки с
ошибкой
String srcLine; //копия
исходной
строки
public ListingLine( ) { lineNo = new
String(); byteOne = new String();
byteTwo = new String(); comAddress =
new String();
srcLine = new String();
}
public ListingLine(int lno ,int ad,
int k1, int k2, String err, String line){
lineNo = new
String(String.format("%4d ", lno));
if (ad<0) comAddress = new
String(String.format("ххх
"));
else comAddress = new
String(String.format("%03x ",ad));
if (k1<0 ) byteOne = new
String(String.format("хх
"));
else byteOne = new
String(String.format("%02x ",k1));
if (k2<0 ) byteTwo = new
String(String.format("хх
"));
else byteTwo = new
String(String.format("%02x ",k2));
errMark = err.substring(0, 1);
srcLine = line;String toString() {
return new String(lineNo+comAddress+byteOne+byteTwo+errMark+srcLine);
}
}
Класс BinComclass
BinCom {
int len;
int addr;
byte byte1, byte2;
int p;
public BinCom ( ) {
len= addr= byte1= byte2=(byte)0;
}
public BinCom ( int l, int pd, int
a, byte b1,byte b2 ) {
len = l; addr = a; p=pd;
byte1 = b1; byte2 = b2;
}
}
Класс
DiagLineclass DiagLine {
public static String[ ] ErrorMess={
"все
хорошо",
//0
"неверная мнемоника", //-1
"неправильно задан операнд(адрес)",
//-2
"операнд
отсутствует"
//-3
};
String lineNo;
String ms;
public DiagLine ( ) {
lineNo = new String();
ms = new String();
}
public DiagLine ( int ln, String msg
) {
lineNo = new String ( String.format
( "%03d", ln) ) ;
ms = msg;
}
public String toString(){
return new String ( lineNo+"
"+ms);
}
}