Разработка Windows-приложений на языке ассемблера

Описание:
Доступные действия
Введите защитный код для скачивания файла и нажмите "Скачать файл"
Защитный код
Введите защитный код

Нажмите на изображение для генерации защитного кода

Текст:

Министерство образования Пензенской области

ГБПОУ ПО «Кузнецкий колледж электронной техники»

ПОЯСНИТЕЛЬНАЯ ЗАПИСКА

к курсовому проекту (работе)

по дисциплине МДК 01.01 «Системное программирование»

на тему «Разработка Windows-приложений на языке ассемблера»

Выполнил  Ахметов Т.Д.      ____________

                                                       (подпись)

Группа  3п5-11

Специальность 230115

Руководитель  Демаева С.Н          ____________

                                                                (подпись)

Проект (работа) защищен (а)         «___» ___________20__г.

Оценка                                                «___» ___________

2014

Реферат

Сегодня трудно найти компьютер, на котором бы не была установлена одна из версий Windows, в реферате пойдет речь о программировании для Windows на платформе Intel.

В подавляющем большинстве книг о программировании для Windows изложение, как правило, ведется на базе языков C/C++, реже — на базе Pascal. Курсовая работа направлена на правильное понимание места ассемблера в архитектуре компьютера. Любая программа на языке самого высокого уровня по сути представляет собой последовательность машинных кодов. А раз так, то всегда остается теоретическая возможность написать ту же программу, но уже на языке ассемблера. Чем можно обосновать необходимость разработки Windows-приложений на языке ассемблера? Приведем следующие аргументы:

Язык ассемблера позволяет программисту полностью контролировать создаваемый им программный код и оптимизировать его по своему усмотрению;

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

При программировании на ассемблере сохраняется полный доступ аппаратным ресурсам компьютера;

Приложение, написанное на ассемблере, как правило, быстрее загружается в оперативную память компьютера;

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

Разумеется, эти аргументы не следует воспринимать, как некоторую рекламную кампанию в поддержку языка ассемблера. Тем более что компиляторы языков высокого уровня постоянно совершенствуются и подчас способны создавать код, весьма близкий по эффективности к ассемблерному. По этой причине приведенные аргументы не являются бесспорными. И все же нельзя забывать о том, что существует бесконечное множество прикладных задач, ждущих своей очереди на компьютерную реализацию. Далеко не все из этих задач требуют тяжеловесных средств разработки — многие из них могут быть изящно исполнены на языке ассемблера, не теряя привлекательности, например, оконных Windows-приложений.

Перед началом обсуждения поясним, в чем состоит разница между программированием для DOS и для Windows. Операционные системы MS-DOS и Windows поддерживают две совершенно разные идеологии программирования. В чем разница? Программа DOS после своего запуска должна быть постоянно активной.

Если ей, к примеру, требуется получить очередную порцию данных с устройства ввода-вывода, то она сама должна выполнять соответствующие запросы к операционной системе. При этом программа DOS работает по определенному алгоритму, она всегда знает, что и когда ей следует делать. В Windows все наоборот. Программа пассивна.

После запуска она ждет, когда ей уделит внимание операционная система. Операционная система делает это посылкой специально оформленных групп данных, называемых сообщениями. Сообщения могут быть разного типа, они функционируют в системе довольно хаотично, и приложение не знает, какого типа сообщение придет следующим. Отсюда следует, что логика построения Windows-приложения должна обеспечивать корректную и предсказуемую работу при поступлении сообщений любого типа. Тут можно провести определенную аналогию между механизмом сообщений Windows и механизмом прерываний в архитектуре IBM PC. Для нормального функционирования своей программы программист должен уметь эффективно использовать функции интерфейса прикладного программирования (Application Program Interface, API) операционной системы.

Windows поддерживает два типа приложений.

 Оконное приложение строится на базе специального набора функций API, со­ставляющих графический интерфейс пользователя (Graphic User Interface,

GUI). Оконное приложение представляет собой программу, которая весь вы­вод на экран производит в графическом виде. Первым результатом работы окон­ного приложения является отображение на экране специального объекта — окна. После того как окно появилось на экране, вся работа приложения направлена на то, чтобы поддерживать его в актуальном состоянии.

Неоконное приложение, также называемое консольным, представляет собой программу, работающую в текстовом режиме. Работа консольного приложения напоминает работу программы MS-DOS. Но это лишь внешнее впечатление.

Поддержка работы консольного приложения обеспечивается специальными функциями Windows.

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

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

В введении рассматривается актуальность выбранной темы, ставятся цели курсового проектирования.

В 1 разделе автор рассматривает особенности построения консольных и графических приложений.

Второй раздел автор посвятил рассмотрению необходимых процедур и функций.

В заключении делается вывод о проделанной работе.

Введение

Язык ассемблера — это символическое представление машинного языка.

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

По-настоящему решить проблемы, связанные с аппаратурой (или даже, более того, зависящие от аппаратуры как, к примеру, повышение быстродействия программы), невозможно без знания ассемблера.

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

Из всего вышесказанного можно сделать вывод, что, так как язык ассемблера для компьютера “родной”, то и самая эффективная программа может быть написана только на нем (при условии, что ее пишет квалифицированный программист). Здесь есть одно маленькое “но”: это очень трудоемкий, требующий большого внимания и практического опыта процесс. Поэтому реально на ассемблере пишут в основном программы, которые должны обеспечить эффективную работу с аппаратной частью. Иногда на ассемблере пишутся критичные по времени выполнения или расходованию памяти участки программы. Впоследствии они оформляются в виде подпрограмм и совмещаются с кодом на языке высокого уровня.

Содержание

1. Создание терминальных приложений для Win32 …………………..…..1

1.1. Вводная информация …………………………………………………….2

1.2. Терминальные функции Win32 …………………………………………..4

1.3. Чтение данных с терминала ……………………………………………...

1.4. Вывод на терминал ……………………………………………………….

1.5. Файловый ВВОД-ВЫВОД ……………………………………………….

1.6. Операции с окном терминала ……………………………………………

1.7. Управление курсором ……………………………………………………

1.8. Изменение цвета текста …………………………………………………

1.9. Функции для работы со временем и датой ……………………………

2. СОЗДАНИЕ ГРАФИЧЕСКИХ ПРИЛОЖЕНИЙ ДЛЯ WINDOWS

2.1. Необходимые структуры ………………………………………………

2.2. Функция MessageBox …………………………………………………..

2.3. Процедура WinMain ……………………………………………………

2.4. Процедура WinProc …………………………………………………….

2.5. Процедура ErrorHandler ………………………………………………..

2.6. Листинг программы ……………………………………………………

3. УПРАВЛЕНИЕ ПАМЯТЬЮ В ПРОЦЕССОРАХ СЕМЕЙСТВА IA-32

3.1.Линейные адреса ………………………………………………………

3.2. Страничная переадресация …………………………………………...

3.5. Процедура ReadString ………………………………………………..

3.6. Ввод и вывод строк …………………………………………………..

3.7.0чисткаэкрана …………………………………………………………

3.8. Случайный вывод на экран  ………………………………………...

3.9. Рисование прямоугольников  ……………………………………...

3.10. Программа регистрации учащихся  ……………………………….

3.11. Прокрутка текстового окна ………………………………………..

3.12. Блочная анимация…………………………………………………..

Заключение………………………………………………………………72


1. Создание терминальных приложений для Win32

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

• Как в 32-разрядных программах выполняется ввод-вывод текстовых данных?

• Как в 32-разрядных программах вывести на экран терминала цветной текст?

• Как работает библиотека объектных модулей lrvine32?

• Как в системе Microsoft Windows выполняются операции с датой и временем?

• Какие функции системы Microsoft Windows используются для чтения и записи данных в файлы?

• Можно ли на языке ассемблера написать фактическое приложение для системы Windows?

• Как в защищенном режиме преобразовываются адреса в форме 

"сегмент-смещение", используемые в программе, в физические адреса?

• Что такое виртуальная память и как она работает?

В этой главе вы найдете ответы на эти и многие другие вопросы, поскольку здесь речь пойдет об основах 32-разрядного программирования для системы Microsoft Windows.

Большую часть времени мы посвятим созданию 32-разрядных терминальных 

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

При написании процедур  библиотеки lrvine32.1ib были использованы только терминальные функции системы Win32,

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

Вы спросите, почему бы нам не начать рассмотрение с написания фактического  приложения для системы Windows, к которым мы все уже так привыкли? Основная причина ~ оно получится слишком длинным и в нем нужно будет учесть много разных моментов.

О том, как писать программы на языках С и С++ для системы Windows, уже написано много хороших книг, в которых рассматриваются масса технических деталей, таких как дескрипторы графических устройств, обработка сообщений, графики шрифтов, битовые карты устройств и режимы отображения. Однако на просторах Web существует  несколько групп увлеченных языком ассемблера программистов, которые хорошо разбираются в вопросах низкоуровневого программирования для Windows.

Однако тот, кто хочет писать на ассемблере фактические приложения для Windows, не будет совсем уж разочарован. В разделе 11.2 мы в общих чертах рассмотрим небольшое 32-разрядное фактическое приложение для Windows, которое можно считать хорошей отправной точкой для дальнейшего изучения этого вопроса. Дело в том, что тема программирования для Windows на ассемблере очень увлекательна. Поэтому для тех, кто  захочет изучить этот вопрос самостоятельно, в конце этой главы будет приведен список рекомендованных книг.

1.1. Создание терминальных приложений для Win32 485

На первый взгляд 32-разрядные терминальные программы для Windows очень похожи на 16-разрядные программы для MS DOS, работающие в текстовом режиме. Оба типа программ выполняют чтение данных со стандартного устройства ввода и записывают данные в стандартное устройство вывода. Они поддерживают перенаправление потоков данных из командной строки и могут вывести на экран текстовые данные в цвете. Однако при более детальном рассмотрении 32-разрядные терминальные программы для Windows существенно отличаются от 16-разрядных программ для MS DOS. Они используют 32-разрядный защищенный режим работы процессора, тогда как программы для MS DOS работают в реальном режиме адресации. По понятным причинам в этих программах используются совершенно разные библиотеки функций. Терминальные приложения, написанные для Win32, вызывают функции из той же библиотеки, что и другие фактические приложения системы Windows. В программах для MS DOS используются функции BIOS и системы DOS, вызываемые посредством программных прерываний, механизм которых 6bui придуман еще при разработке первой модели персонального компьютера IBM PC.

В системе Windows для обращения к функциям используется стандартный интерфейс прикладных программ (Application Programming Interface, или APF), который представляет собой набор определений структур, констант и функций, использующихся во всех  программах. Благодаря этому обеспечивается возможность прямого манипулирования  любым объектом системы посредством обычных вызовов функций. Таким образом, API Win32 позволяет использовать объекты, составляющие 32-разрядную версию системы MS Windows. Интерфейс прикладных программ (AP1) Win32 подробно описан в документе, озаглавленном Platform SDK, изданном фирмой Microsoft. Аббревиатура SDK  расшифровывается как Software Development Kit, или набор инструментальных средств разработки программного обеспечения. Он состоит из различных утилит, библиотек, примеров программного кода и документации, которая помогает программистам создавать  приложения для системы Windows. В названии SDK присутствует слово platform (т.е. платформа), под которым подразумевается определенный тип операционной системы или группы связанных между собой операционных систем.

1.2. Вводная информация

При запуске любого приложения в системе Windows для него создается либо текстовое (терминальное), либо фактическое окно. Для создания терминального приложения при компоновке программы необходимо указать в командной строке программы LINK указанную ниже опцию (мы используем ее командном файлетаке32. bat):

/SUBSYSTEM:CONSOLE

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

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

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

1.3. Терминальные функции Win32

В табл. 1 приведен полный список терминальных функций Win32 и их краткое описание.

Функция

Описание

AllocConsole

Создать новый терминал для вызывающего

процесса

CreateConsoleScreenBuffer

Создать буфер экрана для терминала

FillConsoleOutputAttribute

Устанавливает цвет символов и фона

для указанного числа текстовых ячеек

FillConsoleOutputCharacter

Выводит символ на экран указанное число раз

FlushConsolelnputBuffer

Очищает входной буфер терминала

FreeConsole

Отключает терминал от вызывающего процесса

GenerateConsoleCtr!Event

Посылает указанный сигнал группе обработки

терминала, совместно использующей терминал,

назначенный вызывающему процессу

GetConsoleCP

Определяет номер входной кодовой страницы,

которая используется в терминале, назначенной

вызывающему процессу

GetConsoleCP

Определяет информацию о размере и внешнем

виде курсора для указанногоэкранного буфера терминала

Таблица 1 -  Список терминальных функций

Продолжение таблицы 1, Список терминальных функций

Функция

Описание

GetConsoleMode

Определяет текущий режим ввода для входного

буфера терминала или текущий режим вывода

для экранного буфера терминала

GetConsoleOutputCP

Определяет номер выходной кодовой страницы,

которая используется в терминале, назначенной

вызывающему процессу

GetConsoleScreenBufferinfо

Определяет информацию об указанном

экранном буфере терминала

GetConsoleTitle

Возвращает строку заголовка для текущего

окна терминала

GetConsoleWindow     

Определяет дескриптор окна, используемого

для терминала, которая назначена

вызывающему процессу

GetLargeStConsoleWindowSize

Возвращает размер максимально возможного

окна терминала

GetNumberOfConsoleInputEvents

Возвращает число непрочитанных введенных

записей во входном буфере терминала

GetNumberOfConsoleMouseButtons

Возвращается число кнопок мыщи,

используемой в текущем терминале

GetStdHandle

Возвращается дескриптор для стандартного

устройства ввода, стандартного устройства

вывода либо устройства вывода сообщений

об ощибках

PeekConsoleInput

Читает данные из указанного входного буфера

терминала без удаления их из буфера

ReadConsole

Читает введенные символы из указанного

входного буфера терминала и удаляет их

из буфера

ReadConsoleInput

Читает данные из указанного входного буфера

терминала и удаляет их из буфера

ReadConsoleOutput

Читает символы и цветовые атрибуты

из указанного прямоугольного блока

символьных ячеек буфера экрана терминала

ReadConsoleOutputAttribute

Копирует указанное количество цветовых

атрибутов символов и фона

из последовательности символьных ячеек

буфера экрана терминала

WriteConsoleInput

Записывает данные напрямую во входной буфер

терминала

WriteConsoleOutput

Записывает символы и цветовые атрибуты

в указанный прямоугольный блок символьных

ячеек буфера экрана терминала

WriteConsoleOutputAttribute

Копирует указанное количество цветовых j

атрибутов символов и фона

в последовательность символьных ячеек буфера

экрана терминала

WriteConsoleOutputCharacter

Копирует указанное количество символов

в последовательность символьных ячеек буфера

экрана терминала

Продолжение таблицы 1, Список терминальных функций

1.3. Чтение данных с терминала

До недавнего времени для чтения данных с терминала мы всего несколько раз  пользовались процедурами ReadString и ReadChar, входящими в библиотеку объектных  модулей автора книги. Они были созданы для того, чтобы упростить вам жизнь и не отвлекать вас от решения самой задачи. В обеих процедурах используется функция ReadConsole системы Win32. По сути, они являются оболочками для этой функции. (Оболочка— это

такая процедура, которая упрощает использование другой процедуры.)

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

1.4. Вывод на терминал

При изложении материала в предыдущих главах нам было важно, чтобы вывод текста на экран был максимально упрощен. Напомним, что в главе 5, "Процедуры", была  описана процедура WriteString из библиотеки lrvine32 . lib, в качестве единственного параметра которой нужно передать в регистре EDX адрес нуль-завершенной строки. На самом деле процедура WriteString просто является оболочкой вяя более сложной функции Win32, которая нaзывaeтcя WritвConsolв.

Тем не менее, в этой главе вы познакомитесь с тем, как выполнять прямые вызовы функций Win32, такие KaKWriteConsole и WriteConsoleOutputCharacter. Это  потребует от вас изучения некоторых деталей, но в результате вы сможете реализовать те функциональные возможности, которые не обеспечивают процедуры библиотеки

Irvine32.lib.

1.5. Файловый ввод-вывод

Функция CreateFile

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

 Вот прототип фyнкции CreateFile:

CreateFile PROTO,

pFilename:PTR BYTE, ; Адрес строки, содержащей имя файла

desiredAccess:DWORD, ; Требуемый режим доступа

shareMode:DWORD, ; Режим совместного использования

lpSecurity:DWORD, ; Адрес атрибутов безопасности

creationDisposition:DWORD, ; Действия, выполняемые при

; создании файла

flagsAndAttributes:DWORD, / Атрибуты файла

htemplate:DWORD ; Дескриптор файла, используемого

; в качестве шаблона

Первый параметр функции CreateFile — это адрес нуль-завершенной строки, 

содержащей частично или полностью определенное имя файла в виде: устройство: путьимя_файяа. Параметр desiredAccess определяет требуемый режим доступа к файлу (по чтению или записи). Параметр shareMode управляет режимом доступа к  открытому файлу со стороны других программ, запущенных в системе. Параметр lpSecuri ty- это адрес структуры, с помощью которой в системах Windows NT, 2000 и XP выполняется управление правами доступа к файлу со стороны пользователей. Значение параметра creationDisposition определяет, какие действия будет выполнять операционная система во время создания файла в случае, если такой файл уже есть или его еще не  существует.

 Параметр flagsAndAttributes представляет собой набор битов, значение  которых определяет атрибуты файла, такие как архивируемый, зашифрованный, обычный, системный или временный. Параметр htemplate необязательный. Он определяет дескриптор другого открытого ранее шаблонного файла, атрибуты которого (обычные ирасширенные) будут использоваться при создании текущего файла. Если шаблонный файл не используется, вместо параметра h template нужно подставить нулевое значение.

Требуемый режим доступа. Задав соответствующее значение параметра desiredAccess, приведенного в табл. 2, программа может получить доступ к файлу по чтению, записи, чтению/записи или запросить доступ к устройству как к файлу. Указанные в табл. 2 значения можно комбинировать. Кроме них существует еще большое количество разных значений флагов, которые не приведены в таблице.

Таблица 2.  Возможные значения параметра desiredAccess

Значение

Описание

0

Запрос на доступ к устройству как к файлу. При этом прикладная

программа может опросить атрибуты устройства без доступа к нему i

на физическом уровне

GENERIC_READ

Запрос на доступ к файлу по чтению. При этом данные могут быть

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

ReadFile. Чтение данных из файла вызывает перемещение

внутреннего указателя на величину счетчика прочитанных данных.

Чтобы получить доступ к файлу по чтению/записи, добавьте

в атрибуты константу GENERI C_WRI ТЕ

GENERIC_WRITE

Запрос на доступ к файлу по записи. Запись данных в файл

выполняется с помощью последующих вызовов функции WriteFile.

При этом изменяется значение внуфеннего указателя позиции

в файле на величину счетчика записанных данных. Чтобы получить

доступ к файлу по чтению/записи, добавьте в атрибуты константу

GENERIC_READ

Действия, выполняемые при создании файла. Параметр creationDisposition  определяет, какие действия будет выполнять операционная система во время создания файла в случае, если такой файл уже есть или его еще не существует. Его значения приведены в табл. 3.

В табл. 4 перечислены наиболее употребительные значения параметра flagsAn- dAttributes. (Полный список приведен в документации по Microsoft MSDN.)  Допускается любая комбинация указанных в таблице атрибутов, однако нужно учитывать, что любой указанный атрибут файла замещает атрибут FlLE_ATTRlBUTE_NORMAL.

Примеры. Ниже приведено несколько примеров вызовов функций, позволяющих прояснить, как создавать и открывать файлы. Чтобы получить дополнительную  информацию, обратитесь к описанию функции CreateFile, приведенному в документации Microsoft MSDN.

• Открытие существующего файла для чтения:

INVOKE CreateFile,

ADDR filename,         ;Адрес строки, содержащей имя файла

GENERIC_READ,      ; Требуемый режим доступа

DO_NOT_SHARE,     ; Запрет на совместное использование

NULL,       ; Атрибуты прав доступа отсутствуют

OPEN_EXISTING,  ; Открыть существующий файл

FILE_ATTRIBUTE__NORMAL,   ; Атрибуты файла

FILE_ATTRIBUTE__NORMAL,  ; Атрибуты файла

0       ; Шаблонный файл отсутствует

Таблица 3. Возможные значения параметра creationDisposition

Значение

Описание

CREATE_NEW

Создать новый файл. Если файл с указанным именем существует, функция аварийно завершает свою работу

CREATE ALWAYS

Создать новый файл. Если файл с указанным именем существует, его содержимое будет затерто. При этом

существующие атрибуты файла сбрасываются. Затем функция объединяет атрибуты файла и флажки, указанные в параметре flagsAndAttributes с константой FILE_ATTRIBUTE_ARCHIVE

OPEN_EXISTING

Открывается существующий файл. Если файл не найден, функция аварийно завершает свою работу

OPEN_ALWAYS

Открывается существующий файл. Если файл не найден, он будет создан так, как если было бы указано

значение CREATE_NEW

TRUNCATE_EXISTING

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

свою работу

Значение

Описание

FILE_ATTRIBUTE_ARCHIVE

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

FILE_ATTRIBUTE_HIDDEN

Скрытый файл. Подобные файлы не включаются в список файлов при обычном просмотре каталога

FILE_ATTRIBUTE_NORMAL

Файлу не назначены никакие другие атрибуты. Этот атрибут должен использоваться только сам по себе

FILE_ATTRIBUTE_READONLY

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

в нем данные и удалять файл

FILE_ATTRIBUTE_TEMPORARY

Файл используется для временного хранения данных

Продолжение таблицы 3. Возможные значения параметра creationDisposition

• Открытие существующего файла для записи:

INVOKE CreateFile,

ADDR filename,   ; Адрес строки, содержащей имя файла

GENERIC_WRITE,  ; Требуемый режим доступа

DO_NOT_SHARE, ; Запрет на совместное использование

NULL, ; Атрибуты прав доступа отсутствуют

OPEN_EXISTING,  ; Открыть существующий файл

FILE_ATTRIBUTE_NORMAL,  ; Атрибуты файла

0 ; Шаблонньй файл отсутствует

Создание нового файла с обычными атрибутами, если указанный файл  существует, его содержимое стирается:

INVOKE CreateFile,

ADDR filename,

GENERIC_WRITE,

DO_NOT_SHARE,

NULL,

CREATE_ALWAYS, ; He уничтожать существующий файл

FILE_ATTRIBUTE_NORMAL,

0

1.6. Функция CIoseHandle

Эта функция закрывает ранее открытый файл, определяемый по дескриптору. Вот ее прототип:

CIoseHandle PROTO, handle:DWORD

1.7. Функция ReadFile

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

ReadFile;

PROTO, ; ; Читать в буфер из файла

handle:DWORD, ; Дескриптор файла

pBuffer: ; Адрес буфера

nBufsize: DWORD, ; ; Размер буфера или сколько байтов нужно прочитать

pBytesRead:PTR DWORD,  ; ; Адрес переменной, в которую записывается реальное количество прочитанных данных

pOverlapped:PTR DWORD ; Адрес структуры типа OVERLAPPED, предназначенной для синхронизации операций ввода-вывода

Первый параметр handle— это дескриптор файла, открытого с помощью функции CreateFile. Второй параметр pBuffer содержит адрес буфера, куда будут  записываться данные. Параметр nBufsize определяет размер буфера или максимальное  количество байтов, которое требуется прочитать из файла. Параметр pBytesRead содержит адрес 32-разрядной переменной, в которую записывается реальное количество прочитанных данных. Последний параметр pOverlapped необязательный. Он содержит адрес  структурной переменной типа OVERLAPPED, которая используется для выполнения  асинхронного чтения файла. Если используется обычная (синхронная) операция чтения файла, принятая по умолчанию, вместо адреса структуры подставьте вместо параметра pOverlapped нулевое значение.

1.8. Функция WriteFiIe

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

WriteFile PROTO,

fileHandle:DWORD, ;Выходной дескриптор

pBuffer:PTR BYTE, ; Адрес буфера

nBufsize:DWORD,; Размер буфера

pBytesWritten:PTR DWORD, ; Адрес переменной, в которую помещается реальное количество записанных данных

pOverlapped:PTR DWORD; Адрес структуры типа OVERLAPPED, предназначенной для синхронизации операций ввода-вывода

1.9. Пример: nporpaMMaWriteFiie.asm

Ниже приведена программа Writefile. asm, в которой создается новый файл, и в него записывается некоторый текст. При создании файла используется опция CREATE_ALWAYS, поэтому если файл с таким именем уже существует, его содержимое стирается.

TITLE Использование функции WriteFile (WriteFile.asm)

INCLUDE Irvine32.inc

. data

buffer BYTE "Этот текст будет записан в файл.",Ос1Ь,ОаЬ

bufSize = ($-buffer)

errMsg BYTE "Ошибка при создании файла.",Ос1Ь,ОаЬ,0

filename BYTE "output.txt",0

fileHandle DWORD ? ; Дескриптор файла для записи

bytesWritten DWORD ? ; Число записанных байтов

. code

main PROC

INVOKE CreateFile,

mov

ADDR filename, GENERIC_WRITE, DO_NOT_SHARE, NULL,

CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0

fileHandle,eax ; Сохраним дескриптор файла

.IF eax == INVALID_HANDLE_VALUE

mov edx,OFFSET errMsg ; Выведем сообщение об ошибке

call WriteString

jmp QuitNow

.ENDIF

INVOKE WriteFile, ; Запишем текст в файл

fileHandle, ; Дескриптор файла

ADDR buffer, ;    Адрес буфера

bufSize, ;    Число байтов для записи

ADDR bytesWritten, ;  Адрес переменной

0 ;               Адрес структуры OVERLAPPED не задан

INVOKE CloseHandle, ;                  Закроем файл

fileHandle QuitNow:    

INVOKE ExitProcess,0 ;                 Завершим программу

main ENDP

END main

1.10. Перемещение файлового указателя

Функция SetFilePointer предназначена для перемещения указателя в открытом файле. С помощью этой функции можно сделать так, чтобы при записи данные  добавлялись в конец файла, а также организовать доступ к произвольным участкам данных  файла. Прототип функции приведен ниже:

SetFilePointer PROTO,

handle:DWORD, ;         Дескриптор файла

nDistanceLo:SDWORD, ;      Число байт для перемещения

pDistanceHi:PTR SDWORD, ;        Адрес 32-разрядной переменной, содержащей старшее слово б4-разрядного числа байт для перемещения

moveMethod:DWORD;                   Начальная точка для перемещения

Параметр moveMethod определяет отправную точку, относительно которой  выполняется перемещение указателя. Он может принимать одно из трех значений: FILE_BEGIN, FILE_CURRENT И FILE_END. Собственно значение, определяющее  количество байтов ддя перемещения указателя, является 64-разрядным числом со знаком, разделенное на 2 части:

• nDistanceLo- младшие 32-бита;

• pDistanceHi — адрес переменной, содержащей старшие 32-бита.

Если при вызове функции SetFilePointer параметр pDistanceHi равен нулю, для перемещения файлового указателя будет использоваться только значение параметра nDistanceLo. Ниже приведен пример вызова этой функции для перемещения указателя в конец файла, чтобы при последующей операции записи данные добавлялись в его конец.

INVOKE SetFilePointer,

fileHandle, ;                   Дескриптор файла

0. ;              Младшее значение числа байт

0, ;              Указатель на старшее значение равен нулю

FILE END; Способ перемещения — относительно конца файла

1.11. Пример программы: ReadFile.asm

В программе ReadFile.asm открывается текстовый файл, созданный при запуске программы WriteFile. asm, затем из него считываются данные, файл закрывается и на экране отображается его содержимое:

TITLE Использование функции ReadFile (ReadFile.asm)

INCLUDE Irvine32.inc

.data

buffer BYTE 500 DUP(?)

bufSize = ($-buffer)

errMsg BYTE "Ошибка при открытии фaйлa.",Odh,Oah,0

filename BYTE "output.txt",0

fileHandle DWORD ? ; Дескриптор файла

byteCount DWORD ? ; Число прочитанных байтов

.code

main PROC

INVOKE CreateFile, ; Откроем файл для чтения

ADDR filename, GENERIC_READ,

DO_NOT_SHARE, NULL, OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL, 0

mov fileHandle,eax ; Сохраним дескриптор файла

.IF eax == INVALID_HANDLE_VALUE

mov edx,OFFSET errMsg ; Выведем сообщение об ошибке

call WriteString

jmp QuitNow

.ENDIF

INVOKE ReadFile, ; Читаем содержимое файла в буфер

fileHandle, ADDR buffer,

bufSize, ADDR byteCount, 0

INVOKE CloseHandle, ; Закроем файл

fileHandle

mov esi,byteCount ; Вставим нулевой байт в конец

mov buffer[esi],0 / прочитанной строки

mov edx,OFFSET buffer ; Отобразим содержимое буфера

call WriteString

QuitNow:

INVOKE ExitProcess,0                   ; Завершим выполнение программы    

main ENDP

END main

1.12. Операции с окном терминала

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

Существует несколько функций, позволяющих изменить размер окна терминала и его положение относительно буфера экрана. Функция SetConsoleWindowlnfo задает размер и положения окна терминала относительно буфера экрана. Функция GetConsoleScreen- Bufferlnfo возвращает (кроме всего прочего) координаты прямоугольного окна  терминала относительно буфера экрана. Функция SetConsoleCursorPosition позволяет установить курсор в любую позицию буфера экрана. Если окажется, что это область в  настоящий момент не видна на экране, выполняется автоматическое перемещение окна терминала, чтобы курсор стал видимым. Функция ScrollConsoleScreenBuffer  перемещает часть текста или весь текст, находящийся в буфере экрана, на указанное число позиций. Это непосредственно влияет на содержимое окна терминала.

1.13. Функция SetConsoleTitIe

Эта функция позволяет изменить содержимое строки заголовка окна терминала. Вот пример:

.data

titleStr BYTE "Заголовок окна",0

. code

INVOKE SetConsoleTitIe, ADDR titleStr

1.14. Функция GetConsoIeScreenBufferInfo

Эта функция возвращает информацию о текущем состоянии окна терминала. Ей  передается два параметра: дескриптор терминала и адрес структуры, в которую помещается разнообразная информация о текущем состоянии окна терминала. Вот ее прототип:

GetConsoIeScreenBufferInfo PROTO,

outHandle:DWORD, ; Дескриптор буфера экрана 

терминала

pBufferInfo:PTR CONSOLE_SCREEN_BUFFER_INFO

Структура CONSOLE_SCREEN__BUFFER_INFOoпpeдeляeтcя так:

CONSOLE SCREEN BUFFER INFO STRUCT

dwSize        COORD     <>

dwCursorPos       COORD     <>

wAttributes                   WORD       ?

srWindow            SMALL  RECT <>

maxWinSize        COORD <>

C0NS0LE_SCREEN_BUFFER_INF0      ENDS

После вызова функции GetConsoIeScreenBufferInfo в поле dwSize этой  структуры будет содержаться размер буфера экрана, заданный в виде количества столбцов и строк. Поле dwCursorPos содержит координаты положения курсора в буфере. Оба поля являются структурами типа COORD. В поле wAttributes будут находиться атрибуты цвета символов и фона, которые используются при выводе текста на терминал с  помощью функции WriteConsole. Поле srWindow содержит координаты положения окна терминала относительно буфера экрана. В поле maxWinSize возвращается  максимальный размер экрана терминала, заданный в виде числа столбцов и строк, который  рассчитывается исходя из размеров буфера экрана и шрифта, а также используемого  разрешения экрана. Ниже приведен пример вызова этой функции.

.data

consoleInfo CONSOLE_SCREEN_BUFFER_INFO О

.code

INVOKE GetConsoIeScreenBufferInfo, outHandle,

ADDR consoleInfo

1.15. Функция SetConsoleWindowInfo

Эта функция устанавливает размер и положение окна терминала относительно  буфера экрана. Вот прототип функции:

SetConsoleWindowInfo PROTO, ; Устанавливает позицию окна терминала

nStdHandle:DWORD, ; Дескриптор буфера экрана

bAbsolute:DWORD, ; Тип координат

pConsoleRect:PTR SMALL_RECT; Адрес структуры с координатами окна

Значение параметра bAbsolute влияет на то, как интерпретируются координаты  окна, заданные в структуре типа SMALL_RECT, адрес которой указывается в параметре pConsoleRect. Если оно истинно, то в параметре pConsoleRect указаны новые  абсолютные координаты левого верхнего и правого нижнего углов окна терминала. Если  значение параметра bAbsolute ложно, то новые координаты окна считаются  относительными и прибавляются к его текущим координатам. Ниже приведен исходный код программы Scroll.asm, которая выводит пятьдесят строк текста в буфер экрана терминала. Затем она изменяет размер и положения окна терминала, что вызывает моментальную прокрутку текста в обратном направлении. В программе используется функция SetConsoleWindowInfo:

TITLE Прокрутка окна терминала (Scroll.asm)

INCLUDE Irvine32.inc

.data

message BYTE ": Эта строка текста была записана "

BYTE "в буфер экрана тepминaлa.",Odh,Oah

messageSize = ($-message)

outHandle DWORD 0 Дескриптор стандартного

bytesWritten DWORD ? устройства вывода

lineNum DWORD 0 Число записанных байтов

windowRect SMALL RECT <0,0,60,11> ; Координаты окна: / левого верхнего и правого ; нижнего угла

.code

main PROC

INVOKE GetStdHandle, STD_OUTPUT_HANDLE

mov outHandle,eax

.REPEAT

mov eax,lineNuro

call WriteDec ; Выведем десятичный номер строки

INVOKE WriteConsole,

outHandle, ;  Дескриптор вывода на терминал

ADDR message, ; Адрес выводимой строки

messageSize, ; Длина строки

ADDR bytesWritten, ; Возвращается число реально записанных байтов

0 ; Не используется

inc lineNum ; Перейдем к следующей строке

.UNTIL lineNum > 50

Изменим размер и положение окна терминала по отношению к буферу экрана

INVOKE SetConsoleWindowInfo,

outHandle,

TRUE,

ADDR windowRect Новый размер окна

call ReadChar Ждем нажатия на клавишу

call ClrScr Очистим буфер экрана

call ReadChar Ждем еще одного нажатия

на клавишу

INVOKE ExitProcess,0

main ENDP

END main

Лучше всего запустить эту программу непосредственно из окна программы Проводник системы Windows, а не из интегрированной среды текстового редактора. Дело в том, что программа редактора может изменить внешний вид и режим работы окна терминала. Обратите внимание, что в процессе работы программы вы должны дважды нажать любую клавишу на клавиатуре: один раз для очистки буфера экрана, а второй раз — для  завершения работы программы. Это сделано для того, чтобы облегчить вам наблюдение за  работой программы.

1.16. Функция SetConsoleScreenBuflTerSize

Эта функция позволяет задать размер буфера экрана в виде количества столбцов и строк. Вот ее прототип:

SetConsoleScreenBufferSize PROTO,

outHandle:DWORD/ ; Дескриптор вывода на терминал

dwSize:COORD ; Новый размер буфера экрана

11.1.7. Управление курсором

Эта функция возвращает информацию о размере курсора и виден ли он на экране или нет. Кроме дескриптора терминала ей передается объектная переменная типа CONSOLE_CURSOR_INFO;

GetConsoIeCursorInfo PROTO,

outHandle:DWORD, ; Дескриптор терминала

pCursorInfo:PTR CONSOLE_CURSOR_INFO ; Параметры курсора

По умолчанию размер курсора равен 25. Это означает, что курсор будет занимать 25% символьной ячейки.

11.1.7.2. Функция SetConsoleCursorInfo

С помощью этой функции можно задать размеры курсора и отобразить или скрыть его на экране. Кроме дескриптора терминала, ей передается объектная переменная типа CONSOLE_CURSOR__INFO;

SetConsoleCursorInfo PROTO,

outHandle:DWORD, ; Дескриптор терминала

pCursorInfo:PTR CONSOLE__CURSOR_INFO ; Параметры курсора

11.1.8. Изменение цвета текста

Существует два способа изменения цвета текста, отображаемого в окне терминала. Во-первых, вы можете изменить текущий цвет текста, вызвав функцию SetConso- leTextAttribute, что повлияет на все последующие операции вывода текста на  терминал. Во-вторых, можно установить цветовые атрибуты определенных ячеек на экране, вызвав функцию WriteConsoleOutputAttributa

11.1.8.1. Функция SetConsoIeTextAttribute

С помощью этой функции задаются цвета символов и фона, что повлияет на все  последующие операции вывода текста на терминал. Вот прототип функции:

SetConsoIeTextAttribute PROTO,

outHandle:DWORD, / Дескриптор терминала

nColor:DWORD ; Цветовые атрибуты

Значение атрибутов цвета хранится в младшем байте параметра nColor. Цвета  кодируются точно так же, как и при работе с видео функциями BlOS, которые описаны в  разделе 15.3.2.

11.1.8.2. Функция WriteConsoIeOutputAttribute

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

WriteConsoIeOutputAttribute PROTO,

outHandle:DWORD, Дескриптор терминала

pAttribute:PTR WORD, Адрес массива атрибутов

nLength:DWORD Число ячеек

xyCoord:COORD, , Координаты первой ячейки

lpCount:PTR DWORD Переменная, содержащая реальное число записанных ячеек.

Параметр pAttribute — это адрес массива слов, каждый элемент которого содержит в младшем байте цветовые атрибуты для соответствующей ячейки буфера экрана. Длина массива задается в параметре nLength. Координаты начальной ячейки в буфере экрана задаются с помощью параметра xyCoord. После вызова функции переменная, адрес  которой указан в параметре IpCount, будет содержать реальное число записанных ячеек.

11Л.8.3. Пример программы: WriteCoIors

Чтобы продемонстрировать на примере использование атрибутов цвета, в  программе WriteColors.asm создается два массива: массив символов и массив атрибутов, соответствующих каждому символу. Затем в программе вызывается функция  WriteConsoIeOutputAttribute, которая копирует атрибуты в буфер экрана и функция WriteConsoleOutputCharacter, которая копирует массив символов в те же ячейки буфера экрана:

TITLE Вывод цветного текста (WriteColors.asm)

INCLUDE Irvine32.inc

.data

outHandle DWORD ?

cellsWritten DWORD ?

xyPos COORD <10,2>

; Массив кодов символов:

buffer BYTE 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

BYTE 16,17,18,19,20

BufSize = {$ - buffer)

; Массив цветовых атрибутов:

attributes WORD OFh,OEh,ODh,OCh,OBh,OAh,9,8,7,6

WORD 5,4,3,2,1,OFOh,OEOh,ODOh,OCOh,OBOh

.code

main PROC

; Определим дескриптор стандартного устройства вывода:

INVOKE GetStdHandle,STD_OUTPUT_HANDLE

mov outHandle,eax

; Зададим цвета смежных ячеек на экране:

INVOKE WriteConsoleOutputAttribute,

outHandle, ADDR attributes,

BufSize, xyPos,

ADDR cellsWritten

; Выведем на экран коды символов от 1 до 20:

INVOKE WriteConsoleOutputCharacter,

outHandle, ADDR buffer, BufSize,

xyPos, ADDR cellsWritten

call ReadChar ; Ждем нажатия на клавишу

INVOKE ExitProcess,0 ; Завершим программу

main ENDP

END main

11.1.9. Функции для работы со временем и датой

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

Таблица 5. Набор функций Win32 API

Функция  

Описание

CompareFileTime

Сравнивает две 64-разрядные временные характеристики файла

DosDateTimeToFileTime

Преобразовывает дату и время создания или модификации файла, заданную в формате MS DOS в 64-разрядную временную

характеристику файла

FileTimeToDosDateTime

Преобразовывает 64-разрядную временную

характеристику файла в формат MS DOS

FileTimeToLocalFileTime

Преобразовывает временную характеристику

файла, заданную в формате UTC (универсальное скоординированное время) в локальную временную характеристику

FileTimeToSystemTime

Преобразовывает 64-разрядную временную

характеристику файла в формат системного

времени

GetFileTime

Определяет дату и время создания, последнего

обращения и последней модификации файла

GetLocalTime

Определяет текущее локальное время и дату

GetSystemTime

Определяет текущее время и дату

в формате UTC

GetSystemTimeAdjustment

Позволяет узнать, выполняется ли в операционной системе периодическая

коррекция значения таймера текущего времени

GetSystemTimeAsFileTime

Определяет текущее системное время и дату в формате UTC

GetTickCount

Определяет время в миллисекундах, которое прошло с момента последней загрузки системы

GetTimeZoneInformation

Определяет текущие параметры временной зоны |

LocalFileTimeToFileTime

Преобразует временную характеристику файла,

заданную в локальном формате, в формат UTC

SetFileTime

Задает дату и время создания, последнего обращения и последней модификации файла

SetLocalTime

Устанавливает текущее локальное время и дату

SetSystemTime

Задает текущее системное время и дату в формате UTC

SetSystemTimeAdjustment

Разрещает или запрещает периодическую коррекцию значения системного таймера текущего времени

SetTimeZoneInformation

Задает текущие параметры временной зоны

SystemTimeToFileTime

Преобразует системное время в 64-разрядный формат временной характеристики файла

SystemTimeToTzSpecificLocalTime

Преобразует время, заданное в формате UTC,

в местное время указанной временной зоны

Продолжение таблицы 5. Набор функций Win32 API

Структура SYSTEMTIME, Эта структура так или иначе используется практически во всех функциях для работы со временем и датойУ1пдоУ8 API. Вот ее определение:

SYSTEMTIME STRUCT

wYear WORD     ? ; Год D цифры)

wMonth WORD  ? ; Месяц A-12)

wDayOfWeek WORD ? ; День недели @-6)

wDay WORD      ? ; День месяца A-31)

wHour WORD    ? ; Часы @-23)

wMinute WORD ? ; Минуты @-59)

wSecond WORD ? ; Секунды @-5 9)

wMilliseconds WORD  ? ; Миллисекунды @-999)

SYSTEMTIME ENDS

В поле wDayOfWeek указываются числа, соответствующие дням недели: 0 —  воскресенье, 1 — понедельник и т.д. Значение в поле wMilliseconds указано с некоторой погрешностью, поскольку операционная система не может мгновенно обновить  значение внутреннего таймера компьютера.

11.1.9.1. Функции GetLocaITime и SetLocalTime

Функция GetLocaITime возвращает текущую дату и время на основании показаний системного таймера. Значение времени соответствует локальному значению  установленной временной зоны. При вызове функции ей нужно передать адрес структурной  переменной типа SYSTEMTIME:

GetLocaITime PROTO,

pSystemTime:PTR SYSTEMTIME

Функция SetLocalTime устанавливает локальное время и дату. При вызове нужно указать адрес структурной переменной типа SYSTEMTIME, содержащей нужную  информацию:

SetLocalTime PROTO,

pSystemTime:PTR SYSTEMTIME

Если выполнение функции завершено успешно, возвращается ненулевое значение. При аварийном завершении функция возвращает нулевое значение. Ниже приведен пример вызова функции GetLocalTime:

.data

sysTime SYSTEMTIME <>

. code

INVOKE GetLocalTime,ADDR sysTime

11.1.9.2. Функция GetTickCount

Эта функция возвращает время в миллисекундах, которое прошло с момента  последней загрузки системы:

GetTickCount PROTO ; Значение возвращается

; в регистре EAX

Поскольку функция возвращает интервал времени в виде целого 32-разрядного числа, его значение будет периодически обнуляться через каждые 49,7 дня непрерывной работы системы. Эта функция обычно используется в программе для отслеживания интервалов времени, например времени выполнения некоторого цикла, когда нужно по истечении заданного интервала прервать его выполнение. В приведенной ниже программе каждые 100 мс на экран выводится точка и проверяется, не прошло ли с момента запуска  программы 5000 мс. Фрагмент этого кода можно использовать в разных программах:

TITLE Отслеживание интервалов времени (TimingLoop.asm)

; В этой программе используется функция GetTickCount для

/ определения интервала времени в мс, прошедшего с момента

/ запуска программы

INCLUDE Irvine32.inc

TIME_LIMIT = 5000

.data

startTime DWORD ?

dot BYTE ".",0

. code

main PROC

INVOKE GetTickCount ; Опросим значение таймера

mov startTime,eax

L1:

mov edx,OFFSET dot ; Выведем точку

call WriteString

INVOKE Sleep,100 ; Заморозим выполнение программы

; на 100 мс

INVOKE GetTickCount

sub eax,startTime ; Определим прошедший интервал времени

cmp eax,TIME_LIMIT

jb L1

L2:

exit

main ENDP

END main

11.1.9.3. Функция Sleep

Эта функция замораживает выполнение текущей программы на указанный в  миллисекундах интервал времени:

Sleep PROTO,

dwMilliseconds:DWORD

11.1.9.4. Процедура GetDateTime

Эта процедура входит в библиотеку lrvine32.1ib автора книги. Она возвращает 64-разрядное целое число, которое обозначает время в 100-наносекундных интервалах, прошедшее с I января 1601 года. Этот факт вам может показаться немного странным, по- скольку в то далекое время компьютеров не было и в помине. Тем не менее, специалисты фирмы Microsoft выбрали в качестве точки отсчета именно эту дату для отслеживания времени и даты создания файлов (так называемой временной характеристики файлов). Ниже описана последовательность действий, рекомендованная в Win32 SDK, для  преобразования текущего времени и даты в целое 64-разрядное число, удобное для  выполнения арифметических операций с датой.

1. Вызовите функцию GetLocalTime, которая проинициализирует поля  структурной переменной SYSTEMTIME.

2. Преобразуйте тип структурной переменной с SYSTEMTIME в FILETIME, вызвав функцию SystemTimeToFileTime

3. Скопируйте содержимое структурной переменной типа FILETIME в 64-разрядное учетверенное слово.

Структура FILETIME состоит из двух двойных слов:

FILETIME STRUCT

loDateTime DWORD ?

hiDateTime DWORD ?

FILETIME ENDS

Ниже приведен исходный код процедуры OetDateTime, которой передается адрес 64-разрядной переменной. Она формирует в этой переменной структуру типа FILETIME и заполняет ее поля.

GetDateTime PROC,

pStartTime:PTR QWORD

LOCAL sysTime:SYSTEMTIME, flTime:FILETIME

Определяет текущее время и дату и сохраняет его в виде

б4-разрядного целого числа в формате FILETIME

Определим системное локальное время

INVOKE GetLocalTime,

ADDR sysTime

; Преобразуем его из формата SYSTEMTIME в формат FILETIME

INVOKE SystemTimeToFileTime,

ADDR sysTime,

ADDR flTime

; Скопируем локальную переменную типа FILETIME в 64-разрядное

; целое число

mov esi,pStartTime

mov eax,flTime.loDateTime

mov DWORD PTR [esi],eax

mov eax,flTime.hiDateTime

mov DWORD PTR [esi+4],eax

ret

GetDateTime ENDP

11.1.9.5. Простейший секундомер

Воспользовавшись функцией GetTickCount, мы создадим две процедуры, применяя которые в паре можно получить простейшую программу-секундомер. Одна из процедур называется TimerStart, в ее функции входит фиксация текущего времени. Вторая  процедура TimerStop возвращает количество миллисекунд, прошедших с момента вызова процедуры TimerStart. Ниже приведен исходный код профаммы Timer.asm, в которой вызываются обе процедуры и вводится искусственная задержка с помощью вызова функции Sleep:

TITLE Определение прошедшего интервала времени (Timer.asm)

; Демонстрационная программа простейшего секундомера,

; в которой используется функция GetTickCount Win32 API

INCLUDE Irvine32.inc

TimerStart PROTO,

pSavedTime: PTR DWORD

TimerStop PROTO,

pSavedTime: PTR DWORD

.data

msgl BYTE "Прошло ",0

msg2 BYTE " миллиceкyнд"^Odh,Oah,0

timerl DVIORD ?

.code

main PROC

INVOKE TimerStart,    Запустим таймер

ADDR timerl

INVOKE Sleep, 5000             Запустим таймер

INVOKE TimerStop,             В EAX число прошедших миллисекунд

ADDR timerl

mov edx,OFFSET msgl

call WriteString

call WriteDec       ;Выведем общее время

mov edx,OFFSET msg2

call WriteString

exit

main ENDP

TimerStart PROC uses eax esi,

pSavedTime: PTR DWORD

Запускает таймер секундомера.

Передается: адрес переменной, в которую записывается

текущее время

Возвращается: ничего

INVOKE GetTickCount

mov esi,pSavedTime

mov [esi],eax

ret

TimerStart ENDP

TimerStop PROC uses esi,

pSavedTime: PTR DWORD

Останавливает таймер секундомера. Передается: адрес переменной, содержащей время запуска таймера Возвращается: EAX = величина интервала времени в миллисекундах Примечание: точность отсчета составляет примерно 10 мс

INVOKE GetTickCount

mov esi,pSavedTime

sub eax,[esi]

ret

TimerStop ENDP

END main

В процедуру TimerStart передается адрес двойного слова, в которое записывается текущее значение системного таймера. Процедуре TimerStop передается адрес двойного слова, в которое процедура TisnerStart поместила текущее значение таймера. В  регистре EAX возвращается значение интервала времени в миллисекундах, прошедшего с  момента вызова процедуры TimerStart. Системные функции работы со временем обеспечивают лишь точность измерения интервалов времени, которая не превышает 10 мс.

11.2. Cоздание графических приложений для Windows.

В этом разделе мы рассмотрим процесс создания простейшего фактического  приложения для Microsoft Windows. Наша программа будет создавать и отображать основное окно, выводить на экран окна сообщений и реагировать на события, поступающие от мыши. В этом разделе приведены лишь самые общие сведения, поскольку подробное описание процесса разработки даже самого простого графического приложения для  Windows заняло бы целую главу. За более подробной информацией по этому вопросу  обратитесь к разделу Platform SDK, Win32API компакт-диска Microsoft MSDN Library, входящего в комплект Visual Studio. Необходимые файлы. В табл. 4 перечислен список файлов, которые вам  понадобятся для компиляции и запуска ассемблерной программы, описанной в этом разделе.

Таблица 4. Необходимые файлы

Имя файла

Описание

make32.bat

Командный файл для создания исполняемого файла программы

WinApp.asm

Исходный код программы

GraphWin.inc

Включаемый файл, содержащий описание структур, констант и прототипов функций,используемых в программе

kernel32.1ib

Файл описания точек входа системной библиотеки kerne 132 . dll, содержащей основные функции Win32 API. Он использовался нами и раньше при компоновке терминальных приложений в файле make32.bat

user32.lib

Файл описания точек входа системной библиотеки user32 . dll, содержащей дополнительные функции Win32 API

В файле make32 . bat находятся команды для вызова компилятора ассемблера и  компоновщика. Они практически идентичны тем, которые мы использовали до сих пор для создания терминальных приложений. Но есть одно отличие:

ML -с ~coff %l.asm

LINK %l.obj kernel32.1ib user32.1ib /SUBSYSTEM:WINDOWS

Обратите внимание, что вместо опции командной строки /SUBSYSTEM:CONSOLE,  которую мы использовали до сих пор, здесь указана опция /SUBSYSTEM:WlNDOWS. Кроме того, в командной строке при вызове компоновщика указаны две стандартные  библиотеки системы Windows: kernel32.1ib и user32.1ib, содержащие функции, которые вызываются в нашей программе. Основное окно программы. При запуске программа отображает на экране основное  окно, которое приведено на рис. 11.4. Нам пришлось немного уменьшить его размеры,  чтобы оно поместилось на странице этой книги.

11.2.1. Необходимые структуры

Структура POlNT определяет горизонтальную X и вертикальную Y координаты точки на экране, измеряемые в пикселях. Она используется, в частности, для размещения на экране графических объектов, окон и обработки щелчков кнопки мыши:

POINT STRUCT

ptX DWORD

ptY DWORD

POINT ENDS

Структура MSGStruct определяет формат сообщения, которыми операционная  система Windows обменивается со своими приложениями:

MSGStruct STRUCT

msgWnd              DWORD ?

msgMessage        DWORD ?

msgWparam        DWORD ?

msgLparam                   DWORD ?

msgTime              DWORD ?

msgPt                            POINT <>

MSGStruct ENDS

C помощью структуры WNDCLXSS определяется класс окна. Каждое окно, создаваемое программой, должно относиться к какому-нибудь классу. Поэтому в каждой программе нужно определить класс ее основного окна. Далее, прежде чем окно будет отображено на экране, программа должна зарегистрировать этот класс в операционной системе. Вот  определение структуры:

WNDCLASS STRUC

style DWORD    ? Параметры стиля окна

IpfnWndProc DWORD ? Адрес функции WinProc

cbClsExtra DWORD    ? Размер общей области класса

CbWndExtra DWORD ? Размер дополнительной области окна

hlnstance DWORD       ? Дескриптор текущей программы

hlcon DWORD    ? Дескриптор пиктограммы

hCursor DWORD         ? Дескриптор курсора

hbrBackground DWORD       ? Дескриптор фона окна

lpszMenuName DWORD       ? Адрес строки, содержащей имя меню

IpszClassName DWORD       ? ; Адрес строки, содержащей имя

                                               ; класса

; WinClass ENDS

Ниже приведено краткое описание полей структуры.

• style — определяет внешний вид и характеристики окна программы; допускается комбинация различных параметров стиля, таких KaKWS_CAPTlON и s_BORDER.

• lpfnWndProc — адрес функции в текущей программе, которая обрабатывает различные сообщения, сгенерированные операционной системой в ответ на действия пользователя.

• cbClsExtra — определяет количество общей памяти, которая используется  всеми окнами, относящимися к текущему классу; по умолчанию равно нулю.

• cbWndExtra — определяет количество дополнительных байтов, которые вьщеляются после создания экземпляра окна.

• hlnstance — содержит дескриптор экземпляра текущей программы.

• hlcon и hCursor- содержат дескрипторы ресурсов, определяющий пиктограмму и курсор текущей программы.

• hbrBackground- определяет цвет фона окна программы или дескриптор  кисточки, с помощью которой рисуется фон окна.

• lpszMenuName ~ содержит адрес нуль-завершенной текстовой строки, в которой указано название меню.

• lpszClassName~- содержит адрес нуль-завершенной текстовой строки,  определяющей название класса окна.

11.2.2. Функция MessageBox

В фактических приложениях проще всего вывести текст на экран с помощью окна  сообщений. При этом текст в окне сообщений находится на экране до тех пор, пока  пользователь не щелкнет на кнопке ОК. Для вывода окна сообщений служит функция Win32 API MessageBox. Вот ее прототип:

MessageBox PROTO,

hWnd:DWORD,

pText:PTR BYTE,

pCaption:PTR BYTE,

style:DWORD

Параметр hWnd определяет дескриптор текущего окна. Вместо параметра pText подставляется адрес нуль-завершенной текстовой строки, которая появится внутри окна  сообщений. Параметр pCaption определяет адрес нуль-завершенной текстовой строки, размещаемой в строке заголовка окна сообщений. Параметр style является целым  числом, значение которого определяет тип пиктофаммы, количество и тип кнопок, которые могут быть размещены внутри окна. Пиктограмма в окне сообщений может  отсутствовать, тогда как кнопки ~ нет. Число и тип кнопок определяется с помощью констант,  таких как MB_OK и MB_YESNO. Пиктограммы также определяются с помощью констант, например MB_iCONQUESTlON. При вызове функции константы, определяющие  пиктограмму и кнопки, объединяются вместе:

INVOKE MessageBox, hWnd, ADDR QuestionText,

ADDR QuestionTitle, MB_OK + MB_ICONQUESTION

11.2.3. Процедура WinMain

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

• определяется дескриптор текущей программы;

• загружаются образы пиктограммы и курсора мыши программы из раздела  ресурсов исполняемого файла;

• регистрируется класс основного окна программы и определяется процедура,  которая будет обрабатывать поступающие сообщения, сгенерированные  операционной системой в ответ на действия пользователя с окном программы;

• создается основное окно программы;

• отображается и обновляется содержимое основного окна программы;

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

11.2.4. Процедура WinProc

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

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

WinProc     PROC,

hWnd:        DWORD,   Дескриптор окна

localMsg:    DWORD,   Идентификатор сообщения

wParam:     DWORD,   Параметр 1 (зависит от сообщения)

lParam:       DWORD    Параметр 2 (зависит от сообщения)

Обратите внимание, что значение третьего и четвертого параметров процедуры  зависит от типа поступившего сообщения. Например, при обработке щелчка кнопкой мыши, параметр lPararn указывает координаты X и Y точки на экране, в которой находится  указатель в момент щелчка. В примере программы, которую мы скоро рассмотрим, процедура WinProc будет  обрабатывать всего три сообщения:

• WMLBUTTONDOWN — генерируется в ответ на щелчок левой кнопкой мыши;

• WM_CREATE — уведомляет программу о создании основного окна;

• WM_CLOSE — информирует программу о том, что ее основное окно закрывается.

11.2.5. Процедура ErrorHandler 

Эта процедура не является обязательной и создана нами исключительно ради  удобства. Она вызывается в случае, если при регистрации класса и создании основного окна программы возникнет ошибка. Например, если класс основного окна программы был успешно зарегистрирован, функция RegieterClaes возвращает ненулевое значение. Если эта функция вернет нулевое значение, вызывается процедура ErrorHandler, в  которой отображается сообщение об ошибке, а затем работа программы завершается:

INVOKE RegisterClass, ADDR MainWin

.IF eax == 0

call ErrorHandler

jmp Exit_Program

.ENDIF

В процедуре ErrorHandler выполняются несколько важных действий, 

перечисленных ниже:

• вызывается функция QetLaetError, с помощью которой определяется 

системный код ошибки;

• вызывается функция formatMeesage, которая возвращает адрес строки, 

содержащей сообщение об ошибке, сформированное операционной системой;

• вызывается функция MessageBox, с помощью которой полученная от функции Рогmаt Меssages текстовая строка выводится на экран в окне сообщений;

• вызывается функция LocalFree, которая освобождает память, занимаемую  строкой, содержащей сообщение об ошибке.

11.2.6. Листинг программы

Вас не должна пугать длина этой программы. Дело в том, что большая часть этого кода повторяется практически во всех графических приложениях для cиcтeмыWindows:

.386

.model flat,STDCALL

INCLUDE GraphWin.inc

AppLoadMsgTitle BYTE ; "Приложение загружено",0

AppLoadMsgText BYTE; "Это окно отображено после получения " "сообщения WM__CREATE",0

PopupTitle BYTE; "Окно сообщения",0

PopupText BYTE; "Это окно было активизировано после "

      BYTE; "Окно сообщения",0   

GreetTitle BYTE; "Это окно было активизировано после "

               BYTE; "получения сообщения WM_LBUTTONDOWN",0

GreetText BYTE; "Основное окно программы активизировано",0

     BYTE; "Это окно отображено сразу после вызова "

CloseMsg BYTE; "Получено сообщение WM_CLOSE",0

ErrorTitle BYTE "Ошибка!",0

WindowName BYTE "Графическая ассемблерная программа",0

className BYTE "ASMWin",0

; Определим структурную переменную, описывающую класс окна

MainWin WNDCLASS

COLOR_WINDOW,NULL,className>

msg MSGStruct <>

winRect RECT <>

hMainWnd DWORD ?

hInstance DWORD ?

.code

WinMain PROC

; Определим дескриптор текущего процесса

INVOKE GetModuleHandle , NULL

mov hInstance , eax

mov MainWin.hInstance, eax

; Загрузим образы пиктограммы и курсора программы.

INVOKE LoadIcon, NULL, IDI_APPLICATION

mov MainWin.hIcon , eax

INVOKE LoadCursor, NULL, IDC__ARROW

mov MainWin.hCursor , eax

; Зарегистрируем класс окна

INVOKE RegisterClass, ADDR MainWin

.IF eax == 0

call ErrorHandler

jmp Exit_Program

.ENDIF

; Создадим основное окно программы

INVOKE CreateWindowEx, 0, ADDR className,

ADDR WindowName,MAIN_WINDOW_STYLE,

CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

CW_USEDEFAULT,NULL,NULL,hInstance,NULL

; Если функция CreateWindowEx завершилась аварийно, отобразим

; сообщение в вьшдем из программы.

.IF eax == 0

call ErrorHandler

jmp Exit_Program

.ENDIF

; Запомним дескриптор окна, отобразим окно на экране и

; обновим его содержимое

mov hMainWnd ,eax

INVOKE ShowWindow , hMainWnd, SW_SHOW

INVOKE UpdateWindow, hMainWnd

; Выведем приветственное сообщение

INVOKE MessageBox, hMainWnd, ADDR GreetText,

ADDR GreetTitle, MB_OK

; Создадим цикл обработки сообщений

Message_Loop:

; Получим новое сообщение из очереди

INVOKE GetMessage, ADDR msq, NULL,NULL,NULL

; Если в очереди больще нет сообщений, завершим

; работу программы

.IF eax == 0

jmp Exit_Program

.ENDIF 

; Отправим сообщение на обработку процедуре WinProc нашей программы

INVOKE DispatchMessage, ADDR msg

jmp Message_Loop

Exit_Program:

INVOKE ExitProcess,0

WinMain ENDP

WinProc PROC,

hWnd:DWORD, localMsg:DWORD, wParam:DWORD, lParam:DWORD

Эта процедура обрабатывает некоторые сообщения, посылаемые

системой Windows нашему приложению.

Обработка остальных сообщений выполняется стандартной

процедурой системы Windows.

mov eax,localMsg

.IF eax == WM_LBUTTONDOWN ; Щелчок левой кнопкой мьши?

INVOKE MessageBox, hWnd, ADDR PopupText,

ADDR PopupTitle, MB_OK

jmp WinProcExit

.ELSEIF eax == WM_CREATE ; Окно создано?

INVOKE MessageBox, hWnd, ADDR AppLoadMsgText,

ADDR AppLoadMsgTitle, MB_OK

jmp WinProcExit

.ELSEIF eax == WM_CLOSE ; Окно закрыто?

INVOKE MessageBox, hWnd, ADDR CloseMsg,

ADDR WindowName, MB_OK

INVOKE PostQuitMessage,0

jmp WinProcExit

.ELSE ; Другие сообщения

INVOKE DefWindowProc, hWnd, localMsg, wParam, lParam

jmp WinProcExit

.ENDIF

WinProcExit:

ret

WinProc ENDP

ErrorHandler PROC

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

.data

pErrorMsg DWORD ?

messageID DWORD ?

.code

INVOKE GetLastError

mov messageID,eax

; Адрес сообщения об ошибке

; В EAX возвращается код ошибки

; Определим адрес текстового сообщения об ошибке

INVOKE FormatMessage, FORMAT_MESSAGE_ALLOCATE_BUFFER +

FORMAT_MESSAGE_FROM_SYSTEM,NULL,messageID,NULL,

ADDR pErrorMsg,NULL,NULL

; Отобразим сообщение об ошибке

INVOKE MessageBox,NULL, pErrorMsg, ADDR ErrorTitle,

MB_IConerror+MB_OK

; Освободим память, занимаемую текстовой строкой

; сообщения об ошибке

INVOKE LocalFree, pErrorMsg

ret

ErrorHandler ENDP

END WinMain

11.3. Управление памятью в процессорах семейства IA-32

После появления системы Microsoft Windows версии 3.0 программисты с большим интересом начали обсуждать тему написания программ для защищенного режима работы процессора. Напомним, что до этого все программы писались для реального режима  работы процессора и системы MS DOS. Те, кому приходилось создавать программы для системы Windows версии 2.x, расскажут вам, насколько непросто было "вписаться" в те 640 Кбайт оперативной памяти, которые выделялись программе в реальном режиме  адресации! Данную проблему удалось преодолеть только после того, как в системе Windows стал поддерживаться защищенный (а чуть позже и виртуальный) режим работы  процессора. При этом программистам пришлось осваивать совершенно новые аппаратные средства, которые открывали перед ними доселе невиданные возможности. Однако не стоит забывать, что все это стало возможным только после появления процессора Intel386, который и стал родоначальником семейства IA-32. За прошедшее десятилетие мы  наблюдали процесс эволюции операционных систем и появление новых версий системы Windows и Linux. Их возможности и стабильность работы не идут ни в какое сравнение со старой версией Windows 3.0. В этом разделе мы опишем две основные особенности системы управления памятью процессоров семейства IA-32:

• преобразование логических (сегментированных) адресов в линейные адреса;

• преобразование линейных адресов в физические (страничная организация памяти). А теперь давайте вспомним несколько основных терминов, относящихся к системе управления памятью процессоров семейства1А-32, которые были описаны в главе 2.

• Многозадачность позволяет одновременно запускать в операционной системе  несколько программ (или задач). При этом каждой задаче выделяется небольшой квант времени процессора, в течение которого ЦПУ физически выполняет  команды этой задачи.

• Сегментами называются области памяти переменной длины, в которых хранится программный код или данные.

• Благодаря поддержке механизма сегментации на аппаратном уровне удалось  изолировать участки памяти один от другого. В результате выполняемые  одновременно программы не могут повлиять друг на друга.

• Дескриптор сегмента — это 64-разрядное число, в котором зашифрована  информация об одном сегменте памяти: его базовый адрес, права доступа, длина, тип и способ использования. Теперь мы должны добавить к этому списку еще несколько терминов.

• Селектор сегмента— это 16-разрядное число, которое зафужается в сегментные регистры (CS, DS, SS, ES, FS ИЛИ GS). По сути, ОНО является указателем  дескриптора сегмента, расположенным в одной из системных таблиц дескрипторов.

• Логический адрес — это комбинация селектора сегмента и 32-разрядного смешения. До сих пор мы мало уделяли внимания работе с сегментными регистрами, поскольку в защищенном режиме их содержимое никогда не меняется прикладными программами. В своих программах мы использовали только 32-разрядные смещения. Тем не менее, сегментные регистры очень важны при создании системных программ, поскольку  косвенно они указывают на сегменты памяти.

11.3.1. Линейные адреса

11.3.1.1. Преобразование логических адресов в линейные

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

программы вышляется отдельная область данных. Предположим, что в каждой из трех  выполняемых программ существует переменная, расположенная со смещением 200h  относительно начала сегмента данных. Возникает вопрос: как разделить эти переменные друг от друга так, чтобы изменение их значения в одной из программ не влияло на другие  программы? Для этой цели в процессорах семейства lA-32 используется

одно- или двухэтапный процесс преобразования смещения переменной в уникальный физический адрес памяти. На первом этапе логический адрес, состоящий из селектора сегмента и смещения  переменной, преобразуется линейный адрес, который по сути может являться физическим адресом переменной в памяти. Однако в современных развитых операционных системах, таких как Microsoft Windows и Linux, задействуется еще один механизм процессоров  семейства IA-32, который называется страничной организацией памяти. Благодаря ему  размер используемого в программах линейного адресного пространства может превышать физический размер памяти компьютера. Таким образом, на втором этапе линейный  адрес памяти преобразовывается в физический адрес с помощью механизма страничной  переадресации, который подробнее будет рассмотрен в разделе 11.3.2. Для начала давайте разберемся в том, как процессор по значению селектора сегмента и смещению определяет линейный адрес переменной. Как вы уже знаете, каждый  селектор является указателем дескриптора сегмента, расположенного в одной из системных таблиц дескрипторов. Поэтому сначала по значению селектора определяется адрес  дескриптора сегмента, из которого извлекается базовый адрес сегмента памяти. После этого к значению базового адреса прибавляется 32-разрядное смещение переменной, в результате чего и получается линейный адрес переменной в памяти.

Линейный адрес.Линейный адрес является 32-разрядным целым числом, значение  которого находится в диапазоне от 0 до FFFFFFFFh и определяет адрес объекта в памяти. Линейный адрес может соответствовать физическому адресу объекта в памяти, если  механизм страничной переадресации не используется.

11.3.1.2. Страничная организация памяти

Основной особенностью процессоров семейства IA-32 является поддержка  страничной организации памяти. При ее использовании операционная система может  предоставить в распоряжение прикладных программ такой объем оперативной памяти, какой им требуется для работы, независимо от объема физической памяти, установленной в  компьютере. При этом суммарный объем памяти, используемый всеми приложениями,  может превышать объем физической памяти компьютера. Это стало возможным благодаря тому, что при выполнении программы в физической памяти компьютера находятся  только те участки программы, к которым процессор обращается в текущий момент времени. Все остальные участки программы хранятся на диске и загружаются в физическую  память компьютера по мере того, как в них возникает потребность. Вся область памяти,  используемой программой разбивается на участки небольшой длины (как правило 4 Кбайт каждый), называемых страницами. Во время выполнения программы процессор  выгружает из памяти на диск те страницы, к которым долго не было обращения и загружает на их место другие страницы, к которым нужно немедленно получить доступ. Для отслеживания всех страниц памяти, используемых программами, в  операционной системе создается специальный набор таблиц, состоящий из страничного каталога и ряда таблиц страниц. При обращении в программе частичку памяти, его линейный адрес автоматически преобразовывается процессором в физический адрес. Этот процесс  преобразования называется страничной переадресацией. Если страница, к которой происходит обращение, не находится в памяти, в процессоре возникает специальное прерывание из-за отсутствия страницы Q?agefault). Во время обработки данного прерывания  операционная система находит нужную страницу на диске, загружает ее в свободный участок памяти, изменяет соответствующим образом содержимое таблицы страниц и  возобновляет выполнение программы. Страничная переадресация и прерывание из-за отсутствия страницы происходят совершенно не заметно для прикладной программы. Чтобы почувствовать разницу между физической и виртуальной памятью,  воспользуйтесь диспетчером задач системы Windows 2000/ХР. На рис. 11.12 показана вкладка Быстродействие (Performance) диалогового окна диспетчера задач для компьютера,  оснащенного 1 Гбайт физической памяти. Общее количество виртуальной памяти, которое используется в настоящий момент в операционной системе, можно посмотреть в разделе Выделение памяти (Conimit Charge) вкладки Быстродействие диалогового окна диспетчера задач. Обратите внимание, что установленный предельный размер виртуальной памяти, равный 2521476 Кбайт, практически в 2,5 раза превышает объем физической памяти компьютера^

11.3.1.3. Таблицы дескрипторов

Дескрипторы сегментов могут находиться в одной из двух системных таблиц: в  таблице глобальных дескрипторов {Global Descriptor Table, или GDT) или в таблице локальных  дескрипторов {Local Descriptor Table, или LDT).

Таблица глобальных скрипторов (GDT) ,В процессорах семейства IA-32  поддерживается только одна таблица глобальных дескрипторов. Она создается операционной  системой компьютера в момент переключения процессора в защищенный режим работы.  Базовый адрес таблицы глобальных дескрипторов помещается в специальный системный управляющий регистр, называемый GDTR {Global Descriptor Table Register, или Регистр таблицы глобальных дескрипторов). Элементы этой таблицы называются дескрипторами сегментов {Segment descriptors). Как вы уже знаете, в этих дескрипторах хранится  информация, описывающая конкретный сегмент памяти. В таблице глобальных дескрипторов операционная система хранит описание только тех сегментов, которые используются во всех программах.

Таблица локальных дескрипторов (LDT), В многозадачных операционных системах обычно для каждой задачи выделяется собственная таблица дескрипторов сегментов,  которая называется таблицей локальных дескрипторов. Базовый адрес этой таблицы загружается в момент переключения контекста задачи в специальный системный  управляющий регистр, называемый LDTR {Local Descriptor Table Register, или Регистр таблицы  локальных дескрипторов). В каждом дескрипторе сегмента содержится базовый адрес сегмента, заданный в  линейном адресном пространстве. Обычно все сегменты программы находятся в  непересекающихся областях памяти, как показано на рис. 11.13. Как видите, обращение к трем разным участкам памяти, заданных своими логическими адресами, связано с выборкой трех разных дескрипторов сегментов, находящихся в LDT. На нашем рисунке  предполагается, что механизм страничной переадресации отключен, поэтому линейные адреса соответствуют физическим адресам памяти.

11.3.1.4. Описание дескриптора сегмента

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

Базовый адрес. Представляет собой 32-разрядное целое число, определяющее адрес начала сегмента (т.е. адрес байта со смещением 0) в четырехгигабайтовом линейном  адресном пространстве.

Уровень привилегий. Каждому сегменту назначается специальный уровень привилегий, который представляет собой число от 0 до 3. Число 0 соответствует самому высокому уровню привилегий, который обычно используется только программами ядра  операционной системы. Если программа, которой назначен больший (в числовом измерении) уровень привилегий, попытается воспользоваться сегментом с меньшим уровнем  привилегий, возникнет прерывание процессора.

Тип сегмента. Биты этого поля определяют тип сегмента и способы доступа к нему, а также направление его роста (от младших адресов к старшим или наоборот). Сегменты данных (к ним относится также и сегмент стека) можно защитить от записи, т.е. сделать доступными только для чтения. Поскольку данные в сегмент стека обычно помещаются от старших адресов к младшим, вне скрипторе сегмента был предусмотрен специальный бит для обозначения подобных сегментов. Кроме того, можно запретить считывание данных из сегмента кода и сделать его доступным только для выполнения программ (как вы помните, запись данных в сегмент кода запрещена по определению). Флаг присутствия сегмента в памяти. Этот бит позволяет отслеживать, находится ли данный сегмент в физической памяти. Наличие этого бита позволяло операционной  системе компьютера на основе процессора Intel286 вынуждать на диск сегмент при  недостатке физической памяти. В связи с поддержкой страничной организации памяти в процессорах семейства IA-32, этот бит давно перестал использоваться.

Флагвеличины гранулы. Значение этого бита определяет способ интерпретации поля, определяющего максимальную границу сегмента (т.е. его длину). Если бит величины гранулы не установлен, граница сегмента задана в байтах. В противном случае граница сегмента задается в страницах размером 4 Кбайт.

Поле границы сегмента. Представляет собой 20-разрядное целое число, значение  которого определяет максимальную длину сегмента (точнее, максимально допустимое смещение в пределах данного сегмента, которое равно длине сегмента минус единица). В зависимости от значения бита величины гранулы, могут существовать следующие два типа сегментов: • размером от 1 байтадо 1 Мбайта; • размером от 4096 байтов до 4 Гбайт.


11.3.2. Страничная переадресация

При включении механизма страничной переадресации процессор будет  преобразовывать 32-разрядные линейные адреса в 32-разрядные физические          aдpeca. При этом  используются три перечисленные ниже структуры данных.

• Страничный каталог— массив, состоящий из 1024 элементов, каждый из которых имеет длину 32 бита.

• Таблица страниц — массив, состоящий также из 1024 элементов, каждый из  которых имеет длину 32 бита.

• Страница— область памяти, размер которой составляет либо 4Кбайт, либо

4 Мбайт. Для упрощения последующего изложения будем считать, что используются страницы размером 4 Кбайт.

Линейный адрес, длина которого составляет 32 бита, разбивается на 3 поля. Первое поле определяет индекс используемого элемента страничного каталога, второе поле —  индекс используемого элемента таблицы страниц, а третье поле определяет смещение в текущей странице. Адрес страничного каталога загружается операционной системой в управляющий регистр процессора CR3.

1. Программа обращается в память, указывая линейный адрес объекта.

2. Из линейного адреса извлекается значение 10-битового поля, определяющего  индекс элемента в страничном каталоге. По этому индексу находится  соответствующий элемент страничного каталога, содержащий базовый физический адрес  таблицы страниц.

3. Из линейного адреса извлекается значение 10-битового поля, определяющего  индекс элемента в таблице страниц, адрес которой бьш определен в п. 1. По этому индексу находится соответствующий элемент таблицы страниц, содержащий  базовый физический адрес страницы памяти. 4. К полученному в п. 2 базовому физическому адресу страницы памяти  прибавляется значение 12-битового поля смещения, в результате чего получается  32-разрядный физический адрес операнда в памяти.

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

11.3.2.1. Диспетчер виртуальных машин системы Microsoft Windows

После того как мы в общих чертах описали систему управления памятью,  используемую в процессорах семейства LA-32, будет интересно посмотреть на то, как происходит этот процесс в операционной системе Windows. Ниже приведена выдержка из  документации Microsoft Platform SDK для операционных cncTeMWindows 95/98. Основу ядра операционных систем Windows 95/98 составляет диспетчер виртуальных машин (Vlrtual Machine Manager, или VMM), который является 32-разрядной программой, написанной для защищенного режима работ процессора. К его основным функциям относятся: создание, запуск, отслеживание и завершение работы виртуальных машин. Кроме того, VMM обеспечивает поддержку функций, предназначенных для распределения памяти, управления процессами, прерываниями и исключениями. Он также обеспечивает работу виртуальных устройств ^ 32-разрядных модулей, обрабатывающих прерывания и ошибки, генерируемые реальными устройствами, и управляющих доступом со стороны прикладных программ к оборудованию компьютера и установленного программного обеспечения. И VMM, и модули виртуальных устройств выполняются в едином 32-разрядном линейном адресном пространстве с нулевым уровнем привилегий. Операционная система создает в таблице глобальных дескрипторов два элемента: один для сегмента кода, а другой для сегмента данных. Базовый адрес обоих сегментов равен нулю и никогда не изменяется. Диспетчер виртуальных машин обеспечивает поддержку выполнения множества потоков команд, а также многозадачность с вытеснением на основе приоритетов. Он позволяет одновременно запускать несколько приложений в отдельных виртуальных машинах и распределять время центрального процессора между ними. Нам осталось только уточнить терминологию. В приведенном выше отрывке из  документации Microsoft Platform SDK под термином виртуальная машина понимается процесс или задача, по крайней мере, так это определено в документации по процессорам  семейства IA-32 фирмы Intel. Виртуальная машина состоит из программного кода, обслуживающих ее программ, памяти и регистров. Каждой виртуальной машине назначается  собственное адресное пространство, пространство портов ввода-вывода, таблица векторов прерываний и таблица локальных дескрипторов. Приложениям, которые запускаются в виртуальной машине в режиме эмуляции процессора 8086, назначается третий уровень привилегий. Программы, написанные для защищенного режима, могут выполняться с первым, вторым или третьим уровнем привилегий.

Программа 1

          .386

        .model flat,stdcall

  option   casemap:none

                        include C:masm32INCLUDEWINDOWS.INC

                        include C:masm32INCLUDEKERNEL32.INC

                        include C:masm32INCLUDEUSER32.INC

                        include C:masm32INCLUDEADVAPI32.INC

                        include   C:masm32INCLUDEGDI32.INC                                               

                        include  my.inc

                        includelib C:masm32libcomctl32.lib

                        includelib C:masm32libuser32.lib

                        includelib C:masm32libgdi32.lib

                        includelib C:masm32libkernel32.lib               

                        includelib C:masm32libuser32.lib

                        includelib C:masm32libadvapi32.lib     

;###########################################################

MAIN_WINDOW_PROC     PROTO   :DWORD ,  :DWORD , :DWORD ,  :DWORD

;###########################################################

;data--data--data--data--data--data--data--data--data--data--     PROC

;----------------------------------------------------------------------------------------------

.DATA

HINST        DWORD      NULL

HWND_WIN        DWORD     NULL

;-

String_CLASS              DB    "MY_WINDOW",0 

String_CAPTION             DB  "MY_CAPTION",0

;-

MSG_WIN        MSG      <0>

;##############################################################

;code--code--code--code--code--code--code--code--code--code-- PROC

;--------------------------------------------------------------------------------------------------

.CODE 

START:

                invoke  GetModuleHandle  ,  Null

                 mov    HINST   ,  EAX  

                    ;-       

                      CALL   MY_REGISTER_CLASS

                           cmp   EAX  ,  null

                       je   EXIT

                   ;-      

                invoke    CreateWindowEx   ,  Null  ,   addr    String_CLASS   ,  addr  String_CAPTION  ,

                            WS_VISIBLE +  WS_CAPTION  + WS_SYSMENU   ,   100 , 100 ,  500 ,  500  ,

                            NULL ,  NULL  ,  HINST ,  NULL 

                  ;-      

                  MOV   HWND_WIN  , EAX           

                  ;-       

                invoke  ShowWindow   ,  HWND_WIN   , TRUE 

                invoke  UpdateWindow ,  HWND_WIN     

          ;====================

MSG_LOOP:

                               invoke   GetMessage  ,    addr  MSG_WIN  , null , null , null

                               CMP   Eax  ,  FALSE

                           JE    EXIT     

                               invoke   TranslateMessage  ,   addr   MSG_WIN

                               invoke   DispatchMessage       , addr  MSG_WIN

                        JMP   MSG_LOOP

          ;====================

EXIT:    

             invoke               ExitProcess        ,       Null

;----------------------------------------------------------------------------------------------------------

;                                    GET   POINT  SCREEN                                                       

;----------------------------------------------------------------------------------------------------------

MY_REGISTER_CLASS  PROC  

local  _Struct_WNDCLASS : WNDCLASS 

;-

      Mov  _Struct_WNDCLASS.style               ,       NULL               ;  стиль  окна

            ;  Mov   Eax  , 

      Mov  _Struct_WNDCLASS.lpfnWndProc       ,     MAIN_WINDOW_PROC   ; процедура окна

      Mov  _Struct_WNDCLASS.cbClsExtra     ,    null            ; дополнительная память для класса

      Mov  _Struct_WNDCLASS.cbWndExtra   ,   null            ; дополнительная память для  окна

              Mov   Eax  ,  HINST

      Mov  _Struct_WNDCLASS.hInstance     ,       Eax         ;  handle   приложения

      Mov  _Struct_WNDCLASS.lpszMenuName     ,    NULL   ;  идентификатор меню

      Mov  _Struct_WNDCLASS.lpszClassName   ,     offset  String_CLASS  ;   адрес строки класса

   ;-

            invoke   LoadIcon  ,   NULL  ,    IDI_ASTERISK                     

      Mov  _Struct_WNDCLASS.hIcon       ,      Eax

         ;-

            invoke   LoadCursor  ,  NULL  ,   IDC_IBEAM

      Mov  _Struct_WNDCLASS.hCursor      ,   Eax

         ;-

          ;  invoke   CreateSolidBrush  ,  00FF0000h               ;  возвратит идентификатор  кисти

              invoke   GetStockObject     ,   NULL_BRUSH       

      Mov  _Struct_WNDCLASS.hbrBackground     ,  Eax

;==============  

               invoke  RegisterClassA        ,   addr     _Struct_WNDCLASS            

;============== 

ret 

MY_REGISTER_CLASS   ENDP

;---------------------------------------------------------------------------------------------------------------------------

;                                           WINDOW        PROCEDURE                                                             

;---------------------------------------------------------------------------------------------------------------------------

MAIN_WINDOW_PROC     PROC   USES  EBX   ESI  EDI 

                                                     hWnd_  :DWORD , MESG :DWORD , wParam :DWORD ,  lParam:DWORD

;-

                                            CMP     MESG    ,     WM_DESTROY

                                      JE      WMDESTROY    

;----                                        

               invoke   DefWindowProc ,  hWnd_   ,   MESG  ,  wParam , lParam 

               jmp      FINISH

;----                                         

WMDESTROY:

              invoke    PostQuitMessage ,  False

;-

FINISH:

RET  16                                                                

MAIN_WINDOW_PROC     ENDP

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

END  START

Программа 2

          .386

        .model flat,stdcall

  option   casemap:none

                        include C:masm32INCLUDEWINDOWS.INC

                        include C:masm32INCLUDEKERNEL32.INC

                        include C:masm32INCLUDEUSER32.INC

                        include C:masm32INCLUDEADVAPI32.INC

                        include   C:masm32INCLUDEGDI32.INC                                               

                        include  my.inc

                        includelib C:masm32libcomctl32.lib

                        includelib C:masm32libuser32.lib

                        includelib C:masm32libgdi32.lib

                        includelib C:masm32libkernel32.lib               

                        includelib C:masm32libuser32.lib

                        includelib C:masm32libadvapi32.lib     

;###########################################################

MAIN_WINDOW_PROC     PROTO   :DWORD ,  :DWORD , :DWORD ,  :DWORD

;###########################################################

;data--data--data--data--data--data--data--data--data--data--     PROC

;----------------------------------------------------------------------------------------------

.DATA

HINST        DWORD      NULL

HWND_WIN        DWORD     NULL

;-

ZAGLUSHKA        DWORD    FALSE

;-

String_CLASS              DB    "MY_WINDOW",0 

String_CAPTION             DB  "MY_CAPTION",0

String_CAPTION_SECOND   DB   "MY_SECOND_CAPTION",0

;-

MSG_WIN        MSG      <0>

;##############################################################

;code--code--code--code--code--code--code--code--code--code-- PROC

;--------------------------------------------------------------------------------------------------

.CODE 

START:

                invoke  GetModuleHandle  ,  Null

                 mov    HINST   ,  EAX  

                    ;-       

                      CALL   MY_REGISTER_CLASS

                           cmp   EAX  ,  null

                       je   EXIT

                   ;-      

                invoke    CreateWindowEx   ,  NULL ,    addr    String_CLASS   ,  addr  String_CAPTION  ,

                            WS_OVERLAPPEDWINDOW ,   100 , 100 ,  500 ,  500  ,

                            NULL ,  NULL  ,  HINST ,  NULL 

                  ;-      

                  MOV   HWND_WIN  , EAX           

                  ;-       

                invoke  ShowWindow   ,  HWND_WIN   , TRUE 

                invoke  UpdateWindow ,  HWND_WIN     

          ;====================

MSG_LOOP:

                               invoke   GetMessage  ,    addr  MSG_WIN  , null , null , null

                               CMP   Eax  ,  FALSE

                           JE    EXIT     

                               invoke   TranslateMessage  ,   addr   MSG_WIN

                               invoke   DispatchMessage       , addr  MSG_WIN

                        JMP   MSG_LOOP

          ;====================

EXIT:    

             invoke               ExitProcess        ,       Null

;----------------------------------------------------------------------------------------------------------

;                                    GET   POINT  SCREEN                                                       

;----------------------------------------------------------------------------------------------------------

MY_REGISTER_CLASS  PROC  

local  _Struct_WNDCLASS : WNDCLASS 

;-

      Mov  _Struct_WNDCLASS.style               ,       NULL               ;  стиль  окна

            ;  Mov   Eax  , 

      Mov  _Struct_WNDCLASS.lpfnWndProc       ,     MAIN_WINDOW_PROC   ; процедура окна

      Mov  _Struct_WNDCLASS.cbClsExtra     ,    null            ; дополнительная память для класса

      Mov  _Struct_WNDCLASS.cbWndExtra   ,   null            ; дополнительная память для  окна

              Mov   Eax  ,  HINST

      Mov  _Struct_WNDCLASS.hInstance     ,       Eax         ;  handle   приложения

      Mov  _Struct_WNDCLASS.lpszMenuName     ,    NULL   ;  идентификатор меню

      Mov  _Struct_WNDCLASS.lpszClassName   ,     offset  String_CLASS  ;   адрес строки класса

   ;-

            invoke   LoadIcon  ,   NULL  ,    IDI_ASTERISK                     

      Mov  _Struct_WNDCLASS.hIcon       ,      Eax

         ;-

            invoke   LoadCursor  ,  NULL  ,   IDC_IBEAM

      Mov  _Struct_WNDCLASS.hCursor      ,   Eax

         ;-

          ;  invoke   CreateSolidBrush  ,  00FF0000h               ;  возвратит идентификатор  кисти

              invoke   GetStockObject     ,   LTGRAY_BRUSH          

      Mov  _Struct_WNDCLASS.hbrBackground     ,  Eax

;==============  

               invoke  RegisterClassA        ,   addr     _Struct_WNDCLASS            

;============== 

ret 

MY_REGISTER_CLASS   ENDP

;---------------------------------------------------------------------------------------------------------------------------

;                                           WINDOW        PROCEDURE                                                             

;---------------------------------------------------------------------------------------------------------------------------

MAIN_WINDOW_PROC     PROC   USES  EBX   ESI  EDI 

                                                     hWnd_  :DWORD , MESG :DWORD , wParam :DWORD ,  lParam:DWORD

LOCAL    _hwnd_Win :DWORD                                                         

;-

                                            CMP     MESG    ,     WM_DESTROY

                                      JE      WMDESTROY    

                                            CMP    MESG     ,      WM_CREATE

                                      JE      WMCREATE       

;----                                        

               invoke   DefWindowProc ,  hWnd_   ,   MESG  ,  wParam , lParam 

               jmp      FINISH

;----                                        

WMCREATE:

                            CMP    ZAGLUSHKA   ,    TRUE

                     JE   FINISH                                  

                                   MOV   ZAGLUSHKA    ,    TRUE

                     invoke    CreateWindowEx   ,  NULL ,    addr    String_CLASS   ,

                                                                                                         addr   String_CAPTION_SECOND   ,

                            WS_CHILD + WS_CAPTION + WS_SYSMENU  ,   200 , 200 ,  200 ,  200  ,

                            hWnd_  ,  NULL  ,  HINST ,  NULL        

                       ;-      

                                  Mov   _hwnd_Win   ,   Eax

                       ;-  

                   invoke  ShowWindow   ,    _hwnd_Win   , TRUE 

                    invoke  UpdateWindow ,  _hwnd_Win

             jmp        FINISH

WMDESTROY:

                                mov   Eax  ,  hWnd_

                          cmp  Eax   ,  HWND_WIN

                 Jne   FINISH         

              invoke    PostQuitMessage ,  False

;-

FINISH:

RET  16                                                                

MAIN_WINDOW_PROC     ENDP

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

END  START

Заключение

В данной курсовой работе был изучен теоретический материал по работе с прерываниями DOS, вводом и выводом чисел при помощи подключаемого модуля IO, работе с одномерными и двухмерными массивами, тестированию состояния флагов, рассмотрены основные принципы и навыки работы с компилятором и отладчиком. Разработана схема алгоритма программы и реализована на языке низкого уровня ассемблер.

Язык Ассемблера - мощное средство программирования. Он позволяет программисту осуществлять всестороннее управление аппаратными средствами ЭВМ. Однако такое управление заставляет программиста вникать в детали, далекие от основного содержания программы. Все преимущества языка Ассемблера оборачиваются подчас пустой тратой времени на многочисленные детали.

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

К преимуществам Ассемблера можно отнести:

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

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

К недостаткам языка следует отнести:

1. По мере увеличения своего размера программа на Ассемблере теряет наглядность. Это связано с тем, что в ассемблерных программах следует уделять много внимания деталям. Язык требует планирования каждого шага ЭВМ. Конечно, в случае небольших программ это позволяет сделать их оптимальными с точки зрения эффективности использования аппаратных средств. В случае же больших программ бесконечное число деталей может помешать добиться оптимальности программы в целом, несмотря на то, что отдельные фрагменты программы будут написаны очень хорошо.

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

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

Цели курсового проектирования были достигнуты.

Список источников

1.                Юров В.И. Ассемблер: Учебник для вузов. - СПб.: Питер, 2003. – 637 с.

2.                Юров В.И. Ассемблер. Практикум. 2-е изд. – СПб.: Питер, 2006. – 399 с.

3.                Калашников О.А. Ассемблер? Это просто! Учимся программировать. – СПб.: БХВ -Петербург, 2006. – 384 с.

4.                Магда Ю.С. Ассемблер для процессоров Intel Pentium. – СПб.: Питер, 2006. – 410 с.

5.                В.Ю. Пирогов. Assembler. Учебный курс.: М., 2001. 

6.                Марек Р. Ассемблер на примерах. Базовый курс. – СПб.: Наука и техника, 2005. – 240 с.

7.                Фролов А.В., Фролов Г.В. Библиотека системного программиста. Т. 1. Часть 1, 2, 3. Операционная система MS-DOS. М: ДИАЛОГ-МИФИ, 1991, 1993

8.                Рудаков П. И., Финогенов К. Г. Язык ассемблера: уроки программирования. - М.: ДИАЛОГ-МИФИ, 2001. - 640 с.

9.                Ирвин, Кип. Язык ассемблера для процессоров Intel, 4-е издание.: Пер. с англ. — M.: Издательский дом "Вильямс", 2005. — 912 с.

10.           Питер Абель, Ассемблер и программирование для IBM PC, Пер. с англ. - Технологический институт Британская Колумбия

Информация о файле
Название файла Разработка Windows-приложений на языке ассемблера от пользователя Гость
Дата добавления 10.5.2020, 19:15
Дата обновления 10.5.2020, 19:15
Тип файла Тип файла (zip - application/zip)
Скриншот Не доступно
Статистика
Размер файла 670.37 килобайт (Примерное время скачивания)
Просмотров 745
Скачиваний 89
Оценить файл