Компьютерные подсказки

Вылетает Сталкер: Зов Припяти Программа икс рей 1

Stalker lost alpha гид по прохождению

Pony Express отслеживание почтовых отправлений

Pony Express – время и сроки доставки с Алиэкспресс в Россию

Застряли посылки с Алиэкспресс со статусом Hand over to airline: что делать?

РФ (Nigma) — интеллектуальная поисковая система

Данные для семантики — Яндекс Вордстат

Пиар ВКонтакте при помощи бирж: особенности и использование

Почему я не могу отправить сообщение?

Предупреждение «Подключение не защищено» в Google Chrome по протоколу https Нарушена конфиденциальность данных яндекс браузер

Всё что известно о смартфоне Samsung Galaxy S9 Аккумуляторная батарея Galaxy S9 и мощность

Темы оформления и русификация форума SMF, а так же установка компонента JFusion в Joomla

Автоматическое определение движка форума Позже board powered by smf

Коды в игре скайрим - зелья, ингредиенты, заклинания Код на ингредиенты скайрим

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

Что такое Protected Mode и с чем его едят. Ассемблер переходит в защищенный режим с помощью GDT Переход в защищенный режим ассемблер

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

Режимы работы процессора Intel 80386

С появлением процессора Intel 80386 возникла архитектура IA32 . Она предполагала появление нового режима работы процессора - защищённого ("Protected Mode "). Для совместимости с предыдущими процессорами линейки Intel 80x86 процессор 80386 не запускался сразу в защищённом режиме, а работал в так называемом реальном режиме ("Real Mode "). Кроме этого, у каждого режима есть один или несколько подрежимов. Разберём их.

Реальный режим

В этом режиме процессор находится сразу после нажатия кнопки "Power " компьютера. Доступ к памяти в реальном режиме осуществляется конструкцией "сегмент:смещение ", которая описывает логический адрес. Значение сегмента, как и смещения, лежит в пределах от 0 до 0FFFFh .

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

линейный адрес = сегмент * 16 + смещение

В реальном режиме процессоров 80186 и 8086 значение сегмента лежало в пределах от 0 до 0F000h . Таким образом, максимальный выставленный адрес на адресную шину равен 0FFFFFh , что соответствует (2^20)-1 , т.е. 1 мегабайту.

Конечно, поначалу объём такой памяти казался колоссальным, но со временем одного мегабайта стало не хватать. С появлением процессора 80286 стал доступен так называемый блок памяти UMB , начинающийся с адреса 0FFFFh:0010h и заканчивающийся адресом 0FFFFh:0FFFFh (65520 байт за пределами одного мегабайта). Теперь можно было переконфигурировать операционную систему MS-DOS так, чтобы она занимала этот блок, освобождая в оперативной памяти 64 килобайт.

Защищённый режим

Этот режим имеет сложную конструкцию по сравнению с реальным. Логический адрес представляется конструкцией "селектор:смещение ". Селектор находится в пределах от 0 до 0FFFFh (на самом деле, селекторов в 4 раза меньше - об этом подробнее в следующих статьях). Смещение, в отличие от реального режима, является 32-разрядным, что позволяет адресовать сегменты размером 4 гигабайт. Логический адрес преобразуется в линейный по следующей схеме:

линейный адрес = база сегмента + смещение

Линейный адрес в дальнейшем выставляется на адресную шину, если не включен режим страничной адресации. В противном случае линейный адрес преобразуется в физический, и только после этого выставляется на адресную шину. Кроме этого, защищённый режим позволяет организовать виртуальную память, достигающую размера до 64 терабайт и зависящую лишь от объёма жёсткого диска (например, тот же файл подкачки в Windows реализует виртуальную память). В защищённом режиме функционируют практически все современные операционные системы.

Мультизадачный подрежим защищённого режима

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

Режим виртуального 8086

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

Нереальный режим

Это особый подрежим реального режима. Процессор находится в реальном режиме, но адресуется к памяти путём конструкции "селектор: смещение ". Таким образом доступна память выше 1 мегабайта. В дальнейшем будет рассматриваться защищённый режим и его подрежимы.

Первая программа: переход в защищённый режим

Переход в защищённый режим осуществляется установкой бита 0 регистра CR0 . Переход в реальный режим осуществляется сбросом того же нулевого бита. Рассмотрим программу, которая выполняет эту операцию (язык - Flat Assembler ):

use16 ; Используются 16-разрядные команды

org 100h

Start:

; Мы находимся в реальном режиме

mov eax, cr0 ; Считываем значение регистра CR0
or al, 1 ; Устанавливаем нулевой бит
mov cr0, eax ; Записываем новое значение CR0

; Мы в защищённом режиме

mov eax, cr0 ; Считываем значение CR0
and al, 0feh ; Сбрасваем нулевой бит в 0
mov cr0, eax ; Переходим в реальный режим

; Мы в реальном режиме

ret ; Выходим из программы

Однако эта программа является совсем "сырой", потому что в ней нельзя реализовать зацикливание. Если написать команды вроде hlt или jmp $ , то по срабатыванию первого же прерывания компьютер перезагрузится.

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

1. проверить, возможно ли переключиться в защищённый режим;

2. инициализировать таблицы дескрипторов;

3. запретить прерывания (как маскируемые, так и не маскируемые);

4. открыть линию A20;

5. загрузить регистры управления памятью;

7. выполнить переход на 32-битный сегмент кода, переопределив регистр CS.

Но первой программе достаточно выполнить действия 3, 4, 5. Тогда её зацикливание не приведёт к перезагрузке компьютера. Разберёмся в каждом действии.

Запрещение прерываний предохраняет нас от перезагрузки. Прерывания делятся как на маскируемые, так и немаскируемые. Чтобы запретить маскируемые прерывания, надо сбросить флаг IF регистра EFLAGS командой cli , разрешение же прерываний выполняется командой sti . Немаскируемые прерывания запрещаются несколько иначе. Для этого существуют два способа: программирование регистров контроллера прерываний (этот способ будет рассмотрен несколько позже) или изменение седьмого бита порта 70h : если бит установлен, то прерывания запрещены, если бит сброшен - прерывания могут выполняться.

Теперь зададимся вопросом, в чём заключается функция линии A20 , и что это такое. Линия A20 - одна из 32 адресных линий. При загрузке компьютера линия A20 закрыта. Это приводит к генерации 20-разрядных адресов (то есть всё адресное пространство получается равным (2^20)=1 мегабайт). Введено это для совместимости с процессором 8086 : таким образом, пытаясь записать по линейному адресу 12345678h , мы, на самом деле, запишем по адресу 00045678h , что может привести к совершенно неожиданному результату. Поэтому для полноценного функционирования 32-разрядного приложения линия A20 обязательно должна быть открыта. Осуществляется это установкой бита 1 порта 92h , закрытие линии A20 - сброс этого бита.

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

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

org 100h

Start:

; Мы находимся в реальном режиме

cli ;*

in al, 70h ;*
or al, 80h ;*
out 70h , al ;*

; Открыть линию A20

in al, 92h ;*
or al, 2 ;*
out 92h , al ;*

mov eax, cr0
or al, 1
mov cr0, eax


; Небольшой двойной цикл

mov cx, 20 ;*

cycle: ;*
mov ax, cx ;*
mov cx, 0ffffh ;*
loop $ ;*
mov cx, ax ;*
loop cycle ;*

mov eax, cr0
and al, 0feh
mov cr0, eax

; Закрыть линию A20

in al, 92h ;*
and al, 0fdh ;*
out 92h , al ;*

in al, 70h ;*
and al, 7fh ;*
out 70h , al ;*

sti ;*

ret ; завершить программу

Убедиться в том, что программа работает, можно, запустив исполняемый файл из чистого MS-DOS . Если программа корректно завершается, то всё в порядке.

Однако могут возникнуть и следующие проблемы:

1. компьютер "зависает";

2. компьютер перезагружается.

Это может возникнуть из-за следующих причин:

1. программа запущена в режиме V86 (режим виртуального 8086 );

2. программа запущена в защищённом режиме или под конкретной операционной системой.

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

Проверка возможности перехода в защищённый режим

В предыдущей главе у нас возникла следующая проблема: программа не распознаёт то, что она находится в защищённом режиме или режиме V86 , что приводит к зависанию системы или её перезагрузке. Если же мы попытаемся запустить программу под управлением операционной системы Windows , то Windows отловит попытку программы перейти в защищённый режим и предложит перезагрузиться в режиме эмуляции MS-DOS (для платформы 9x ), либо завершит работу программы принудительно (платформа NT ).

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

1. проверить нулевой бит регистра CR0 ;

2. убедиться, что не загружена операционная система Windows.

Первая операция осуществляется прямым чтением регистра CR0 с дальнейшей проверкой нулевого бита регистра EAX , AX или AL . Если бит не установлен, то мы находимся в реальном режиме. В противном случае дальнейшее выполнение программы становится бессмысленным.

Второе действие осуществляется вызовом функции 1600h прерывания 2fh . Эта функция позволяет получить текущую версию операционной системы Windows . Если после вызова функции в регистре AL содержится ноль, то операционная система не загружена. В противном случае, опять же, нашей программе бессмысленно продолжать какие-либо дальнейшие действия.

Рассмотрим пример следующей программы. Она является модификацией предыдущей программы, все новые инструкции помечены звёздочкой (*).

Org 100h

Start:

; Настроить сегментные регистры

mov ax, cs ;*
mov ds, ax ;*

; Проверка того, что мы действительно в реальном режиме

mov eax, cr0 ;* проверка нулевого бита
test al, 1 ;* регистра CR0
jz no_pm ;*

mov ah, 09h ;* функция DOS 09h
mov dx, pm_msg ;* вывод строки
int 21h ;*
ret ;* и выйти

no_pm:
; Проверка: не запущена ли программа под Windows

mov ax, 1600h ;* функция 1600h мультиплексорного
int 2fh ;* прерывания - получить версию Windows
test al, al ;* если не 0 - ошибка
jz no_windows

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

mov ah, 09h ;*
mov dx, win_msg ;*
int 21h ;*
ret ;*

no_windows:
; Мы точно находимся в реальном режиме
; Запретить маскируемые прерывания

; Запретить немаскируемые прерывания (NMI)

in al, 70h
or al, 80h
out 70h , al

; Открыть линию A20

in al, 92h
or al, 2
out 92h , al

; Переключиться в защищённый режим

mov eax, cr0
or al, 1
mov cr0, eax

; Теперь находимся в защищённом режиме
; Небольшой двойной цикл

mov cx, 20

cycle:
mov ax, cx
mov cx, 0ffffh
loop $
mov cx, ax
loop cycle

; Переключиться в реальный режим

mov eax, cr0
and al, 0feh
mov cr0, eax

; Закрыть линию A20

in al, 92h ;*
and al, 0fdh ;*
out 92h , al ;*

; Разрешить немаскируемые прерывания (NMI)

in al, 70h ;*
and al, 7fh ;*
out 70h , al ;*

; Разрешить маскируемые прерывания

sti ;*

; Мы снова находимся в реальном режиме

ret ; завершить программу

; Сообщения об ошибках

pm_msg: ;*
db "Error: already running in protected mode!$" ;*
win_msg: ;*
db "Error: Microsoft Windows detected!$" ;*
Этот пример тоже пока не показывает реализацию 32-разрядных команд. Для этого следует ещё познакомиться с материалом последующей главы. Кроме этого, у примера есть следующий недостаток: вызываются функции DOS (int 21h ), что уже начинает противоречить независимости нашей программы от операционной системы MS-DOS . В дальнейшем придётся избавиться от использования функций операционной системы и перейти к использованию функций BIOS . Но пока достаточно ограничиться и таким кодом.

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

Ситуация такова:

У меня есть программа, загруженная в 0x7e0, которая переключает CPU в защищенный режим и переходит к соответствующей метке в коде:

[...] code to switch CPU in Protected Mode [...] jmp ProtectedMode [...] bits 32 ProtectedMode: .halt: hlt jmp .halt

Пока все работает отлично. «Jmp ProtectedMode» работает без явного далекого перехода, чтобы очистить очередь предварительной выборки, поскольку эта программа загружается со смещением 0 (org 0 в начале), заставляя сегмент кода указывать на нужное место.

Моя текущая проблема заключается в том, что в ярлыке «ProtectedMode» я хочу перейти к другой программе, загруженной с 0x8000 (я проверил это с дампом памяти, функция загрузки работала правильно и программа загружалась правильно до 0x8000) ,

Поскольку CPU теперь находится в ProtectedMode, а не в RealMode, схема адресации отличается. ProtectedMode использует дескрипторы для поиска базового адреса и лимита в таблице дескриптора, чтобы добавить данное смещение и получить физический адрес (как я понял). Поэтому перед входом ProtectedMode необходимо было установить GDT.

Мой выглядит следующим образом:

%ifndef __GDT_INC_INCLUDED__ %define __GDT_INC_INCLUDED__ ;********************************* ;* Global Descriptor Table (GDT) * ;********************************* NULL_DESC: dd 0 ; null descriptor dd 0 CODE_DESC: dw 0xFFFF ; limit low dw 0 ; base low db 0 ; base middle db 10011010b ; access db 11001111b ; granularity db 0 ; base high DATA_DESC: dw 0xFFFF ; data descriptor dw 0 ; limit low db 0 ; base low db 10010010b ; access db 11001111b ; granularity db 0 ; base high gdtr: Limit dw 24 ; length of GDT Base dd NULL_DESC ; base of GDT %endif ;__GDT_INC_INCLUDED__

и загружается в регистр GDT через

Lgdt

То, что я до сих пор не понял, как мне теперь перейти на физический адрес 0x8000 в ProtectedMode с помощью GDT?

Мои первые мысли заключались в том, чтобы выбрать дескриптор кода (CODE_DESC), который должен указывать на 0x7e00 (были загружены текущие программы) и использовать смещение, необходимое для получения 0x8000 (512 байт), в результате чего команда перехода:

Jmp CODE_DESC:0x200

Но это не работает.

Jmp 0x7e0:0x200

тоже не работает...

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

Полный код:

Bits 16 org 0 ; loaded with offset 0000 (phys addr: 0x7e00) jmp Start Start: xor ax, ax mov ax, cs mov ds, ax ; update data segment cli ; clear interrupts lgdt ; load GDT from GDTR (see gdt_32.inc) call OpenA20Gate ; open the A20 gate call EnablePMode ; jumps to ProtectedMode ;****************** ;* Opens A20 Gate * ;****************** OpenA20Gate: in al, 0x93 ; switch A20 gate via fast A20 port 92 or al, 2 ; set A20 Gate bit 1 and al, ~1 ; clear INIT_NOW bit out 0x92, al ret ;************************** ;* Enables Protected Mode * ;************************** EnablePMode: mov eax, cr0 or eax, 1 mov cr0, eax jmp ProtectedMode ; this works (jumps to label and halts) ;jmp (CODE_DESC-NULL_DESC):ProtectedMode ; => does not work ;jmp 08h:ProtectedMode , => does not work ;*************** ;* data fields * ;* &includes * ;*************** %include "gdt_32.inc" ;****************** ;* Protected Mode * ;****************** bits 32 ProtectedMode: ;here I want to jump to physical addr 0x8000 (elf64 asm program) .halt: hlt jmp .halt

11

Все процессоры Intel , начиная с i80286 и до последних включительно, по включению электропитания (после начального «сброса») работают в режиме реального адреса (реальном режиме). Обычно реальный режим используется либо как промежуточный для перехода в защищенный режим после инициализации микропроцессорной системы, либо для более быстрого выполнения программ, написанных для микропроцессоров 8086 , 80186 , но, по сравнению с 8086 , 80186 , современные микропроцессоры в реальном режиме имеют более широкий набор выполняемых команд и возможность обработки 32-разрядных операндов.
Переключение процессора в защищенный режим из реального осуществляется загрузкой в CR0 (рис. 1) слова с единичным значением бита РЕ (Protect Enable ). Для совместимости с ПО для 80286 бит РЕ может быть установлен также инструкцией LMSW. До переключения в памяти должны быть проинициализированы необходимые таблицы дескрипторов IDT и GDT . Сразу после включения защищенного режима процессор имеет CPL = 0 .

Рис. 1

Для всех 32-разрядных процессоров рекомендуется выполнять следующую последовательность действий для переключения в защищенный режим:
1. Запретить маскируемые прерывания сбросом флага IF, а возникновение немаскируемых прерываний блокировать внешней логикой Программный код на время «переходного периода» должен гарантировать отсутствие исключений и не использовать программных прерываний. Это требование вызвано сменой механизма вызова обработчиков прерываний.
2. Загрузить в GDTR базовый адрес GDT (инструкцией LGDT).
3. Инструкцией MOV CRO установить флаг РЕ, а если требуется страничное управление памятью, то и флаг PG.
4. Сразу после этого должна выполняться команда межсегментного перехода (JMP Far ) или вызова (CALL Far ) для очистки очереди инструкций декодированных в реальном режиме, и выполнения сериализации процессора. Если включается страничное преобразование, то коды инструкций MOV CRO и JMP или CALL должны находиться в странице, для которой физический адрес совпадает с логическим (для кода, которому передается управление, это требование не предъявляется).
5. Если планируется использование локальной таблицы дескрипторов, инструкцией LLDT загрузить селектор сегмента для LDT в регистр LDTR
6. Инструкцией LTR загрузить в регистр задач селектор TSS для начальной задачи защищенного режима.
7. Перезагрузить сегментные регистры (кроме CS), содержимое которых еще относится к реальному режиму, или выполнить переход или вызов другой задачи (при этом перезагрузка регистров произойдет автоматически). В неиспользуемые сегментные регистры загружается нулевое значение селектора.
8. Инструкцией LIDT загрузить в регистр IDTR адрес и лимит IDT — таблицы дескрипторов прерываний защищенного режима.
9. Разрешить маскируемые и немаскируемые аппаратные прерывания

рПУМЕ ОЕДЕМШОПЗП РЕТЕТЩЧБ ЧОПЧШ НПЗХ ПВТБДПЧБФШ ЧБУ ОПЧЩН ЧЩРХУЛПН УЧПЕК ТБУУЩМЛЙ.

йФБЛ, ЛБЛ ЧЩ РПНОЙФЕ, Ч ТЕБМШОПН ТЕЦЙНЕ ТБВПФЩ РТПГЕУУПТБ ОБН ДПУФХРЕО ЧУЕЗП МЙЫШ 1 НЕЗБВБКФ БДТЕУОПЗП РТПУФТБОУФЧБ (ЙЪ ЛПФПТЩИ ПВЩЮОПК РБНСФША СЧМСЕФУС ЧУЕЗП МЙЫШ 640 ЛЙМПВБКФ). фБЛ ЬФП Й ВЩМП ЧП ЧТЕНЕОБ РЕТЧЩИ РТПГЕУУПТПЧ ЧТПДЕ 8086, ОП РПУФЕРЕООП ПВЯЈНБ ПРЕТБФЙЧОПК РБНСФЙ УФБМП ОЕ ИЧБФБФШ. ч ФП ЦЕ ЧТЕНС ФТЕВПЧБМПУШ УПИТБОЙФШ РПМОХА ПВТБФОХА УПЧНЕУФЙНПУФШ, ЮФПВЩ 16-ТБЪТСДОЩЕ ПРЕТБГЙПООЩЕ УЙУФЕНЩ ЧТПДЕ DOS УНПЗМЙ ОПТНБМШОП ТБВПФБФШ. рПЬФПНХ ВЩМ ЧЧЕДЈО ОПЧЩК ТЕЦЙН ТБВПФЩ РТПГЕУУПТБ - ЪБЭЙЭЈООЩК ТЕЦЙН. рПУМЕ РЕТЕИПДБ Ч ОЕЗП ДМС БДТЕУБГЙЙ ЙУРПМШЪХЕФУС ОЕ 16, Б 32 ЙМЙ ДБЦЕ 64 ВЙФБ, Б УЕЗНЕОФЩ Ч УФБТПН РПОЙНБОЙЙ ЙУЮЕЪБАФ. фБЛЦЕ ДПВБЧМСАФУС ЪБЭЙФОЩЕ НЕИБОЙЪНЩ (ЙНЕООП РПЬФПНХ ЪБЭЙЭЈООЩК ТЕЦЙН), ЮФПВЩ СДТП пу ВЩМП ЙЪПМЙТПЧБООП ПФ РТЙМПЦЕОЙК Й НПЗМП ЙНЙ УЧПВПДОП ХРТБЧМСФШ. ьФП ОЕПВИПДЙНП МАВПК РПМОПГЕООПК НОПЗПЪБДБЮОПК УЙУФЕНЕ.

оБЮОЈН У ФПЗП, ЮФП ДПВБЧМСЕФУС Ч ТЕБМШОПН ТЕЦЙНЕ ОБ РТПГЕУУПТБИ, ЛПФПТЩЕ РПДДЕТЦЙЧБАФ 32-ВЙФОЩК БДТЕУ (i386 Й ЧЩЫЕ) - ДПВБЧМСАФУС ОПЧЩЕ ТЕЗЙУФТЩ, ЧЕТОЕЕ ТБУЫЙТСАФУС УФБТЩЕ: EAX, EBX, ECX, EDX, ESP, EBP, EIP, ESI, EDI, EFLAGS. лБЛ НПЦОП ДПЗБДБФШУС, ЬФП 32-ВЙФОЩЕ ЧЕТУЙЙ ПВЩЮОЩИ ТЕЗЙУФТПЧ ТЕБМШОПЗП ТЕЦЙНБ (Л ЙНЕОЙ ТЕЗЙУФТБ ДПВБЧМСЕФУС РТЙУФБЧЛБ "E"). чУЕ ЬФЙ 32-ВЙФОЩЕ ТЕЗЙУФТЩ ЛТПНЕ EIP ДПУФХРОЩ Й Ч ТЕБМШОПН ТЕЦЙНЕ, ОП Ч ФБЛПН УМХЮБЕ ВХДХФ ЪБОЙНБФШ ОБ 1 ВБКФ ВПМШЫЕ (Л ОЙН ДПВБЧМСЕФУС УРЕГЙБМШОЩК РТЕЖЙЛУ). оБ РТПГЕУУПТЕ НПМПЦЕ 286 ЬФЙ ЛПНБОДЩ ВХДХФ ОЕЛПТТЕЛФОЩ. нЩ НПЦЕН, ОБРТЙНЕТ, ОБРЙУБФШ mov eax, 0x12345678 Й РПУМЕ ЬФПЗП Ч AX ВХДЕФ 0x5678, РПФПНХ ЮФП ПО ЛБЛ ВЩ СЧМСЕФУС "ПЛОПН" Ч НМБДЫХА ЮБУФШ ТЕЗЙУФТБ EAX (БОБМПЗЙЮОП, AL НМБДЫБС ЮБУФШ AX). тЕЗЙУФТБ-ПФПВТБЦЕОЙС УФБТЫЕК ЮБУФЙ 32-ВЙФОЩИ ТЕЗЙУФТПЧ ОЕ УХЭЕУФЧХЕФ - НПЦОП ЕЈ ЙЪЧМЕЮШ ФПМШЛП У РПНПЭША БТЙЖНЕФЙЛЙ (ОБРТЙНЕТ, УДЧЙОХФШ EAX ОБ 16 ВЙФ ЧРТБЧП У РПНПЭША shr eax, 16, ФПЗДБ Ч AX ВХДЕФ УФБТЫБС РПМПЧЙОБ, ОП УПДЕТЦЙНПЕ НМБДЫЙИ ВЙФ ВХДЕФ ХФЕТСОП). юФП ИБТБЛФЕТОП, Ч ЪБЭЙЭЈООПН ТЕЦЙНЕ ОБПВПТПФ, ЛПНБОДЩ ТБВПФЩ У 16-ВЙФОЩНЙ ТЕЗЙУФТБНЙ (ОП ОЕ 8-ВЙФОЩНЙ) ФТЕВХАФ РТЕЖЙЛУ, РПЬФПНХ ОЕУНПФТС ОБ ФП, ЮФП ТБЪТСДОПУФШ Ч ДЧБ ТБЪБ ВПМШЫЕ, Ч ЪБЭЙЭЈООПН ТЕЦЙНЕ ВЩУФТЕЕ ЧЩРПМОСАФУС Й ЪБОЙНБАФ НЕОШЫЕ НЕУФБ ЙНЕООП ЛПНБОДЩ 32-ВЙФОПК БТЙЖНЕФЙЛЙ.

фБЛЦЕ, ФЕРЕТШ Х ОБУ ОБ 2 УЕЗНЕОФОЩИ ТЕЗЙУФТБ ВПМШЫЕ - GS Й FS. тБВПФБ У ОЙНЙ РПМОПУФША БОБМПЗЙЮОБ DS Й ES Й ЧЩ НПЦЕФЕ ЙИ УЧПВПДОП ЙУРПМШЪПЧБФШ Ч ТЕБМШОПН ТЕЦЙНЕ. пФМЙЮЙЕ ФПМШЛП Ч ФПН, ЮФП ОЙЛБЛЙЕ ЛПНБОДЩ ЙИ СЧОП ОЕ РПДТБЪХНЕЧБАФ (DS ЙУРПМШЪХЕФУС РП ХНПМЮБОЙА РТБЛФЙЮЕУЛЙ ЧУЕНЙ ЛПНБОДБНЙ Й ОЕЛПФПТЩНЙ УФТПЛПЧЩНЙ ПРЕТБГЙСНЙ, ES ОЕЛПФПТЩНЙ УФТПЛПЧЩНЙ ПРЕТБГЙСНЙ) Й ОБДП СЧОП ХЛБЪЩЧБФШ, ЮФП ЧЩ ИПФЙФЕ ПВТБЭБФШУС ЮЕТЕЪ ОЙИ. оБРТЙНЕТ, mov ax, .

рПНЙНП ЬФПЗП ТБУЫЙТЕОЙС ТЕЗЙУФТПЧ, ДПВБЧМСАФУС ОПЧЩЕ ХРТБЧМСАЭЙЕ ТЕЗЙУФТЩ (ТБОШЫЕ ЧМЙСМ ОБ ТЕЦЙН ТБВПФЩ РТПГЕУУПТБ ФПМШЛП FLAGS) - CR0, CR2, CR3 Й CR4. еУФШ Й ДТХЗЙЕ (ОБРТЙНЕТ, ПФМБДПЮОЩЕ ТЕЗЙУФТЩ), ОП ПОЙ ОБУ УЕКЮБУ ОЕ ЙОФЕТЕУХАФ. йНЕООП У РПНПЭША ЬФЙИ ТЕЗЙУФТПЧ РТПЙЪЧПДЙФУС РЕТЕЛМАЮЕОЙЕ РТПГЕУУПТБ Ч ЪБЭЙЭЈООЩК ТЕЦЙН Й ОБУФТПКЛБ ОПЧЩИ ЖХОЛГЙК ЧТПДЕ УФТБОЙЮОПК БДТЕУБГЙЙ. пОЙ ДПУФХРОЩ Ч ТЕБМШОПН ТЕЦЙНЕ.

ч ЪБЭЙЭЈООПН ТЕЦЙНЕ РПОСФЙЕ УЕЗНЕОФБ ЙЪНЕОСЕФУС. фЕРЕТШ ЬФП ОЕ РТПУФП ВБЪПЧЩК БДТЕУ, Б ОПНЕТ ЬМЕНЕОФБ (ДЕУЛТЙРФПТБ УЕЗНЕОФБ) Ч УРЕГЙБМШОПК ФБВМЙГЕ. фБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ УПЪДБЈФУС ПРЕТБГЙПООПК УЙУФЕНПК Й НПЦЕФ УПДЕТЦБФШ ОЕПВИПДЙНПЕ ЛПМЙЮЕУФЧП ПРЙУБОЙК УЕЗНЕОФПЧ ЪБЭЙЭЈООПЗП ТЕЦЙНБ. лБЦДЩК ЬМЕНЕОФ ФБВМЙГЩ ЪБОЙНБЕФ 8 ВБКФ Й Ч УРЕГЙБМШОПН ЖПТНБФЕ ПРЙУЩЧБЕФ ВБЪПЧЩК БДТЕУ УЕЗНЕОФБ, ТБЪНЕТ, РТБЧБ ДПУФХРБ Й Ф. Д.

уЕЗНЕОФЩ ЪБЭЙЭЈООПЗП ТЕЦЙНБ ДЕМСФУС ОБ ДЧБ ФЙРБ - УЕЗНЕОФЩ ЛПДБ Й УЕЗНЕОФЩ ДБООЩИ (ОБ УБНПН ДЕМЕ ЕУФШ ЕЭЈ ЧУСЛЙЕ TSS Й LDT, ОП РПЛБ ПОЙ ОБН ОЕ ЧБЦОЩ ФПЦЕ). ч CS НПЦОП ЪБЗТХЦБФШ ФПМШЛП ОПНЕТБ ДЕУЛТЙРФПТПЧ, ПРЙУБООЩИ ЛБЛ УЕЗНЕОФ ЛПДБ, Ч ПУФБМШОЩЕ УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ НПЦОП ЪБЗТХЦБФШ МАВЩЕ УЕЗНЕОФЩ - ЛБЛ ДБООЩИ, ФБЛ Й ЛПДБ. чБЦОБС ТБЪОЙГБ Ч ФПН, ЮФП УЕЗНЕОФ ЛПДБ НПЦОП ФПМШЛП ЮЙФБФШ Й ЙУРПМОСФШ, Б УЕЗНЕОФ ДБООЩИ ФПМШЛП ЮЙФБФШ Й РЙУБФШ. л УЮБУФША, УЕЗНЕОФЩ НПЗХФ РЕТЕЛТЩЧБФШУС Ч РБНСФЙ, РПЬФПНХ НПЦОП УПЪДБФШ ДЧБ ДЕУЛТЙРФПТБ, УУЩМБАЭЙЕУС ОБ ПДЙО Й ФПФ ЦЕ ТЕЗЙПО РБНСФЙ, ОП ПДЙО ЙЪ ОЙИ УДЕМБФШ ЙУРПМОСЕНЩН, Б ДТХЗПК ДПУФХРОЩН ДМС ЪБРЙУЙ.

оЕУНПФТС ОБ РПДДЕТЦЛХ УЕЗНЕОФБГЙЙ, ПОБ УЮЙФБЕФУС ХУФБТЕЧЫЕК. оЙ Windows, ОЙ Linux ОЕ ЙУРПМШЪХАФ ЕЈ Ч РПМОПК НЕТЕ, Б ОБ ПФМЙЮОЩИ ПФ x86 БТИЙФЕЛФХТЩ (ОБРТЙНЕТ, ARM) ПОБ ЧПЧУЕ ПФУХФУФЧХЕФ. дМС ТБЪЗТБОЙЮЕОЙС ДПУФХРБ Л РБНСФЙ ЙУРПМШЪХЕФУС ЗПТБЪДП ВПМЕЕ ЗЙВЛЙК НЕИБОЙЪН УФТБОЙЮОПК БДТЕУБГЙЙ, ЛПФПТЩК НЩ ТБУУНПФТЙН ДБМЕЕ. юФПВЩ ЙЪВБЧЙФШУС ПФ УЕЗНЕОФБГЙЙ пу РТПУФП ПРЙУЩЧБЕФ ФБВМЙГХ ЙЪ ДЧХИ ДЕУЛТЙРФПТПЧ, Х ЛБЦДПЗП ЙЪ ЛПФПТЩИ ВБЪПЧЩК БДТЕУ 0, Б ТБЪНЕТ 4 зв (НБЛУЙНБМШОЩК ТБЪНЕТ БДТЕУХЕНПК РБНСФЙ Ч 32-ВЙФОПН ТЕЦЙНЕ). ч ФБЛПН УМХЮБЕ ЗПЧПТСФ, ЮФП НЩ ЧЛМАЮЙМЙ ТЕЦЙН МЙОЕКОЩИ БДТЕУПЧ - УНЕЭЕОЙЕ УППФЧЕФУФЧХЕФ ЖЙЪЙЮЕУЛПНХ БДТЕУХ. ьФП ПЮЕОШ ХДПВОП Й С РПКДХ РП ФПНХ ЦЕ РХФЙ. оЕ УМЕДХЕФ РЩФБФШУС ЙУРПМШЪПЧБФШ УЕЗНЕОФБГЙА Ч УЧПЕК ПРЕТБГЙПООПК УЙУФЕНЕ - ЬФП УЙМШОП ХУМПЦОСЕФ ЛПД СДТБ, СЪЩЛЙ ЧЩУПЛПЗП ХТПЧОС (ОБРТЙНЕТ, у ЙМЙ у++) ОЕ РПДДЕТЦЙЧБАФ УЕЗНЕОФБГЙА (ФП ЕУФШ ЧЩ УНПЦЕФЕ РПМОПГЕООП РТПЗТБННЙТПЧБФШ ФПМШЛП ОБ Assembler) Й, ОБЛПОЕГ, ЧЩ ОЕ УНПЦЕФЕ РЕТЕОЕУФЙ УЙУФЕНХ ОБ ДТХЗХА БТИЙФЕЛФХТХ, РПФПНХ ЮФП x86 ЕДЙОУФЧЕООБС, ЛПФПТБС ХНЕЕФ ЬФПФ НЕИБОЙЪН (Й ФП, Ч 64-ВЙФОПН ТЕЦЙНЕ РПМС ВБЪПЧПЗП БДТЕУБ Й ТБЪНЕТБ УЕЗНЕОФБ ЙЗОПТЙТХАФУС, Б ЙУРПМШЪХЕФУС МЙЫШ ЙОЖПТНБГЙС П РТБЧБИ ДПУФХРБ).

лБЛ С ХЦЕ УЛБЪБМ, ФБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ЖПТНЙТХЕФУС УБНПК ПРЕТБГЙПООПК УЙУФЕНПК. юФПВЩ ХЛБЪБФШ РТПГЕУУПТХ, ЗДЕ ПОБ ОБИПДЙФУС ЙУРПМШЪХЕФУС УРЕГЙБМШОБС ЛПНБОДБ - lgdt (Load Global Descriptor Table). пОБ РТЙОЙНБЕФ 6-ВБКФПЧХА РЕТЕНЕООХА Ч РБНСФЙ. рЕТЧЩЕ ЕЈ 16 ВЙФ УПДЕТЦБФ ТБЪНЕТ ФБВМЙГЩ Ч ВБКФБИ (ФБЛЙН ПВТБЪПН, НБЛУЙНБМШОПЕ ЛПМЙЮЕУФЧП ДЕУЛТЙРФПТПЧ - 65536 / 8 = 8192), РПУМЕДХАЭЙЕ 32 ВЙФБ - ВБЪПЧЩК МЙОЕКОЩК БДТЕУ Ч РБНСФЙ УБНПК ФБВМЙГЩ (ФП ЕУФШ ВЕЪ ХЮЈФБ ЧУЕИ УЕЗНЕОФПЧ). йНЕЕФ УНЩУМ ЧЩТБЧОСФШ ОБЮБМП ФБВМЙГЩ ОБ 16 ВБКФ, РПФПНХ ЮФП ЬФП ХМХЮЫБЕФ УЛПТПУФШ ДПУФХРБ Л ЕЈ ЬМЕНЕОФБН. рЕТЧЩК ЬМЕНЕОФ ФБВМЙГЩ ЧУЕЗДБ ДПМЦЕО ВЩФШ ТБЧЕО ОХМА Й МАВПЕ ЙУРПМШЪПЧБОЙЕ ОХМЕЧПЗП УЕМЕЛФПТБ (ХЛБЪБФЕМШ ОБ ЬМЕНЕОФ ФБВМЙГЩ ДЕУЛТЙРФПТПЧ Ч УЕЗНЕОФОПН ТЕЗЙУФТЕ ОБЪЩЧБЕФУС ФБЛ) РТЙЧПДЙФ Л ПЫЙВЛЕ. ъОБЮЙФ ВПМЕЕ-НЕОЕЕ ТБВПФПУРПУПВОБС ФБВМЙГБ ДЕУЛТЙРФПТПЧ ДПМЦОБ УПДЕТЦБФШ ИПФС ВЩ ФТЙ ДЕУЛТЙРФПТБ - РХУФПК, ДЕУЛТЙРФПТ ЛПДБ, ДЕУЛТЙРФПТ ДБООЩИ.

оХ ЮФП ЕЭЈ УФПЙФ ТБУУЛБЪБФШ, РТЕЦДЕ, ЮЕН НЩ РПРТПВХЕН РЕТЕКФЙ Ч ЪБЭЙЭЈООЩК ТЕЦЙН? рПЦБМХК, ЕЭЈ УФПЙФ ХРПНСОХФШ РТП ХТПЧОЙ ДПУФХРБ. лПД СДТБ УЙУФЕНЩ Й ЛПД РТЙМПЦЕОЙК ПФДЕМЕОЩ ДТХЗ ПФ ДТХЗБ У ФПК ГЕМША, ЮФПВЩ СДТП НПЗМП РПМОПУФША ХРТБЧМСФШ РТПГЕУУПТПН, Б РТЙМПЦЕОЙС ОЕ НПЗМЙ ЧНЕЫБФШУС Ч ТБВПФХ СДТБ (ЧЕДШ Х ОБУ НОПЗПЪБДБЮОБС пу). лПД ЙУРПМОСЕФУС У ПРТЕДЕМЈООЩН ХТПЧОЕН РТЙЧЙМЕЗЙК . ч x86 ЙИ ГЕМЩИ 4 ЫФХЛЙ - ПФ 0 ДП 3. оХМЕЧПК ХТПЧЕОШ УБНЩК РТЙЧЙМЕЗЙТПЧБООЩК (НПЦЕФ ЧЩРПМОСФШ МАВЩЕ ЛПНБОДЩ Й НЕОСФШ ТЕЦЙНЩ ТБВПФЩ РТПГЕУУПТБ), ФТЕФЙК УБНЩК "ВЕУРТБЧОЩК". лБЛ Й Ч УМХЮБЕ У УЕЗНЕОФБГЙЕК, ТБЪТБВПФЮЙЛЙ x86 РЕТЕВПТЭЙМЙ У ЖХОЛГЙПОБМПН Й ЧУЕ пу ЙУРПМШЪХАФ МЙЫШ ДЧБ ХТПЧОС ЙЪ ЮЕФЩТЈИ ЧПЪНПЦОЩИ, Б ДТХЗЙЕ БТИЙФЕЛФХТЩ РТПГЕУУПТБ РПДДЕТЦЙЧБАФ ФПМШЛП ЙИ. х ЛБЦДПЗП УЕЗНЕОФБ Ч ЕЗП ДЕУЛТЙРФПТЕ ХЛБЪБО DPL (Descriptor privilege level) - ХТПЧЕОШ ДПУФХРБ ОЕПВИПДЙНЩК ДМС ДБООПЗП УЕЗНЕОФБ. оЕРТЙЧЙМЕЗЙТПЧБООЩК ЛПД ОЕ НПЦЕФ РПМХЮЙФШ ДПУФХР Л УЕЗНЕОФХ У ХТПЧОЕН ДПУФХРБ 0, Б РТЙЧЙМЕЗЙТПЧБООЩК ЛПД НПЦЕФ РПМХЮЙФШ ДПУФХР ЛП ЧУЕН УЕЗНЕОФБН.

уЕМЕЛФПТ УЕЗНЕОФБ, ЛПФПТЩК УПДЕТЦЙФУС Ч УЕЗНЕОФОПН ТЕЗЙУФТЕ, СЧМСЕФУС ОЕ РТПУФП ОПНЕТПН ЬМЕНЕОФБ Ч ФБВМЙГЕ, ОП Й ХЛБЪБФЕМЕН ХТПЧОС ДПУФХРБ - НМБДЫЙЕ 2 ВЙФБ УПДЕТЦБФ ХТПЧЕОШ РТЙЧЙМЕЗЙК (ПФ 0 ДП 3), Б ХЦЕ УФБТЫЙЕ ОПНЕТ УБНПК ФБВМЙГЩ. фБЛЙН ПВТБЪПН УЕМЕЛФПТ = (ЙОДЕЛУ_ДЕУЛТЙРФПТБ shl 2) + RPL. RPL - Requested privelege level - ЪБРТБЫЙЧБЕНЩК ХТПЧЕОШ РТЙЧЙМЕЗЙК. рТЙ ЬФПН RPL ДПМЦЕО ВЩФШ ВПМШЫЕ ЙМЙ ТБЧЕО НБЛУЙНБМШОПНХ ЙЪ DPL Й CPL (Current privilege level). CPL ТБЧЕО RPL УЕМЕЛФПТБ Ч CS. фБЛЙН ПВТБЪПН ЛПД ОЕ НПЦЕФ РПМХЮЙФШ ДПУФХРБ Л УЕЗНЕОФБН, Х ЛПФПТЩИ ХТПЧЕОШ ДПУФХРБ Ч ЮЙУМПЧПН ЧЙДЕ ОЙЦЕ, ЮЕН Х ОЕЗП УБНПЗП. с, ЧЕТПСФОП, ПРЙУБМ ДПУФБФПЮОП ЪБРХФБООП, ОП ЧРПМОЕ НПЦОП ПВПКФЙУШ RPL = DPL , ЛБЛ НЩ Й РПУФХРЙН.

рПЛБ НЩ РЙЫЕН ФПМШЛП СДТП, НЩ ВХДЕН ТБВПФБФШ Ч ОХМЕЧПН ЛПМШГЕ ЪБЭЙФЩ (ФБЛ ЕЭЈ ОБЪЩЧБАФ ХТПЧОЙ РТЙЧЙМЕЗЙК), ЮФПВЩ ЙНЕФШ РПМОЩК ДПУФХР Л БРРБТБФХТЕ.

уЕЗНЕОФБГЙС ОБН ОЕ ОХЦОБ, РПЬФПНХ С ОЕ ВХДХ ПУФБОБЧМЙЧБФШУС РПЛБ ЮФП ОБ ЖПТНБФЕ ДЕУЛТЙРФПТБ, Б ДБН ЗПФПЧЩЕ ЪОБЮЕОЙС. еУМЙ ЙОФЕТЕУОП, НПЦЕФЕ РПЮЙФБФШ ЬФХ УФБФША . тБУУНПФТЙН РТПУФЕКЫЙК ЛПД РЕТЕИПДБ Ч ЪБЭЙЭЈООЩК ТЕЦЙН.

; ъБРХУЛ 32-ТБЪТСДОПЗП СДТБ.start32: ; чЩЧПДЙН ХЧЕДПНМЕОЙЕ П ЪБРХУЛЕ 32-ВЙФОПЗП СДТБ mov si, start32_msg call write_str ; ъБЗТХЪЙН ЪОБЮЕОЙЕ Ч GDTR lgdt ; ъБРТЕФЙН РТЕТЩЧБОЙС cli ; рЕТЕКДЈН Ч ЪБЭЙЭЈООЩК ТЕЦЙН mov eax, cr0 or eax, 1 mov cr0, eax ; рЕТЕКДЈН ОБ 32-ВЙФОЩК ЛПД jmp 8:start32 ; фБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ДМС 32-ВЙФОПЗП СДТБ align 16 gdt32: dq 0 ; NULL - 0 dq 0x00CF9A000000FFFF ; CODE - 8 dq 0x00CF92000000FFFF ; DATA - 16 gdtr32: dw $ - gdt32 - 1 dd gdt32 ; 32-ВЙФОЩК ЛПД use32 start32: ; оБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ Й УФЕЛ mov eax, 16 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax movzx esp, sp ; чЩЧПДЙН УЙНЧПМ ОБ ЬЛТБО mov byte, "!" ; ъБЧЕТЫЕОЙЕ jmp $

ьФПФ ЛПД УМЕДХЕФ ДПРЙУБФШ Л ОБЫЕНХ ОБЮБМШОПНХ ЪБЗТХЪЮЙЛХ.

рЕТЕД РЕТЕИПДПН Ч ЪБЭЙЭЈООЩК ТЕЦЙН ОЕПВИПДЙНП ЪБРТЕФЙФШ РТЙЈН БРРБТБФОЩИ РТЕТЩЧБОЙК (ЛМБЧЙБФХТБ, НЩЫШ, ФБКНЕТ Й ДТХЗЙЕ ХУФТПКУФЧБ), РПФПНХ ЮФП BIOS РПУМЕ РЕТЕИПДБ ПУФБЈФУС ОЕ Х ДЕМ, Б УЧПЙ ПВТБВПФЮЙЛЙ НЩ ЕЭЈ ОЕ ОБРЙУБМЙ, РПЬФПНХ РЕТЧПЕ ЦЕ РТЕТЩЧБОЙЕ ПВТХЫЙФ УЙУФЕНХ.

оЕРПУТЕДУФЧЕООП РЕТЕИПД Ч ЪБЭЙЭЈООЩК ТЕЦЙН ПУХЭЕУФЧМСЕФ ХУФБОПЧЛБ ОХМЕЧПЗП ВЙФБ Ч CR0. йНЕООП ЬФП НЩ Й ДЕМБЕН (РТСНПК ДПУФХР Л CR0,2,3,4 ОЕЧПЪНПЦЕО ФБЛ ЦЕ ЛБЛ Й Л УЕЗНЕОФОЩН ТЕЗЙУФТБН, РПЬФПНХ ЙУРПМШЪХЕН EAX). оЕУНПФТС ОБ ФП, ЮФП НЩ ХЦЕ РЕТЕЫМЙ Ч ЪБЭЙЭЈООЩК ТЕЦЙН, ЛПД РТПДПМЦБЕФ ЙУРПМОСФШУС РП-РТЕЦОЕНХ 16-ВЙФОЩК. дМС ПЛПОЮБФЕМШОПЗП РЕТЕИПДБ ОБН ОХЦОП ПВОПЧЙФШ УПДЕТЦЙНПЕ УЕЗНЕОФОЩИ ТЕЗЙУФТПЧ. дЙТЕЛФЙЧБ бУУЕНВМЕТБ use32 ЗПЧПТЙФ ЕНХ, ЮФП ДБМШОЕКЫЙК ЛПД ЧЩРПМОСЕФУС Ч ЪБЭЙЭЈООПН ТЕЦЙНЕ Й ОЕПВИПДЙНП РЕТЕЛМАЮЙФШУС Ч ТЕЦЙН ЗЕОЕТБГЙЙ ЛПНБОД ДМС ОЕЗП, Б ОЕ 16-ВЙФОПЗП (ПО ЙУРПМШЪХЕФУС РП ХНПМЮБОЙА) .

лПНБОДБ movzx ТБУЫЙТСЕФ ЧФПТПК БТЗХНЕОФ ДП РЕТЧПЗП. ч УНЩУМЕ, ЮФП ЙЪ 16 ВЙФОПЗП ЪОБЮЕОЙС SP РПМХЮБЕФУС 32-ВЙФОПЕ. уФБТЫЙЕ ВЙФЩ ПВОХМСАФУС (НБМП МЙ, ЮФП ФБН ВЩМП ДП ОБУ). рТЕДРПУМЕДОСС ЛПНБОДБ ДЕНПОУФТЙТХЕФ ОБН ЧПЪНПЦОПУФЙ ЪБЭЙЭЈООПЗП ТЕЦЙНБ - НЩ ПВТБЭБЕНУС РП БВУПМАФОПНХ 32-ВЙФОПНХ БДТЕУХ Л ЧЙДЕП-РБНСФЙ ФЕЛУФПЧПЗП ТЕЦЙНБ, ЧЩЧПДС УЙНЧПМ "!" Ч РТБЧЩК ОЙЦОЙК ХЗПМ ЬЛТБОБ (ФЕЛУФПЧЩК ЬЛТБО ЙНЕЕФ ТБЪТЕЫЕОЙЕ 80 x 25 УЙНЧПМПЧ, ЛБЦДЩК УЙНЧПМ ЪБОЙНБЕФ Ч РБНСФЙ ДЧБ ВБКФБ - ЛПД УЙНЧПМБ Й ЕЗП БФТЙВХФЩ ГЧЕФБ).

нЩ ВПМШЫЕ ОЕ НПЦЕН ПВТБЭБФШУС Л УЕТЧЙУБН BIOS, ФЕРЕТШ РТЙЫМП ЧТЕНС ОБН УФБФШ РПМОПУФША УБНПУФПСФЕМШОЩНЙ Й УБНЙН ХРТБЧМСФШ ЧУЕН ПВПТХДПЧБОЙЕН. рЕТЕЪБЗТХЦБФШУС Й ЦДБФШ ОБЦБФЙС ОБ ЛМБЧЙЫХ НЩ РПЛБ ОЕ ХНЕЕН, РПЬФПНХ РТПУФП ЪБЧЙУБЕН У РПНПЭША ЛПНБОДЩ jmp $ (РЕТЕИПД ОБ ФХ ЦЕ УБНХА ЛПНБОДХ - ВЕУЛПОЕЮОЩК ГЙЛМ).

ч ОБЫЕН boot.cfg ЛПНБОДХ S64 РПЛБ ЪБНЕОЙН ОБ S32. фЕРЕТШ, ЕУМЙ ЧЩ ЧУЈ РТБЧЙМШОП УДЕМБМЙ, ОБЫ ЪБЗТХЪЮЙЛ ВХДЕФ ЪБЧЕТЫБФШ УЧПА ТБВПФХ ЧЩЧПДПН ЧПУЛМЙГБФЕМШОПЗП ЪОБЛБ Ч ХЗПМ ЬЛТБОБ ЙЪ ЪБЭЙЭЈООПЗП ТЕЦЙНБ. ьФП ФПМШЛП ОБЮБМП. нЩ ОБЛПОЕГ-ФП РТБЛФЙЮЕУЛЙ ХЫМЙ ЙЪ ТЕБМШОПЗП ТЕЦЙНБ (ОБ УБНПН ДЕМЕ ФБН ЕЭЈ ПУФБМПУШ ОЕНОПЗП ДЕМ) Ч ЪБЭЙЭЈООЩК. рПУЛПМШЛХ ОБЫ ЪБЗТХЪЮЙЛ ЧЩРПМОСЕФУС Ч ОХМЕЧПН УЕЗНЕОФЕ ТЕБМШОПЗП ТЕЦЙНБ, ЧУЕ УНЕЭЕОЙС УППФЧЕФУФЧХАФ ЖЙЪЙЮЕУЛЙН БДТЕУБН Й РТЙ РЕТЕИПДЕ Ч ЪБЭЙЭЈООЩК ТЕЦЙН, ОБН ОЕ РТЙЫМПУШ ОЙЮЕЗП РЕТЕУЮЙФЩЧБФШ.

ч ЪБЧЕТЫЕОЙЕ ЧЩРХУЛБ, РПЦБМХК, ДПВБЧМА РПУМЕДОЙК ЫФТЙИ - РТПЧЕТЛХ, ЮФП РТПГЕУУПТ РПДДЕТЦЙЧБЕФ ЪБЭЙЭЈООЩК ТЕЦЙН. уХФШ РТПЧЕТЛЙ Ч ФПН, ЮФП ОЕ ЧУЕ ВЙФЩ FLAGS НПЦОП ЙЪНЕОЙФШ РТПЗТБННОП. фП ЕУФШ ТЕЗЙУФТ ОЕ УПЧУЕН 16-ВЙФОЩК. оБ ОПЧЩИ РТПГЕУУПТБИ ДПУФХРОП ДМС ЙЪНЕОЕОЙС ВПМШЫЕ ВЙФ Й ЬФП НПЦОП ПВОБТХЦЙФШ. тБЪВЕТЙФЕ ЛПД ОЙЦЕ УБНЙ, УЛБЦХ ФПМШЛП, ЮФП ЛПНБОДБ pushf РПНЕЭБЕФ ТЕЗЙУФТ ЖМБЗПЧ Ч УФЕЛ, Б popf ЧЩФБМЛЙЧБЕФ УПДЕТЦЙНПЕ УФЕЛБ ЧП FLAGS. фБЛЙН ПВТБЪПН ЕЗП НПЦОП НЕОСФШ ГЕМЙЛПН, Б ОЕ ПФДЕМШОЩНЙ ЛПНБОДБНЙ. чПФ РПМОЩК ЛПД ОБЫЕЗП ЪБЗТХЪЮЙЛБ:

Org 0x7C00 jmp boot ; ъБЗПМПЧПЛ ListFS align 4 fs_magic dd ? fs_version dd ? fs_flags dd ? fs_base dq ? fs_size dq ? fs_map_base dq ? fs_map_size dq ? fs_first_file dq ? fs_uid dq ? fs_block_size dd ? ; ъБЗПМПЧПЛ ЖБКМБ virtual at 0x800 f_info: f_name rb 256 f_next dq ? f_prev dq ? f_parent dq ? f_flags dq ? f_data dq ? f_size dq ? f_ctime dq ? f_mtime dq ? f_atime dq ? end virtual ; дБООЩЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ label sector_per_track word at $$ label head_count byte at $$ + 2 label disk_id byte at $$ + 3 reboot_msg db "Press any key...",13,10,0 boot_file_name db "boot.bin",0 ; чЩЧПД УФТПЛЙ DS:SI ОБ ЬЛТБО write_str: push si mov ah, 0x0E @: lodsb test al, al jz @f int 0x10 jmp @b @: pop si ret ; лТЙФЙЮЕУЛБС ПЫЙВЛБ error: pop si call write_str ; рЕТЕЪБЗТХЪЛБ reboot: mov si, reboot_msg call write_str xor ah, ah int 0x16 jmp 0xFFFF:0 ; ъБЗТХЪЛБ УЕЛФПТБ DX:AX Ч ВХЖЕТ ES:DI load_sector: push dx add ax, word adc dx, word cmp byte, 0xFF je .use_EDD push bx cx si div mov cl, dl inc cl div mov dh, ah mov ch, al mov dl, mov bx, di mov al, 1 mov si, 3 @: mov ah, 2 int 0x13 jnc @f xor ah, ah int 0x13 dec si jnz @b .error: call error db "DISK ERROR",13,10,0 @: pop si cx bx dx ret .use_EDD: push si mov byte, 0x10 mov byte, 0 mov word, 1 mov , di push es pop word mov , ax mov , dx mov word, 0 mov word, 0 mov ah, 0x42 mov dl, mov si, 0x600 int 0x13 jc .error pop si dx ret ; рПЙУЛ ЖБКМБ У ЙНЕОЕН DS:SI Ч ЛБФБМПЗЕ DX:AX find_file: push cx dx di .find: cmp ax, -1 jne @f cmp dx, -1 jne @f .not_found: call error db "NOT FOUND",13,10,0 @: mov di, f_info call load_sector push di mov cx, 0xFFFF xor al, al repne scasb neg cx dec cx pop di push si repe cmpsb pop si je .found mov ax, word mov dx, word jmp .find .found: pop di dx cx ret ; ъБЗТХЪЛБ ФЕЛХЭЕЗП ЖБКМБ Ч РБНСФШ РП БДТЕУХ BX:0. лПМЙЮЕУФЧП ЪБЗТХЦЕООЩИ УЕЛФПТПЧ ЧПЪЧТБЭБЕФУС Ч AX load_file_data: push bx cx dx si di mov ax, word mov dx, word .load_list: cmp ax, -1 jne @f cmp dx, -1 jne @f .file_end: pop di si dx cx mov ax, bx pop bx sub ax, bx shr ax, 9 - 4 ret @: mov di, 0x8000 / 16 call load_sector mov si, di mov cx, 512 / 8 - 1 .load_sector: lodsw mov dx, add si, 6 cmp ax, -1 jne @f cmp dx, -1 je .file_end @: push es mov es, bx xor di, di call load_sector add bx, 0x200 / 16 pop es loop .load_sector lodsw mov dx, jmp .load_list ; фПЮЛБ ЧИПДБ Ч ОБЮБМШОЩК ЪБЗТХЪЮЙЛ boot: ; оБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ jmp 0:@f @: mov ax, cs mov ds, ax mov es, ax ; оБУФТПЙН УФЕЛ mov ss, ax mov sp, $$ ; тБЪТЕЫЙН РТЕТЩЧБОЙС sti ; ъБРПНОЙН ОПНЕТ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov , dl ; пРТЕДЕМЙН РБТБНЕФТЩ ЪБЗТХЪПЮОПЗП ДЙУЛБ mov ah, 0x41 mov bx, 0x55AA int 0x13 jc @f mov byte, 0xFF jmp .disk_detected @: mov ah, 0x08 xor di, di push es int 0x13 pop es jc load_sector.error inc dh mov , dh and cx, 111111b mov , cx .disk_detected: ; ъБЗТХЪЙН РТПДПМЦЕОЙЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ mov si, boot_file_name mov ax, word mov dx, word call find_file mov bx, 0x7E00 / 16 call load_file_data ; рЕТЕИПДЙН ОБ РТПДПМЦЕОЙЕ jmp boot2 ; рХУФПЕ РТПУФТБОУФЧП Й УЙЗОБФХТБ rb 510 - ($ - $$) db 0x55,0xAA ; дПРПМОЙФЕМШОЩЕ ДБООЩЕ ЪБЗТХЪЮЙЛБ load_msg_preffix db "Loading "",0 load_msg_suffix db ""...",0 ok_msg db "OK",13,10,0 config_file_name db "boot.cfg",0 start16_msg db "Starting 16 bit kernel...",13,10,0 start32_msg db "Starting 32 bit kernel...",13,10,0 ; тБЪВЙЕОЙЕ УФТПЛЙ DS:SI РП УЙНЧПМХ УМЕЫБ split_file_name: push si @: lodsb cmp al, "/" je @f test al, al jz @f jmp @b @: mov byte, 0 mov ax, si pop si ret ; ъБЗТХЪЛБ ЖБКМБ У ЙНЕОЕН DS:SI Ч ВХЖЕТ BX:0. тБЪНЕТ ЖБКМБ Ч УЕЛФПТБИ ЧПЪЧТБЭБЕФУС Ч AX load_file: push si mov si, load_msg_preffix call write_str pop si call write_str push si mov si, load_msg_suffix call write_str pop si push si bp mov dx, word mov ax, word @: push ax call split_file_name mov bp, ax pop ax call find_file test byte, 1 jz @f mov si, bp mov dx, word mov ax, word jmp @b @: call load_file_data mov si, ok_msg call write_str pop bp si ret ; рТПДПМЦЕОЙЕ ОБЮБМШОПЗП ЪБЗТХЪЮЙЛБ boot2: ; ъБЗТХЪЙН ЛПОЖЙЗХТБГЙПООЩК ЖБКМ ЪБЗТХЪЮЙЛБ mov si, config_file_name mov bx, 0x1000 / 16 call load_file ; чЩРПМОЙН ЪБЗТХЪПЮОЩК УЛТЙРФ mov bx, 0x9000 / 16 mov bp, 0x6000 mov dx, 0x1000 .parse_line: mov si, dx .parse_char: lodsb test al, al jz .config_end cmp al, 10 je .run_command cmp al, 13 je .run_command jmp .parse_char .run_command: mov byte, 0 xchg dx, si cmp byte, 0 je .parse_line ; рХУФБС УФТПЛБ cmp byte, "#" je .parse_line ; лПННЕОФБТЙК cmp byte, "L" je .load_file ; ъБЗТХЪЛБ ЖБКМБ cmp byte, "S" je .start ; ъБРХУЛ СДТБ; оЕЙЪЧЕУФОБС ЛПНБОДБ mov al, mov [.cmd], al call error db "Unknown boot script command "" .cmd db ? db ""!",13,10,0 .config_end: ; рТЙ РТБЧЙМШОПН ЛПОЖЙЗХТБГЙПООПН ЖБКМЕ НЩ ОЕ ДПМЦОЩ УАДБ РПРБУФШ; ъБЧЕТЫЕОЙЕ jmp reboot ; ъБЗТХЪЛБ ЖБКМБ.load_file: push dx inc si call load_file push ax mov cx, 512 mul cx mov word, ax mov word, dx mov word, 0 mov word, 0 mov ax, bx mov cx, 16 mul cx mov word, ax mov word, dx mov word, 0 mov word, 0 pop ax shr ax, 9 - 4 add bx, ax add bp, 16 pop dx jmp .parse_line ; ъБРХУЛ СДТБ.start: ; рТПЧЕТЙН, ЮФП ЪБЗТХЦЕО ИПФС ВЩ ПДЙО ЖБКМ cmp bx, 0x9000 / 16 ja @f call error db "NO KERNEL LOADED",13,10,0 @: ; ъБРПМОСЕН РПУМЕДОЙК ЬМЕНЕОФ УРЙУЛБ ЖБКМПЧ xor ax, ax mov cx, 16 mov di, bp rep stosw ; рЕТЕИПДЙН Л РТПГЕДХТЕ ЙОЙГЙБМЙЪБГЙЙ СДТБ ДМС ОХЦОПК ТБЪТСДОПУФЙ inc si cmp word, "16" je .start16 cmp word, "32" je .start32 ;cmp word, "64" ;je start64 ; оЕЙЪЧЕУФОБС ТСЪТСДОПУФШ СДТБ call error db "Invalid start command argument",13,10,0 ; ъБРХУЛ 16-ТБЪТСДОПЗП СДТБ.start16: mov si, start16_msg mov bx, 0x6000 mov dl, jmp 0x9000 ; ъБРХУЛ 32-ТБЪТСДОПЗП СДТБ.start32: ; чЩЧПДЙН ХЧЕДПНМЕОЙЕ П ЪБРХУЛЕ 32-ВЙФОПЗП СДТБ mov si, start32_msg call write_str ; рТПЧЕТЙН, ЮФП РТПГЕУУПТ ОЕ ИХЦЕ i386 mov ax, 0x7202 push ax popf pushf pop bx cmp ax, bx je @f call error db "Required i386 or better",13,10,0 @: ; ъБЗТХЪЙН ЪОБЮЕОЙЕ Ч GDTR lgdt ; ъБРТЕФЙН РТЕТЩЧБОЙС cli ; рЕТЕКДЈН Ч ЪБЭЙЭЈООЩК ТЕЦЙН mov eax, cr0 or eax, 1 mov cr0, eax ; рЕТЕКДЈН ОБ 32-ВЙФОЩК ЛПД jmp 8:start32 ; фБВМЙГБ ДЕУЛТЙРФПТПЧ УЕЗНЕОФПЧ ДМС 32-ВЙФОПЗП СДТБ align 16 gdt32: dq 0 ; NULL - 0 dq 0x00CF9A000000FFFF ; CODE - 8 dq 0x00CF92000000FFFF ; DATA - 16 gdtr32: dw $ - gdt32 - 1 dd gdt32 ; 32-ВЙФОЩК ЛПД use32 start32: ; оБУФТПЙН УЕЗНЕОФОЩЕ ТЕЗЙУФТЩ Й УФЕЛ mov eax, 16 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax movzx esp, sp ; чЩЧПДЙН УЙНЧПМ ОБ ЬЛТБО mov byte, "!" ; ъБЧЕТЫЕОЙЕ jmp $

Для того, чтобы писать операционку, нужно разбираться во многих деталях. Вот давайте я вас немного просвещу, (но давайте договоримся, что маны вы будете читать сами, чтобы было о чём побеседовать).
Честно говоря, на просторах сети есть туча тучная материалов по PM, да и iley и pehat несколько рассказали об этом режиме, но меня попросили всё равно описать в общих рамках его. Сейчас кратко выдам теорию (вообще то специально для этого Intel маны писала), потом начнём писать код.

Введение в защищённый режим.
Итак, PM значительно отличается от всем привычного ещё со времён DOS’a реального режима (RM). Теперь придётся привыкать: здесь нет статичных, 64 килобайтных сегментов, таблицы прерываний в 1’ом килобайте, адресов баз сегментов в сегментных регистрах, в общем совершенно новый мир.
Теперь сегменты описываются в Global Descriptor Table (GDT) . Сия таблица может быть только в одном экземпляре. Она структура в памяти. Не сегмент! Может располагаться в памяти где угодно, но её адрес и лимит записываются в регистр GDTR. Вот его структура:

Сама таблица состоит из записей следующей структуры (кстати нулевая запись пустая. Это важно. При обращении к памяти, ‘описываемой’ нулевым дескриптором, получите #GP – General Protection Fault):
Давайте рассмотрим эту структуру повнимательней.

1. Segment Limit:
Назначение этого поля понятно по названию, но есть тонкость. Собака зарыта в бите G (Granularity).
Если он неустановлен, то память ‘отсчитывается’ в байтах. В таком случае размер сегмента может варьироваться от 1 байта до 1 мегабайта на размер в 1 байт.
Если установим его в 1, то будет введена страничная адресация памяти. Тогда мы сможем адресовать от 4 килобайт до 4 гигабайт оперативки с изменением размера на 4 килобайта (размер страницы). Вообще страничная адресация предпочтительней (сравните (1Мб+64Кб-16байт) и 4Гб). Давайте в этом посте поговорим только о сегментной адресации. Paging заслуживает отдельного разговора.

2. Base Address:
Здесь указываем физический адрес базы.

3. Type field:
Комбинации битов определяют тип сегмента:

4. S (descriptor type):
В документации интеловской сказано, что если этот бит не установлен, то этот дескриптор для системного сегмента, иначе – кода или данных. Под системным подразумевается LDT, TSS, Interrupt Gates и иже с ними (о них позже).

5. DPL (Descriptor Privilege Level):
Привилегии описываемого сегмента. Всем знакомые Rings.

6. P (segment present):
Если этот бит установлен, то процессор ‘знает’, что сегмент в уже памяти (хотя лучше сказать валидный). Если загрузите в сегментный регистр селектор дескриптора с неустановленным битом P, то произойдёт исключение #NP (not present). Вообще смысл этой витиеватой фразы объясню чуть позже.

7. D/B:
Для сегментов разного типа по-разному трактуется.
1. Для сегментов кода:
32 или 16 битная длина эффективного адреса и размерность операндов.
(1-32; 0-16);
2. Для стека:
Указатель стека 32 или 16 битный. (1-32; 0-16);

8. G:
Влияет на то, в каких единицах (байты, страницы) измеряется лимит сегмента. Вообще Paging можно включить при переходе в PM, установив 31 бит регистра CR0.

Ещё немного информации:
Догадываемся, что слово Global поставили не напрасно. Значит есть ещё какая-то табличка. Верно, есть также Local Descriptor Table . Их может быть великое множество. К примеру они могут использоваться в реализации задач и.т.д. А вот LDT уже представляет собой сегмент! Так что привыкайте к фразам типа ‘дескриптор сегмента локальной таблички дескрипторов’.

После того, как мы описали таблицу, нужно ей загрузить в регистр GDTR . Это делается далеко не mov’ом. GDTR заполняется командой lgdt fword (значение) . То есть надо сформировать самостоятельно эту структуру и загрузить в вышеупомянутый регистр. Есть ещё команды работы с этим регистром, но мы несёмся галопом по Европам.

Ещё один момент. В PM в сегментных регистрах хранятся не базовые адреса сегментов (как в RM), а специально обученные штуки, под названием селекторы . Их структура такова:

Здесь Index – порядковый номер дескриптора в таблице.
TI показывает где искать дескриптор (в GDT или LDT ).

Теперь, когда уже понятно как строить таблицу, поговорим о том, как перейти в PM (замечу, это можно сделать только из RM). Вообще … нужно всего установить бит 0 управляющего регистра CR0. Хотя вру. Для начала нужно запретить все прерывания (NMI (Non Maskable Interrupts ) в том числе), открыть адресную линию A20 (чтобы была доступна 32-битная адресация), загрузить GDTR , и прыгнуть на метку – старт.

Давайте воспользуемся загрузчиком (можно KOLIBRI’ский взять), который будет грузить наш код по адресу 1000h:0 (RM’овский, замечу, адрес).
Здесь будет не всё так гладко, как в тех манах, когда в PM переходят прям из бутлоадера. Всё чуточку сложнее. Но сначала давайте разберём код, который бутлоадер будет загружать (всё пишем на FASM"е). Это своеобразный helloworld. Загрузимся, перейдём в PM и напечатаем приветствие. Всё.

Format binary
xor ax,ax
cli ;реинициализируем сегментные регистры
mov ss,ax
xor sp,sp
sti
mov ax,3
int 10h

Jmp 1000h:r_start

Mov ax,1000h;перенастраиваем регистры
mov ds,ax
mov es,ax

In al, 0x92;включаем A20
or al, 2
out 0x92, al

Lgdt fword ;загружаем регистр GDTR
mov eax,cr0
or al,1;устанавливаем 0-вой бит
mov cr0,eax;включаем PM

Jmp fword 08h:Startup32; прыгаем в PM

Align 8 ;процессор быстрее обращается с выравненной табличкой
GDT:
dq 0 ;пустой
db 0FFh,0FFh,0,0,0,9Ah,0CFh,0 ;код
db 0FFh,0FFh,0,0,0,92h,0CFh,0;данные
db 0FFh,0FFh,0,80h,0Bh,92h,40h,0 ;видеосегмент
label GDT_SIZE at $-GDT
GDTR:
dw GDT_SIZE-1
dd GDT+10000h
; нужно записать 32-битный адрес. Сейчас мы находимся в сегменте 1000h, база которого 1000h*10h (по;физическому адресу) => физический адрес GDTR (метки!) = 10000h (физический адрес базы сегмента)+offset

Virtual ;теперь, фактически, забиваем пространство до конца сегмента
rb 10000h-$;
end virtual
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;PM32 Entry;;;;;;;;;;;;;;;;;;;
use32
org $+10000h;вот для чего: в PM мы работаем с Flat-сегментами, и если мы оставим код;для PM перед org’ом, то;внутрисегментный адрес не будет совпадать с Flat адресом. Так вот.

Startup32: ;точка входа в PM
mov ax,10h ;здесь пихаем селекторы. Зачастую (! не забываем про порядковый номер в
mov es,ax ;таблице) селектор сегмент кода - 08h. данных - 10h, видеосегмент - 18h
mov ds,ax
mov fs,ax
mov ss,ax
mov esp,10000h;стек
mov ax,18h
mov gs,ax

Mov esi,hi_string ;покажем, что мы удачно перешли
call print
jmp $

;ESI - адрес строки
print:
pushad
xor ebx,ebx
mov ah,07h;атрибут
puts:
mov al,
mov ,ax
inc ebx
test al,al
jnz puts
popad
ret
hi_string db ‘Welcome to PM, dude’,0

Что мы сделали? Загрузчик нас успешно загрузил по адресу 1000h:0, откуда мы и продолжили выполнение. Сначала включили А20 , запретили все прерывания, загрузили в GDTR подходящее значение, прыгнули на метку входа. Замечу, что прыгали мы на
jmp fword 08h:Startup32
Т.е 08h - селектор дескриптора кода. Привыкайте.

Теперь как сие чудо запустить. Лично я пользуюсь WinImage и VirtualBox. Запихиваем загрузчик в бутсектор дискеты и кладём.bin’овский файл в корень. Сохраняем в.vfd, прописываем путь к образу дискеты в свойствах виртуальной машины, запускаем и видим результат.

В следующем выпуске рассмотрим interrupts, faults, traps, aborts и как они работают, ловятся и отлаживаются. Начнём говорить об архитектуре.

Источники информации.
1) Сразу хочу выразить благодарность Phantom_84 aka egos за то, что указал на путь истинный и помог мне в самом начале. Без него мне было бы гораздо труднее разобраться.

Вам также будет интересно:

Читы и консольные команды для Counter-Strike: Global Offensive Команда в кс го чтобы летать
В этой статье мы рассмотрим некоторые из наиболее полезных и забавных консольных команд в...
Arduino и четырехразрядный семисегментный индикатор Семисегментный индикатор 4 разряда распиновка
В сегодняшней статье поговорим о 7-сегментных индикаторах и о том, как их «подружить» с...
«Рабочие лошадки» Hi-Fi: собираем бюджетную систему Хороший бюджетный hi fi плеер
Выбор плеера - это сложный процесс, иногда человек желает получить не просто коробочку,...
Как правильно пользоваться сургучными печатями
На самом деле, сургуч - это смесь смол, окрашенная в определенный цвет. Если у вас на руках...
Лагает fallout 4 как снизить графику
10 ноября состоялся релиз долгожданной игры на ПК, PlayStation 4 и Xbox One, и постепенно...