Навигация
Главная
Поиск
Форум
FAQ's
Ссылки
Карта сайта
Чат программистов

Статьи
-Delphi
-C/C++
-Turbo Pascal
-Assembler
-Java/JS
-PHP
-Perl
-DHTML
-Prolog
-GPSS
-Сайтостроительство
-CMS: PHP Fusion
-Инвестирование

Файлы
-Для программистов
-Компонеты для Delphi
-Исходники на Delphi
-Исходники на C/C++
-Книги по Delphi
-Книги по С/С++
-Книги по JAVA/JS
-Книги по Basic/VB/.NET
-Книги по PHP/MySQL
-Книги по Assembler
-PHP Fusion MOD'ы
-by Kest
Professional Download System
Реклама
Услуги

Автоматическое добавление статей на сайты на Wordpress, Joomla, DLE
Заказать продвижение сайта
Программа для рисования блок-схем
Инженерный калькулятор онлайн
Таблица сложения онлайн
Популярные статьи
OpenGL и Delphi... 65535
Форум на вашем ... 65535
21 ошибка прогр... 65535
HACK F.A.Q 65535
Бип из системно... 65535
Гостевая книга ... 65535
Invision Power ... 65535
Пример работы с... 65535
Содержание сайт... 65535
ТЕХНОЛОГИИ ДОСТ... 65535
Организация зап... 65535
Вызов хранимых ... 65535
Создание отчето... 65535
Имитационное мо... 65535
Программируемая... 65535
Эмулятор микроп... 65535
Подключение Mic... 65535
Создание потоко... 65535
Приложение «Про... 65535
Оператор выбора... 65535
Реклама
Как работает вебкам студия webcam студия vipland54.ru.
Сейчас на сайте
Гостей: 9
На сайте нет зарегистрированных пользователей

Пользователей: 13,372
новичок: vausoz
Новости
Реклама
Выполняем курсовые и лабораторные по разным языкам программирования
Подробнее - курсовые и лабораторные на заказ
Delphi, Turbo Pascal, Assembler, C, C++, C#, Visual Basic, Java, GPSS, Prolog, 3D MAX, Компас 3D
Заказать программу для Windows Mobile, Symbian

База данных междугородних телефонных разговоров на Delphi
Моделирование интернет магазина (Apache, Php, Html) на GPSS + Блок схема
Диплом RSA, ЭЦП, сертификаты, шифрование на C#

Урок 3. Продолжаем работать с памятью. Первый цикл. Изучение двухкомпонетного адреса.
Упс, как говорит Б.Спирс, ай дид ит эгейн. Ну и мы тоже эгейн. В смысле, снова
поработаем с памятью.
Итак, память. Памяти у нас примерно миллион ячеек по 1 байту. Где-то находятся
байты нашей программы, которую процессор сейчас читает байт за байтом и выполняет,
где-то байты программ БИОС-а, где-то - ДОС-а. Ну а начиная с байта нумер 655360
находится так называемый видеобуфер. Можно сказать по-другому - с номера A0000h.
Это - шестнадцетеричная система исчисления. Эти числа равны: 655360=A0000h, просто они
записаны по-разному. Здесь мы не будем подробно разбирать эту систему, приведем только
пример преобразования числа A0000h в "нормальный" вид:

A0000h = A*16^4+0*16^3+0*16^2+0*16^1+0*16^0 = 10*16^4 = 655360.

h - просто обозначение такой системы исчисления;
A = 10, B = 11, C = 12, D = 13, E = 14, F = 15 - поскольку основанием является 16, то для записи
числа НЕ ХВАТАЕТ чисел "привычной" десятичной системы - 0,1,2,3..9.
Вы вполне можете обойтись виндовым или любым другим калькулятором для
такого рода преобразований.
Ага, так что же такое видеобуфер ? Он же - видеопамять, видеокарта, ... Для
нас нет никаких железок, джемперов, жестких дисков, повторяю !
Есть только память, порты и процессор. Видеобуфер для нас в данном
случае - просто КУСОК ПАМЯТИ. Начинается с байта номер A0000h. Почему же мы
выделяем этот кусок особо ? А вот почему: помните, у нас в Уроке 1 был такой код:

{Устанавливаем графический режим монитора 13h, 320x200, 256 цветов}
asm
mov ax,0013h
int 10h
end;




Всего две команды, а делают сколько ! После их выполнения БИОС сделает так, что
всякая запись чисел в этот видеобуфер приведет к изменениям на мониторе !
Как это происходит ? С нашей стороны "забора" мы пишем в ячейки этого куска памяти
числа. Они там и остаются, пока их не изменить. С другой стороны "забора" сидит
аппаратура, "железо", как ее еще называют, которая СОВЕРШЕННО НЕЗАВИСИМО от
выполнения наших программ начинает трактовать эти числа КАК ЦВЕТА ТОЧЕК монитора.
Это ее БИОС так заставил делать. Переключил в такой режим. Бывают и другие режимы
работы видеокарты-видеоаппаратуры - текстовые там, графические ... Мы в графическом.
Но это все нам неважно. Важно нам только то, как надо писать в эту память числа, чтобы
аппаратура эта нарисовала на их основе изображение на мониторе.
Более детально. Вспомним, что режим у нас - 320x200, 256 цветов. 320 точек по
горизонтали, 200 - по вертикали. Каждая точка может иметь 256 различных цветов.
1 байт памяти как раз может иметь 256 различных состояний. Число в байте номер
A0000h аппаратура трактует как цвет верхнего левого пикселя, число в следующем
байте - как цвет следующего(двигаясь направо) пикселя и так далее. На одну строчку
320 пикселей - значит, и в памяти 320 байт. 321 байт начиная с байта A0000h - это уже
самый левый пиксель второй стороки и так далее:

Номер пикселя по
горизонтали/вертикали - номер байта в памяти
0 1 2 3
0 A0000h A0000h+1 A0000h+2 A0000h+3 ...
1 A0000h+320...
2
...

Ага. Нетрудно сообразить, что для всех 320x200 точек монитора требуется
320x200 = 64000 байт памяти. Например, запись в байт с номером
A0000h+320x200-1 приведет к изменению цвета нижнего правого пикселя.
Проверим это. Нарисуем вертикальную горизонтальную линию на мониторе,
желтого цвета, по самой первой строке от левого до правого края монитора.
От уха до уха, так сказать. Для этого мы должны записать
число 14(желтый цвет) в байты с номерами
A0000h,A0000h+1... A0000h+319 - всего 320 последовательных байт.
Вспоминаем, как процессор может делать это:

mov ax,0A000h {Заносим в регистр AX число 0A000h}
mov es,ax {Копируем число из регистра AX в регистр ES, фактически - 0A000h}
mov byte ptr es:[0],14 {Записываем по адресу 0A000hx16+0 байт значением 14}
mov byte ptr es:[1],14 {Записываем по адресу 0A000hx16+1 байт значением 14}
mov byte ptr es:[2],14 {Записываем по адресу 0A000hx16+2 байт значением 14}
...




Не надоело ? Верно, надо делать цикл. Как и в других языках программирования,
в асме тоже есть циклы. Что же за цикл нам нужен ? Цикл из 320 повторений,
в цикле должен вертеться индекс, позволяющий нам записывать СМЕЩЕНИЕ
адреса в команде от 0 до 319. СМЕЩЕНИЕ - это та часть эффективного адреса,
который процессор вычисляет из команды и добавит его к СЕГМЕНТНОМУ
регистру, умноженному на 16:

mov byte ptr es:[1],14
Эффективный адрес = es * 16 + 1
СЕГМЕНТНАЯ ЧАСТЬ СМЕЩЕНИЕ.

В этой команде смещение указано явно. Жестко. Сколько бы раз мы не выполнили
эту команду, она запишет байт 14 в одну и ту же ячейку памяти. Если только не
менять значение сегментного регистра es. Почему бы нам не поменять его ?
А вот, например, почему: нетрудно заметить, что ЛЮБОЕ значение в сегментном
регистре es при смещении = 1 нам НИКОГДА не позволит писать в ячеку памяти
номер A0000h(подумайте, почему !. Теперь вспомним, что в Уроке 2 мы проходили
так называемую КОСВЕННУЮ адресацию:

mov bx,1
mov byte ptr es:[bx],14 {Записываем по адресу 0A000hx16+bx байт значением 14}.




Процессор использовал для получения смещения регистр ! А регистры мы можем
менять ! Тогда начнем писать цикл:

mov ax,0A000h {Заносим в регистр AX число 0A000h}
mov es,ax {Копируем число из регистра AX в регистр ES, фактически - 0A000h}
mov bx,0 {Смещение первой точки равно 0}
mov byte ptr es:[bx],14
ADD bx,1 {Добавить к регистру BX 1 !}




Вот, новая команда(ADD) ! До сих пор мы работали с одной командой засылки
значения - mov(от move, англ.). А команда "ADD" (addition, сложить) позволяет
добавлять к приемнику операции (здесь - регистр BX) значение источника - "1".
И регистр BX станет равен 1 !
Что же дальше ? Последуем классическому типу циклов:

repeat
{Тело цикла}
Until {Условие=TRUE}

Очевидно, что телом цикла у нас выступает команда
закраски - mov byte ptr es:[bx],14. Командой add bx,1 мы изменили регистр BX
и можем анализировать его состояние (Условие=TRUE). Что же мы будем
анализировать ? Если мы хотим задать смещение от 0 до 319, то нам годятся
все значения bx в этом диапазоне, иначе цикл надо прерывать. Делаем это:

add bx,1 {inc(BX) или BX:=BX+1 или BX++}
CMP bx,319 {Сравнить BX и 319}
JBE МЕТКА_НАЧАЛА_ЦИКЛА - @repeat.




Опять, опять новые команды ! Не бойтесь, их вообще не так много, а выучить
их проще простого ! Я обязательно далее раскажу подробно о командах, а пока
лишь вкратце:

CMP bx,319 {COMPARE BX WITH 319}
JBE @repeat {JUMP IF BELOW OR EQUAL}




Вы видите расшифровку сокращений этих команд. CMP-это сравнение.
BX с 319. По результатам этого сравнения мы можем писать команды
переходов. А фактически - приказать процессору перейти на конкретную
команду, да еще и в зависимости от результата сравнения - JBE.
А вот как выглядит весь код:

begin
{Устанавливаем графический режим монитора 13h, 320x200, 256 цветов}
asm
mov ax,0013h
int 10h
end;
asm
{Кусок рисования}
mov ax,0A000h
mov es,ax
mov bx,0
@Repeat:
mov byte ptr es:[bx],14
add bx,1
cmp bx,319
jbe @Repeat
{Кусок рисования - Закончен}
end;
{Ожидаем нажатия клавиши}
asm
mov ah,0
int 16h
end;
{Устанавливаем текстовый режим монитора 03h, 80x25, 16 цветов текста и фона}
asm
mov ax,0003h
int 10h
end;
end.




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

Выполните этот код. Убедитесь в наличии желтой полосы вверху экрана.

Метка начала цикла у нас называется @Repeat. Почему вначале @ ?
Это особенность asm-вставок языка Паскаль. В настоящем ассемблере
Вы можете извращаться так, как вам хочется, хотя это ограничение не слишком
сильное. Вы могли назвать метку @MY_LABEL. Важно одно: Вы сравнили командой
CMP регистр BX с 319, и, если его значение МЕНЬШЕ(BELOW) или(OR)
равно(EQUAL) 319, то передаете управление на эту метку.

Примечание: Вам должно быть непонятно, ОТКУДА команда JBE знает про
результаты выполнения команды сравнения (CMP).

Усложним слегка пример. Хочу, чтобы мы нарисовали желтую полосу не по
первой сверху строке, а по строке номер 160 ! Думаю, Вам понятно, где
начинаются байты, отвечающие этой строке:

Номер пикселя по
горизонтали/вертикали - номер байта в памяти
0 1 2
160 A0000h+160*320 A0000h+160*320+1 A0000h+160*320+2...

А это означает, что начальное смещение в цикле надо задать просто
не с нуля, а с 160*320:

{Кусок рисования}
mov ax,0A000h
mov es,ax
mov bx,160*320 {Начальное смещение !}
@Repeat:
mov byte ptr es:[bx],14
add bx,1
cmp bx,160*320+319 {Внимание ! Мы изменили и эту команду !}
jbe @Repeat
{Кусок рисования - Закончен}




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

Важный момент:

cmp bx,160*320+319 {Внимание ! Мы изменили и эту команду !}

У нас регистр BX уже меняется не от нуля ! И закончим мы рисовать не тогда,
когда он станет 320, а когда 160*320+320 !

А теперь еще навороченне !!! Мы можем задавать эффективный адрес, варьируя
две компоненты: сегмент и смещение. Только что мы сместили полосу на экране,
меняя смещение. Это было естественно, поскольку мы занесли в регистр es такое
число, что процессор, умножая его на 16, как раз втыкался в начало видеобуфера:

es * 16 = 0A000h * 16 = 0A0000h !

И нам было наглядно задавть смещение от нуля(словно индексация в массиве).
Но мы попробуем устроить себе трабл и использовать другое (не 0A000h)
значение, заносимое в сегментный регистр es, чтобы нарисовать ту же желтую
линию в 160 строке. Нам надо задавть в командах такие вот адреса:

Номер пикселя по
горизонтали/вертикали - номер байта в памяти
0 1 2
160 A0000h+160*320 A0000h+160*320+1 A0000h+160*320+2...

A0000h бралось ранее из es. Смещения - 160*320+0,1,2.. - из bx.
А можно ли задать так значение es, чтобы смещение, к примеру,
начиналось с 0 ? Вот так:

0 1 2
160 X+0h X+1 X+2... - ?

Здесь X - это неизвестное. Оно должно быть получено процессором так:

X = es*16

А ячейки памяти должны иметь одинаковые адреса:

X+0h = A0000h+160*320.




Решая это несложное уравнение, найдем, что X = AC800h = 706560.
Значит, es = AC800h / 16 = AC80h ! И измененный код готов:

{Кусок рисования}
mov ax,0AC80h
mov es,ax
mov bx,0 {Начальное смещение ! - опять нуль !}
@Repeat:
mov byte ptr es:[bx],14
add bx,1
cmp bx,319 {Внимание ! Мы изменили и эту команду ! - вернули !}
jbe @Repeat
{Кусок рисования - Закончен}




Заделаем этот пример понагляднее ! Сначала нарисуем желтую полосу
в строке 160 "обычным" способом - занося перед циклом в регистр es число
0A000h, а потом нарисуем поверх желтой полосы фиолетовую, но уже занося
в es значение 0AC80h ! Вот кодец:

{Кусок рисования}
mov ax,0A000h
mov es,ax
mov bx,160*320 {Начальное смещение !}
@RepeatYellow:
mov byte ptr es:[bx],14
add bx,1
cmp bx,160*320+319
jbe @RepeatYellow
{Остановим программу известным нам способом}
mov ah,0
int 16h
{Рисуем, но уже другим способом !}
mov ax,0AC80h
mov es,ax
mov bx,0 {Начальное смещение ! - опять нуль !}
@RepeatMagneta:
mov byte ptr es:[bx],13 {Цвет не желтый !}
add bx,1
cmp bx,319
jbe @RepeatMagneta
{Кусок рисования - Закончен}




Проверьте этот код !

Ну вот. А теперь я хочу, чтобы Вы выполнили два небольших заданьица:

1) Заполнить ВЕСЬ экран каким-нибудь цветом. Желательно - красным.
2) Нарисовать горизонтальную полосу зеленого цвета в 64 строке экрана:
- занеся в регистр es адрес начала видеобуфера;
- занеся в регистр es такое значение, чтобы рисовалась та же полоса в
64 строке экрана, Но вот смещение Вы бы задавали с нуля !
Опубликовал Kest June 19 2009 07:53:37 · 1 Комментариев · 9257 Прочтений · Для печати

• Не нашли ответ на свой вопрос? Тогда задайте вопрос в комментариях или на форуме! •


Комментарии
mv January 30 2010 02:43:32
Нормально, только кажется странным что в уроке 4 упомянута команда MUL - которая (особенно для понимания начанающих) сложнее многих других, но при этом (пока) нерасказано о более простых.
поскольку урок посвящён (как я понял) циклам, то хорошо бы упамянуть
команду LOOP, а также сказать о том что команды типа ADD BX,1 нужно заменять на INC BX (INC увеличивает значение ровно на единицу).
подсказка для заданий: что будет если в этом примере заменить единицу в команде ADD BX,1 на числа 320, 319 и 321 ?
p.s. извиняюсь если мой коментарий не по теме.
Добавить комментарий
Имя:



smiley smiley smiley smiley smiley smiley smiley smiley smiley
Запретить смайлики в комментариях

Введите проверочный код:* =
Рейтинги
Рейтинг доступен только для пользователей.

Пожалуйста, залогиньтесь или зарегистрируйтесь для голосования.

Нет данных для оценки.
Гость
Имя

Пароль



Вы не зарегистрированны?
Нажмите здесь для регистрации.

Забыли пароль?
Запросите новый здесь.
Поделиться ссылкой
Фолловь меня в Твиттере! • Смотрите канал о путешествияхКак приготовить мидии в тайланде?
Загрузки
Новые загрузки
iChat v.7.0 Final...
iComm v.6.1 - выв...
Visual Studio 200...
CodeGear RAD Stud...
Шаблон для новост...

Случайные загрузки
XPcontrol
GamesBase 3.0
AntiRus
Синтаксический ан...
INSTANT BOOSTER v...
CoolHints2k v1.03
JBlabel3D
TmxOutlookBarPro
Socoban
mmmJlabel
mp3tag
Работа с картотеками
FormShape [Исходн...
Crystal Button
MiniTetris [Исход...
Простой текстовый...
CABfiles
Просмотр коммент...
Adapter (пример D...
Image Browser [Ис...

Топ загрузок
Приложение Клие... 100793
Delphi 7 Enterp... 98016
Converter AMR<-... 20298
GPSS World Stud... 17059
Borland C++Buil... 14238
Borland Delphi ... 10373
Turbo Pascal fo... 7390
Калькулятор [Ис... 6080
Visual Studio 2... 5228
Microsoft SQL S... 3674
Случайные статьи
Языа С: адаптеры к...
Язык С: как тестир...
Параллельная магис...
Метод onServiceCon...
Предотвратить реги...
Восстановление дан...
Сопротивление = На...
Задачи, решаемые н...
О зеркале покерной...
MARK (ОТМЕТИТЬ)
Стратегия обмена с...
Моделирование расп...
Уникальные возможн...
Структура базы данных
указатель примечание
почтовым сервером,...
• NTFS-разрешения,...
Основные понятия О...
Загрузка кода с пл...
От Русского Вулкан...
Основы передачи ин...
Введение в создани...
Максимальная нагля...
Аутсайдер в обществе
PHP и DNS. Проверк...
Статистика



Друзья сайта
Программы, игры


Полезно
В какую объединенную сеть входит классовая сеть? Суммирование маршрутов Занимают ли таблицы память маршрутизатора?