Навигация
Главная
Поиск
Форум
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
HACK F.A.Q 65535
Бип из системно... 65535
Гостевая книга ... 65535
Invision Power ... 65535
Содержание сайт... 65535
Вызов хранимых ... 65535
Программируемая... 65535
Эмулятор микроп... 65535
Подключение Mic... 65535
Создание потоко... 65535
Приложение «Про... 65535
Оператор выбора... 65535
Организация зап... 65320
Создание отчето... 61774
Модуль Forms 61654
ТЕХНОЛОГИИ ДОСТ... 58230
Пример работы с... 55853
Имитационное мо... 53598
Реклама
Сейчас на сайте
Гостей: 8
На сайте нет зарегистрированных пользователей

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

Медиа плейер на Delphi + Пояснительная записка
Меры близости на векторах в Delphi + Блок схемы
Компьютерный магазин на Turbo Pascal (База данных) + Пояснительная записка

Реклама



Подписывайся на YouTube канал о программировании, что бы не пропустить новые видео!

ПОДПИСЫВАЙСЯ на канал о программировании
Урок 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 11:53:37 · 1 Комментариев · 7017 Прочтений · Для печати

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


Комментарии
mv January 30 2010 05: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...
Шаблон для новост...

Случайные загрузки
3D Тетрис [Исходн...
Proeffectimage
Формирование отче...
База данных фильм...
Файловый менеджер
Delphi 2006 - Спр...
Crystal Button
Программирование ...
TrayComp
DateEdit
Просмотр файлов и...
Delphi 6/7 базы д...
«Философия» прогр...
Нестандартные при...
Delphi 2005 Учимс...
Berg
PHP/MySQL для нач...
netBIOS
PCX
Delphi 2005 для .NET

Топ загрузок
Приложение Клие... 100399
Delphi 7 Enterp... 84062
Converter AMR<-... 20052
GPSS World Stud... 11503
Borland C++Buil... 11306
Borland Delphi ... 8257
Turbo Pascal fo... 6994
Visual Studio 2... 4975
Калькулятор [Ис... 4501
FreeSMS v1.3.1 3517
Случайные статьи
Исследование персо...
Просмотр списка до...
Однополярное кодир...
Управляющая логика
Добавление и удале...
Сжатие и кодирован...
6.7. Формирование...
Какие из следующих...
Подкастинг: размещ...
Получение курсора ...
Реальные стандарты...
Сайты для бесплатн...
ЧТО ТАКОЕ НОРМАЛИ...
Средства отладки -...
Программирование: ...
Особенности примен...
Структура ОП должн...
Цели, стоящие пере...
Алгоритм с унарным...
— издание 381— авт...
• Модернизированны...
Office 365 уже сег...
Игровые автоматы. ...
Где происходит кон...
На протяжении всей...
Статистика



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


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