1.Процедура проектирования и
разработки программных систем
2.Проект ассемблера: цели и требования
3.Внешний проект ассемблера
4.Проект архитектуры ассемблера
Проект (projectus - брошенный вперёд) - система документов, определяющая процесс создания какого-либо продукта (его бумажная модель). Здесь за основу принят структурный подход с его методом пошаговой детализации, что вполне целесообразно в применении к программным системам (ПС) малого и даже среднего размера (до 10 т. операторов). Основные составляющие проекта и этапы разработки ПС:
1) Назначение;
2) Определение пользователя;
3) Подробное перечисление функций;
4) Публикации;
5) Эффективность;
6) Совместимость;
7) Конфигурация ТО и БПО;
8) Безопасность;
9) Обслуживание;
10) Установка;
11) Надёжность;
12) Мобильность.
Следует стремиться к использованию измеряемых категорий и чётких критериев.
Содержит описание ожидаемого поведения ПС с точки зрения пользователя в форме внешних спецификаций. Проектируемый продукт на этом этапе рассматривается как чёрный ящик. Общая идея этого этапа состоит в достаточно детальном определении входов и выходов ПС, включая определение входных и выходных языков, структуры входных и выходных файлов, содержания экранных форм и т.д. Иногда этот этап называют созданием прототипа ПС. В последующем внешний проект служит основой создания проекта архитектуры ПС и одновременно служит источником информации для технических писателей, составляющих комплекс документации для пользователя.
Основные изобразительные средства этого этапа: чертежи, схемы, рисунки, словесные описания, формализованные описания (математические выкладки, БНФ, расширенная БНФ (РБНФ), синтаксические диаграммы Вирта).
РБНФ позволяет заменить рекурсию
итерацией. К метасимволам БНФ добавлены скобки
факторизации "{" и "}", в которые
заключают повторяющуюся часть грамматической
конструкции, в том числе и нуль раз. Пример.
<Составной оператор> ::= begin
{<Оператор> ; } <Оператор> end .
Cинтаксические диаграммы Вирта впервые были использованы при определении синтаксиса АЯ Pascal. Это графическая форма РБНФ.
Под архитектурой понимается
распределение функций в системе. Для ПС это
означает разбиение её на структурные и
функциональные модули, в качестве которых могут
быть единицы компиляции (файлы), процедуры,
объекты, типы (классы) и т.д. Проект архитектуры
включает:
- определение метода преобразования данных,
- определение основных структур данных,
- разработка иерархической схемы вызовов
модулей,
- составление спецификаций модулей.
На этом этапе выполняется проектирование и разработка отдельных модулей ПС. Методология проектирования и разработки подсистемы в общих чертах повторяет методологию проектирования и разработки системы в целом.
Запись программы на языке разработки.
ПС среднего размера обычно отлаживают по мере сборки. Есть два классических метода сборки - нисходящий и восходящий. Нисходящий метод базируется на использовании заглушек - процедур, которые в очень упрощённой форме моделируют поведение реальных процедур нижних уровней ПС. По мере продвижения разработки ПС заглушки заменяются реальными процедурами. Восходящий подход базируется на использовании драйверов - программных единиц, моделирующих поведение реальных программных единиц вызывающего уровня. На практике используют всевозможные комбинации этих двух классических методов. ПС малого размера отлаживают обычно методом большого скачка, т.е. сразу всю систему целиком, в некоторых случаях предварительно отлаживая лишь процедуры самых нижних уровней.
Проект должен содержать необходимую систему тестов для проведения отладки системы на всех уровнях, включая внешнее тестирование. Этот вид тестирования относится ко всей системе в целом.
Сопровождение ПС предполагает внесение в неё изменений на этапе её эксплуатации. Такая необходимость может быть вызвана изменением условий применения или обнаружением ошибок. Важность этого этапа вытекает из структуры трудозатрат на обеспечение жизненного цикла ПС среднего размера: проектирование - 15%, кодирование - 10%, тестирование и отладка - 25%, сопровождение - 50%. В этой связи разумно предъявить достаточно жёсткие требования к качеству сопровождающей систему документации.
Сразу же дадим имя разрабатываемому
продукту - BAS (Базовый ассемблер). Учитывая
учебный характер проекта, определим следуюшие
цели и требования:
1) Назначение. Ассемблирование
программ в процессе изучения студентами методов
низкоуровнего программирования на примере
учебной ЦВМ.
2) Пользователи. Студенты ФИПМ ВлГУ.
3) Функции. Перевод исходного
модуля (ИМ) на языке ассемблера в формат
абсолютного загрузочного модуля (АМ) для учебной
ЦВМ с выводом протокола ассемблирования
(листинга).
4) Публикации. 1) BAS: описание языка
ассемблера для учебной ЦВМ. 2) BAS: инструкция по
установке и эксплуатации.
5) Конфигурация. Стандартный состав
IBM PC, MS DOS любой версии.
6) Установка. Методом копирования
EXE-файла с установочной дискеты на жёсткий диск PC.
Вход: ИМ. Выходы: АМ и листинг. Все файлы текстовые. Установим стандарт на расширение физических имён файлов: ИМ - *.bsm,АМ - *.abm, листинг - *.lst.
2. Определение синтаксиса BAS
<Программа> ::= { <Оператор> |
<Комментарий> | <Пустая строка> }
<Оператор> ::= [<Метка>] " " {" "}<Код
операции> [ " " {" "}<Операнд>[ ,
<Операнд>]] <Комментарий>
<Метка> ::= <Имя>
<Код операции> ::= <Имя>
<Операнд> ::= <Имя> | <Целое число>
<Комментарий> ::= { " " } ; <Строка из любых
неуправляющих литер кодовой таблицы ЦВМ>
И так далее до полного определения синтаксиса языка.
Здесь должны быть даны сведения о структуре команд ЦВМ и способах адресации, система команд ЦВМ с указанием мнемоник кодов операций, ограничения на значения операндов операторов BAS. Мы ограничимся только описанием псевдокоманд (п/к) BAS - операторов, которые используются для управления ассемблером и в машинные инструкции не переводятся.
П/к start: Единственный операнд команды определяет адрес первого размещаемого объекта (машинной команды или данного) программы. Его значение должно быть в пределах 0..4095. Операнд должен задаваться только целым числом. Метка этой команды одновременно является именем всей программы. По умолчанию - "NoName". П/к start обязательно должна присутствовать в программе и быть единственной. Предшествовать этой команде могут только комментарии.
П/к end: Определяет конец ИМ. Никакой текст после команды end ассемблером не обрабатывается и в листинге не отображается. Единственный операнд этой команды рассматривается как адрес точки входа в программу. Его значение должно быть в пределах 0..4095. Это обязательная команда программы и должна быть в ней единственной.
П/к word: Определяет инициализированную переменную в памяти ЦВМ. Операнд определяет значение переменной. Допустимый диапазон значений операнда: -2^23..(2^23 - 1).
П/к resb: Резервирует блок памяти заданной длины. Длина блока определяется операндом команды и может быть в диапазоне значений 0..4096.
<АМ> ::= <Заголовок> { <Текст> }
<Концевик>
<Заголовок> ::= H <ИМ (6)> <НАМ (6)> <ДМ (6)>
<Текст> ::= T <АЗТ (6)> <ДТ (2)> <Байты кода
программы (2*ДТ)>
<Концевик> ::= E <АТВ (6)>
Каждый элемент ИМ типов H, T и E размещается в отдельной строке текстового файла. Обозначения: ИМ - имя модуля, НАМ - начальный адрес модуля, ДМ - длина модуля, АЗТ - адрес загрузки текста, ДТ - длина текста, АТВ - адрес точки входа в программу. Все эти поля записываются в символьном шестнадцатеричном формате. В скобках указаны длины полей. Мы сознательно используем символьный формат представления АМ как более наглядный, хотя на практике для записи кода используют исключительно бинарный формат.
Здесь необходимо детально определить структуру листинга как документа (см. пример ниже).
Файл inc.bsm:
; var = var + 1 inc start 20 lda var add c1 sta var hlt ; var resb 3 c1 word 1 end inc
Файл inc.abm:
Hinc 000014000010 T0000140A00001E1800210C001EFF T00002103000001 E000014
Файл inc.lst:
Базовый учебный ассемблер BAS 1.0 Первый просмотр: Таблица символов: a = 000 x = 001 l = 002 inc = 014 var = 01E c1 = 021 Второй просмотр: Адр. Код # Оператор 1 ; var = var + 1 014 2 inc start 20 014 00 00 1E 3 lda var 017 18 00 21 4 add c1 01A 0C 00 1E 5 sta var 01D FF 6 hlt 7 ; 01E 8 var resb 3 021 00 00 01 9 c1 word 1 024 10 end inc Статистика: *** Ошибок не обнаружено *** Адрес загрузки программы: 014 Пусковой адрес программы: 014 Длина программы: 010 Имя файла входа: inc.bsm
Мы рассмотрим эту тему несколько шире, чем этого обычно требует содержание программного проекта. Это связано с тем, что попутно мы должны изучить сами принципы ассемблирования и понять как организован ассемблер. Так как на данном этапе мы ешё не обладаем опытом проектирования таких программ, то будем вынуждены затронуть здесь и кодирование, чего в реальных проектах, естественно, не делают. С другой стороны в учебных целях мы существенно сократим рутинную часть проекта и во многих случаях ограничимся лишь примерами.
Ассемблер строится по классической двухпроходной схеме. В ходе первого прохода строится таблица символов, определяемых программистом. В ходе второго просмотра генерируются командды и заносятся в АМ. Попутно в ходе второго прохода формируется файл листинга.
Основные структуры данных первого
прохода:
файл ИМ;
var count, dcount: Integer; { Cчётчик адреса и его приращение }
const mtmk = 21; {Длина таблицы машинных команд (ТМК) } Tmk: array[1..mtmk] of {ТМК} record m: string[6]; {Мнемоника команды} c: string[2]; {Шестнадцатеричный код команды} f: Integer {Формат команды} end = ((m: 'add'; c: '18'; f: 1), . . . (m: 'nop'; c: 'FE'; f: 4));
const mts = 100; {Длина таблицы символов (ТС)} var ts: array[1..mts] of {ТС} record n: string[6]; {Имя} v: Integer {Значение} end;
const mtpk = 4; {Длина таблицы псевдокоманд} tpk: array of[1..mtpk] of string[5] = ("start", "end", "word", "resb");
Основные структуры данных второго
прохода:
файл ИМ; ТМК; ТС; ТПК; строка для сборки команд;
файл АМ; файл листинга; буферы файлов АМ и
листинга.
procedure Pass1; begin {Инициализация переменных}; repeat {Ввод очередного оператора ИМ}; dcount := 0; if {Это комментарий} then Continue; {Выделение метки и кода операции}; if {Метка есть} then {Поместить метку в ts с значением count}; {Поиск кода операции в tpk}; if {Найдена} then {Обработка п/к с определением dcount} else begin {поиск кода операции в tmk}; if {Найдена} then {Определение dcount} else {Сообщение об ошибке "Неопределённый код операции"} end; count := count + dcount; until {Код операции = 'end'} end; {Pass1}
procedure Pass2; begin {Инициализация переменных}; repeat {Ввод очередного оператора ИМ}; dcount := 0; if {Это комментарий} then Continue; {Выделение кода операции}; {Поиск кода операции в tpk}; if {Найдена} then {Обработка п/к с определением dcount} else begin {поиск кода операции в tmk}; if {Найдена} then {Генерация машинной команды и определение dcount} else {Сообщение об ошибке "Неопределённый код операции"} end; {Размещение команды в АМ}; {Формирование строки листинга и её вывод}; count := count + dcount; until {Код операции = 'end'}; {Определение и вывод статистики ассемблирования}; end; {Pass2}
Анализ алгоритмов первого и второго
проходов ассемблера позволяет определить
основные процедуры данного шага детализации. Это
могут быть:
RdLine - ввод очередной строки ИМ с проверкой
неожиданного окончания входного файла;
GetLex - лексический анализатор для определения
имён и констант;
PutInTs - помещение имени в ТС;
PoiskInTpk - поиск в ТПК;
ExecPk1 - обработка п/к на первом проходе;
ExecPk2 - обработка п/к на втором проходе;
PoiskInTmk - поск в таблице машинных команд;
Error - обработка ошибок ассемблирования;
GenCode - генерация кода машинной команды;
PutInAM - размещение сгенерированной команды в АМ;
PrintLine - вывод строки листинга.
На данном этапе разработки можно составить такую схему вызовов:
BAS(
Pass1( RdLine, GetLex, PutInTs, PoiskInTpk, ExecPk1, PoiskInTmk, Error),
Pass2( RdLine, GetLex, PoiskInTpk, ExecPk2, PoiskInTmk, GenCode, PutInAM,
PutLine)
)
Обозначим через Arg значение операнда
текущего оператора п/к. На первом проходе
необходимо выполнить следующие операции:
start: count := Arg;
end: ;
word: dcount := 3;
resb: dcount := Arg;
На втором проходе необходимо выполнить
следующие операции:
start: count := Arg; Определение имени программы и её
длины. Открытие файла АМ. Формирование заголовка
АМ.
end: Определение адреса точки входа в программу и
формирование концевика АМ. Закрытие файла АМ.
word: dcount := 3; Генерация в Code кода константы с
значением Arg.
resb: dcount := Arg; Установка признака формирования
новой записи текста АМ.
Составим для примера спецификацию процедуры GenCode.
Спецификация модуля GenCode - "Генерация машинной команды"
1. Заголовок: procedure
GenCode(Index: Integer);
2. Входы: Index - индекс записи в ТМК,
соответствующей текущему оператору;
3. Выходы: нет;
4. Глобальные имена: ts, tmk, Code;
5. Функция: построение в поле строковой
переменной Code шестнадцатеричного кода команды,
соответствующей текущему оператору ИМ.
Предполагается, что указатель текущей литеры
входа установлен на первую литеру после
мнемонического кода операции текущего
оператора.
7. Используемые процедуры: GetLex, PoiskInTs, ToInt, ToHex;
8. Особые случаи: нет.
Рассмотрим алгоритм генерации кода команды на примере команды 1-го формата. Команды этого формата имеют длину три байта. Код операции занимает биты 23..16; бит индексной адресации - бит 15, адрес операнда - биты 11..0.
procedure GenCode(Index: Integer); var Arg1,Arg2: Integer; {Значения аргументов оператора} ... begin Code := tmk[Index].c; {Установка кода операции} case tmk[Index].f of 1: begin {Генерация кода команды 1-го формата} GetLex(LexType,Str); {Чтение первого операнда}
case LexType of NAME: PoiskInTS(Str,Arg1); {Получение из ts значения имени} NUMBER: ToInt(Str,Arg1); {Преобразование числа в целый тип} else begin Arg1 := 0; Error(17) end {Ошибка в операнде} end; {case}
GetLex(LexType, Str); {Чтение запятой} if Str = ',' then begin GetLex(LexType,Str); {Чтение второго операнда} case LexType of NAME: PoiskInTS(Str,Arg2); {Получение из ts значения имени} NUMBER: ToInt(Str,Arg2); {Преобразование числа в целый тип} else begin Arg2 := 0; Error(17) end {Ошибка в операнде} end; {case} end else Arg2 := 0; {Значение по умолчанию}
Code := Code + ToHex(Arg2<<15 + Arg1,4); {Генерация кода} end; {Конец генерации кода команды 1-го формата}
2: ...
end; {case tmk[Index].f} end; {GenCode}
Наличие ошибок в программе пользователя
не может считаться исключением. Каков бы ни был
файл ИМ программа BAS должна достойно завершить
свою работу. При трансляции программы, в которой
могут содержаться ошибки, возникает три
проблемы:
- обнаружение ошибки;
- определение местоположения ошибки;
- восстановление программы с целью продолжения
её трансляции.
Различают следующие виды ошибок:
- синтаксические;
- семантические (например, повторное определение
метки в программе);
- ошибки ограничения реализации (например,
переполнение таблицы символов).
Способы восстановления:
- игнорирование (например, пропуск оператора с
неправильным кодом операции);
- обнуление (например, выход значения операнда из
допустимых пределов).
Способы организации обработки:
- трансляция до первой ошибки;
- прекращение обработки, если серьёзность ошибки
превышает установленный уровень;
- включение сообщений об ошибках в конец
листинга;
- вывод сообщений об ошибках вместе со строками, в
которых они были обнаружены.
Copyright г Барков Валерий Андреевич, 2000