Программирование в MenuetOS - v0.3
Для версии 0.67 pre-5 и выше
V.Turjanmaa
Программирование в MenuetOS
MenuetOS — это полностью написанная на 32-битном языке ассемблера операционная
система, которая имеет многозадачную/многопотоковую конструкцию и обеспечивает
графический интерфейс пользователя (GUI) для программирования на языке ассемблера.
Menuet владеет эффективным прикладным интерфейсом программирования (API), который
быстро и легко освоить.
1) Программирование приложений
2) Программирование ядра
1) Программирование приложений для MenuetOS
1a) Структура приложения
Программирование в Menuet является довольно легкой и нетривиальной задачей
даже если вы только начинаете изучать основную структуру приложений. В таком
случае от вас требуется лишь немного опыта программирования на языке ассемблера.
MenuetOS API — это легкий для освоения набор функций с не иерархическим доступом.
Работа приложений основана на событиях.
Приложение извещается операционной системой (далее сокращенно ОС) типом события
и работает соответственно. В приложении по-умолчанию существует три типа событий:
window redraw (перерисовка окна), keypress (нажатие клавиши) и buttonpress (нажатие
кнопки).
Ниже представлена диаграмма потока и структура приложения с событиями по-умолчанию:
;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; ЗАГОЛОВОЧНЫЕ ДАННЫЕ ;
; ;
;;;;;;::;;;;;;;;;;;;;;;;;
START:
call draw_window
;;;;;;;;;;;;;;;;::;;;;;;;
; ;
; ОЖИДАНИЕ СОБЫТИЯ ; <----------------------------------------------------------I
; ; I
;;;;;::;;;;;;;;;;;;;;;;;; I
I I
;;;;;;;::;;;;;;;;;;;;;;;; I
; ; перерисовка -> вызов draw_window -> I
;СЧИТЫВАНИЕ ТИПА СОБЫТИЯ; -> ключ -> считать нажатие клавиши -> обработка ->I
; ; кнопка -> считать нажатие кнопки -> обработка -> I
;;;;;::;;;;;;;;;;;;;;;;;;
draw_window:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; ПРОРИСОВКА СТАТИЧЕСКИХ ;
; ЭЛЕМЕНТОВ ОКНА ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ret
DATA AREA:
;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; СТАТИЧЕСКИЕ ДАННЫЕ ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;
1b) Пример на ассемблере
Реализация на ассемблере вышеуказанной структуры (1a) с множеством комментариев.
Системные вызовы Menuet выполняются с помощью команды 'int 0x40' с номером
функции в регистре eax и других регистрах, используемых при необходимости.
Детали всех доступных системных вызовов описаны в разделе (1g)
Системные вызовы.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; ПРИМЕР ПРИЛОЖЕНИЯ ;
; ;
; Компилируется с помощью FASM для Menuet ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Заголовок
use32 ; директива компилятора - генерировать 32-битный код
org 0x0 ; директива с аргументом 0x0 - адресом,
; с которого код будет начинаться в памяти
db 'MENUET01' ; 8-байтовый идентификатор для приложения
dd 0x01 ; версия заголовка
dd START ; указывает начало программы
dd I_END ; метка в конце кода, указывает размер программы
dd 0x100000 ; требуемое количество оперативной памяти
; Вы можете получить доступ к памяти с 0x0 до
; значения, указанного здесь. Перемещение
; кода выполнено с помощью набора селекторов ОС.
dd 0x7fff0 ; позиция стэка в области памяти
dd 0x0 ; Параметр, передающий величину,
; если установлено не нулевое значение,
; возможные параметры передаются на старте.
dd 0x0 ; Зарезервировано для иконки
; Код
START: ; начало выполнения программы
call draw_window ; первым делом прорисовка окна
; После того, как окно прорисовано, обязанностью функции является организация основного цикла.
; События распределяются отсюда.
event_wait:
mov eax,10 ; функция 10 : ожидание события
int 0x40
; тип события возвращается в eax
cmp eax,1 ; Запрос события redraw ?
je red ; объяснение: на экране должна быть активность, тогда
; части приложения перерисовываются.
cmp eax,2 ; Ключ события в буфере ?
je key ; объяснение: пользователь нажимает клавишу пока
; приложение находится в верхней части оконного стэка.
cmp eax,3 ; Событие button в буфере ?
je button ; объяснение: пользователь нажал одну из
; кнопок приложения.
jmp event_wait
; Следующая секция считывает событие и обрабатывает данные.
red: ; Переключатель события redraw
call draw_window ; Мы вызываем функцию window_draw и
jmp event_wait ; переходим назад к event_wait
key: ; Переключатель события keypress
mov eax,2 ; Ключ возвращается в ah. Ключ должен быть
int 0x40 ; считан и очищен из системной очереди.
jmp event_wait ; Простое считывание ключа, игнорирование его и
; и переход к функции event_wait.
button: ; Переключатель события buttonpress
mov eax,17 ; Номер кнопки определяется в window_draw и
int 0x40 ; возвращается в ах.
cmp ah,1 ; идентификатор кнопки =1 ?
jne noclose
mov eax,-1 ; Функция -1 : закрытие программы
int 0x40
noclose:
jmp event_wait ; Эта команда для проигнорированных событий,
; полезна при разработке.
; *********************************************
; ***** ОКОННЫЕ ОПРЕДЕЛЕНИЯ И ПРОРИСОВКА *****
; *********************************************
;
; Статические части окна прорисовываются в этой функции. Доступ к оконному холсту
; можно будет получить позже из любой части этого кода (потока), к примеру, для отображения
; процессов или записанных данных.
;
; Статические части *должны* быть размещены вместе с функцией 12, ebx = 1 и ebx = 2.
draw_window:
mov eax,12 ; функция 12:уведомляет ОС о прорисовке окна
mov ebx,1 ; 1, запуск прорисовки
int 0x40
; ПРОРИСОВКА ОКНА
mov eax,0 ; функция 0 : определение и прорисовка окна
mov ebx,100*65536+300 ; [начало по x] *65536 + [ширина]
mov ecx,100*65536+120 ; [начало по y] *65536 + [высота]
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
mov edx,labelt ; указатель начала текста
mov esi,labellen-labelt ; длина текста
int 0x40
; КНОПКА ЗАКРЫТИЯ (CLOSE BUTTON)
mov eax,8 ; функция 8 : определение и прорисовка кнопки
mov ebx,(300-19)*65536+12 ; [начало по x] *65536 + [ширина]
mov ecx,5*65536+12 ; [начало по y] *65536 + [высота]
mov edx,1 ; идентификатор кнопки
mov esi,0x6677cc ; цвет кнопки RRGGBB
int 0x40
mov ebx,25*65536+35 ; прорисовка информационного текста с функцией 4
mov ecx,0x224466
mov edx,text
mov esi,40
newline: ; текст из ОБЛАСТИ ДАННЫХ
mov eax,4
int 0x40
add ebx,10
add edx,40
cmp [edx],byte 'x'
jne newline
mov eax,12 ; функция 12:уведомляет ОС о прорисовке окна
mov ebx,2 ; 2, конец прорисовки
int 0x40
ret
; *********************************************
; ************ ОБЛАСТЬ ДАННЫХ *************
; *********************************************
;
; Данные могут быть свободно смешаны с кодом в любых частях программы.
; Заголовочная информация требуется лишь в начале программы.
text: db 'ЭТО ПРИМЕР ПРОГРАММЫ, КОТОРЫЙ ВЫ МОЖЕТЕ '
db 'ИСПОЛЬЗОВАТЬ, КОД A:\EXAMPLE.ASM ПРОКОММЕНТИРОВАН '
db 'И ЧИСТ(Т.Е. БЕЗ ОШИБОК). СИСТЕМНЫЕ ФУНКЦИИ НАХОДЯТСЯ '
db 'В ФАЙЛЕ SYSFUNCS.TXT А КОМАНДЫ В CMD.TXT '
db 'x <- МАРКЕР КОНЦА, НЕ УДАЛЯЙТЕ '
labelt: db 'ПРИМЕР ПРИЛОЖЕНИЯ'
labellen:
I_END:
Структура приложений ОС Menuet не ограничивается программированием на ассемблере,
заголовок может быть написан практически на любом языке программирования. Тем
не менее, общая конструкция программирования приложений предназначена для легкого
32-битного ассемблерного программирования. GUI крайне легок в управлении, особенно
на ассемблере.
1c) Использование однородных системных цветов
В отличие от предыдущего примера, здесь мы сосредоточимся на создании основного приложения.
В этом разделе больше внимания уделяется внешнему виду окна.
Вы можете использовать однородные системные цвета, определенные цветовой установкой
приложения.
В данном примере появляется новая функция под названием get_system_colours
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; ПРИМЕР ОДНОРОДНЫХ СИСТЕМНЫХ ЦВЕТОВ ;
; ;
; Компилируется с помощью FASM для Menuet ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Заголовок
use32 ; директива компилятора - генерировать 32-битный код
org 0x0 ; директива с аргументом 0x0 - адресом,
; с которого код будет начинаться в памяти
db 'MENUET01' ; 8-байтовый идентификатор для приложения
dd 0x01 ; версия заголовка
dd START ; указывает начало программы
dd I_END ; метка в конце кода, указывает размер программы
dd 0x100000 ; требуемое количество оперативной памяти
; Вы можете получить доступ к памяти с 0x0 до
; значения, указанного здесь. Перемещение
; кода выполнено с помощью набора селекторов ОС.
dd 0x7fff0 ; позиция стэка в области памяти
dd 0x0 ; Параметр, передающий величину,
; если установлено не нулевое значение,
; возможные параметры передаются на старте.
dd 0x0 ; Зарезервировано для иконки
window_size_X equ 300
window_size_Y equ 150
START: ; начало выполнения программы
call draw_window ; первым делом прорисовка окна
; После того, как окно прорисовано, обязанностью функции является организация основного цикла.
; События распределяются отсюда.
event_wait:
mov eax,10 ; функция 10 : ожидание события
int 0x40
; тип события возвращается в eax
cmp eax,1 ; Запрос события redraw ?
je red ; объяснение: на экране должна быть активность, тогда
; части приложения перерисовываются.
cmp eax,2 ; Ключ события в буфере ?
je key ; объяснение: пользователь нажимает клавишу пока
; приложение находится в верхней части оконного стэка.
cmp eax,3 ; Событие button в буфере ?
je button ; объяснение: пользователь нажал одну из
; кнопок приложения.
jmp event_wait
; Следующая секция считывает событие и обрабатывает данные.
red: ; Переключатель события redraw
call draw_window ; Мы вызываем функцию window_draw и
jmp event_wait ; переходим назад к event_wait
key: ; Переключатель события keypress
mov eax,2 ; Ключ возвращается в ah. Ключ должен быть
int 0x40 ; считан и очищен из системной очереди.
jmp event_wait ; Простое считывание ключа, игнорирование его и
; и переход к функции event_wait.
button: ; Переключатель события buttonpress
mov eax,17 ; Номер кнопки определяется в window_draw и
int 0x40 ; возвращается в ах.
cmp ah,1 ; идентификатор кнопки =1 ?
jne noclose
mov eax,-1 ; Функция -1 : закрытие программы
int 0x40
noclose:
jmp event_wait ; Эта команда для проигнорированных событий,
; полезна при разработке.
get_system_colours:
pusha
mov eax,48 ; функция 48, системные цвета
mov ebx,3 ; подфункция 3 : получить
mov ecx,app_colours ; указатель на область возврата
mov edx,10*4 ; номера байтов для возврата
int 0x40
popa
ret
app_colours: ; ТАБЛИЦА СИСТЕМНЫХ ЦВЕТОВ
w_frames dd 0x0 ; - РАМКА
w_grab dd 0x0 ; - ПОЛОСА ЗАГОЛОВКА
w_grab_button dd 0x0 ; кнопка полосы заголовка
w_grab_button_text dd 0x0 ; текст кнопки полосы заголовка
w_grab_text dd 0x0 ; текст полосы заголовка
w_work dd 0x0 ; - РАБОЧАЯ ОБЛАСТЬ
w_work_button dd 0x0 ; кнопка рабочей области
w_work_button_text dd 0x0 ; текст кнопки рабочей области
w_work_text dd 0x0 ; текст рабочей области
w_work_graph dd 0x0 ; графика рабочей области
; *********************************************
; ***** ОКОННЫЕ ОПРЕДЕЛЕНИЯ И ПРОРИСОВКА *****
; *********************************************
;
; Статические части окна прорисовываются в этой функции. Доступ к оконному холсту
; можно будет получить позже из любой части этого кода (потока), к примеру, для отображения
; процессов или записанных данных.
;
; Статические части *должны* быть размещены вместе с функцией 12, ebx = 1 и ebx = 2.
;
; При использовании системных цветов, оконные цвета считываются из
; ТАБЛИЦЫ СИСТЕМНЫХ ЦВЕТОВ
;
draw_window:
mov eax,12 ; функция 12:уведомляет ОС о прорисовке окна
mov ebx,1 ; 1, запуск прорисовки
int 0x40
call get_system_colours ; выборка системных цветов из ОС
; ПРОРИСОВКА ОКНА
mov eax,0 ; функция 0 : определение и прорисовка окна
mov ebx,100*65536+window_size_X ; [начало по x] *65536 + [ширина]
mov ecx,100*65536+window_size_Y ; [начало по y] *65536 + [высота]
mov edx,[w_work] ; цвет рабочей области 0xRRGGBB
or edx,0x02000000 ; 0x02000000 = окно 2-го типа
mov esi,[w_grab] ; цвет полосы заголовка 0xRRGGBB
or esi,0x80000000 ; 0x80000000 = цвет плана
mov edi,[w_frames] ; цвет рамки 0xRRGGBB
int 0x40
; МЕТКА ОКНА
mov eax,4 ; функция 4 : написать в окне текст
mov ebx,8*65536+8 ; [начало по x] *65536 + [начало по y]
mov ecx,0x00ddeeff ; цвет текста RRGGBB
mov edx,labelt ; указатель начала текста
mov esi,labellen-labelt ; длина текста
int 0x40
mov ebx,25*65536+35 ; прорисовка информационного текста с функцией 4
mov ecx,[w_work_text]
mov edx,text
mov esi,40
newline: ; текст из ОБЛАСТИ ДАННЫХ
mov eax,4
int 0x40
add ebx,10
add edx,40
cmp [edx],byte 'x'
jne newline
; КНОПКА ЗАКРЫТИЯ
mov eax,8 ; функция 8 : определение и прорисовка кнопки
mov ebx,window_size_X
sub ebx,19
shl ebx,16
mov bx,12 ; ebx = [начало по x] *65536 + [ширина]
mov ecx,5*65536+12 ; [начало по y] *65536 + [высота]
mov edx,1 ; идентификатор кнопки
mov esi,[w_grab_button] ; цвет кнопки RRGGBB
int 0x40
mov eax,12 ; функция 12:уведомляет ОС о прорисовке окна
mov ebx,2 ; 2, конец прорисовки
int 0x40
ret
; *********************************************
; ************ ОБЛАСТЬ ДАННЫХ *************
; *********************************************
;
; Данные могут быть свободно смешаны с кодом в любых частях программы.
; Заголовочная информация требуется лишь в начале программы.
text: db 'В ЭТОЙ ПРОГРАММЕ ИСПОЛЬЗУЮТСЯ ОДНОРОДНЫЕ СИСТЕМНЫЕ ЦВЕТА'
db 'ВОЗВРАЩЕННЫЕ В ТАБЛИЦУ '
db 'x <- МАРКЕР КОНЦА, НЕ УДАЛЯЙТЕ '
labelt: db 'ПРИМЕР ПРИЛОЖЕНИЯ'
labellen:
I_END:
1d) Окно произвольной формы
В данном разделе мы рассмотрим пример изменения прямоугольной формы окна на
любую другую. Новая функция здесь: shape_window.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; ПРИМЕР ПРИЛОЖЕНИЯ ИЗМЕНЕНИЯ ФОРМЫ ;
; ;
; Компилируется с помощью FASM для Menuet ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Заголовок
use32 ; директива компилятора - генерировать 32-битный код
org 0x0 ; директива с аргументом 0x0 - адресом,
; с которого код будет начинаться в памяти
db 'MENUET01' ; 8-байтовый идентификатор для приложения
dd 0x01 ; версия заголовка
dd START ; указывает начало программы
dd I_END ; метка в конце кода, указывает размер программы
dd 0x100000 ; требуемое количество оперативной памяти
; Вы можете получить доступ к памяти с 0x0 до
; значения, указанного здесь. Перемещение
; кода выполнено с помощью набора селекторов ОС.
dd 0x7fff0 ; позиция стэка в области памяти
dd 0x0 ; Параметр, передающий величину,
; если установлено не нулевое значение,
; возможные параметры передаются на старте.
dd 0x0 ; Зарезервировано для иконки
; Код
START: ; начало выполнения программы
call shape_window ; функция для формирования
call draw_window ; сначала прорисовка окна
still:
mov eax,10 ; ожидание события
int 0x40
cmp eax,1 ; запрос события redraw ?
je red
cmp eax,2 ; ключ в буфере ?
je key
cmp eax,3 ; кнопка в буфере ?
je button
jmp still
red: ; перерисовка
call draw_window
jmp still
key: ; ключ
mov eax,2 ; считывание ключа и игнорирование его
int 0x40
jmp still
button: ; кнопка
mov eax,17 ; получить идентификатор
int 0x40
cmp ah,1 ; идентификатор кнопки =1 ?
jne noclose
mov eax,-1 ; закрытие этой программы
int 0x40
noclose:
jmp still
shape_window:
pusha
mov eax,50 ; представление ссылки на область формы
mov ebx,0
mov ecx,shape_reference
int 0x40
mov eax,50 ; представление масштаба формы 32 x 32 -> 128 x 128
mov ebx,1 ; нет необходимости давать эту команду, масштаб по-умолчанию 1:1
mov ecx,2 ; установка масштаба в 2^ecx
int 0x40
popa
ret
shape_reference: ; 32 x 32 ( window_size_X + 1 ) * ( window_size_Y + 1 )
db 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0
db 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0
db 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0
db 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0
db 0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0
db 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0
db 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0
db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0
db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0
db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0
db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0
db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0
db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0
db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0
db 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0
db 0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0
db 0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0
db 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0
db 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0
db 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0
db 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0
; *********************************************
; ***** ОКОННЫЕ ОПРЕДЕЛЕНИЯ И ПРОРИСОВКА *****
; *********************************************
draw_window:
mov eax,12 ; функция 12:уведомляет ОС о прорисовке окна
mov ebx,1 ; 1, запуск прорисовки
int 0x40
; ПРОРИСОВКА ОКНА
mov eax,0 ; функция 0 : определение и прорисовка окна
mov ebx,100*65536+300 ; [начало по x] *65536 + [ширина]
mov ecx,100*65536+120 ; [начало по y] *65536 + [высота]
mov bx,word [x_size]
mov cx,word [y_size]
mov edx,0x00cccc00 ; цвет рабочей области RRGGBB,8->цвет плана
mov esi,0x00cccc00 ; цвет полосы заголовка RRGGBB,8->цвет плана
mov edi,0x00cccc00 ; цвет рамки RRGGBB
int 0x40
; КНОПКА ЗАКРЫТИЯ (CLOSE BUTTON)
mov eax,8 ; функция 8 : определение и прорисовка кнопки
mov ebx,(300-19)*65536+12 ; [начало по x] *65536 + [ширина]
mov ecx,5*65536+12 ; [начало по y] *65536 + [высота]
mov edx,1 ; идентификатор кнопки
mov esi,0x6677cc ; цвет кнопки RRGGBB
int 0x40
mov eax,12 ; функция 12:уведомляет ОС о прорисовке окна
mov ebx,2 ; 2, конец прорисовки
int 0x40
ret
; DATA
x_size dd 127
y_size dd 127
I_END:
1e) Потоки
Ассемблерная поточность MenuetOS имеет большие преимущества перед языками высокого
уровня. Если вы сохраняете все переменные в регистрах, то у вас есть возможность
запустить сколько угодно потоков из того же кода, так как память не используется
и не требуется сохранение. Регистры сохраняются в сегментах переключения задач
(Task Switch Segments) MenuetOS. Все, что вам нужно сделать, это установить новый
стэк.
Потоки не отличаются от главного процесса и используют ту же область памяти,
что и процесс, запустивший их. Они могут иметь свои собственные независимые
окна и т.п. При закрытии приложения все потоки завершают свою работу посредством
системного вызова по-умолчанию (eax = -1). Новая функция в данном примере: create_thread.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; ПРИМЕР ИСПОЛЬЗОВАНИЯ ПОТОКОВ ;
; ;
; Компилируется с помощью FASM для Menuet ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
use32
org 0x0
db 'MENUET01' ; 8-байтовый идентификатор
dd 0x01 ; версия заголовка
dd START ; указывает начало программы
dd I_END ; метка в конце кода, указывает размер программы
dd 0x100000 ; требуемое количество оперативной памяти
dd 0x80000 ; esp
dd 0x0 , 0x0 ; I_Param , I_Icon
START: ; начало выполнения программы
call draw_window ; сначала прорисовка окна
event_wait:
mov eax,10 ; ожидание события
int 0x40
cmp eax,1 ; Запрос события redraw ?
je red
cmp eax,2 ; ключ в буфере ?
je key
cmp eax,3 ; кнопка в буфере ?
je button
jmp event_wait
red: ; перерисовка
call draw_window
jmp event_wait
key: ; ключ
mov eax,2 ; считывание и игнорирование
int 0x40
jmp event_wait
button: ; кнопка
mov eax,17 ; получение идентификатора
int 0x40
cmp ah,1 ; идентификатор кнопки =1 ?
jne noclose
mov eax,-1 ; закрытие программы (потока)
int 0x40
noclose:
cmp ah,2 ; вызов create_thread
jne no_thread
call create_thread
jmp event_wait
no_thread:
jmp event_wait
; СОЗДАНИЕ ПОТОКА
;
; Все, что нам нужно сделать - это отправить потоковый адрес входа в ecx и
; позицию нового стэка в edx с функцией eax=51, ebx=1
create_thread:
cmp [thread_stack],0xf0000
jge no_new_thread
add [thread_stack],0x1000
mov eax,51 ; системный вызов thread_create
mov ebx,1
mov ecx,START
mov edx,[thread_stack]
int 0x40
no_new_thread:
ret
thread_stack dd 0x80000
; *********************************************
; ***** ОКОННЫЕ ОПРЕДЕЛЕНИЯ И ПРОРИСОВКА *****
; *********************************************
draw_window:
mov eax,12 ; функция 12:уведомляет ОС о прорисовке окна
mov ebx,1 ; 1, запуск прорисовки
int 0x40
; ПРОРИСОВКА ОКНА
mov eax,0 ; функция 0 : определение и прорисовка окна
mov ebx,100*65536+300 ; [начало по x] *65536 + [ширина]
mov ecx,100*65536+120 ; [начало по y] *65536 + [высота]
mov esi,[thread_stack]
sub esi,0x80000
shr esi,11
shl esi,16
add ebx,esi
add ecx,esi
mov edx,0x02ffffff ; цвет рабочей области RRGGBB,8->цвет плана
mov esi,0x808899ff ; цвет полосы заголовка RRGGBB,8->цвет плана
mov edi,0x008899ff ; цвет рамки RRGGBB
int 0x40
; МЕТКА ОКНА
mov eax,4 ; функция 4 : написать в окне текст
mov ebx,8*65536+8 ; [начало по x] *65536 + [начало по y]
mov ecx,0x00ddeeff ; цвет текста RRGGBB
mov edx,labelt ; указатель на начало текста
mov esi,labellen-labelt ; длина текста
int 0x40
; КНОПКА ЗАКРЫТИЯ
mov eax,8 ; функция 8 : определение и прорисовка окна
mov ebx,(300-19)*65536+12 ; [начало по x] *65536 + [ширина]
mov ecx,5*65536+12 ; [начало по y] *65536 + [высота]
mov edx,1 ; идентификатор кнопки
mov esi,0x6677cc ; цвет кнопки RRGGBB
int 0x40
mov eax,8 ; КНОПКА НОВОГО ПОТОКА
mov ebx,25*65536+128
mov ecx,88*65536+20
mov edx,2
mov esi,0x6677cc
int 0x40
mov ebx,25*65536+35 ; прорисовка информационного текста с функцией 4
mov ecx,0x224466
mov edx,text
mov esi,40
newline:
mov eax,4
int 0x40
add ebx,10
add edx,40
cmp [edx],byte 'x'
jne newline
mov eax,12 ; функция 12:уведомляет ОС о прорисовке окна
mov ebx,2 ; 2, конец прорисовки
int 0x40
ret
; ОБЛАСТЬ ДАННЫХ
text:
db 'ЭТОТ ПРИМЕР СОЗДАЕТ ПОТОКИ ПУТЕМ ЗАПУСКА '
db 'ОДНОГО И ТОГО ЖЕ КОДА МНОГО РАЗ. ВСЕ, ЧТО '
db 'НАМ НУЖНО, ЭТО НОВЫЙ СТЕК ДЛЯ КАЖДОГО ПОТОКА. '
db 'ВСЕ ПОТОКИ ИСПОЛЬЗУЮТ ОДИНАКОВУЮ ПАМЯТЬ. '
db ' '
db ' '
db ' СОЗДАНИЕ НОВОГО ПОТОКА '
db 'x <- МАРКЕР КОНЦА, НЕ УДАЛЯЙТЕ '
labelt:
db 'ПРИМЕР ИСПОЛЬЗОВАНИЯ ПОТОКОВ'
labellen:
I_END:
1f) Данные реального времени
Следующий пример сфокусирован на формировании данных реального времени и их
обработке. Приложение информирует ОС о всех портах и типах данных для считывания
по специфическому номеру прерывания IRQ.
Шаги:
- резервирование области порта Ввода/Вывода;
- резервирование номера IRQ;
- программирование IRQ;
- программирование списка СОБЫТИЙ для нужного IRQ;
- временная обработка данных;
- возвращение к событиям по-умолчанию — освобождение IRQ из списка СОБЫТИЙ;
- освобождение IRQ;
- освобождение области порта;
- завершение работы программы.
После того, как IRQ спрограммирован, приложение получает новое событие для организации
главного цикла событий, номер (IRQ+16). Когда приложение получает это событие,
ОС записывает данные, готовые для обработки приложением.
Основная структура обработки данных реального времени показана ниже в таблице.
Все шаги слева от (A) — обрабатываются ОС, справа от (A) — приложением.
IRQ ВЛАДЕЛЕЦ => ЗАПИСЬ ДАННЫХ (A) SYS_EVENT => СЧИТЫВАНИЕ ДАННЫХ => ОБРАБОТКА
0 ТАЙМЕР SYS
1 КЛАВИАТУРА SYS
2 free ->
3 COM МЫШЬ SYS/free ?>
4 COM МЫШЬ SYS/free ?>
5 ЗВ. КАРТА SYS
6 ФЛОППИ SYS
7 free ->
8 free ->
9 free ->
10 free ->
11 free ->
12 PS2 МЫШЬ SYS/free ?>
13 МАТ. ПРОЦ SYS
14 IDE SYS
15 IDE SYS
Пример обработки данных реального времени:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ;
; ДАННЫЕ РЕАЛЬНОГО ВРЕМЕНИ ;
; ;
; Компилируется с помощью FASM для Menuet ;
; ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
use32
org 0x0
db 'MENUET00' ; 8-байтовый идентификатор
dd 56 ; требуемая версия ОС
dd START ; указывает начало программы
dd I_END ; метка в конце кода, указывает размер программы
dd 0x100000 ; требуемое количество оперативной памяти
; esp = 0x7FFF0
dd 0x00000000 ; зарезервировано=отсутствует расширенный заголовок
START: ; начало выполнения программы
call draw_window ; сначала прорисовка окна
call program_real_time_data ; программирование ОС для получения данных реального времени
call program_com_port ; программирование com-порта для специфических устройств
event_wait:
mov eax,10 ; ожидание события
int 0x40
cmp eax,1 ; Запрос события redraw ?
je red
cmp eax,2 ; ключ в буфере ?
je key
cmp eax,3 ; кнопка в буфере ?
je button
cmp eax,16+4 ; РВ: новое событие для требуемых IRQ данных (16+IRQ)
je read_rt
jmp event_wait
; Следующая секция считывает событие и обрабатывает данные.
read_rt: ; данные реального времени (РВ)
mov eax,42 ; Функция 42 возвращает данные для IRQ 4
mov ebx,4 ;
int 0x40 ; ОС возвращает записанные данные
; eax номер байтов в буфере слева
; bl данные
; ecx 0 = успешно, другое значение = нет данных в буфере.
call process_data
jmp event_wait
red: ; перерисовка
call draw_window
jmp event_wait
key: ; ключ
mov eax,2 ; считывание и игнорирование
int 0x40
jmp event_wait
button: ; кнопка
mov eax,17 ; получить идентификатор
int 0x40
cmp ah,1 ; идентификатор кнопки =1 ?
jne noclose
call free_real_time_data
mov eax,-1 ; закрытие программы
int 0x40
noclose:
jmp event_wait
program_real_time_data:
; Программирование выборки данных реального времени
;
; 1) резервирование области порта Ввода/Вывода
; 2) резервирование IRQ
; 3) программирование IRQ
; 4) программирование списка СОБЫТИЙ для требуемого IRQ
;
pusha
mov eax,46 ; резервирование портов 0x3f0 - 0x3ff
mov ebx,0
mov ecx,0x3f0
mov edx,0x3ff
int 0x40
mov eax,45 ; резервирование irq 4
mov ebx,0
mov ecx,4
int 0x40
mov eax,44 ; установка считывающих портов для 4
mov ebx,irqtable
mov ecx,4
int 0x40
mov eax,40 ; получение данных из com 1 с irq 4
mov ebx,0000000000010000b shl 16 + 111b ; после этого у нас появляется новое
; событие (16+4)
int 0x40
popa
ret
irqtable:
dd 0x3f8+0x01000000 ; 3f8 =port to read : 01 =считан байт, 02 =считано слово
dd 0x0 ; 0x0 = завершает работу считывания через число IRQ событий
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
dd 0x0
free_real_time_data:
; Освобождение использованных ресурсов
;
; 1) получение событий по-умолчанию
; 2) освобождение irq посредством функции 45,1
; 3) освобождение области порта посредством функции 46,1
;
pusha
mov eax,40 ; события по-умолчанию - блокировка irq 4 события
mov ebx,111b
int 0x40
mov eax,45 ; освобождение irq
mov ebx,1
mov ecx,4
int 0x40
mov eax,46 ; освобождение портов 0x3f0-0x3ff
mov ebx,1
mov ecx,0x3f0
mov edx,0x3ff
int 0x40
popa
ret
; Следующие функции предназначены для обработки специфических данных устройств
process_data:
cmp ebx,80
jne nocd
mov eax,19
mov ebx,cdplayer
mov ecx,0
int 0x40
nocd:
push ebx
mov eax,[pos]
add eax,1
cmp eax,10*20+1
jb noeaxz
mov esi,text+10*4
mov edi,text
mov ecx,10*21*4
cld
rep movsb
mov eax,13
mov ebx,20*65536+260
mov ecx,22*65536+220
mov edx,[wcolor]
int 0x40
mov eax,10*19+1
noeaxz:
mov [pos],eax
pop ebx
and ebx,0xff
call draw_data
ret
draw_data:
pusha
xchg eax,ebx
mov ecx,10
shl ebx,2
mov esi,3
newnum:
xor edx,edx
div ecx
add edx,48
mov [ebx+text-1],dl
dec ebx
dec esi
jnz newnum
call draw_text
popa
ret
draw_text:
pusha
mov ebx,25*65536+35 ; прорисовка информационного текста с помощью функции 4
mov ecx,0xffffff
mov edx,text
mov esi,40
mov edi,20
newline:
mov eax,4
int 0x40
add ebx,10
add edx,40
dec edi
jne newline
popa
ret
program_com_port:
; Следующая последовательность программирует COM-порт для инфракрасного получателя
mov cx,0x3f3+8
mov bl,0x80
mov eax,43
int 0x40
mov cx,0x3f1+8
mov bl,0
mov eax,43
int 0x40
mov cx,0x3f0+8
mov bl,0x30 / 4
mov eax,43
int 0x40
mov cx,0x3f3+8
mov bl,3
mov eax,43
int 0x40
mov cx,0x3f4+8
mov bl,0xB
mov eax,43
int 0x40
mov cx,0x3f1+8
mov bl,1
mov eax,43
int 0x40
mov eax,5
mov ebx,100
int 0x40
mov cx,0x3f8
mov bl,'I'
mov eax,43
int 0x40
mov eax,5
mov ebx,10
int 0x40
mov cx,0x3f8
mov bl,'R'
mov eax,43
int 0x40
ret
; *********************************************
; ***** ОКОННЫЕ ОПРЕДЕЛЕНИЯ И ПРОРИСОВКА *****
; *********************************************
draw_window:
mov eax,12 ; функция 12:уведомляет ОС о прорисовке окна
mov ebx,2 ; 2, конец прорисовки
int 0x40
; ПРОРИСОВКА ОКНА
mov eax,0 ; функция 0 : определение и прорисовка окна
mov ebx,100*65536+300 ; [начало по x] *65536 + [ширина]
mov ecx,100*65536+120 ; [начало по y] *65536 + [высота]
mov edx,[wcolor] ; цвет рабочей области RRGGBB,8->цвет
mov esi,0x8099bbff ; цвет полосы заголовка RRGGBB,8->цвет плана
mov edi,0x00ffffff ; цвет рамки RRGGBB
int 0x40
; МЕТКА ОКНА
mov eax,4 ; функция 4 : написать в окне текст
mov ebx,8*65536+8 ; [начало по x] *65536 + [начало по y]
mov ecx,0x00ddeeff ; цвет текста RRGGBB
mov edx,labelt ; указатель на начало текста
mov esi,labellen-labelt ; длина текста
int 0x40
; КНОПКА ЗАКРЫТИЯ
mov eax,8 ; функция 8 : определение и прорисовка окна
mov ebx,(300-19)*65536+12 ; [начало по x] *65536 + [ширина]
mov ecx,5*65536+12 ; [начало по y] *65536 + [высота]
mov edx,1 ; идентификатор кнопки
mov esi,0x6677cc ; цвет кнопки RRGGBB
int 0x40
call draw_text
mov eax,12
mov ebx,2
int 0x40
ret
; ОБЛАСТЬ ДАННЫХ
wcolor dd 0x0
pos dd 0x0
cdplayer db 'CDPLAY '
labelt db 'ИНФРАКРАСНЫЙ ПОЛУЧАТЕЛЬ ДЛЯ IRMAN В COM 1'
labellen:
text:
I_END:
1g) Системные функции
Номер функции сохраняется в регистре eax, а команда INT 0x40 выполняет ее.
00 = ОПРЕДЕЛЕНИЕ И ПРОРИСОВКА ОКНА
ebx [начало по x]*65536 + [ширина]
ecx [начало по y]*65536 + [высота]
edx цвет тела 0xXYRRGGBB если X=8 -> цвет плана
если Y=0 -> окно 1-го типа
если Y=1 -> резервирование области, без прорисовки
если Y=2 -> окно 2-го типа
если Y=3 -> пустое окно,
кнопка закрытия идентификатор=1 добавлена автоматически
esi цвет полосы заголовка 0xX0RRGGBB если X=8 -> негативный цвет плана
если X=4 -> позитивный цвет плана
edi цвет рамки 0x00RRGGBB
ret: никаких изменений
01 = ПИКСЕЛЬНАЯ УСТАНОВКА
ebx [x]
ecx [y]
edx цвет пикселя 0x0XRRGGBB
^ 0 нормальная установка, 1 негативная
ret: никаких изменений
02 = ПОЛУЧЕНИЕ КЛЮЧА
ret: al 0 удачно -> ah = ключ
al 1 ключ в буфере отсутствует
03 = ПОЛУЧЕНИЕ СИСТЕМНЫХ ЧАСОВ
ret: eax 0x00SSMMHH сек,мин,часы
04 = ЗАПИСЬ ТЕКСТА В ОКНО
ebx [начало по x]*65536 + [начало по y]
ecx цвет текста 0x00RRGGBB
edx указатель на начало текста
esi длина текста
ret: никаких изменений
05 = ЗАДЕРЖКА X/100 СЕКУНД
ebx задержка в 1/100 секундах
ret: никаких изменений
06 = ОТКРЫТИЕ ФАЙЛА С ДИСКЕТЫ
ebx указатель на файл -> 11 заглавных букв
ecx множество 0x00000000 - зарезервировано
edx множество 0xffffffff - зарезервировано
esi считывание в позицию памяти
ret: никаких изменений
07 = ВСТАВКА ИЗОБРАЖЕНИЯ
ebx указатель на изображение в памяти - RRGGBBRRGGBB..
ecx размер окна [x]*65536+[y]
edx позиция изображения в окне [x]*65536+[y]
ret: eax 0 удачно, 1 перекрытие
08 = ОПРЕДЕЛЕНИЕ КНОПКИ
ebx [начало по x]*65536 + [ширина]
ecx [начало по y]*65536 + [высота]
edx номер идентификатора кнопки
esi цвет кнопки 0x 00 RR GG BB
ret: никаких изменений
09 = ИНФОРМАЦИЯ О ПРОЦЕССАХ
ebx указатель на 1024-байтовую таблицу
ecx номер процесса
ret: eax номера процессов
table : +00 dword использование процессора
+04 word позиция процессов в оконном стэке
+06 word значение оконного стэка в регистре ecx
+10 12 db имя процесса
+22 dword начало памяти процессов
+26 dword использование памяти процессами
+30 dword идентификатор процесса (PID)
10 = ОЖИДАНИЕ СОБЫТИЯ
ret: eax тип события, 1 перерисовка окна, 2 ключ в буфере, 3 нажата кнопка
11 = ПРОВЕРКА СОБЫТИЯ, БЕЗ ОЖИДАНИЯ
ret: eax 0 нет события, 1 перерисовка окна, 2 ключ в буфере, 3 нажата кнопка
12 = СТАТУС ПЕРЕРИСОВКИ ОКНА
ebx 1 начало перерисовки, 2 конец перерисовки
ret: никаких изменений
13 = ЗОНА ПРОРИСОВКИ
ebx [начало по x]*65536 + [ширина]
ecx [начало по y]*65536 + [высота]
edx цвет 0x00RRGGBB
ret: никаких изменений
14 = ПОЛУЧЕНИЕ ЭКРАНА МАКСИМУМА
ret: eax [экран x макс]*65536 + [экран y макс]
15 = ФОН
ebx 1 : установка размера фона
ecx x размер
edx y размер
ebx 2 : запись в фоновую память - макс (0x100000-16)
ecx позиция в памяти в байтах
edx цвет 0x00RRGGBB
ebx 3 : прорисовка фона
ebx 4 : тип прорисовки фона
ecx 1 - размножение
ecx 2 - растягивание ebx
ebx 5 : перемещение изображения в фоновую память ОС
ecx - откуда
edx - куда в фоновую память ОС
esi - количество байтов для перемещения
16 = [ не готова - запись кэша на дискету ]
17 = ПОЛУЧЕНИЕ ИДЕНТИФИКАТОРА НАЖАТОЙ КЛАВИШИ
ret: al 0 удачный -> ah = номер идентификатора
al 1 ключ в буфере отсутствует
18 = СИСТЕМНЫЙ СЕРВИС
ebx 1 - системная загрузка
ebx 2 - завершение, ecx нет обработки
ebx 4 - холостые циклы часов / секунды
ebx 5 - счетчик отметки времени / секунды - скорость процессора
19 = ЗАПУСК ПРОГРАММ
ebx указатель на 11-и символьное имя файла
ecx 0, или указатель на начальные параметры ASCIIZ - макс 256 байт
ret: eax 0 удачно
eax прочее код ошибки
20 = MIDI ИНТЕРФЕЙС - MPU401
ebx 1 - сброс устройства
ebx 2 - данные cl midi для вывода
21 = НАСТРОЙКА ДЛЯ УСТРОЙСТВ
ebx 1=roland mpu midi base, базовый адрес Ввода/Вывода
ebx 2=клавиатура 1 базовая раскладка 2 раскладка shift (ecx указатель на раскладку)
9 страна 1eng 2fr 3ger 4rus
ebx 3=cd база 1 pri.master 2 pri slave,
3 sec master 4 sec slave
ebx 4=sb16 база, базовый адрес Ввода/Вывода
ebx 5=системный язык, 1eng 2fi 3ger 4rus
ebx 6=wss база, базовый адрес Ввода/Вывода
ebx 7=hd база, 1 pri.master 2 pri slave
3 sec master 4 sec slave
ebx 8=fat32 раздел на жестком диске
22 = НЕ ИСПОЛЬЗУЕТСЯ
23 = ОЖИДАНИЕ СОБЫТИЯ С ТАЙМ-АУТОМ
ebx время задержки в часах
ret: eax тип события: 0 нет события, 1 перерисовка окна,
2 ключ в буфере, 3 кнопка
24 = CD AUDIO
ebx 1 - проигрывание из ecx 00 FR SS MM
ebx 2 - отправка размера списка песен ecx в [edx]
ebx 3 - stop/pause play
25 = SB16 - mixer I
ebx 1 - установка громкости в cl [L]*16+[R]
ebx 2 - установка громкости cd в cl [L]*16+[R]
26 = ПОЛУЧЕНИЕ НАСТРОЙКИ ДЛЯ УСТРОЙСТВ
ebx 1=roland mpu midi base, базовый адрес Ввода/Вывода
ebx 2=клавиатура 1 базовая раскладка 2 раскладка shift
9 страна 1eng 2fi 3ger 4rus
ebx 3=cd база 1 pri.master 2 pri slave,
3 sec master 4 sec slave
ebx 4=sb16 база, базовый адрес Ввода/Вывода
ebx 5=системный язык, 1eng 2fi 3ger 4rus
ebx 6=wss база, базовый адрес Ввода/Вывода
ebx 7=hd база, 1 pri.master 2 pri slave
3 sec master 4 sec slave
ebx 8=fat32 раздел на жестком диске
ebx 9=1/100 таймер stard-> eax
возвращение значения в eax
27 = ЗВУКОВАЯ СИСТЕМА ОКОН
ebx 1 - установка громкости в cl 0-255
ebx 2 - установка громкости cd в cl 0-255
28 = SB16 - mixer II
ebx 1 - установка громкости в cl 0-255
ebx 2 - установка громкости cd в cl 0-255
29 = ПОЛУЧЕНИЕ ДАТЫ
ret: eax 0x00YYDDMM год день месяц
30 = ЧТЕНИЕ ДАННЫХ С ЖЕСТКОГО ДИСКА
ebx указатель на файл
ecx длина файла
edx блок для считывания, начинается с 1, размер блока = 512 байт
esi зарезервирован, установлен как 1
edi указатель на область возврата/работы (как минимум 20 000 байт)
return: work_area+1024 <- запрошенный блок 512 байт
31 = ЗАПУСК ПРИЛОЖЕНИЯ ИЗ ЖЕСТКОГО ДИСКА
ebx указатель на файл
ecx длина файла
edx указатель на область возврата/работы (как минимум 20 000 байт)
ret eax=0 удачно, eax<>0 код ошибки
32 = УДАЛЕНИЕ ФАЙЛА ИЗ ОБРАЗА ДИСКЕТЫ В ПАМЯТИ
ebx указатель на файл
33 = СОХРАНЕНИЕ ФАЙЛА В ОБРАЗ ДИСКЕТЫ В ПАМЯТИ
ebx указатель на имя файл
ecx указатель на дату
edx количество для записи в байтах
esi 0 создание нового, ( 1 добавление - еще не реализовано )
34 = СЧИТЫВАНИЕ КАТАЛОГА С ДИСКЕТЫ
ebx зарезервирован : установлен как 0
ecx зарезервирован : установлен как 0
edx начинает считывать блок 512
esi зарезервирован : установлен как 1
edi указатель на область возврата
35 = СЧИТЫВАНИЕ ЭКРАННОГО ПИКСЕЛЯ
ebx = количество пикселей по диагонали (от верхней левой части экрана)
возвращает : eax = 0x00RRGGBB
36 = СЧИТЫВАНИЕ СТРОКИ (не закончена)
37 = СЧИТЫВАНИЕ ПОЗИЦИИ МЫШИ
ebx=0 родительский экран
ebx=1 родительское окно
ebx=2 нажаты кнопки
возвращает в eax
38 = ПРОРИСОВКА ЛИНИИ
ebx [начало по x] shl 16 + [x конец]
ecx [начало по y] shl 16 + [y конец]
edx цвет 0x00RRGGBB
ret: никаких изменений
39 = ПОЛУЧИТЬ ФОН
ebx=1 -> eax=[фон x размер] shl 16 + [фон y размер]
ebx=2
ecx= позиция фоновой карты памяти для возвращения в eax
ebx=4 -> eax=1 размноженный, eax=2 растянутый
40 = УСТАНОВКА ПОЛЯ БИТОВ ДЛЯ ТРЕБУЕМЫХ СОБЫТИЙ
as default:
ebx = 00000000 00000000 00000000 00000111b события:
I оконная прорисовка
I ключ в буфере
I кнопка в буфере
I (конец запроса)
I прорисовка настольного фона
I (изменение мыши)
I---------------I получение данных о прерываниях
41 = ПОЛУЧЕНИЕ ВЛАДЕЛЬЦА IRQ
ebx = irq
возвращает: идентификатор процесса
42 = СЧИТЫВАНИЕ ДАННЫХ ПОСРЕДСТВОМ IRQ
ebx номер IRQ
return : eax номер байтов в буфере
bl данные
ecx 0 = удачное считывание данных
1 = данные отсутствуют в буфере
2 = некорректный владелец IRQ
43 = ОТПРАВЛЕНИЕ ДАННЫХ В УСТРОЙСТВА
bx : порт
cl : данные
возвращает : eax = если 0 удачно, other = ошибка
44 = ПРОГРАММИРОВАНИЕ IRQ
ebx указатель на таблицу
ecx номер irq
45 = РЕЗЕРВИРОВАНИЕ/ОСВОБОЖДЕНИЕ IRQ
ebx 0 зарезервировано 1 освобождено
ecx номер IRQ
ret eax 0 удачно, 1 ошибка
46 = РЕЗЕРВИРОВАНИЕ/ОСВОБОЖДЕНИЕ ОБЛАСТИ ПОРТА
ebx 0 зарезервировано 1 освобождено
ecx начало области порта
edx конец области порта
ret eax 0 удачно, 1 ошибка
47 = ОТОБРАЖЕНИЕ ЧИСЕЛ В ОКНЕ
ebx = тип вывода, bl=0 -> ecx - номер
bl=1 -> ecx - указатель
bh=0 -> отображение в десятичном виде
bh=1 -> отображение в шестнадцатиричном виде
bh=2 -> отображение в двоичном виде
bits 16-21 = номер цифр для вывода (0-32)
bits 22-31 = зарезервировано
ecx = номер или указатель
edx = x shl 16 + y
esi = цвет
48 = ОПРЕДЕЛЕНИЕ ОБЩИХ СВОЙСТВ ОКНА
ebx = 0 применить/перерисовать
ecx = 0 , применить/перерисовать desktop
ebx = 1 определение стиля кнопки
ecx = 0 , установка плоских кнопок
ecx = 1 , установка трехмерных кнопок
ebx = 2 определение оконных цветов
ecx = указатель на таблицу
edx = номер определенных байтов
ebx = 3 получение определенных оконных цветов
ecx = указатель на таблицу
edx = номер байтов для получения
49 = ОПРЕДЕЛЕНИЕ ВНУТРЕННИХ ПРЕРЫВАНИЙ ПРИЛОЖЕНИЯ
ebx = 0
ecx указатель на таблицу данных прерываний dword x 256
внутри приложения
ret : никаких изменений
50 = ОКНА ПРОИЗВОЛЬНОЙ ФОРМЫ И МАСШТАБА
ebx = 0 ; формирование ссылочной области
ecx = указатель на ссылочную область
байт на пиксель, 0 не используется, 1=используется, другие = зарезервированы
ebx = 1 ; масштаб ссылочной области (по-умолчанию 1:1)
ecx : установка масштаба в 2^ecx
ret : никаких изменений
51 = СОЗДАНИЕ ПОТОКА
ebx = 1 ; создание
ecx ; = точка входа потока
edx ; = позиция стэка потока
ret : никаких изменений
52 = СТАТУС СТЭКОВОГО ДРАЙВЕРА
- еще не готова
53 = ИНТЕРФЕЙС СОКЕТОВ
- еще не готова
54 = ПОЛЬЗОВАТЕЛЬСКИЕ СОБЫТИЯ
- еще не готова
55 = ЗВУКОВОЙ ИНТЕРФЕЙС
ebx = 0 ; загрузка 44 кГц 8 бит моно звукового блока
ecx ; = указатель на 65536-байтовый звуковой блок
ebx = 1 ; проигрывание 44 кГц 8 бит моно звукового блока
56 = ЗАПИСЬ ФАЙЛА НА ЖЕСТКИЙ ДИСК
ebx указатель на 12-ти символьное имя файла
ecx байты для записи
edx указатель на данные для записи
esi указатель на путь
path db 0
57 = УДАЛЕНИЕ ФАЙЛА С ЖЕСТКОГО ДИСКА
ebx указатель на имя файла : 11 заглавных букв
edx указатель на путь : path db 0
58 = ДОСТУП К СИСТЕМНОМУ ДЕРЕВУ
ebx указатель на блок файловой информации
fileinfo:
dd 0x0 ; 0=чтение (/запись/удаление/добавление)
dd 0x0 ; 512 блок для чтения 0+
dd 0x1 ; блоки для чтения (/байты для записи/добавление)
dd 0x20000 ; возвращение указателя данных
dd 0x10000 ; рабочая область для ОС - 16384 байт
db '/RAMDISK/FIRST/KERNEL.ASM',0 ; ASCIIZ каталог & имя файла
59 = ТРАССИРОВКА СИСТЕМНЫХ ВЫЗОВОВ ИЗ ПРОЦЕССОВ
ebx = 0 ; получение системных событий
ecx ; указатель на таблицу -> ; дескриптор 64 байты/системные вызовы
; +00 PID
; +32 EDI
; +36 ESI
; +40 EBP
; +44 ESP
; +48 EBX
; +52 EDX
; +56 ECX
; +60 EAX
edx ; номер байтов для возвращения в таблицу (к настоящему времени макс 16*64)
ret: eax = номер системных вызовов с начала
данные последнего системного вызова сохраняются
(eax mod 16)*64 в таблице
ebx = 0 : вышеописанный формат
-1 = КОНЕЦ ПРИЛОЖЕНИЯ
Программирование ядра MenuetOS
2a) Файлы ядра
KERNEL.ASM - KERNEL16.INC - BOOTCODE.INC загрузочная информация
- BOOTENG.INC язык загрузки
- KERNEL32.INC - ДИСПЛЕЙНЫЕ ФАЙЛЫ
- ЗВУКОВЫЕ ФАЙЛЫ
- ФАЙЛЫ ЖЕСТКОГО ДИСКА
Главный код ядра находится в файле KERNEL.ASM. Файлы Kernel16.inc и Kernel32.inc
содержат драйверы устройств.
В будущем появится отдельная установочная программа для выбора нужных драйверов.
Для точной настройки компьютера у пользователя будет скомпилированное ядро.
Ассемблер Fasm 1.30+ используется при компиляции ядра. На данный момент Fasm
запускается на таких платформах: Menuet, Dos, Windows и Wine/dosemu в Linux.
2b) Последовательность загрузки
После включении питания компьютера системный bios загружает загрузочный сектор
bootmosf.asm, который, в свою очередь, загружает файл ядра KERNEL.MNT с дискеты.
Для обозначения загрузки ядра, на экране отображается несколько точек. В случае,
если ваш компьютер зависает на этом этапе, значит, ваша дискета повреждена и
у загрузочного сектора возникают трудности при получении доступе к ней.
При удачной загрузке вы должны увидеть синий экран установки с надписью версии
MenuetOS. На этом этапе ядро выводит версию bios vesa и выдает запрос пользователю
сделать выбор видеорежима. Почти все современные карты поддерживают стандарт
VESA 3.0. Для работы в Menuet вполне хватает поддержки стандарта VESA 2.0 или
VESA 1.2. Для современных карт вы можете смело выбирать видеорежимы с поддержкой
VESA 2.0. Режимы работы некоторых видеокарт вы можете посмотреть в HardWare-листе.
Далее идут вопросы о подключении мыши, использовании MTRR и LFB. Ну с мышью
все понятно, просто выбираем интерфейс по которому она подсоеденина к компьютеру.
MTRR — это регистры определяющие тип кеширования памяти для процессоров Intel
Pentium Pro и выше (а также K6-2 и выше). Если у вас более поздний процессор,
чем Pentium Pro или K6-2, то выберите единичку. LFB — этот тип выбора определяет
по какому принципу (прямой/постраничный) будет происходить обмен с памятью вашей
видеокарты (только для VESA 2.0). Некоторые карты требуют постраничного обращения
к своей памяти.
Если вы загружаете Menuet в первый раз и вы не уверены, что настройки Menuet
подходят вам, первым делом отключите их. После отключения некоторых настроек
Menuet может работать медленнее, но при этом она будет полностью функциональной.
После того, как ваши выборы были переданы ОС, ядро загружается с дискеты и
переходит в графический режим, выбранный пользователем. На этом этапе ОС загружает
и устанавливает множество таблиц защищенных режимов и структуры, необходимые
для выполнения ОС.
'Fonts loaded'
Загружает набор шрифтов по-умолчанию для использования в приложениях.
'Reprogramming IRQs'
Перепрограммирует PIC для IRQ.
'Setting TSSs'
Устанавливает по-умолчанию сегменты переключения задач и указатели.
'Setting IDTs'
Создает IDT таблицу для прерываний.
'Reading CPUIDs'
Читает CPUID информацию для процессоров семейства pentium.
'Detecting devices'
Для определения жестких дисков и разделов в будущем.
'Setting timer'
Перепрограммирование таймера на 100 Гц.
'Setting mouse'
Программирование портов ps/2 / com для мыши.
'Setting window defaults'
Создание двух оконных стэков по-умолчанию.
'Calculating background'
По-умолчанию черный фон.
'Reserving IRQs & ports'
Резервирование IRQ и портов для использования операционной системой.
'Setting addresses for IRQs'
Считывание портов для IRQ.
'Setting OS task'
Определение задачи ОС TSS.
'Unmasking all IRQs'
Демаскировка всех IRQ вместе с CLI
'Reading TSC'
Чтение TSC / сек.
'Setting EGA/CGA 320x200 palette'
Установка палитры для режима 320x200.
'Setting VGA 640x480 palette'
Установка палитры для режима 640x480.
'Setting MTRR'
Программирование MTRR для более быстрой графики.
'All set - press ESC to start'
2c) Многозадачность
Menuet использует карусельную, вытесняющую многозадачность и многопотоковость
переключения задач с возможностью выборки данных жесткого реального времени.
Первой задачей списка процессов является задача ОС. Задача ОС отвечает за все
общие системные функции, такие как мышь, перемещение окон и завершение работы
процессов. Так как задача ОС является процессом, она не имеет преимуществ перед
другими процессами. Так что если вы замечаете, что ваша мышь начала двигаться
медленно — это результат высокой загрузки процессора.
Приложения загружаются в память начиная с 16 Mб (может изменяться). Системные
вызовы реализованы в прерывании 0x40 и номере функции, сохраненном в eax и возможных
других параметрах в других регистрах или таблице памяти. Так как Menuet имеет
монолитное ядро, задача, выполняемая на стороне ОС не отличается от задачи,
выполняемой на стороне приложения. Сегменты данных установлены в ноль, но стэковый
сегмент остается таким же, как при использования системных функций. Таким образом
приложение должно получить 1024 байт для использования стэка системных функций.
Структура выполнения процессов:
------------------------- ---------------------------
I Код ядра Menuet I I Загруженные задачи I
I ------------------- I I --------------------- I
I I I I
I Планировщик по I I Задачи 2, 3 .. I
I номеру IRQ 0 I I Любой аппаратный I
I Выделение времени I I доступ к ядру. I
I 1/100 для задачи I I I
I если время вышло - I I I
I change_task I I I
I Задача 1 : цикл ОС I ------- I Системные функции с I
I мышь, оконный стэк I I I вызовом int 0x40 I
I I I I I
I I I --> I возвращение к I
I INT 0x40 I <-- I I приложению I
I Системные функции I I I I
I I ------- I I
------------------------- ---------------------------
Планировщик на irq0 запрограммирован так, что запускается каждые 1/100 секунды.
Единственное исключение — вызов функции change_task. Например, вызов из приложения
delay или event_wait приводит к этому, и приложение не захватывает больше процессорного
времени, чем ему нужно.
Первое запущенное приложение сохраняется в 'firstapp' файла kernel.asm.
2d) Информация о процессах
Menuet использует сегменты переключения задач для сохранения содержимого процессов.
TSS сохраняются в памяти, начиная с 0x40000. Информация о работающих приложениях
сохраняется также по адресам:
004 dword номер процесса
3000 dword счетчик процессов
2000 -> 2FFF информация об окне (фрагментами по 32 байта)
; Первое окно по смещению 0x20
+00 dword Начало по x (Левый край)
+04 dword Начало по y (Верхний край)
+08 dword Размер по x (Ширина)
+0C dword Размер по y (Высота)
+10 dword Цвет рабочей области окна
+14 dword Цвет заголовка окна
+18 dword Цвет рамки окна
3000 -> 3FFF список задач (фрагментами по 32 байта)
; Первая задача по смещению 0x20
+00 dword маска событий приложения
+04 dword номер процесса
+0a word 0=запущенный, состояние 3=зомби, 4=завершенный
+0e byte номер окна на экране
+0f byte позиция в памяти
+10 dword точная позиция в памяти
+14 dword встречная сумма
+18 dword временная отметка счетчика
+1c dword использование процессора
8000 -> 8FFFF различная информация (фрагментами по 256 байт)
; Первый фрагмент по смещению 0x100
00 имя приложения
Планировщики используют команду jmp для переключения задач, их структура:
JMP -> TSS указатели в порядке запущенных приложений в GDT
(0x40000+) (gdts + app_data / app_code 2.->)
-> 1 ------------> селекторы данные / код
-> 2 --+ |------->
-> 3 --+ | ----->
-> 4 --+ | ----->
-> 5 --+ | ----->
-> 6 ----| ----->
.
.
Системное ядро занимает память с 0x0 до 8Мб, далее идет линейный рамочный буфер
экрана. Пространство между 12 и 16 Mб не используется, так как некоторые современные
компьютеры используют эту область для передач между устройствами. Таким образом
приложения загружаются в память, начиная с 16M. Тем не менее эти установки могут
изменяться, смотрите файл kernel.asm, set_variables.
За более детальной информацией об использовании памяти смотрите файл MEMMAP.INC.
2e) Графический интерфейс пользователя
Основы работы GUI выглядят следующим образом: каждое приложение на экране имеет
собственный идентификационный номер. Это позволяет располагать окна произвольным
образом.
Ссылки на GUI начинаются по адресу 0x400000 и каждый пиксель кодируется одним
байтом.
Допустим, у нас запущены ОС и два приложения: os=1, app1=2, app2=3.
В памяти это будет выглядеть так:
11111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111
11112222222222222222221111111111111111111111111
11112222222222222222221111111111111111111111111
11112222222222222222221111111111111111111111111
11112222222222333333333333333333333333311111111
11112222222222333333333333333333333333311111111
11112222222222333333333333333333333333311111111
11112222222222333333333333333333333333311111111
11112222222222333333333333333333333333311111111
11111111111111333333333333333333333333311111111
11111111111111333333333333333333333333311111111
11111111111111333333333333333333333333311111111
11111111111111333333333333333333333333311111111
11111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111
Почти всякий раз, когда приложение рисует что-нибудь на экране, оно сверяется
с данным участком в памяти. Такая техника работает быстрее, чем может показаться.
Узким местом текущей реализации GUI является система кэширования, которая еще
не оптимизирована. Графический интерфейс расположен по адресу 0x1C00000 (28Mб)
и отображается на видеобуфер графической карты.
2f) Добавление системных вызовов
Когда прерывание 0x40 получает номер функции в eax, i40 вызывает функцию, определенную
в таблице ядра servetable.
Значения регистров смещаются следующим образом: EBX переходит в EAX, ECX в
EBX, EDX в ECX, ESI в ECX, EDI в ESI.
Все параметры возвращаются приложению путем модификации стэка, например:
mov [esp+36],eax ; возврат eax для приложения
mov [esp+24],ebx ; ebx
mov [esp+32],ecx ; ecx
mov [esp+28],edx ; edx
ret ; возвращение в сохраненное приложение
Заметьте также, что выполнение команды ret следует сразу после того, как возвращенные
данные сохраняются в стэке.
Для того, что бы вычислить физический адрес приложения, вызвавшего функцию,
следует проделать следующее:
mov esi,[0x3010] ; Помещаем в eax адрес таблицы приложения
mov edi,[esi+0x10] ; Помещаем в edi физический адрес начала
; приложения в памяти
Это бывает необходимо при передачи в приложение большого объема информации.
Тогда просто нужно выделить для приложения свободное место под массив, и зная
начало приложения в памяти, вычислить адрес массива и записать туда информацию.
Copyright © 2003 Перевод Андрей С. Кухар.