Copy Link
Add to Bookmark
Report
KolibriOS Issue 1
##############################################################################
##############################################################################
################+--------------------------------------------+################
################| Электронный журнал "Вестник КолибриОС" |################
################+-------------+--------------+-+-------------+################
##############################| 16.08.2006 |#| Выпуск #1 |################
##############################+--------------+#+-------------+################
##############################################################################
##############################################################################
==============================================================================
=== Введение =================================================================
==============================================================================
Эту первый выпуск мы, прежде всего, адресуем тем, кто начал или только
начинает делать свои первые шаги на пути освоения Ассемблера и построения
операционных систем. Если вы очень хотите нам помочь, но не знаете "как",
напишите нам или дочитайте этот выпуск до конца.
В связи с тем, что это первый выпуск электронного журнала нашего сообщества,
мы ещё не определились, как он должен выглядеть, что должно в него входить. Не
удивляйтесь некоторой его сумбурности, смотрите на это, как на пробу пера. Мы
совершенствуемся и постараемся и впредь радовать вас и себя новыми выпусками.
Если вы читаете сейчас эти строки, найдите, пожалуйста, время, чтобы написать
нам, что вы думаете об этом электронном журнале, чтобы вам хотелось видеть в
нём нового и чего бы не хотелось. Нам важно ваше мнение и ваша поддержка.
Операционная система Колибри переживает период стремительного роста и
развития. Нам нужны новые программисты и новые идеи. Конечно, на одних
программистах свет клином не сошёлся, поэтому нам также нужны люди из самых
различных областей - дизайнеры, веб-мастера, писатели, промоутеры,
железячники.
И ещё, нам нужны тестеры. Те, кто будет тестировать систему на самых различных
конфигурациях компьютеров, и присылать нам отчёты о работе. Это, пожалуй, один
из самых важных пунктов - отладка и тестирование системы. Приготовьтесь
погрузиться в увлекательный мир операционных систем и низкоуровнего
программирования, и мы начнём...
[KolibriOS`E-zine Team]
==============================================================================
=== Содержание журнала =======================================================
==============================================================================
0. Новости проекта
1. Прикладное программирование для Kolibri OS. Вводный курс. Часть 1
2. Использованию компонента checkbox
3. Немного о теории "плагинописания"
4. Модификация ядра Kolibri OS. Часть 1. Добавление новых функций в ядро
5. Модификация ядра Kolibri OS. Часть 2. Изменяем существующие функции
6. Программирование сокетов под КолибриОС. Часть 1
7. КолибриОС в лицах. Интервью с Mario79
==============================================================================
=== Новости проекта ==========================================================
==============================================================================
Автор: Veliant
*** Animage ******************************************************************
Автор: andrew_programmer
Вышла новая версия этого замечательного графического редактора. Автор
переписал декодер BMP файлов, вследствие чего он стал работать быстрее,
портировал процедуры вывода графических примитивов, сделал функции отмены
последнего действия, зеркального отражения рисунка. Этот проект идет
семимильными шагами - нынешний релиз был сделан за месяц. В ближайших планах
автора сделать редактор, ничем не уступающий всем знакомому Paint от MS. Будем
ждать новых версий Animage.
*** Screenshooter ************************************************************
Автор: Maxxxx32
Этот проект родился два месяца назад. Программа предназначена для сохранения
содержимого экрана в графический файл. На данный момент реализованы такие
функции как: автосохранение каждые n миллисекунд (n можно указать),
автонумерация файлов, автосъемка (позволяет делать анимацию), а также сделаны
прекрасные компоненты, которые может использовать каждый в своих приложениях,
мерцание этих компонентов сведено к минимуму. Сейчас автор работает над
функциями съемки произвольной части экрана и съемки активного окна.
*** Гробница Фараона *********************************************************
Автор: rabid rabbit
Красивая, интересная и затягивающая логическая игра. Эта игра - хороший пример
программирования на С++ под Колибри. Смысл игры состоит в том, чтобы,
перемещая иероглифы на входе в очередную комнату гробницы фараона, открыть
проход. Игра заканчивается, если игрок не может составить ни одной комбинации
имеющимися у него в распоряжении иероглифами. Игра оптимизирована по размерам,
но все равно она с трудом помещается на загрузочную дискету.
==============================================================================
=== Прикладное программирование для Kolibri OS. Вводный курс. Часть 1 ========
==============================================================================
Автор: Johnny_B
*** Вступление ***************************************************************
В этой рубрике мы будем постепенно учиться создавать полноценные приложения
для Колибри. Предполагается, что вы имеете начальные сведения о
программировании на Ассемблере, вам известны наиболее распространённые его
команды, вы имеете понятие о том, что такое прерывание и не понаслышке знакомы
со словом "указатель" ("метка"). Если это так, то можно приступать.
Да, и ещё, оговорюсь, возможно, кому-то, неплохо подкованному в данном
вопросе, покажется, что здесь слишком много лишних объяснений и примеров. Если
это так, можете спокойно пропускать эту статью, специально для вас в каждом
дистрибутиве системы есть файл sysfuncr.txt, можете смело открывать его и
программировать, эта же статья рассчитана на новичков, тех, кто начинает
делать первые шаги в создании прикладных программ для Колибри.
Итак, мы напишем простенькую программку "Bye World!". Если до этого вам
доводилось создавать приложения для других операционных систем, вы увидите
насколько просто создавать их для Колибри.
*** Основная часть ***********************************************************
Для начала разберём структуру программы. Она - простейшая и состоит всего из
3-х частей:
0. заголовок - указания для ОС, как правильно загрузить приложение
1. основной цикл - ожидаем и обрабатываем события
2. тело программы - реакция на события, сам код и данные
Проще может быть только формат .com-файлов ;) Итак разберём что же такое
важное нужно сообщить системе в Заголовке.
0. Структура заголовка на примере
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
db 'MENUET01' ; 1. идентификатор исполняемого файла (8 байт)
dd 0x01 ; 2. версия формата заголовка исполняемого файла
dd START ; 3. адрес, на который система передаёт управление
; после загрузки приложения в память
dd I_END ; 4. размер приложения
dd 0x100000 ; 5. объём необходимой приложению памяти
; можно обращаться к памяти в диапазоне от 0x0
; до значения, определённого здесь
dd 0x100000 ; 6. вершина стека в диапазоне памяти, указанном выше
dd 0x0 ; 7. указатель на строку с параметрами.
; если после запуска неравно нулю, приложение было
; запущено с параметрами из командной строки
dd 0x0 ; 8. указатель на строку, в которую записан путь,
; откуда запущено приложение
Итого: 36 байт (8 параметров)
Примечание. Номера параметров обозначены в связи с тем, что далее по тексту я
буду обращаться к ним по этому номеру.
1. Основной цикл
~~~~~~~~~~~~~~~~
После заголовка обычно идёт метка, указанная в параметре (3) - START, а за ней
начинается основной цикл ожидания событий.
Всего в Колибри существует 3 отслеживаемых по умолчанию (есть и другие, но об
этом позже ;) ) события:
1 - redraw - изменилось состояние окна, система просит вас, его перерисовать
2 - keypress - пользователь нажал клавишу, система просит вас на это
отреагировать
3 - buttonpress - пользователь нажал кнопку в окне, систем просит вас
отреагировать
Конечно, вовсе не обязательно, реагировать на запросы системы, никто нас не
заставляет, но подумаем, что будет, если мы не отреагируем на событие 1.
Представьте, что на экране у вас два окна - какого-то другого приложения и
вашего. Допустим, окна находятся одно под другим и на переднем плане окно
вашего приложения. Пользователь переключается на другое приложение, затем
обратно. В этот момент система просит ваше приложение перерисовать окно, а вы,
допустим, не предусмотрели реакции на это событие. Что будет? На экране
останется рисунок окна другого приложения, а ваше приложение так и будет
располагаться на заднем плане, хотя номинально, оно является активным и
находится на переднем плане, и все кнопки, кстати, будут нажиматься. И нажатие
клавиш пользователем так же будет фиксироваться системой, как относящееся к
вашему приложению. Надеюсь, суть ясна. Написал много, но это легко проверить
на практике.
На событие 2 (нажатие клавиши) можно в принципе не реагировать, если вам это
не требуется. Но помните, что все нажатые клавиши записываются во внутренний
буфер системы, и если вы не будете их оттуда считывать соответствующей
функцией, рано или поздно он переполнится. В принципе, мне этого добиться не
удавалось, но чем чёрт не шутит. Лучше не рискуйте ;)
А вот с событием 3 дело обстоит иначе. Его необходимо обрабатывать. Это
связано с тем, что кнопка закрытия окна, так же относится к обычным кнопкам на
окне и имеет свой идентификатор. Представьте, что её нажатие не будет
обрабатываться, тогда пользователь не сможет закрыть приложение (кроме как
насильно прибить процесс, конечно).
Да-а, понесло меня, похоже, не туда. Наверное, не с того конца начал. Пойдём
дальше, и надеюсь, что всё будет ясно.
Забыл сказать, что интерфейс взаимодействия с системой (API) в Колибри
является низкоуровневым и построен на прерываниях. Параметры системной функции
передаются через регистры (обычно, ebx,ecx,edx,esi,edi), номер функции в eax и
сам системный вызов - это прерывание 0x40. Впрочем, может это было лишнее,
дальше по тексту это становится очевидным ;)
Основной цикл выглядит так:
START: ; адрес начала программы
call draw_window ; вызываем функцию рисования окна
; затем переходим в цикл ожидания событий
event_wait:
mov eax,10 ; функция 10: ожидание события
int 0x40
; тип события возвращён в eax, далее проверяем, какое событие произошло
cmp eax,1 ; запрос на перерисовку?
je redraw
cmp eax,2 ; нажата клавиша?
je key
cmp eax,3 ; нажата кнопка в окне программы?
je button
jmp event_wait ; возвращаемся к началу цикла ожидания событий
; после того, как событие идентифицировано, его надо обработать
redraw: ; пришёл запрос на перерисовку!
call draw_window ; вызываем функцию draw_window и
jmp event_wait ; возвращаемся назад к циклу ожидания
key: ; была нажата клавиша!
mov eax,2 ; считываем код нажатой клавиши. Возвращен в ah.
int 0x40 ; Клавиша должна быть прочитана для очистки
; системного буфера.
jmp event_wait ; возврат к event_wait
button: ; была нажата кнопка в окне!
mov eax,17 ; считываем идентификатор нажатой кнопки
int 0x40 ; возвращен в ah.
; смотрим, какая кнопка была нажата и соответствующим образом реагируем.
cmp ah,1 ; кнопка с id=1("закрыть")?
jne noclose
mov eax,-1 ; функция -1: завершить программу
int 0x40
noclose:
; здесь проверяем остальные кнопки (если они есть)
jmp event_wait ; и, конечно, возвращаемся к циклу ожидания :)
Пояснение. Общепринято, что кнопка с идентификатором (ID) = 1, это кнопка
закрытия окна.
2. Тело программы
~~~~~~~~~~~~~~~~~
Далее следует основная часть приложения, где обрабатываются конкретные нажатые
кнопки и клавиши, а также присутствует функция отрисовки окна, и выполняется
полезная нагрузка. Из всего перечисленного, мы будем лишь рисовать окно с
надписью и кнопкой выхода. На первый раз, думаю, достаточно.
Вот мы и добрались до самого интересного :)
; функция отрисовки окна
draw_window:
mov eax,12 ; функция 12: сообщить системе о
; состоянии перерисовки окна
mov ebx,1 ; подфункция 1, начало перерисовки
int 0x40
; ОКНО
mov eax,0 ; функция 0: определите и выведите окно
mov ebx,100*65536+300 ; [x начальный] *65536 + [x размер]
mov ecx,100*65536+120 ; [y начальный] *65536 + [y размер]
mov edx,0x02ffffff ; цвет рабочей области RRGGBB
; 0x02000000 = тип окна 2 (что это
; значит, я объясню в следующий раз)
mov esi,0x808899ff ; цвет области заголовка RRGGBB
; 0x80000000 = цвет перетекает
mov edi,0x008899ff ; цвет рамки RRGGBB
int 0x40
; НАДПИСЬ в заголовке
mov eax,4 ; функция 4: написать текст в окне
mov ebx,8*65536+8 ; [x начальный] *65536 + [y начальный]
mov ecx,0x00ddeeff ; цвет текста RRGGBB
; старший байт (0x00) - размер шрифта
; (маленький)
mov edx,text ; указатель на начало текста
mov esi,textend-text ; длина текста в байтах
int 0x40
; НАДПИСЬ в центре окна
mov eax,4 ; функция 4: написать текст в окне
mov ebx,120*65536+60 ; [x начальный] *65536 + [y начальный]
mov ecx,0x102222ff ; цвет текста RRGGBB
; старший байт (0x10) - размер шрифта
; (большой)
mov edx,text ; указатель на начало текста
mov esi,textend-text ; длина текста в байтах
int 0x40
; КНОПКА
mov eax,8 ; функция 8: определить и вывести кнопку
mov ebx,(300-19)*65536+12 ; [x начальный] *65536 + [x размер]
mov ecx,5*65536+12 ; [y начальный] *65536 + [y размер]
mov edx,1 ; идентификатор кнопки
mov esi,0x6677cc ; цвет кнопки RRGGBB
int 0x40
mov eax,12 ; функция 12: сообщить системе о
; состоянии перерисовки окна
mov ebx,2 ; подфункция 2, перерисовка окончена
int 0x40
ret ; конец функции
; ДАННЫЕ ПРОГРАММЫ
text db 'Bye World!'
textend: ; в следующий раз мы освоим вывод ASCIIZ-строк, а пока что
; используем этот дедовский метод ;-)
I_END: ; необходимо, чтобы правильно указать системе размер приложения
Полный код программы размещён в главе "Приложение", в самом конце файла.
Скопируйте его в отдельный файл, скомпилируйте и запускайте, потом издевайтесь
над кодом, чтобы разобраться, что там к чему ;)
Если вы не знаете, как запихнуть его в Колибри, можете пока что компилировать
и ставить эксперименты в fasm'е for Windows и запускать под эмулятором
(MeOSEmul by mike.dld или KlbrInWin by diamond). Но уверен, что вам это вряд
ли понадобится.
*** Заключение ***************************************************************
В заключении хочу поблагодарить всех тех людей, кто помог мне написать эту
статью(mistifi(ator,Victor,Lrz,mike.dld), указал на ошибки и просто
поддерживал морально, а также вас, дорогие читатели, без вас этой статьи бы
точно не было ;)
За основу взят документ "Программирование в MenuetOS" (привет, Mario79 ;) и
sysfuncr.txt
С вами был я, Джонни, удачного всем дня. До новых встреч...
P.S. Да, и чуть не забыл. Если у вас возникли вопросы, не стесняйтесь
задавать, мы обязательно ответим.
*** Приложение ***************************************************************
--- начало bye.asm ---------------------------------------------------- >8 ---
use32 ; транслятор, использующий 32-х разрядные команды
org 0x0 ; базовый адрес кода, всегда 0x0
db 'MENUET01' ; 1. идентификатор исполняемого файла (8 байт)
dd 0x01 ; 2. версия формата заголовка исполняемого файла
dd START ; 3. адрес, на который система передаёт управление
; после загрузки приложения в память
dd I_END ; 4. размер приложения
dd 0x100000 ; 5. объём необходимой приложению памяти
; можно обращаться к памяти в диапазоне от 0x0
; до значения, определённого здесь
dd 0x100000 ; 6. вершина стека в диапазоне памяти, указанном выше
dd 0x0 ; 7. указатель на строку с параметрами.
; если после запуска неравно нулю, приложение было
; запущено с параметрами из командной строки
dd 0x0 ; 8. указатель на строку, в которую записан путь,
; откуда запущено приложение
START: ; адрес начала программы
call draw_window ; вызываем функцию рисования окна
; затем переходим в цикл ожидания событий
event_wait:
mov eax,10 ; функция 10: ожидание события
int 0x40
; тип события возвращён в eax, далее проверяем, какое событие произошло
cmp eax,1 ; запрос на перерисовку?
je redraw
cmp eax,2 ; нажата клавиша?
je key
cmp eax,3 ; нажата кнопка в окне программы?
je button
jmp event_wait ; возвращаемся к началу цикла ожидания событий
; после того, как событие идентифицировано, его надо обработать
redraw:
call draw_window ; вызываем функцию draw_window и
jmp event_wait ; возвращаемся назад к циклу ожидания
key:
mov eax,2 ; считываем код нажатой клавиши. Возвращен в ah.
int 0x40 ; Клавиша должна быть прочитана для очистки
; системной очереди.
jmp event_wait ; возврат к event_wait
button:
mov eax,17 ; считываем идентификатор нажатой кнопки
int 0x40 ; возвращен ah.
; смотрим, какая кнопка была нажата и соответствующим образом реагируем.
cmp ah,1 ; кнопка с id=1("закрыть")?
jne noclose
mov eax,-1 ; функция -1: завершить программу
int 0x40
noclose:
; здесь проверяем остальные кнопки
jmp event_wait ; и, конечно, возвращаемся к циклу ожидания :)
; функция отрисовки окна
draw_window:
mov eax,12 ; функция 12: сообщить системе о
; состоянии перерисовки окна
mov ebx,1 ; подфункция 1, начало перерисовки
int 0x40
; ОКНО
mov eax,0 ; функция 0: определить и вывести окно
mov ebx,100*65536+300 ; [x начальный] *65536 + [x размер]
mov ecx,100*65536+120 ; [y начальный] *65536 + [y размер]
mov edx,0x02ffffff ; цвет рабочей области RRGGBB
; 0x02000000 = тип окна 2
mov esi,0x808899ff ; цвет области заголовка RRGGBB
; 0x80000000 = цвет перетекает
mov edi,0x008899ff ; цвет рамки RRGGBB
int 0x40
; НАДПИСЬ в заголовке
mov eax,4 ; функция 4: написать текст в окне
mov ebx,8*65536+8 ; [x начальный] *65536 + [y начальный]
mov ecx,0x00ddeeff ; цвет текста RRGGBB
; старший байт (0x00) - размер шрифта
; (маленький)
mov edx,text ; указатель на начало текста
mov esi,textend-text ; длина текста в байтах
int 0x40
; НАДПИСЬ в центре окна
mov eax,4 ; функция 4: написать текст в окне
mov ebx,120*65536+60 ; [x начальный] *65536 + [y начальный]
mov ecx,0x102222ff ; цвет текста RRGGBB
; старший байт (0x10) - размер шрифта
; (большой)
mov edx,text ; указатель на начало текста
mov esi,textend-text ; длина текста в байтах
int 0x40
; КНОПКА
mov eax,8 ; функция 8: определить и вывести кнопку
mov ebx,(300-19)*65536+12 ; [x начальный] *65536 + [x размер]
mov ecx,5*65536+12 ; [y начальный] *65536 + [y размер]
mov edx,1 ; идентификатор кнопки
mov esi,0x6677cc ; цвет кнопки RRGGBB
int 0x40
mov eax,12 ; функция 12: сообщить системе о
; состоянии перерисовки окна
mov ebx,2 ; подфункция 2, перерисовка окончена
int 0x40
ret ; конец функции
; ДАННЫЕ ПРОГРАММЫ
text db 'Bye World!'
textend: ; в следующий раз мы освоим вывод ASCIIZ-строк, а пока что
; используем этот дедовский метод ;-)
I_END: ; необходимо, чтобы правильно указать системе размер приложения
--- конец bye.asm ----------------------------------------------------- >8 ---
==============================================================================
=== Использование компонента checkbox ========================================
==============================================================================
Автор: Lrz (Теплов Алексей)
Сегодня я расскажу, как написать компонент checkbox, который в дальнейшем мы
будем использовать для своих нужд.
Давайте, определим, как будет выглядеть наш компонент. Он представляет собой
квадрат, слева от которого надпись. Что-то на подобии этого:
---------------
[] Checkbox
---------------
Т.е объект checkbox состоит из прямоугольника и надписи. Рассмотрим, как можно
нарисовать прямоугольник и надпись.
Прямоугольник состоит из линий, и его можно нарисовать, используя 4 линии
функцией 38. Надпись можно вывести с помощью функции 4.
Итак, рисуем прямоугольник линиями, а надпись выводим обычно при помощи
функции 4 на канву приложения. Очень важно правильно обработать события мышки,
наш компонент будет переходить из одного состояния в другое, при нажатии на
области компонента клавишей мышки (любой или можно всеми двумя). Итак,
алгоритм проверки:
0. Нажата ли кнопка мышки? Да то пункт 1, иначе выйти из обработчика мышки.
1. Получим координаты курсора мышки и сравним с координатами всех наших чек
боксов. Да - пункт 2. Иначе выйти из обработчика мышки.
2. Начинаем сравнивать положения мышки с координатами чек боксов. Если
произошло совпадение, то установить флаг, чек бокс стал активен, затем
его нарисовать. После этого нет никакого смысла проверять остальные чек
боксы при этих координатах мышки. По этому, просто необходимо
прекратить текущую проверку.
Рассмотрим детально код приложения и разберем его:
--- начало Листинг #1 ------------------------------------------------- >8 ---
;Эффективное программирование в KOLIBRI
;Оптимизированный компонент CheckBox (Исходный вариант от Maxxxx32)
;Оптимизирован вывод строки, надписи для CheckBox'a + теперь при проверке не
; происходит подсчет кол-ва символов в строке
;Оптимизация команд.
;<Lrz> - Теплов Алексей www.test-kolibri.narod.ru
;заголовок приложения
use32 ; транслятор, использующий 32 разрядных команды
org 0x0 ; базовый адрес кода, всегда 0x0
db 'MENUET01' ; идентификатор 8 байтов для прикладной программы
dd 0x1 ; версия верхнего колонтитула
dd start ; начало выполнения, указывается точка входа в программу
dd i_end ; размер образа
dd i_end+0x100 ; Объем используемой памяти, для стека отведем 0х100 байт
dd i_end+0x100 ; расположим позицию стека в области памяти
dd 0x0,0x0 ; значение передачи параметров; зарезервировано для иконки
;------------------
include 'check.inc ; включить файл check.inc
use_check_box ; используя макросы внести процедуры для рисования чек
; бокса
;Область кода
start: ; Точка входа в программу
red_win:
call draw_window ; первоначально необходимо нарисовать окно
still: ; основной обработчик
mov eax,23 ; Ожидать события в течениt указанного времени
xor ebx,ebx ; обнулить регистр ebx
add ebx,2 ; добавить в регистр ebx значение =2
int 0x40 ; ожидать событие в течение 2 миллисекунд
cmp eax,0x1 ; если изменилось положение окна
jz red_win
cmp eax,0x2 ; если нажата клавиша то перейти
jz key
cmp eax,0x3 ; если нажата кнопка то перейти
jz button
mouse_check_boxes check_boxes,check_boxes_end ; проверка чек бокса
jmp still ; если ничего из перечисленного то снова в цикл
button:
mov eax,17 ; получить идентификатор нажатой клавиши
int 0x40
test ah,ah ; если в ah 0, то перейти на обработчик событий
jz still
or eax,-1 ; выйти: в eax,-1 - 5 байтов, у нас же только 3
int 0x40 ; далее выполняется выход из программы
key: ; обработка нажатия клавиши
xor eax,eax ; обнулить eax
add eax,2 ; добавь к eax 2
int 0x40
jmp still ; перейти на обработчик событий still
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
draw_window: ; рисование окна приложения
xor eax,eax ; начало рисования обнулить регистр eax
add eax,12 ; добавить в регистр значение eax 12
xor ebx,ebx ; обнулить регистр ebx
add ebx,1 ; быстрее на скалярных процессорах прибавить 1
xor eax,eax ; обнулить eax
mov ebx,50*65536+180 ; [координата по x]*65536 + [размер по x]
mov ecx,30*65536+100 ; [координата по y]*65536 + [размер по y]
mov edx,0x03AABBCC ; 0xXYRRGGBB
mov esi,0x805080DD ; 0xXYRRGGBB - цвет заголовка
mov edi,0x005080DD ; 0x00RRGGBB - цвет рамки
int 0x40 ; нарисовать окно приложения
shr eax,1 ; то же но сдвиги быстрее т.е. получается 8
; разделить на 2 (на 386 процессорах) Функция
; 4 - вывести текст в окно
; mov eax,4 ; если предполагается использовать скалярный
; процессор, то можно использовать данню
; инструкцию
mov ebx,8*65536+8 ; [координата по x]*65536 + [координата по y]
mov ecx,0x10DDEEFF ; 0xX0RRGGBB, где RR, GG, BB задают цвет
; текста
mov edx,hed ; указатель на начало строки
mov esi,i_end - hed ; длина строки, должна быть не больше 255
int 0x40 ; вывести текст
draw_check_boxes check_boxes,check_boxes_end ;рисование чекбоксов
xor eax,eax ; Функция 12 - начать/закончить перерисовку
add eax,12 ; окна
xor ebx,ebx ; обнулить регистр
add ebx,2 ; Подфункция 2 - закончить перерисовку окна
int 0x40
ret
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;DATA данные
;Формат данных чек бокса:
;10 - координата чек бокса по х
;30 - координата чек бокса по у
;0xffffff - цвет внутри чек бокса
;0 - цвет рамки чек бокса
;0 - цвет текста надписи
;ch_text.1 - указатель на начало строки
;ch_text.e1-ch_text.1 - длина строки
;
check_boxes:
ch1 check_box 10,30,0xffffff,0,0,ch_text.1,ch_text.e1-ch_text.1
ch2 check_box 10,45,0xffffff,0,0,ch_text.2,ch_text.e2-ch_text.2
ch3 check_box 10,60,0xffffff,0,0,ch_text.3,ch_text.e3-ch_text.3
check_boxes_end:
ch_text: ; Сопровождающий текст для чекбоксов
.1 db 'Check_Box #1'
.e1:
.2 db 'Check_Box #2'
.e2:
.3 db 'Check_Box #3'
.e3:
hed db 'Checkbox' ; заголовок приложения
i_end: ; конец кода
--- конец Листинг #1 -------------------------------------------------- >8 ---
Данный исходный код хорошо иллюстрирует методику внедрения чек бокса в
приложение.
Основные процедуры вывода и обработки расположены в начале приложения. Точка
входа определена ниже (Start). Между заголовком и точкой входа располагается
код чек бокса. Преимуществом данного кода является то, что один код
используется для обработки всех чек боксов в программе.
Рассмотрим более детально, как происходит обработка и вывод чекбоксов:
--- начало Листинг #2 ------------------------------------------------- >8 ---
;Эффективное программирование в KOLIBRI
;Оптимизированный компонент CheckBox (Исходный вариант от Maxxxx32)
;Оптимизирован вывод строки надписи для CheckBox'a + теперь, при проверке не
; происходит подсчет кол-ва символов в строке
;Оптимизация команд - отказался от сохранения регистров в стеке.
;17.07.2006 произведена оптимизация, при установке чек бокса в положение
; включено последующие чек боксы в тот же промежуток времени не проверяются
;<Lrz> - Теплов Алексей www.test-kolibri.narod.ru
macro use_check_box ;Структура чек бокса
{
ch_text_margin=4 ; расстояние от прямоугольника чек бокса до
; надписи
ch_size=10 ; размер квадрата чек бокса
ch_left equ [edi] ; координата начала рисования по х
ch_top equ [edi+2] ; координата начала рисования по у
ch_color equ [edi+4] ; цвет внутри checkbox
ch_border_color equ [edi+8] ; цвет рамки checkbox
ch_text_color equ [edi+12] ; цвет текста
ch_text_ptr equ [edi+16] ; указатель на начало текстовой строки
ch_text_length equ [edi+20] ; длина надписи (2^64 такой длины может быть
; текст)
ch_flags equ [edi+22] ; флаги
check_box:
.draw: ; рисование чек боксов
pusha ; сохраним все регистры
mov eax,38 ; рисование линии
mov bx,ch_left ; положение по х
mov cx,bx ; сохраним в регистре cx значение bx
; 1 микрооперация
; push bx ; 3 микрооперации используя стек можно
; выиграть в размере, используя регистры - в
; скорости
shl ebx,16 ; сдвинем на 16 разрядов влево (умножим на
; 65536)
; pop bx ; 2 микрооперации на данный момент
; сформирована [координата начала по x]*65536 +
; [координата начала по x]
mov bx,cx ; восстановим значение bx
mov cx,ch_top ; загрузим в cx значение y
mov si,cx ; сохраним значение регистра cx в регистр
; указатель si
; push cx
shl ecx,16 ; сдвинем на 16 разрядов влево (умножим на
; 65536)
mov cx,si ; восстановим значение регистра cx
mov cx,si ; восстановим значение регистра cx
; pop cx ; [координата начала по y]*65536 + [координата
; начала по y]
; push cx
add cx,ch_size ; [координата начала по y]*65536 + [координата
; конца по оси y]
mov edx,ch_border_color ; Цвет линии
int 0x40 ; рисование вертикальной левой линии квадрата
; (прямоугольника)
mov bp,bx ; сохраним регистр bx в регистре указателя базы
; push bx ; втолкнуть в bx [координата начала по х]*65536
; + [координата начала по x]
add bx,ch_size ; [координата начала + длина стороны по х]
ror ebx,16 ; [координата начала + дина стороны по х]*65536
add bx,ch_size ; [координата начала + длина стороны по х]*65536
; + [координата начала + длина стороны по x]
int 0x40
mov bx,bp ; восстановим значение регистра bx
; pop bx
mov cx,si ; сохраним значение регистра cx в регистр
; указатель
; pop cx
int 0x40
add cx,ch_size ; добавим размер стороны
mov si,cx ; сохраним значение регистра cx в регистр
; указатель si
; push cx
shl ecx,16
mov cx,si
; pop cx
int 0x40 ; нарисовали прямоугольник
mov eax,13 ; закрашиваем его. Функция 13 - нарисовать
; полосу
mov bx,ch_left ; загрузить в bx, положение по х
add bx,1 ; сдвинем на 1, т.е. прибавим 1, иначе затрется
; рамка
shl ebx,16 ; сдвинем на 16 разрядов в лево (умножим на
; 65536)
mov bx,ch_size ; прибавим длину стороны прямоугольника
sub bx,1 ; вычтем 1 т.к. иначе затрется рамка
mov bp,bx ; сохраним регистр bx в регистре указателя базы
; push bx
mov cx,ch_top ; загрузим координаты по y
add cx,1 ; сдвинем на 1, т.е. прибавим 1, иначе
; затрется рамка
shl ecx,16 ; сдвинем на 16 разрядов влево (умножим на
; 65536)
mov cx,bp ; восстановим значение регистра cx
; pop cx
mov edx,ch_color ; загрузим цвет полосы
int 0x40 ; закрасили
bt dword ch_flags,1 ; достать значение бита из переменной и
; поместить в флаг CF
jnc @f ; в если CF=1, то выполним следующую процедуру
; иначе перейти на нижнюю @@
call .draw_ch ; нарисовать включенный чек бокс
@@:
;------------------------------------------
; расчет куда будет произведен вывод текста
;------------------------------------------
mov bx,ch_left ; загрузить значение х для чек бокса
add bx,(ch_size+ch_text_margin) ; добавить размер стороны и
; расстояние на котором начнется
; вывод текста
shl ebx,16 ; сдвинем на 16 разрядов в лево (умножим на
; 65536)
mov bx,ch_top ; загрузим значение по y
add bx,(ch_size-9+2) ; добавим значение длины стороны -9+2
mov ecx,ch_text_color ; загрузим цвет надписи
mov edx,ch_text_ptr ; укажем адрес, откуда нужно выводить строку
movzx esi,word ch_text_length ; Загрузим длину надписи в esi
xor eax,eax ; внесем в eax значение вывода надписи на
; канву
add eax,4
int 0x40 ; Вывод
popa ; восстановить значения регистров из стека
ret ; выйдем из процедуры
.clear_ch: ; очистка чек бокса
mov edx,ch_color ; цвет внутри чек бокса
jmp @f ; безусловный прыжок на нижнюю метку @@
.draw_ch: ; нарисовать включенный чек бокс
mov edx,ch_border_color ; загрузить цвет
@@:
mov bx,ch_left ; загрузить координату по х
add bx,(ch_size/3) ; добавить (сторона прямоугольника/3)
shl ebx,16 ; сдвинем на 16 разрядов в лево (умножим на
; 65536)
mov bx,(ch_size/2) ; загрузить (сторона прямоугольника/2)
mov bp,bx ; сохраним регистр bx в регистре указателя
; базы
; push bx
mov cx,ch_top ; загрузить координату по у
add cx,(ch_size/3) ; добавить (сторона прямоугольника/3)
shl ecx,16 ; сдвинем на 16 разрядов в лево (умножим на
; 65536)
mov cx,bp ; загрузим значения регистра указателя базы
; в cx
; pop cx
mov eax,13 ; в eax - значения функции для вывода
; полосы, т.е. по сути прямоугольника,
; который отображает включенный компонент
; чек бокс
int 0x40 ; вывод
ret ; выйти из процедуры
.mouse: ; обработка мыши
pusha
mov eax,37 ; будем что-то делать, если у нас что-нить
; нажато
xor ebx,ebx ; обнулить регистр ebx (изменяет флаги)
add ebx,2 ; внести в регистр значение 2
int 0x40 ; проверка, не нажал ли пользователь кнопку
; мышки
test eax,eax ; проверка если у нас в eax=0, то установим
; флаг и выйдем
jnz @f ; перейти на нижнюю метку @@
btr dword ch_flags,2 ; извлечение значения заданного бита в флаг
; cf и изменение его значения на нулевое.
popa ; если ничего не произошло, то восстановим
; значения регистров из стека
ret ; выход
@@:
bts dword ch_flags,2 ; проверка флага т.е. перенос в cf значение
; бита и установка бита в состояние включено
jc .mouse_end ; если CF=1 то перейти в конец т.е. это
; выход
movzx esi,word ch_text_length ; загрузить кол-во символов в
; текстовой строке
; Умножение на 6. Быстрое умножение, можно воспользоваться любым мз методов,
; но на старых Процессорах (386,486,P1)быстрее будет с инструкцией Lea
; lea esi,[eax*2+eax]
; shl eax,1
imul esi,6 ; или можно и так умножить на 6
add esi,ch_text_margin ; добавить 3 - расстояние от чек бокса до
; надписи
mov eax,37 ; получим координаты мышки
xor ebx,ebx ; обнулить регистр
add ebx,1 ; добавить 1
int 0x40 ; получить координаты курсора относительно
; окна
mov bx,ch_top ; загрузить в bx значение координаты у
cmp ax,bx ; сравнить с с координатой курсора
jl .mouse_end ; SF <> OF если меньше
add bx,ch_size ; добавить размер
cmp ax,bx ; сравнить
jg .mouse_end ; ZF = 0 и SF = OF если больше
shr eax,16 ; разделим на 65536 или просто сдвинем биты
; на 16 значений
mov bx,ch_left ; произведем аналогичное сравнение
cmp ax,bx ; сравнить регистры
jl .mouse_end ; если меньше
add bx,ch_size ; добавить длину стороны прямоугольника
add bx,si ; Учесть в значении по х еще и длину надписи
; к чек боксу
cmp ax,bx ; стравнить регистры
jg .mouse_end ; если больше
bts dword ch_flags,1 ; извлечение значения заданного бита в флаг
; cf и изменение его значения на 1.
jc @f ; CF=1 то перейти на нижнюю @@
call .draw_ch ; отобразить включенный чек бокс
mov dword [esp+24],1 ; дальнейшая проверка чек боксов бесмыслена,
; по этому в стек, где располагается ecx
; поместим 0
jmp .mouse_end ; выйти
@@:
btr dword ch_flags,1 ; извлечение значения заданного бита в флаг
; cf и изменение его значения на нулевое.
call .clear_ch ; выключить чек бокс т.е. на месте
; закрашенного прямоугольника отобразить
; цвет фона.
.mouse_end:
popa ; восстановить регистры из стека
ret ; выйти
}
struc check_box left,top,color,border_color,text_color,text,text_length,flags
{ ;структура параметров для чек бокса
.left: dw left ; +0 положение по х
.top: dw top ; +2 положение по у
.color: dd color ; +4 цвет внутри чек бокса
.border_color: dd border_color ; +8 цвет рамки
.text_color: dd text_color ; +12 цвет текста надписи
.text: dd text ; +16 адрес в коде программы где расположен
; текст
.text_length: dw text_length ; +20 длина текста
.flags: dd flags+0 ; +22 флаги
}
ch_struc_size = 26 ; общая структура 26 байт
macro draw_check_boxes start,end ; рисовать чек боксы
{
mov edi,start ; Указатель на начало данных
; чек боксов, т.е. на начало
; данных первого чекбокса
mov ecx,((end-start)/ch_struc_size) ; Количество чек боксов
@@:
call check_box.draw ; Отобразить чек бокс
add edi,ch_struc_size ; Указатель на последующие чек
; боксы т.е. +28
loop @b ; прыгнуть если в ecx/cx
: значение не 0 на верхнюю @@
}
macro mouse_check_boxes start,end ; установка чек боксов, в зависимости от
; события
{
mov edi,start ; Указатель на начало данных
; чек боксов, т.е. на начало
; данных первого чекбокса
mov ecx,((end-start)/ch_struc_size) ; Количество чек боксов
@@:
call check_box.mouse ; проверка мышки и обработка
; событий
add edi,ch_struc_size ; Указатель на последующие чек
; боксы
loop @b ; прыгнуть если в ecx/cx
; значение не 0 на верхнюю @@
}
--- конец Листинг #2 -------------------------------------------------- >8 ---
==============================================================================
=== Hемного о теории "плагинописания" ========================================
==============================================================================
Автор: Victor
Здравствуйте, дорогие телезрители, телечитатели и телеслушатели. Это моя
первая статья (я бы даже сказал статейка), и возможно последняя. Это зависит
от вас, от моих читателей (если такие имеются)...
... переходя к делу:
Раз вы читаете эту статью, то наверняка вы уже знаете о такой маленькой, но
очень... Ну, в общем, я уверен, о Колибри ещё услышат... Это конечно зависит и
от нас. Каждый с чего то начинает, и я вот решил начать сразу с полезного. Во
всех операционных системах (осях) есть возможность записать свои программы в
виде библиотек, которые потом можно использовать в других программах. В
Колибри как ни странно такой возможности нет. Вот я и решил компенсировать
этот недостаток. Результаты пока не впечатляет, но я не сдаюсь...
Вкратце расскажу об основных трудностях, которые возникли у меня на пути.
Библиотеки надо как-то вызывать. Один из способов - загрузить файл библиотеки
в своё адресное пространство (АП, у каждой программы своя виртуальная память,
но нам это знать не надо) и там уже с ним разбираться. Опять же есть могут
быть разные варианты загрузки. В моей реализации предполагается что программа,
пользуясь системным сервисом (int 0x40, функция 70, подробнее в документации
от Diamond-а) программа загружается по заданному адресу. Что бы сделать
механизм более универсальным, нам не нужен этот заданный адрес. Нужен какой-то
механизм динамического распределения памяти, или как его называют менеджер
памяти (МП, в ЯВУ аналог alloc, free...). Так как в этой области я не силён, а
на всё сил сразу не хватит, решил в качестве такового взять МП от Халявина.
Это позволяет просто спросить у МП, нет ли свободного куска нужного размера.
Если он есть, то в ответ получаем прописку в найденном куске и грузим туда
свою библиотеку. Но просто загрузить обычно недостаточно... Разработчик, когда
пишет свою библиотеку, даже не догадывается куда (или по какому адресу) будет
загружена эта библиотека. Рассмотрим небольшой пример.
org 0 ; указывает на то что программа начинается с этого адреса
A db 1
B dd 0xABCDEF01 ; какие то произвольные "переменные"
movzx eax,byte [A]
mov [B],eax
...
После компиляции этого куска кода не останется никаких переменных А и В,
процессору проще работать с цифрами. Поэтому предполагается что А у нас будет
по адресу 0х0000, а В по адресу 0х0001. С учетом этого данный кусок кода
можно переписать так:
db 1
dd 0xABCDEF01
movzx eax,byte [0x0000]
mov [0x0001],eax
Этот код полностью аналогичен, за исключением того, что он стал менее гибким.
Теперь чтобы приспособить эту программу к другому адресу (другой прописке)
необходимо вручную править все адреса. В большинстве ОСей для этих целей
применяется информация о настройке ссылок, или в простонародий релоки. Они
генерируются компиляторами в случае необходимости (зависит от формата
выходного файла...). Что бы всё работало необходимо вычислить разницу между
адресами (тем по которому предполагалось грузить, и тем по которому фактически
загружено) и вычесть из каждой ссылки это число. Т.е. если вышеприведённый код
будет загружен по адресу 0х0100, то А будет по адресу 0х0100, а В - 0х0101,
искомая разница 0х0000 - 0х0100 = -0х01000, и из каждой ссылки мы вычитаем это
число. Фактически переменные А и В - это и есть ссылки (адреса, указатели...).
Называть их можно по-разному, но суть то одна? С учетом сказанного получаем:
0х0100: db 1
dd 0xABCDEF01
movzx eax,byte [0x0000+0x01000]
mov [0x0001+0x01000],eax
и код опять полностью функционален.
Есть и другой способ, применяемый, как правило, в каких-нибудь вирусах. Вся
программа строится на относительной адресации. Например, предполагается, что в
регистре EBX находится адрес, по которому загружена программа. Операции уже
проводятся не напрямую с переменными (адресами). Например, mov eax,[EBX+A] уже
на так привязан к конкретному адресу. Правда, эта палка о двух концах. С одной
стороны экономим на релоках (они здесь не нужны), с другой - такая операции
занимает больше байт.
Некоторые возможно зададутся вопросом, как вычислить этот адрес. Вот один из
способов который известен мне:
E800000000 call a
5B83 a: pop ebx
EB05 sub ebx,5
Листинг приведён сразу с машинными кодами. Как вы могли заметить, инструкция
call вызывается на адрес 0. Но это не значит, что программа передаст
управление в самое начало. Есть несколько разновидностей инструкции call. Она
может ссылаться как на абсолютный адрес, так и на относительный, но логически
их можно назвать одним общим именем. В данном случае call ссылается на
относительный адрес 0, т.е. на инструкцию, следующую сразу следом. При этом в
стеке сохранится адрес инструкции, следующей за "call a", т.е. тот же адрес,
на который мы перешли. Следующим шагом мы этот адрес выталкиваем в EBX. Если
считать что инструкция "call a" - начало нашего кода, то, вычислив адрес это
инструкции, мы решим поставленную задачу (найти адрес, по которому программа
загружена). В 32-ух битном режиме инструкция "call a" занимает 5 байт, поэтому
далее мы вычитаем из EBX 5.
Из приведённых выше способов я выбрал традиционный, т.е. релоки. Но опять же,
загрузив в АП библиотеку и применив релоки, мы не можем вызывать функции из
библиотеки, т.к. как мы не можем знать адрес этой функции. Можем только
предположить, что она находится в этом куске памяти... где мы и "прописали"
библиотеку.
Вот здесь на арену и выступают разные форматы представления библиотек. В
виндовс формат называется PE. Он применяется как для обычных программ, так для
библиотек. Отличия в них несущественны, различен процесс загрузки, т.е. как их
грузит кто-то третий - загрузчик. Здесь наверно и скрыто больше всего
"подводных камней" и путей вариантов оптимизации. Загрузчик, как правило, сам
загружает в АП библиотеку, сам находит релоки (если кто не понял, релоки - это
по сути адреса тех мест, значение которых надо подправить на "разницу"),
применяет их к ссылкам, устанавливает связи между функциями библиотеки и
программы.
Эти "связи" можно описывать по-разному. В упрощённом варианте есть две
таблички формата ИМЯ - АДРЕС. Одна в основной программе (таблица импорта),
другая в библиотеке(таблица экспорта). Загрузчик при загрузке библиотеки
подменяет адреса из таблицы импорта на адреса из таблицы экспорта. Позиции
функций в этих таблицах могут быть разными, поэтому заменяются адреса с
одинаковым именем.
При дальнейшем рассмотрении возникают другие проблемы. Как различать
одноимённые функции из разных библиотек, и как вообще различать разные
библиотеки? Для этих целей можно сделать таблицу, в которой хранится
информация о библиотеках, которые уже загружены. Процесс работы с такими
библиотека уже напоминает такой же в виндовс. Для определения адреса функции
можно вручную загрузить библиотеку функцией loadlybrary, и по адресу, который
она вернёт определить адрес нужных функций с помощью getProcAddress (имена
функций отличаются от имён используемых в виндовс... давно это было... :).
Осталось только решить что из себя представляет загрузчик. в простейшем
варианте (который я и выбрал), загрузчик - часть базовой программы,
подключаемая с помощью обычного инклюда. Конечно, при более детальном
рассмотрении можно понять, что загрузчик, расположенный в ядре, сулит больше
преимуществ. Здесь перед нами открывается кэширование (библиотека загружается
в память с диска один раз, при последующих запросах от программ им выдаётся
лишь копия загруженной) и разделяемое использование библиотек (несколько
программ могут использовать одну копию памяти библиотеки, при этом можно
достигаться существенная экономия памяти, но на такие библиотеки могут
накладываться существенные ограничения). Для реализации такого варианта
загрузчика надо хотя бы разбираться в ядре Колибри, с чем у меня пока
проблемы...
За сим спешу откланяться. Если вам моя статья не понравилась, можете не писать
мне гневных писем, а удалить эту статью прямо в корзинку (да, да ... именно
туда, может вы ещё передумаете ;) Конструктивная критика всегда
приветствуется. Свою реализацию КДЛ (динамические библиотеки колибри,
(r)...(c)...tm... все права мои ;) я делаю по мере возможности (которой все
меньше и меньше :/). Если вам захочется посмотреть на неё подробнее то
последнюю версию вы можете найти на victor.kolibrios.org . Пока очень скромно,
но мы над этим работаем :)
Так же, насколько мне известно, подобными делами промышляют Willow и Serge. О
их успехах вы можете узнать у них :Р
PS: забыл отметить, что при написании своего формата могут возникнуть проблемы
с релоками... Свой формат - он в Африке совсем не свой. Поэтому можно со 100%
уверенностью сказать, что его не будут сразу поддерживать хоть какие-нибудь
компиляторы. Для этого я решил сделать конвертер coff2kdl. Подробности опять
же на сайте в ближайшее, но неопределённое время...
==============================================================================
=== Модификация ядра Kolibri OS. Часть 1. Добавление новых функций в ядро ====
==============================================================================
Автор: Hex
*** Инсайд *******************************************************************
Процесс написания новых функций для ядра KolibriOS настолько прост, что для
этого даже не обязательно знать и понимать принципы функционирования самого
ядра. Итак, попробуем написать новую функцию для прерывания 0x40. Для этого
найдём в исподниках ядра файл syscall.inc . Там находится таблица прерывания
0x40 - "servetable", начинается она так:
servetable:
dd sys_drawwindow ; 0-DrawWindow
dd syscall_setpixel ; 1-SetPixel
dd sys_getkey ; 2-GetKey
. . .
Дописываем в конец таблицы новую процедуру:
. . .
dd sys_internal_services ; 68-Some internal services
dd sys_debug_services ; 69-Debug
dd file_system_lfn ; 70-Common file system interface, version 2
dd my_func ; 71-Имя нашей новой функции
ВНИМАНИЕ: Имя процедуры которое мы сейчас задали это имя подпрограммы которую
мы опишем дальше!
Соответственно на данный момент это будет функция 71, вызывающаяся из программ
прерыванием 0x40:
mov eax,71
int 0x40
Если мы добавим еще одну запись, то это уже будет функция 72.
Теперь посмотрим, как наша функция принимает значения регистров из программы.
Все дело в том, что значения регистров смещаются следующим образом: EBX
переходит в EAX, ECX в EBX, EDX в ECX, ESI в ECX, EDI в ESI. Рассмотрим на
примере:
mov eax,71
mov ebx,0x00202020
mov edi,0x00303030
int 0x40
Теперь при старте кода функции в регистре EAX окажется число 0x00202020, а в
регистре ESI - число 0x00303030.
И наконец, посмотрим как функция передает значения регистров в приложение и
напишем полноценную функцию прерывания. Найдем в коде место с телом последней
функции и после него вставляем код нашей:
my_func:
mov eax,0x00101010
mov [esp+36],eax
mov ebx,0x00202020
mov [esp+24],ebx
mov ecx,0x00303030
mov [esp+32],ecx
mov edx,0x00404040
mov [esp+28],edx
ret
Давайте разберёмся, что здесь что. В общем мы передаем значения регистров в
приложение. Пример достаточно прозрачен, и естественно, что в приложении после
исполнения нашей функции в регистре EAX окажется число 0x00101010.
Перекомпилируем ядро (см. раздел "Компиляция ядра"), и перезагрузим машину.
Теперь немного изменим, простейшее приложение template.asm, и посмотрим, как
работает наша функция. Пусть, если после вызова мы получим в регистре EAX
0x00101010, то в окне приложения напечатается текст, и так мы узнаем, что наша
новая функция работает:
mov eax,71 ; наша функция
int 0x40 ; вызываем её
cmp eax,0x00101010 ; в регистре EAX значение 0x00101010?
jne no_text ; если нет - переходим на метку no_text
mov eax, 4 ; вывести текст
mov ebx, 8*65536+30 ; координаты
mov ecx, 0xaba53274 ; цвет & шрифт
mov edx, text ; адрес текста
mov esi, text_len ; длина текста
int 0x40
no_text:
. . .
И в завершении этой статьи полезная примочка:
Для того, что бы вычислить физический адрес приложения вызвавшего нашу
функцию, следует проделать следующее:
mov esi,[0x3010] ; Помещаем в EAX адрес таблицы приложения
mov edi,[esi+0x10] ; Помещаем в EDI физический адрес начала
; приложения в памяти
Это бывает нужно, если мы передаем в приложение большой объем информации.
Тогда просто выделяем в приложении свободное место под массив, и, зная начало
приложения в памяти, вычисляем адрес массива и записываем туда информацию.
*** Компиляция ядра **********************************************************
Давайте посмотрим, как проходит компиляция ядра. Вы загружаете FASM.
Указываете в качестве входящего файла kernel.asm, а в качестве исходящего
kernel.mnt. Компилируете ядро. Теперь скидываете образ рамдиска на дискету:
Выходим из Kolibri штатным образом, и выбираем пункт ЯДРО(home). После этого
перезагружаем машину. Ядро перекомпилировано.
*** Приложение ***************************************************************
--- начало template.asm ----------------------------------------------- >8 ---
; include all KoOS stuff
include "lang.inc"
include "macros.inc"
; start of KolibriOS application
MEOS_APP_START
; start of code
CODE
call draw_window ; at first create and draw the window
wait_event: ; main cycle
mov eax, 10
int 0x40
cmp eax, 1 ; if event == 1
je redraw ; jump to redraw handler
cmp eax, 2 ; else if event == 2
je key ; jump to key handler
cmp eax, 3 ; else if event == 3
je button ; jump to button handler
jmp wait_event ; else return to the start of main cycle
redraw: ; redraw event handler
call draw_window
jmp wait_event
key: ; key event handler
mov eax, 2 ; get key code
int 0x40
jmp wait_event
button: ; button event handler
mov eax, 17 ; get button identifier
int 0x40
cmp ah, 1
jne wait_event ; return if button id != 1
or eax, -1 ; exit application
int 0x40
draw_window:
mov eax, 12 ; start drawing
mov ebx, 1
int 0x40
mov eax, 0 ; create and draw the window
mov ebx, 100*65536+300 ; (window_cx)*65536+(window_sx)
mov ecx, 100*65536+200 ; (window_cy)*65536+(window_sy)
mov edx, 0x03ffffff ; work area color & window type 3
int 0x40
mov eax, 4 ; window header
mov ebx, 8*65536+8 ; coordinates
mov ecx, 0xffffffff ; color & font N1
mov edx, header ; address of text
mov esi, header.size ; length of text
int 0x40
mov eax,71
int 0x40
cmp eax,0x00101010
jne no_text
mov eax, 4 ; window header
mov ebx, 8*65536+30 ; coordinates
mov ecx, 0xaba53274 ; color & font N1
mov edx, text ; address of text
mov esi, text_len ; length of text
int 0x40
no_text:
mov eax, 12 ; finish drawing
mov ebx, 2
int 0x40
ret
; initialised data
DATA
lsz header,\
ru, "Шаблон программы",\
en, "Template program",\
fr, "La programme poncive"
text db "Function works!"
text_len:
; uninitialised data
UDATA
; end of KolibriOS application
MEOS_APP_END
--- конец template.asm ------------------------------------------------ >8 ---
==============================================================================
=== Модификация ядра Kolibri OS. Часть 2. Изменяем существующие функции ======
==============================================================================
Автор: Johnny_B
*** Вступление ***************************************************************
Изначально эта статья задумывалась для того, чтобы объяснить новичкам, как
выводить в своих программах ASCIIZ-строки, то есть строки, оканчивающиеся
NULL-символом(0x00), например, "text db 'Assembler',0". Дело в том, что в
самой Колибри, вывод ASCIIZ-строк не предусмотрен, есть лишь возможность
вывода строки с заранее известной длиной (сис.функция #4). И, в связи с этим,
программист сам должен предусмотреть в своей программе подобную функцию, если
он хочет работать с ASCIIZ-строками.
Потом, я подумал, "зачем городить костыли в каждой программе? не лучше ли
будет, добавить эту функцию в ядро системы, чтобы любая программа могла
обращаться к ней. Это избавит программиста от лишних заморочек и позволит
сократить код приложения". Сказано - сделано.
Как появилась эта статья? Один хороший человек (привет, Lrz ;) попросил
объяснить, как я добавлял эту функцию в ядро. Дабы это было понятно всем,
появилась эта статья, чтобы рассказать о том, как внести изменения в ядро всем
желающим. Недовольные проходят мимо.
Чем меня не устраивает обычный (паскалеподобный) вывод строки? Почему ASCIIZ?
Не знаю, как вы, а лично я считаю, что ASCIIZ очень удобный способ записи и
вытекающий из него механизм работы с динамически изменяющимися строками. Очень
удобно организовывать интерактивный ввод/вывод и изменение данных. Короче,
взаимодействие с пользователем.
Меня тут упрекали в том, что это слишком тормозно (Lrz, привет ещё раз ;),
неоптимально. Да, согласен, вывод строки с заранее неизвестной длиной, будет
немного помедленнее, чем обычной. Но на современных мощностях компьютеров, это
заметить практически невозможно.
Если в процессе прочтения, вы обратите внимание на слабую оптимальность кода,
учтите, что ассемблерщик я - не ахти какой крутой. Да и статья эта не про
оптимизацию. Она для того, чтобы вы поняли, что в ядре нет ничего сложного, и
не боялись смело вгрызаться своим мозгом в его недра.
Ядро - это та же программа, только написанная немного по другим правилам,
нежели ваше приложение, но от этого оно не становится чем-то недоступным для
понимания, там те же команды, известного вам, Ассемблера. Итак, если вы
готовы, то начнём наше путешествие вглубь ядра, хотя не такое уж оно и
глубокое...
Я привожу исходники всего того, что я делал в конце каждой статьи. Почему не в
отдельном файле? Да потому что, не раз напарывался на такое нехорошее дело -
заходишь на какой-либо сайт, читаешь статью, в тексте даётся ссылка на
исходник, описание которого идёт по ходу статьи, щёлкаешь по ссылке и...
обламываешься. Исходника нет. Почему? А потому что статья была скопирована с
другого сайта, а исходник скопировать не догадались. Короче говоря, дабы не
обламывать вас в подобных случаях. Все исходники даны в конце(раздел
"Приложение"). Вам надо просто раскидать их по файлам и пользоваться
наздоровье. Недовольным придётся снова пройти мимо.
*** Основная часть ***********************************************************
С чего начнём? А начнём мы с того, что разберёмся, что же из себя представляет
структура ядра. Я рассчитываю, что у вас уже имеются в наличии исходники
Колибри(версии младше, чем 0581, потому что потом эти изменения были внедрены
;). Если нет, ничего страшного, думаю будет понятно и без них.
Итак, смотрим содержимое каталога kernel(в исходниках Колибри), он как раз и
содержит в себе основу, то, на чём держится Колибри. Что мы видим внутри:
kernel.asm - головной файл ядра, к нему подключаются остальные
[blkdev] - работа с блочными устройствами(floppy, cd/dvd-rom)
[boot] - загрузочный код
[bus] - работа с шинами данных(PCI)
[core] - ядро ядра ;) сис.функции, менеджер памяти, планировщик
[detect] - определение подключённых устройств
[fs] - работа с файловыми системами
[gui] - графический интерфейс
[hid] - драйверы мыши и клавиатуры
[network] - работа с сетью
[skin] - скин окон
[sound] - работа со звуком
[video] - работа с видео
На данном этапе для нас представляют интерес два каталога - [core] и [gui].
Точнее всего два файла из них:
[core]/syscall.inc - обработчик прерывания #64
(0x40 - системный вызов Колибри)
[gui]/font.inc - вывод текстовой строки
Рассмотрим их по очереди и увидим, как можно внедрить вывод строки ASCIIZ.
[core]/syscall.inc
~~~~~~~~~~~~~~~~~~
В этом файле содержится код обработчика прерывания 0x40. Вы ведь не раз писали
в своих программах инструкцию "int 0x40", не так ли? Так вот. При этом,
происходил вызов программы, которая находится в файле syscall.inc.
Здесь находится таблица с адресами системных функций(servetable:). Обработчик
всего лишь передаёт управление по одному из этих адресов.
Нас интересует функция #4 (вывод текста). Находим её в таблице:
dd syscall_writetext ; 4-WriteText
Отлично. Теперь производим поиск во всех файлах ядрах с ключевым словом
"syscall_writetext:", дабы выйти на саму функцию вывода текста.
Поиск приводит нас к файлу: kernel.asm. Открываем его и находим там строку
"syscall_writetext:". Здесь производятся операции загрузки регистров и
происходит jmp dtext. Отыщим эту метку. Выходим на файл [gui]/font.inc.
Открываем...
[gui]/font.inc
~~~~~~~~~~~~~~
Здесь мы остановимся поподробнее, т.к. именно этот файл мы и будем изменять.
Для начала нам необходимо знать значения регистров на входе. Благодаря кем-то
любезно оставленным комментариям, нам не придётся никуда лезть.
; eax x & y
; ebx font ( 0xX0000000 ) & color ( 0x00RRGGBB )
; ecx start of text
; edx length
; edi 1 force
Думаю тут всё ясно? Внимание нам достаточно обратить на два регистра -
ecx(указатель на начало строки) и edx(длина строки).
Как вы уже, наверняка заметили, в своих программах вы помещаете указатель в
edx, а длину - в esi. В ядре же для этих параметров используются совсем другие
регистры(ecx и edx, соответственно).
В чём же дело? А дело в том, что при вызове сис.функции регистры загружаются в
перекрёстном порядке(чёртикаком), как мне объяснили, это было сделано ещё
очень давно, во времена рождения Menuet'а для совместимости с предыдущими
версиями, а теперь от этого атавизма избавиться слишком сложно - нужно
переписать практически все сис. функции.
Но нас сейчас интересует не это. Итак, пользователь передаёт нам указатель на
ASCIIZ-строку в ecx, нам нужно вычислить её длину(дойти до 0) и записать в
edx. Этот код мы должны вставить сразу за меткой "dtext:".
Как мы определим, что приложение передаёт указатель именно на ASCIIZ-строку, а
не на строку заданной длины? Для этого введём новое правило. Если в edx(длина)
передаётся число -1(0xFFFFFFFF), значит в ecx-указатель на ASCIIZ, иначе - на
строку фиксированной длины. Ведь никому(я надеюсь) не придёт в голову выводить
строку длиной 4294967295(0xFFFFFFFF) символов? ;))
Пишем нашу функцию. Следим за тем, чтобы не попортить никакие регистры, для
этого старательно смотрим, что надо push'ить, а что нет. Т.к. я уже сделал эту
функцию, мы не будем подробно останавливаться на её написании, это не тема
данной статьи, а просто возьмём готовую:
;ASCIIZ-string output by Johnny_B
push esi
mov esi,edx ; esi=длина
inc esi
test esi,esi ; if (esi = -1) asciiz-string
jnz @F ; else standard string
cld
mov esi,ecx ; esi=указатель на строку
push eax
.s_check:
lodsb ; al=byte[esi]
test al, al ; дошли до 0?
jne .s_check ; нет
pop eax
sub esi,ecx ; esi=длина ASCIIZ-строки,включая 0
@@:
dec esi ; отбрасываем 0
mov edx,esi ; edx=длина строки
pop esi
;END
Это мы должны вставить сразу после метки "dtext:". Работа функции сводится к
следующему:
0. принять два параметра - ecx (указатель на строку), edx (длина строки)
1. если edx (длина) не равна -1, выйти, ничего не делая.
2. иначе подсчитать длину строки ASCIIZ и занести в регистр edx (длина).
Итого, на выходе мы получаем в любом случае длину нашей строки в edx и
спокойно разрешаем дальнейшему коду работать, даже не вникая в его суть. Если
стала интересна его суть, можете поковыряться в нём сами, или попросить меня
(или ещё кого-нибудь) рассказать, что там происходит.
*** PostScriptum *************************************************************
Написание этой статьи подтолкнуло меня не останавливаться на вышеописанном, а
провести глубокую оптимизацию вывода текста, удалось мне это или нет, решать
вам, изменения можно смотреть там же - в сорцах ядра.
*** Заключение ***************************************************************
Я благодарю всех людей, кто помог мне написать эту статью и указал на ошибки:
Lrz, в первую очередь, за то, что подтолкнул
Poddubny, спасибо, что объяснил некоторые недоразумения в ядре
спасибо всем, кто прочитал этот монолог до конца
С вами снова был Джон. Как обычно напоминаю, что вы можете не стесняться
задавать свои вопросы, мы постараемся на них ответить.
*** Приложение ***************************************************************
--- начало font.inc (исходный) ---------------------------------------- >8 ---
align 4
dtext:
; eax x & y
; ebx font ( 0xX0000000 ) & color ( 0x00RRGGBB )
; ecx start of text
; edx length
; edi 1 force
pushad
mov esi,edx
and esi,0xff
test esi,esi ; zero length ?
jnz @f
popad
ret
@@:
test ebx,0x10000000
jnz .letnew2
align 4
.letnew:
push eax ecx edx
movzx ebx,ax
shr eax,16
movzx edx,byte [ecx]
mov ecx,[esp+3*4+32-16]
call drawletter
pop edx ecx eax
add eax,6*65536
inc ecx
dec edx
jnz .letnew
popad
ret
.letnew2:
push ecx
push edx
movzx ebx,ax
shr eax,16
movzx edx,byte [ecx]
mov ecx,[esp+2*4+32-16]
call drawletter2
shl eax,16
add eax,ebx
pop edx
pop ecx
inc ecx
dec edx
jnz .letnew2
popad
ret
align 4
drawletter:
;eax - x
;ebx - y
;ecx - color
;edx - ascii code
pushad
call [disable_mouse]
mov esi,9
lea ebp,[0x3F600+8*edx+edx]
.symloop:
push esi
mov dl,byte [ebp]
mov esi,8
.pixloop:
test dl,1
jz .nopix
call [putpixel]
.nopix:
shr dl,1
inc eax
dec esi
jnz .pixloop
sub eax,8
inc ebx
inc ebp
pop esi
dec esi
jnz .symloop
popad
ret
align 4
drawletter2:
;eax - x
;ebx - y
;ecx - color
;edx - symbol
;edi - force?
;result - eax=eax+sym_size
pushad
call [disable_mouse]
shl edx,1
mov esi,9
lea ebp,[0x3EC00+4*edx+edx+1]
.symloop:
push esi
mov dl,byte [ebp]
xor esi,esi
.pixloop:
test dl,1
jz .nopix
call [putpixel]
.nopix:
shr dl,1
inc esi
inc eax
cmp esi,8
jl .pixloop
sub eax,8
inc ebx
pop esi
inc ebp
dec esi
jnz .symloop
movzx edx,byte [ebp-10]
add [esp+32-4],edx
popad
ret
--- конец font.inc (исходный) ----------------------------------------- >8 ---
--- начало font.inc (изменённый) -------------------------------------- >8 ---
align 4
dtext:
; eax x & y
; ebx font ( 0xX0000000 ) & color ( 0x00RRGGBB )
; ecx start of text
; edx length
; edi 1 force
;ASCIIZ-string output - Johnny_B
push esi
mov esi,edx
inc esi
test esi,esi ; if (esi = -1) asciiz-string
jnz @F ; else standard string
cld
mov esi,ecx
push eax
.s_check:
lodsb
test al, al
jne .s_check
pop eax
sub esi,ecx
@@:
dec esi
mov edx,esi
pop esi
;END
pushad
mov esi, edx
and esi, 0xff
test esi, esi ; zero length ?
jnz @f
popad
ret
@@:
test ebx,0x10000000
jnz .letnew2
align 4
.letnew:
push eax ecx edx
movzx ebx,ax
shr eax,16
movzx edx,byte [ecx]
mov ecx,[esp+3*4+32-16]
call drawletter
pop edx ecx eax
add eax,6*65536
inc ecx
dec edx
jnz .letnew
popad
ret
.letnew2:
push ecx
push edx
movzx ebx,ax
shr eax,16
movzx edx,byte [ecx]
mov ecx,[esp+2*4+32-16]
call drawletter2
shl eax,16
add eax,ebx
pop edx
pop ecx
inc ecx
dec edx
jnz .letnew2
popad
ret
align 4
drawletter:
;eax - x
;ebx - y
;ecx - color
;edx - ascii code
pushad
call [disable_mouse]
mov esi,9
lea ebp,[0x3F600+8*edx+edx]
.symloop:
push esi
mov dl,byte [ebp]
mov esi,8
.pixloop:
test dl,1
jz .nopix
call [putpixel]
.nopix:
shr dl,1
inc eax
dec esi
jnz .pixloop
sub eax,8
inc ebx
inc ebp
pop esi
dec esi
jnz .symloop
popad
ret
align 4
drawletter2:
;eax - x
;ebx - y
;ecx - color
;edx - symbol
;edi - force?
;result - eax=eax+sym_size
pushad
call [disable_mouse]
shl edx,1
mov esi,9
lea ebp,[0x3EC00+4*edx+edx+1]
.symloop:
push esi
mov dl,byte [ebp]
xor esi,esi
.pixloop:
test dl,1
jz .nopix
call [putpixel]
.nopix:
shr dl,1
inc esi
inc eax
cmp esi,8
jl .pixloop
sub eax,8
inc ebx
pop esi
inc ebp
dec esi
jnz .symloop
movzx edx,byte [ebp-10]
add [esp+32-4],edx
popad
ret
--- конец font.inc (изменённый) --------------------------------------- >8 ---
==============================================================================
=== Программирование сокетов под КолибриОС. Часть 1 =========================
==============================================================================
Автор: Hex
*** Введение *****************************************************************
Приветствую всех читателей! Данная статья начинает цикл по программированию
под сетевую часть КоОС и является описанием методов работы с UDP сокетами.
Рассчитана она прежде всего на новичков, которые хотят узнать аспекты работы и
программирования сетевых приложений. Все остальные могут не читать эту статью.
Так как работа сетевых протоколов описана в множестве статей и книг, я не буду
останавливаться на объяснении теории и сразу перейду к практической части.
Советую вначале ознакомиться с файлом Stack_ru.txt (или STACK.TXT в
оригинальном переводе), который входит в стандартную поставку КоОС. В данный
файле находится краткая теория по сетям и описание вызывов сетевых функций
Колибри/Менуэт ОС. Без ознакомления с данным файлом не имеет смысла двигаться
дальше, так как информация по сетевым функция находится именно там.
*** Первые шаги **************************************************************
Начнём мы пожалуй с написания двух программ. Одна будет посылать данные по
сети (клиент), а другая - принимать эти данные (сервер) и выводить их на
экран. Клиент не будет иметь графического интерфейса и при запуске сразу
пошлёт данные, а потом отключится. Взаимодействовать они будут по протоколу
UDP.
UDP (от англ. User Datagram Protocol - протокол пользовательских датаграмм) -
это сетевой протокол для передачи данных в сетях TCP/IP. В отличие от TCP, UDP
не гарантирует доставку пакета, поэтому его часто расшифровывают как
"Unreliable Datagram Protocol" (протокол ненадёжных датаграмм). Он является
одним из самых простых протоколов транспортного уровня и как раз подойдёт для
наших опытов.
*** Собираем Клиент **********************************************************
У клиента должен быть такой план работы:
0. Открыть сокет
1. Послать данные
2. Закрыть сокет
За открытие UDP - сокета отвечает подфункция 0 функции 53. Разберёмся с
вызовом этой функции:
mov eax,53 ; Интерфейс сокетов
mov ebx,0 ; Открыть UDP - сокет
mov ecx,0x1234 ; Локальный порт (клиент)
mov edx,0x4321 ; Удалённый порт (сервер)
mov esi,dword [server_ip] ; IP удалённого компьютера
int 0x40 ; Вызываем ф-цию создания сокета
mov [socknum],eax ; Помещаем в переменную socknum
; идентификатор открывшегося сокета
Думаю, что с первыми двумя строчками всё понятно (если нет, то смотрите
Stack_ru.txt), а вот следующие я объясню. В регистр ecx помещается локальный
порт, через который клиент пересылает свои данные серверу. В edx находится
удалённый порт сервера и клиент на этот порт будет слать свои данные. В
регистр esi помещается ip - адрес сервера, к которому подключается клиент.
После того, как мы вызвали данную функцию, в регистр eax вернулся
идентификатор нашего сокета. Сохраняем его в какую-либо переменную (я для
примера выбрал socknum), которую мы будем использовать для доступа к записи и
чтения в/из сокета. НО будте внимательны, если выбранный локальный порт занят
или произошла какая-либо ошибка - в регистр eax вернётся значение 0xFFFFFFFF,
тогда стоит перезапустить программу, исправив ошибку или написать обработчик,
проверяющий возвращённое значение. Теперь, когда мы произвели открытие
сокета, нужно послать данные на сервер. Для этого мы будем использовать
подфункцию 4 функции 53. Разберём:
mov eax,53 ; Интерфейс сокетов
mov ebx,4 ; Пишем данные в сокет
mov ecx,[socknum] ; Используем идентификатор
mov edx,4 ; Длина сообщения
mov esi,message ; Само сообщение
int 0x40 ; Вызов ф-ции записи в сокет
В регистр ecx помещается идентификатор сокета для того, чтобы действие записи
произошло именно на этом сокете. В edx помещается длина записываемых данных (в
нашем случае она будет равна количеству знаков в сообщении). Само сообщение,
записанное в переменной message(в данном примере там находится слово test ),
пишется в регистр esi. При удачном выполнении ф-ции в регистр eax возвратится
0, при неудачном - 0xFFFFFFFF (при недостатке памяти возвращается 0xFFFF).
Ну теперь осталось закрыть сокет подфункцией 1 ф-ции 53. Разберём:
mov eax,53 ; Интерфейс сокетов
mov ebx,1 ; Закрываем сокет
mov ecx,[socknum] ; Используем идентификатор
int 0x40 ; Вызов ф-ции закрытия сокета
Если сокет закрылся нормально, то возвратится 0, в противном случае будет
возвращено 0xFFFFFFFF. НО помните, что сокеты не всегда закрываются корректно
(может понадобиться дополнительное время или просто произойти ошибка).
В общем, с клиентом мы разобрались, теперь перейдём к написанию сервера...
*** Собираем сервер **********************************************************
Давайте решим, что будет делать сервер:
0. Открыть сокет
1. Перевести его в режим ожидания данных
2. Если поступили данные, то обработать их
3. Вывод на экран
4. Закрыть сокет
Открываем сокет таким же способом, как и у клиента, но с небольшими
изменениями. Разберём:
client_ip db 192,168,0,1 ; ip - адрес клиента
...
mov eax, 53 ; Интерфейс сокетов
mov ebx, 0 ; Открыть UDP - сокет
mov ecx, 0x4321 ; Локальный порт (Сервер)
mov edx, 0x1234 ; Удалённый порт (Клиент)
mov esi, dword [client_ip] ; ip - адрес удалённого компьютера
int 0x40 ; Вызываем ф-цию создания сокета
mov [socknum],eax ; Помещаем в переменную socknum
; идентификатор открывшегося сокета
Сервер, передающий данные по протоколу UDP, должен знать ip - адрес клиента, в
противном случае соединение не будет установленно. Локальный и удалённые
порты должны быть назначены зеркально портам клиента, т.е. в регистр ecx
должен быть помещён локальный порт сервера, а в edx - порт клиента (хотя можно
его и не помещать, а записать 0xFFFF, эффект будет тот же). В остальном всё
идентично клиенту.
Теперь нужно перевести свежесозданный сокет в режим ожидания. Для этого
используем подфункцию 2. Рассмотрим пример:
mov eax,53 ; Интерфейс сокетов
mov ebx,2 ; Опросить сокет на наличие данных
mov ecx,[socknum] ; Используем идентификатор
int 0x40 ; Вызываем ф-цию опроса сокета
cmp eax,0 ; равно 0?
jne data_arrived ; нет, переходим в режим получения
С этой ф-цией должно быть всё понятно. Единственно хочется отметить, что в
регистр eax должно возвращаться число полученных байт. Если eax = 0, то данные
не приходили.
Теперь рассмотрим, как получаются и обрабатываются данные. Для этого
существует специальная процедура:
get_data: ; Процедура получения данных
mov eax,53 ; Интерфейс сокетов
mov ebx,3 ; Чтение байтов из сокета
mov ecx,[socknum] ; Используем идентификатор
int 0x40 ; Вызываем ф-цию получения данных из
; сокета
mov [edi],bl ; Помещаем полученные данные в регистр
; edi
inc edi ; инкременируем (увеличиваем на 1)
; регистр edi
mov eax,53 ; Интерфейс сокетов
mov ebx,2 ; Опросить сокет на наличие данных
mov ecx,[socknum] ; Используем идентификатор
int 0x40 ; Вызываем ф-цию опроса сокета
cmp eax,0 ; равно 0?
jne get_data ; нет, возвращаемся для получения
; оставшихся данных
В этой процедуре мы начинаем получать данные подфункцией 3. Потом помещаем
полученные данные в регистр и проверяем наличие данных в сокете. Если данные
закончились (eax = 0), то продолжаем выполнять программу. Теперь нам вывести
полученные данные на экран, что мы сделаем с помощью системной ф-ции 4,
которая выводит текст на экран. Осталось только закрыть сокет, но с этим не
должно возникнуть проблем, так как мы уже это делали при написании клиента.
*** Заключение ***************************************************************
Ну теперь можно расслабиться и посмотреть на работу наших программ. Для этого
скопируйте из "Приложения" исходные коды клиента и сервера, скомпилируйте и
запустите (сначало сервер, потом клиент). Советую запускать их на двух разных
компьютерах или с виртуальной машины (VMWare, Bochs, VirtualPC and etc.),
т.к. КоОС не может открывать более 2-ух сокетов. В итоге, в окне сервера
должно появиться наше сообщение (проверьте перед запуском состояние и
настройку сети/модема). Если у вас возникли какие-либо вопросы, то можете их
написать на форуме проекта Колибри: http://www.meos.sysbin.com. На этой ноте я
с вами прощаюсь и желаю удачи в программировании под Колибри ОС!
*** Приложения ***************************************************************
--- начало NetUDPClient.asm ------------------------------------------- >8 ---
use32 ; Включить 32-битный режим ассемблера
org 0x0 ; Адресация с нуля
db 'MENUET01' ; 8-ми байтный идентификатор Менуэт/Колибри
dd 0x01 ; Версия заголовка
dd START ; Адрес первой комманды
dd I_END ; Размер программы
dd 0x5000 ; Количество памяти
dd 0x5000 ; Адрес вершины стека
dd 0x0 , 0x0 ; I_Param , I_Icon
START: ; Начало выполнения программы
mov eax,53 ; Интерфейс сокетов
mov ebx,0 ; Открыть UDP - сокет
mov ecx,0x1234 ; Локальный порт (клиент)
mov edx,0x4321 ; Удалённый порт (сервер)
mov esi,dword [server_ip] ; IP удалённого компьютера
int 0x40 ; Вызываем ф-цию создания сокета
mov [socknum],eax ; Помещаем в переменную socknum
; идентификатор открывшегося сокета
mov eax,53 ; Интерфейс сокетов
mov ebx,4 ; Пишем данные в сокет
mov ecx,[socknum] ; Используем идентификатор
mov edx,4 ; Длина сообщения
mov esi,message ; Само сообщение
int 0x40 ; Вызов ф-ции записи в сокет
mov eax, 53 ; Интерфейс сокетов
mov ebx, 1 ; Закрываем сокет
mov ecx, [socknum] ; Используем идентификатор
int 0x40 ; Вызов ф-ции закрытия сокета
mov eax,-1 ; Выходим из программы
int 0x40
; Данные
socknum dd 0x0
server_ip db 192,168,0,2 ; ip - адрес сервера
message db 'test' ; Сообщение, отправляемое на сервер
I_END:
--- конец NetUDPClient.asm -------------------------------------------- >8 ---
--- начало NetUDPServer.asm ------------------------------------------- >8 ---
use32 ; Включить 32-битный режим ассемблера
org 0x0 ; Адресация с нуля
db 'MENUET01' ; 8-ми байтный идентификатор Менуэт/Колибри
dd 0x01 ; Версия заголовка
dd START ; Адрес первой комманды
dd I_END ; Размер программы
dd 0x5000 ; Количество памяти
dd 0x5000 ; Адрес вершины стека
dd 0x0 , 0x0 ; I_Param , I_Icon
client_ip db 192,168,0,1 ; ip - адрес клиента
START: ; Начало выполнения программы
mov eax, 53 ; Интерфейс сокетов
mov ebx, 0 ; Открыть UDP - сокет
mov ecx, 0x4321 ; Локальный порт (Сервер)
mov edx, 0x1234 ; Удалённый порт (Клиент)
mov esi, dword [client_ip] ; ip - адрес удалённого компьютера
int 0x40 ; Вызываем ф-цию создания сокета
mov [socknum],eax ; Помещаем в переменную socknum
; идентификатор открывшегося сокета
mov [0],eax ; сохраняем для удалённых данных
call draw_window ; сперва перересуем окно
still:
mov eax,23 ; ждём событий...
mov ebx,1
int 0x40
cmp eax,1 ; запрос на перерисовку?
jz red
cmp eax,2 ; нажата клавиша?
jz key
cmp eax,3 ; нажата кнопка в окне программы?
jz button
mov eax,53 ; Интерфейс сокетов
mov ebx,2 ; Опросить сокет на наличие данных
mov ecx,[socknum] ; Используем идентификатор
int 0x40 ; Вызываем ф-цию опроса сокета
cmp eax,0 ; равно 0?
jne data_arrived ; нет, переходим в режим получения
jmp still
red:
call draw_window
jmp still
key:
mov eax,2
int 0x40
jmp still
button:
mov eax,53 ; Интерфейс сокетов
mov ebx,1 ; Закрываем сокет
mov ecx,[socknum] ; Используем идентификатор
int 0x40 ; Вызов ф-ции закрытия сокета
mov eax,-1
int 0x40
data_arrived:
mov eax,5 ; Ждем секунду для того, чтобы пришли
; все данные
mov ebx,10
int 0x40
mov edi,I_END
get_data: ; Процедура получения данных
mov eax,53 ; Интерфейс сокетов
mov ebx,3 ; Чтение байтов из сокета
mov ecx,[socknum] ; Используем идентификатор
int 0x40 ; Вызываем ф-цию получения данных из
; сокета
mov [edi],bl ; Помещаем полученные данные в регистр
; edi
inc edi ; инкременируем (увеличиваем на 1)
; регистр edi
mov eax,53 ; Интерфейс сокетов
mov ebx,2 ; Опросить сокет на наличие данных
mov ecx,[socknum] ; Используем идентификатор
int 0x40 ; Вызываем ф-цию опроса сокета
cmp eax,0 ; равно 0?
jne get_data ; нет, возвращаемся для получения
; оставшихся данных
mov eax,4 ; функция 4: написать текст в окне
mov ebx,10*65536+60
add ebx,[y] ; отступаем немного вниз от
; предыдущего сообщения
mov ecx,0x000000
mov edx,I_END ; Местонахождение присланных данных
mov esi,10
int 0x40
add [y],10
jmp still
draw_window:
mov eax,12 ; функция 12: сообщить системе о
; состоянии перерисовки окна
mov ebx,1 ; подфункция 1, начало перерисовки
int 0x40
; ОКНО
mov eax,0 ; функция 0: определите и выведите
; окно
mov ebx,100*65536+300 ; [x начальный] *65536 + [x размер]
mov ecx,100*65536+330 ; [y начальный] *65536 + [y размер]
mov edx,0x03ffffff ; цвет рабочей области RRGGBB
mov esi,0x80aabbcc ; цвет области заголовка RRGGBB
mov edi,0x00aabbcc ; цвет рамки RRGGBB
int 0x40
; КНОПКА
mov eax,8 ; функция 8: определить и вывести
; кнопку
mov ebx,(286-19)*65536+12 ; [x начальный] *65536 + [x размер]
mov ecx,4*65536+12 ; [y начальный] *65536 + [y размер]
mov edx,1 ; идентификатор кнопки
mov esi,0xaabbcc ; цвет кнопки RRGGBB
int 0x40
; НАДПИСЬ в заголовке
mov eax,4 ; функция 4: написать текст в окне
mov ebx,8*65536+8 ; [x начальный] *65536 + [y начальный]
mov ecx,0x00ffffff ; цвет текста RRGGBB
mov edx,labeltext ; указатель на начало текста
mov esi,lte-labeltext ; длина текста в байтах
int 0x40
; Перерисовываем текст на экране
cld
mov ebx,10*65536+30 ; функция 4: написать текст в окне
mov ecx,0x000000
mov edx,text
mov esi,40
.newline:
mov eax,4
int 0x40
add ebx,16
add edx,40
cmp [edx],byte 'x'
jnz .newline
mov eax,12 ; функция 12: сообщить системе о
; состоянии перерисовки окна
mov ebx,2 ; подфункция 2, перерисовка окончена
int 0x40
ret
; DATA AREA
text:
db 'Server IP : 192.168.0.2 '
db 'Local port : 0x4321 '
db 'Data: '
db 'x'; <- END MARKER, DONT DELETE
labeltext db 'NetUDPServer'
lte:
socknum dd 0x0
y dd 0x10
I_END:
--- конец NetUDPServer.asm -------------------------------------------- >8 ---
==============================================================================
=== КолибриОС в лицах. Интервью с Mario79 ====================================
==============================================================================
Автор: Сергей Кузьмин
Интервью с Mario79 (в миру - Марат Закиянов), человеком месяца в феврале 2005
года проекта MenuetOS - операционной системы, написанной полностью на
ассемблере. Вопросы задавал Сергей Кузьмин (http://coolthemes.narod.ru) для
ньюслеттера #1 (январь/февраль 2005).
Привет! Твой дистрибутив произвел фурор в российской команде. Интересно было
бы узнать о тебе побольше.
1. Представьтесь - где Вы живете, работаете?
Я живу в России. Работаю на кабельном ТВ.
2. Какие языки Вы знаете?
Русский, татарский и плохо знаю английский, ещё хуже английский матерный. :-)
3. Mario79 - интересное прозвище. Что оно означает?
Я не люблю игру Super Mario, я не люблю мультфильм Super Mario Brothers, зато
мне очень понравился фильм Super Mario Brothers. К тому же мое имя ближе всего
к этому псевдониму.
4. Ваши любимые музыка, книги, кино, игры, спортивные состязания, еда, веб-
сайты?
Музыка нравится разная по случаю. Книги, кино, игры - я предпочитаю
фантастику, но не фентези. Я не люблю спорт, но мне нравятся пробежки на
лыжах, хотя давно ими не занимался, вот уже 4-ю или 5-ю зиму. Из еды нравятся:
пельмени, борщ и гречка или рис с котлетами, с майонезом и кетчупом. Сайты в
сети я оцениваю по полезности, а не по красоте.
5. У Вас есть хобби?
Да, как и все люди на этом шарике (включая шизиков). :-)
Электроника и компьютеры.
6. Вы верите в Бога?
Да. Но у меня своё представление о нём.
7. Когда Вы начали программировать? Какие книги были полезными для Вас?
Начал я программировать - давно, не помню точно, но где-то в 7-8 классе
средней школы. Насчет книг могу сказать только одно - даже в самой занудной
книге можно найти полезные знания, которых нет в других местах.
8. Какие языки программирования Вы знаете? Какие из них Вы любите больше
других и почему?
Я знаю плохо БЕЙСИК и плохо ассемблер. Правда!
Ассемблер самый лучший язык, если уметь им пользоваться! А БЕЙСИК самый
простой язык, если не смотреть на него презрительно.
9. Какая ваша любимая ОС? Вы можете показать нам Ваш "список попробованных
ОС"?
У меня нет любимых ОС. Пробовал Win95osr2, Win98, Win98se, Win2K, Linux (Alt
Linux Junior), Menuet и, конечно, шедевр MS-DOS.
10. Что можете Вы сказать относительно ваших проектов (предыдущих и текущих),
которые не связаны с MenuetOS?
Я не программирую в данный момент ни для каких сред, кроме Менует. Я не могу
распыляться на много вещей сразу, так как программирование - это не мой хлеб,
это мое хобби. Раньше пытался писать на VB6, результаты можно посмотреть на
моём сайте, печальное зрелище. :-)
11. Какие программы Вы написали для MenuetOS и почему? Опишите их текущее
состояние и историю.
Почему я пишу программы - сложный вопрос, я не могу дать на него ответ. :-)
Начинал я с простого - моя первая программа переводит температуру из системы
Фаренгейта в систему Цельсия, и наоборот. Примитивная программа, написанная
для моего изучения программирования под ОС.
Дальше я изучал ядро и достиг некоторых результатов. Я первым выложил в сеть
исходники драйвера Fat16, хотя некоторые заграничные товарищи пытаются всех
убедить, что я этот драйвер украл у них и опубликовал под собственным
псевдонимом. Я не спорю, что их драйвер лучше, не в этом вопрос.
Результатом моих разработок стал выпуск дистрибутивов Колибри1 и Колибри2.
В них нет моих собственных приложений, так как я дорабатывал только ядро и
внешний вид дистрибутива в целом. В принципе, это результат коллективного
творчества советских программистов, так как Вилле упорно не желал принимать ни
наши приложения, ни наши доработки ядра. Из-за чего собственно сложилось
полное незнание заграничными пользователями о наших разработках.
Дальше был период бездействия и депрессии.
Но затем родился Колибри3. Каким он получился, судить пользователям. Могу лишь
сказать, что объём работ произведенных с 3-й версией превысил суммарный объём
работ, произведённых в первых двух дистрибутивах. И, конечно, и этот
дистрибутив в большой степени основан на приложениях, разработанных советской
группой программистов. Основные обновления от меня: мультипоточный ICON (объём
образа программы в ОЗУ уменьшился в десятки раз), новый удобный Panel
(приятный внешний вид, озвучка через спикер, переключение раскладки клавиатуры
через CTRL+SHIFT), удобный CPU (теперь сложно промазать по удаляемому
приложению). Также много программ было доработано в плане уменьшения
занимаемого в ОЗУ образа и многие программы были упакованы для экономии
пространства на RD (образе дискеты).
12. Откуда Вы узнали о MenuetOS и почему Вы программируете для неё?
Я узнал об ОС Менует из журнала CHIP, издаваемого в России. Статья была
помещена летом 2003 года. В начале осени я увидел журнал и полез в сеть. Я
программирую под эту ОС, потому что мне это интересно, потому что проект все
ещё развивается и исходники открыты.
13. Какой дистрибутив MenuetOS вы используете?
Разумеется, свои, а когда Иван Поддубный выпускает его дистрибутивы, то его.
Мы с ним работаем параллельно, к сожалению, он намерен бросить выпуск
дистрибутивов. Дистрибутивы Вилле никогда не отличались удобством и новизной,
из них очень сложно программировать. Ведь в них нет даже VRR! Который он
необоснованно отказался принять в свой дистрибутив. Остальные удобства в его
дистрибутиве вообще на улице. :-)
14. Что вы думаете о других дистрибутивах?
Я уже выразил своё мнение в 13 вопросе. Кстати, я люблю число 13. Fallout -
foreva!
15. Популярен ли MenuetOS в России?
Откуда мне знать, одно точно: сегодня программистов, пишущих для Менует в
странах бывшего СССР больше, чем программистов, пишущих в остальном мире.
16. Сейчас странная ситуация вокруг MenuetOS - MenuetOS не имеет лидера, и это
очень плохо. Кто может быть нашим новым лидером?
Вообще-то Вилле уже назначил лидера - Ярека. Но мне думается решение не
столько неверное, сколько наплевательское по отношению к другим людям. Вилле
сказал - "Лидером будет Ярек", остальные согласились, хотя их было всего 5-6
человек. Такие решения должны решаться общим голосованием на форуме, а не
диктаторским решением.
17. Какое приложение вам нравится больше всего в MenuetOS?
Хм, не хочется отвечать на провокационные вопросы. В принципе все они нужны,
так как их всё ещё мало.
18. Вы разработали/улучшили много программ и исправлений для ваших
дистрибутивов. Сколько часов в неделю вы работаете?
Когда как, так как у меня скользящий график основной работы (для получения
зарплаты) - 3 дня я работаю по 11 часов и затем у меня 3 дня выходных. В 3
выходных дня я иногда успеваю программировать, если нет других более важных
дел. Ну, в среднем можно считать 3-4 часа в выходные дни. Соответственно 12-16
часов в неделю. Но это не всегда.
19. Какие Ваши текущие проекты?
Сделать второй выпуск третьей версии Колибри. Типа Колибри3se. :-)
20. Вы имеете некоторые планы - " список того, что вы собираетесь делать"?
Ну, основные глобальные задачи: встроить плеер АС97, разработанный Иваном, в
ядро в виде драйвера, сделать выбор VRR из загрузочного синего экрана,
возможно совместно с другими советскими программистами заняться MP3 плеером и
драйвером NVidia и сделать нормальную поддержку мыши. Но это только план,
который не обязательно осуществится в ближайшем будущем.
21. Что вы думаете о прошлом 2004-м годе? Что было главным событием для вас?
Даже и не знаю, много чего было и хорошего, и плохого. Мы все надеялись, что
после отказа Вилле от проекта им хорошо займется Майк Хиббет. Но как видно не
судьба! Очевидно, Вилле ещё долго будет присутствовать в управлении ОС. Как
наш первый президент, разваливший СССР, который тоже никак не хотел оставить
пост.
22. Что Вы думаете относительно MenuetOS - где нам нужно сделать необходимые
изменения, усовершенствования?
Не мне судить об этом вопросе, обычно пользователи указывают на очевидные
недостатки. В этом случае бывает, полезен свежий взгляд свежего человека.
23. Вы можете предлагать некоторый план развития для MenuetOS?
Не знаю, я обычно решаю проблемы по мере их поступления и текущей важности, по
крайней мере, я так думаю.
24. Какое будущее программирования вы видите?
Сложный вопрос. Я думаю, не смотря на все новинки, необходимость в ассемблере
не исчезнет. По крайней мере, пока компьютеры не превратятся в роботов,
которые осознают себя личностями и не позволят больше своим создателям
ковыряться в своих мозгах. :-)
25. Какой-нибудь совет для начинающих компьютерных программистов? Какие языки
надо попробовать?
Пробовать можно сколько угодно! Просто нужно в один прекрасный день сделать
выбор и работать.
==============================================================================
=== Контакты =================================================================
==============================================================================
Состав группы, работающей над журналом:
Hex
<mestack@yandex.ru>
icq:191489175
mike.dld
<mike.dld@gmail.com>
icq:321860711
mistifi(ator
<bendik@bk.ru>
icq:308718162
Lrz
<tay-ugur@chel.surnet.ru>
icq:267994076
Victor
<victor@kolibrios.org>
icq:203760251
Johnny_B
<john@kolibrios.org>
Wildwest
http://coolthemes.narod.ru
==============================================================================
=== Послесловие ==============================================================
==============================================================================
Вот и подошёл к концу этот первый выпуск нашего журнала. Мы выражаем
благодарность всем тем, кто учавствовал в его создании - писал статьи,
присылал исправления и отзывы. А Колибри продолжает жить, продолжает
совершенствоваться, мы продолжаем совершенствоваться вместе с ней.
Мы надеемся, что после прочтения этого журнала, вы узнали что-то новое или
почувствовали нечто, ни с чем, ни сравнимое - вкус низкоуровневого
программирования, где не машина управляет человеком, а человек - машиной. А
также погрузились в мир альтернативных операционных систем.
На этом журнал подходит к концу, спасибо, что оставались с нами. До новых
встреч на просторах Сети и в дебрях низкого уровня... :)
16.08.2006 [KolibriOS`E-zine Team]