Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Met_Lab2_121113

.pdf
Скачиваний:
16
Добавлен:
11.05.2015
Размер:
1.72 Mб
Скачать

Юникод и традиционные кодировки

Внедрение Юникода привело к изменению подхода к традиционным 8-битным кодировкам. Если раньше кодировка задавалась шрифтом, то теперь она задаётся таблицей соответствия между данной кодировкой и Юникодом. Фактически 8-битные кодировки превратились в форму представления некоторого подмножества Юникода. Это намного упростило создание программ, которые должны работать с множеством разных кодировок: теперь, чтобы добавить поддержку ещё одной кодировки, надо всего лишь добавить ещё одну таблицу перекодировки в Юникод.

Кроме того, многие форматы данных позволяют вставлять любые символы Юникода, даже если документ записан в старой 8-битной кодировке. Например, в HTML можно использовать коды с амперсандом.

Юникод — это система для линейного представления текста. Символы, имеющие дополнительные надили подстрочные элементы, могут быть представлены в виде построенной по определённым правилам последовательности кодов (составной вариант, composite character) или в виде единого символа (монолитный вариант, precomposed character).

Вдокументах Unicode используются различные шрифты. Как правило, файл шрифта Unicode содержит начертания не для всех символов, определенных в стандарте, а лишь для символов из некоторых областей.

Некоторые программы, например ядро Windows NT, ее графический интерфейс (GDI)

ифайловая система (NTFS), реализованы с использованием Unicode. Программы, запущенные в среде NT, могут работать также с однобайтовыми символами, кодировка которых в этом случае соответствует установленной по умолчанию кодовой странице ANSI (для России-Windows Cyrillic).

Перед вызовом некоторых функций программного интерфейса NT программы, работающие с кодовой страницей ANSI, преобразуют однобайтовые символы в Unicode. Чтобы преобразование выполнялось без ошибок, обычно указывается страна в приложении Regional Settings. Такой же подход используется для корректной работы с национальными символами программ в среде MS-DOS.

Вотличие от Windows NT ядро и графический интерфейс Windows 95 не используют Unicode, а работают с кодовыми страницами. Однако в этой системе предусмотрена возможность динамического изменения наборов символов и раскладок клавиатуры, что позволяет создавать документы, содержащие одновременно символы из разных наборов. Буфер обмена Windows 95 способен хранить тексты в формате CF_UNICODETEXT. В составе Windows 95 и более поздних версий поставляется набор шрифтов Unicode, с которыми, в частности, могут работать программы Microsoft Office.

Выяснить, какие наборы символов присутствуют в том или ином шрифте Unicode, можно с помощью стандартной утилиты Character Map (таблица символов), включенной в состав Windows NT. Выбрав в списке Subset строку Cyrillic, вы увидите таблицу с символами кириллицы, соответствующую кириллической области Unicode.

Использование Unicode значительно упрощает создание многоязычных документов, публикаций и программных приложений. Поэтому следует ожидать его широкого применения в электронных изданиях.

31

Логические основы ЭВМ

32

1. Системный отладчик Debug

DEBUG - что в переводе означает "отладчик", системная программа, позволяющаяя просматривать оперативную память, вводить команды Ассемблера непосредственно в ячейки памяти, а так же вводить машинные коды, что является самым низким уровнем управления процессором компьютера.

Для запуска данной программы необходимо ввести слово "debug" в командной строке DOS, в ОС Windows это можно сделать через меню "Пуск" и "Выполнить" и в появившейся строке ввести "debug".

Далее будем считать что работа ведется в среде ОС Windows.

На экране появится DOS окно в котором не будет никаких сообщений, только символ "-" который означает "режим приема команд "DEBUG". Данная программа может работать в двух режимах - в "режиме приема команд "DEBUG" и в "режиме приема команд "Assembler". Теперь программа ждет от пользователя ввода команд.

1.1. Адресация памяти

Одним из основных аспектов низкоуровневого программирования является понимание процесса адресации памяти компьютера. В компьютере минимальной единицей памяти, которая может быть адресована, является байт. Каждый байт оперативной памяти имеет свой уникальный адрес, который складывается из двух составляющих — номера сегмента и смещения в этом сегменте.

Вся память логически разделена на сегменты, каждый из которых содержит некоторое количество ячеек (байт) памяти. На рисунке 1.1.1. показана примерная логическая структура оперативной памяти.

Рис. 1.1.1. Оперативная память

Для того, что бы обратиться к ячейке памяти, нужно знать номер сегмента, в котором она находится и ее смещение в этом сегменте. Под смещением будем понимать порядковый номер от начала сегмента. Для 16-разрядного режима адресации номера сегментов и смещений лежат в диапазоне 0000 — FFFF, получая таким образом 65536 возможных адресов. Аналогичен диапазон смещений.

В итоге полный адрес ячейки складывается из номера сегмента и смещения и записывается через символ «:», например 01BF:0010.

33

1.2. Работа в отладчике

Весь процесс работы в отладчике складывается из ввода команд. Все основные команды "DEBUG" состоят из одной буквы. Ввод команд осуществляется с помощью клавиши Enter.

Рассмотрим имеющиеся команды:

D - от слова DUMP. Команда выводящая на экран дамп памяти, проще говоря, содержимое ячеек некоторой области памяти. Если ввести эту команду без параметров, т.е. ввести "D" и нажать Enter, то программа выдаст дамп памяти, начиная с нулевой ячейки сегмента, номер которого в данный момент находится в регистре DS. Появится таблица, состоящая из 3 основных колонок и 8 строк (всего 128 значимых элемента (ячейки памяти)). В первой колонке - адреса, во второй двухзначные шестнадцетеричные числа, содержащиеся в каждой ячейке и в третьей - символьная информация, соответствующая ASCII коду, находящемуся в этих ячейках. Символ «.» в третьей колонке чаще обозначает символ, который не может быть выведен на экран, например символ перехода на новую строку.

Рис. Результат выполнения команды D

Данную команду, как и у многие другие, можно вводить с параметрами, т.е. саму команду, а через пробел еще какую либо информацию, например D 0040:0013. По такому запросу, программа выдаст дамп памяти, начиная с 13-й ячейки 40-го сегмента, и далее при вводе команды без параметров адрес сегмента останется 40-м, пока его не изменят другими параметрами. Т.е. в общем случае - командой D мы можем узнать содержимое ячеек нашей оперативной памяти.

R - от слова READ. Данная команда выводит содержимое регистров процессора. Без параметров данная команда выводит содержимое всех основных регистров, а так же состояние флагов и код следующей по списку команды. В качестве параметра данной команды можно вводить имена регистров - например R AX - выдаст на экран содержимое регистра АХ, и после чего вы увидите знак ":", в отличии от стандартного "-", который означает что программа ждет от пользователя не команду, а какое то шестнадцетеричное число, вводимое число и будет записано в этот регистр. Если вы не хотите менять текущее сожержимое регистра, можно просто, не вводя никаких чисел, нажать Enter, оставив строчку пустой. Т.е. в общем случае командой R мы можем смотреть содержимое регистров, а так же менять это содержимое.

Е - от слова Enter. Одна из самых важных команд! Данная команда вводит машинные коды и данные в память. Машинный код - это двухзначная шестнадцетеричная команда

34

(например B8). Все команды разделяются пробелами. На самом деле на процессор подаются команды в двоичном коде, поскольку других он не понимает, но для облегчения записи используется шестнадцетеричная СС. Т.е. если код B8 - значит на процессор поступает код 10111000. Команда Е принимает только машинные коды и данные.

!ОЧЕНЬ ВАЖНО, ЗАПОМНИТЕ! Данные записываются в сегмент данных, за который отвечает регистре DS. Коды записываются сегмент команд (кодов), соответственно за который отвечает регистр CS. Не путайте машинные коды и данные.

Данные так же можно хранить в поле машинной инструкции, но этого лучше не делать, потому что байты чисел записываются в перевернутом виде - старший байт по старшему адресу, младший байт по младшему адресу. Например машинная команда B8 04 30 запишет в регистр АХ не 0430, а 3004. В8 - это код команды (в данном случае В8 - это пересылка в регистр АХ).

Первым параметром должен быть указан один из 4х сегментных регистров (CS, DS, SS или ES). Если мы вводим команды - нужно указать регистр CS, если данные - регистр DS, 2 последних использовать не будем. Сразу за имененм регистра через ":" без пробелов указываем смещение, т.е. начиная с какой ячейки данного сегмента, будем записывать коды или данные. После смещения начинаем ввод информации по 1 байту через пробел (см. пример).

Пример:

Ввести в регистр BX число 0FF1 - E CS:0100 BB F1 0F ничего больше. Данная команда записывает в 0100 ячейку код В8, в 0101 ячейку число F1, и в 0102 ячейку число 0F. Т.е. данной командой мы использовали 3 ячейки памяти (3 байта). Возникает вопрос - если писать через пробел много, много кодов и данных, программа сама разберется в них? Ответ - да программа уже "знает" все машинные коды и знает сколько байт должно следовать за каждой командой и автоматически записывает каждый байт в последующую ячейку. Например, нельзя записать E CS:0100 B8 01 если требуется переслать единицу в регистр АХ, поскольку регистр АХ имеет объем 2 байта и программа ждет ввода 2х чисел, то введено будет в АХ число ХХ01, где ХХ - это то что хранится в следующей ячейке памяти после 01 (не забываем что байты в поле машинных кодов перевернуты).

А - от слова Assemble. Тоже важная команда, которая переводит программу DEBUG во второй режим - режим приема команд Ассемблера. В этом режиме DEBUG ждет приема команд языка Ассемблер. Может записываться без параметров и прием команд и их запись начнется в ячейки памяти по умолчанию (номер сегмента берется из CS, номер ячейки автоматически 0100), при условии первоначального ввода команды А, если команда А уже вводилась раньше, то ввод без параметров продолжится с той ячейки на которой остановился ввод в последний раз. В этом режиме мы видим не стандартный курсор "-", а адрес, в который будет записана следующая команда.

Пример:

Ввести команду MOV AX,BX в 120-ю ячейку 92-го сегмента кодов:

А 0092:0120 далее нажимаем <Enter> и вводим саму команду MOV AX,BX.

U - от слова Unassemble. Команда дизасемблирования. Выводит на экран таблицу из 3 основных столбцов, где указывается адрес (1 столбец), машинный код (2 столбец) и команда Ассемблера (3 столбец). Очень нужная команда для сопоставления команд Ассемблера машинным кодам. Допустим вам нужно узнать какой машинный код у команды DIV CX (деление СХ на АХ), нужно ввести команду А, затем ввести команду Ассемблера DIV CX, запомнить адрес ячейки в которую вводилась команда Ассемблера, после чего набрать U <адрес>, где <адрес> - это адрес той ячейки памяти, где хранится команда Ассемблера. На экран выведется таблица, в верхней строчке которой вы увидите <адрес> F7F1 DIV CX. F7F1 - это и есть искомый машинный код команды DIV CX.

Т - от слова Trace. Пусковая команда, которая выполняет одну машинную инструкцию (или команду Ассемблера).

35

!ОЧЕНЬ ВАЖНО, ЗАПОМНИТЕ! При вводе команды Т, выполняется команда, находящааяся в ячейке памяти по адресу CS:IP. Вот тот самый важный регистр IP - счетчик команд (Instruction Pointer), он указывает на адрес следующей выполняемой команды. Фактически в IP хранится только смещение, а номер сегмента, как видно, хранится в регистре CS. После выполнения команды Т, регистр IP автоматически увеличивается на 1,2 или 3, в зависимости от длины выполненной команды. Например при выполнении команды NOP регистр IP увеличится на 1, т.к. код машинный для данной команды - "90" - т.е. всего 1 байт, для команды DIV CX - на 2 байта, т.к. код - F7F1 - т.е. 2 байта, или на 3 байта после выполнения команды MOV AX,0001, т.к. код - B80100 - т.е. 3 байта. И если нужно выполнить команду которая находится в ячейке со смещением 0150, сначала нужно установить в регистр IP значение 0150 (командой R), а затем уже выполнять команду Т.

G - от слова Go. Аналог команды Т только данная команда выполняет массив команд, а не одну. Начинает выполнять команды, начиная с команды CS:IP до конца программы. Рекомендуется после данной команды вводить параметр в виде адреса, до которого будет выполняться программа.

ВНИМАНИЕ!!! Обращаться с командой G аккуратно, т.к. не имеея конца программы, команда G заставит DEBUG выполнять огромное количество команд и в списке миллионов команд может попасться деление на 0 (в этом случае окно DEBUG само закроется и все введенные данные в DEBUG'е будут потеряны), иногда встречаются конфликтующие команды, которые "вешают" компьютер намертво (данный случай более опасен, т.к. теряются все несохраненные данные).

Q - от слова Quit. Команда выхода и закрытия окна DEBUG.

Лабораторная работа № 1

Тема работы. Работа в системном отладчике Debug.

Цель работы. Освоить основы низкоуровневого программирования, работы с памятью компьютера и машинными кодами.

Содержание работы. Основой данной лабораторной работы является использование системного отладчика DEBUG, который позволяет просматривать память, вводить программы и осуществлять трассировку их выполнения. В лабораторной работе изучается процесс ввода команд непосредственно в память в область сегмента кодов. Изучаются команды отладчика DEBUG.

Для запуска этой программы в MS Windows нажмите «ПУСК» и выберите пункт «Выполнить», затем введите DEBUG и запустите. После окончания загрузки на экране появится приглашение в виде дефиса, что свидетельствует о готовности программы DEBUG для приема команд.

В оперативной памяти компьютера хранится много различных сервисных данных, например количество памяти, доступной в текущий момент. Для того, чтобы узнать сколько памяти доступно, введите команду:

D 40:13

Первые два байта, появившиеся в результате на экране, содержат объем памяти в килобайтах и в шестнадцатиричном представлении, причем байты располагаются в обратной последовательности.

Несколько следующих примеров показывают шестнадцатиричное обратное, шестнадцатиричное нормальное и десятичные представления.

Шестн. обратн.

Шестн. норм.

Десятичное

 

 

 

80 00

00 80

128

 

 

 

00 01

01 00

256

 

 

 

80 01

01 80

384

 

 

 

00 02

02 00

512

 

 

 

80 02

02 80

640

36

Далее рассмотрим использование команды E. Введем простую программу, которая складывает два числа – 10 и 20. Для этого, с помощью команды Е, начиная со смещения 100 в сегменте кода, начнем ввод следующих команд:

B8 10 00

BB 20 00 01 D8

Здесь три машинных команды, которые выполняют следующие действия:

Загрузка в регистр AX числа 10;

Загрузка в регистр BX числа 20;

Сложение регистров AX и BX.

Первые две команды (B8 и BB) содержат поля данных, в которых, как уже

упоминалось в теории, байты данных перевернуты. Видно что, вместо нужного числа 10 в команде B81000 находится число 1000, перевернув байты получим 0010. Аналогично для команды BB2000.

Ввести данные команды в память можно с помощью команды Е двумя способами:

Одиночной командой E CS:0100 B8 10 00 BB 20 00 01 D8;

Побайтовым вводом. Введя E CS:100, нажав <Enter>, через пробел вводим каждый байт.

При вводе команд вторым способом перед вводом каждого байта отображается

текущее содержимое байта в который планируется ввод.

Далее рассмотрим команду R, которая отображает состояние процессорной (регистровой) памяти. Введите команду R без параметров и нажмите <Enter>.

В результате на экран будет выведено состояние всех основных регистров, флагов, а так же адрес, машинный код и ассемблерный мнемокод следующей выполняемой команды.

Команду R можно использовать для изменения текущего значения какого-либо регистра. Изменим содержимое регистра DX, для этого введите команду:

R DX

Результатом данной команды будет вывод на экран текущего содержимого регистра DX и прием нового значения, о чем будет сигнализировать символ «:». Введите после символа «:» значение FF0E и нажмите <Enter>. Что бы убедиться в том, что значение в регистре DX поменялось, введите команду R или R DX еще раз.

Следующая команда Debug, необходимой для выполнения введенной программы, это команда T. Данная команда выполняет одну машинную инструкцию, находящуюся по адресу CS:IP. Поскольку при вводе программы использовалось только смещение, то регистр CS устанавливать не надо, но регистр IP желательно проверить. Введите команду R IP. Убедившись, что значение в данном регистре равно 0100, начинаем выполнение программы. Если значение в регистре IP отлично от 0100, в таком случае введите 0100 после символа «:», в противном случае не нужно ничего вводить, просто нажмите <Enter>, оставив пустую строку.

Введите команду T без параметров, в результате чего на экран будет выведено сообщение, аналогичное тому, что выводится при вводе команды R. Просмотрите содержимое регистров и убедитесь, что содержимое регистра AX изменилось на 0010. Введите команду Т еще раз. Убедитесь, что значение регистра BX изменилось на 0020. Для завершения программы введите команду Т третий раз и посмотрите результат сложения регистров AX и BX, находящийся в регистре AX.

Далее рассмотрим команды U и A.

Команда A так же как и команда E вводит в память команды, за исключением того, что данные команды принимаются не в машинных кодах, а в мнемокодах языка ассемблера. Так же, команда A, в отличии от команды Е не принимает набор команд одной строкой.

37

С помощью команды А введем программу, которая умножает число E1 на 5. Введите команду:

A 100

Затем программу, разделяя ввод каждой команды нажатием <Enter>:

MOV AX, 00E1

MOV BX, 0005 MUL BX

Для завершения ввода программы последнюю строку оставьте пустой, нажав <Enter>. Теперь необходимо выполнить эту программу. С помощью команды Т выполните

введенную программу предварительно установив в регистр IP значение 0100.

Команда U не вводит никакие данные, а служит только для сопоставления мнемокодов ассемблера машинным кодам. Введите команду U 100. В результате вы увидите дамп памяти, начиная с 100 смещения, в котором будут указаны машинные коды и соответствующие им команды ассемблера.

Задание к работе:

1.Командой E запишите в память по адресу DS:0500 строку байт, соответствующую строке “Hello world” в представлении ASCII. Командой D сделайте дамп памяти начиная с данного адреса, убедившись в правильности ввода. Используйте таблицу ASCII кодировки для определения кодов символов.

2.С помощью команды А, начиная с адреса CS:0100, введите программу

MOV AX, 15

MOV BH, 3 DIV BH

Определите какие машинные коды соответствуют этим трем введенным командам. Выполните все эти команды и посмотрите результат. Команда ассемблера DIV BH делит содержимое регистра AX на содержимое регистра BH, объясните, почему при делении 15 на 3 результатом, который поместится в AX, стало число 7.

3. Выполните задание 2, заменив команду деления DIV на команду умножения MUL.

38

2. Основы программирования

2.1. Процесс программирования

Программированием мы называем составление программы, т.е. списка инструкций (команд), которые должен будет последовательно выполнять процессор. Этот список инструкций в виде двоичного кода должен быть размещен в памяти компьютера. Выполнение программы начинается тогда, когда процессор будет настроен на первую выполняемую команду программы. Настройка производится следующим образом: сегмент первой выполняемой команды загружается в регистр сегмента команд CS, а смешение первой команды загружается а регистр указателя команд IP. После этого первая команда выполняется, а в регистр IР автоматически заносится смещение следующей команды, подлежащей выполнению. Изменение значений в регистре IP производится автоматически, программно доступа к этому регистру нет. Обычно команды выполняются в естественной последовательности, т.е. в порядке их написания в программе, но иногда эта последовательность может нарушаться, а именно тогда, когда среди команд встречается команда перехода (иногда называют также командой передачи управления). Такие команды фактически изменяют содержимое регистра IP (а иногода и CS). Явным образом изменить содержимое этих регистров невозможно. После выполнения всех инструкций программы управление передается на другую программу (например, в операционную систему, которая также является программой), т.е. фактически производится перенастройка IP и CS.

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

Язык Ассемблер является языком программирования самого нижнего уровня. В него введены лишь минимальные удобства для программиста: возможность использования имен переменных, меток для переходов и мнемокодов вместо кодов операций.

Итак, в комплект программных продуктов для разработки ассемблерных программ должен входить компилятор и компоновщик. Поскольку за логикой ассемблерной программы проследить гораздо труднее, чем за логикой программы на языках внешнего уровня, третьим неотъемлемым компонентом инструментария для разработки ассемблерных программ является отладчик, дающий возможность пошагового выполнения программы, наблюдения за значениями регистров и ячеек памяти, остановки выполнения в фиксированных местах программы и проч.

Существует несколько комплектов программных продуктов для разработки ассемблерных программ. Наиболее популярными из них являются компилятор, компоновщик и отладчик TASM, TLINK и TD компании Borland, а также MASM, LINK и CV компании Microsoft. Сами языки Ассемблера TASM и MASM также несколько различаются, однако в большинстве случаев удается создавать программы, переносимые с одного стандарта на другой.

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

39

информацию по форматированию текста и т. п.) и помещается в файл с любым именем и расширением *.asm. Такой файл называется исходным модулем. Затем запускается программа компилятора Ассемблера и в качестве аргумента ей передается имя исходного модуля. Если компиляция прошла без ошибок, создается новый файл с тем же именем и расширением *.obj, называемый обьектным модулем. Затем запускается программа компоновщика, предназначенная для разрешения внешних ссылок в объектном модуле и для объединения объектных модулей, если их несколько. В качестве аргумента программы компоновщика ей передается имя объектного модуля (или имена нескольких модулей). В результате работы компоновщика при отсутствии ошибок порождается исполняемый модуль - готовая к выполнению программа в файле с расширением ЕХЕ или СОМ.

Задавая различные опции компилятора и отладчика, можно попутно получать или не получать файл листинга (с расширением LST), содержащий протокол компиляции, и файл распределения памяти (с расширением MAP).

2.1.1. Этапы создания программы

Разработка программы на языке ассемблера включает четыре этапа.

1-й этап. Подготовка исходного текста программы и оформление его в виде текстового файла (одного или нескольких) с помощью какого-нибудь редактора в формате DOS с расширением .asm.

2-й этап. Ассемблирование программы с применением транслятора Tasm, результатом которого является объектный файл с расширением obj. Когда программа состоит из нескольких файлов (модулей), то их ассемблирование производится независимо друг от друга. Если в процессе трансляции будут обнаружены ошибки, то объектный файл не создаётся, а формируется сообщение об ошибках. Ошибки устраняются, после чего трансляция повторяется. Объектный файл (двоично-кодированное представление программы) не может быть запущен на исполнение, так как в нём не содержится информация о загрузке сегментов программы в памяти компьютера.

3-й этап. Компоновка программы производится компоновщиком (редактором связей) Turbo Linker и заключается в объединении объектных модулей в один исполняемый файл с назначением стартового адреса программы. Исполняемый файл имеет расширение exe. 2-й и 3-й этапы определяют процесс подготовки исполнительного файла программы, называемого

трансляцией.

4-й этап состоит в отладке программы с использованием отладчика Turbo Debugger, который будет являться основным инструментом при изучении форматов команд, их кодирования, а также представления переменных программы в памяти.

Можно написать простой командный файл для последовательной компиляции ассемблерной программы, ее компоновки и выполнения полученного исполняемого файла, например, для компилятора TASM:

echo off

tasm -zi -l %1

if errorlevel 1 goto exit

tlink -v -x %1

if errortevel t goto exit

40

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]