Форум программистов, компьютерный форум, киберфорум
JIuXOMAHT
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

ОС на ассемблере. Полуношный проэкт. часть 2.

Запись от JIuXOMAHT размещена 18.02.2016 в 13:50
Показов 2688 Комментарии 2

Прелюдия...

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

И первый шаг мы сделаем прямо в следующий раз... В следующий раз... Постоянно он идет следующим, все время отдаляя тот долгожданный миг, когда вымысел становится явью. Эти мирские делишки постоянно всовывают свои немаскируемые прерывания в наш прекрасный дзэн. Прости, что я только трачу свое время. Но такова моя судьба, выбор которой я сделал несколько миллиардов тактов назад... А пока довольствуйся тем, что написано немножко ниже... Давай погрустим немножко вдвоем. Пусть это будет немножко меланхоличная ОС...

Итак, наследственная многозадачность. Каждая следующая программа принимает себе в обязанность исполнять основные функции предыдущей, при этом управление, расчет и распределение ресурсов между задачами находится всецело на ее плечах. Таким образом, сразу возникают первое, второе и третье правила написания кода программ для нашей ОС: 1)Код должен содержать минимизированный листинг всех функций и констант, которые будут впоследствии наследованы следующей программой; 2) Код должен содержать в себе особую выделенную зону для вставки в нее этих кусков кода, наследуемых у предыдущей программы; 3) Код должен содержать собственный диспетчер ресурсов, который распределяет ресурсы между основной и наследованными задачами. Как ты уже понял, прямого переключения между приложениями не будет. Каждая запускаемая программа будет заново ассемблироваться с наследственными включениями. Соответственно, ОС должна будет иметь собственный компилятор FASM. А программы будут храниться в виде текстовых файликов. Для защиты их содержимого можно изменить их тип на новый, системный, допустим, *_sm, и запретить внесение туда изменений. Мало того, еще и каталог с разрешенными *_sm , будет пополняться только с "кивка" нашего компилятора. Это позволит нам впоследствии написать приложение для написания программ и "дописывания" ОС в реальном времени, не прибегая к услугам эмуляторов... Но это все пока мечты... мечты... жалкие, бесформенные мечты...

Которые беспомощно бьются о подводные камни. Например, включение линии A20 перед запуском защищенного режима... Допустим, включить три следующие строчки не так сложно:

in al, 92h
or al, 02h; здесь может понадобится время ожидания
out 92h, al

Однако, по словам самой Интел, уже поддержка этого рудимента ошибок 286-го относительно спорна на ядре Nehalem, а на ядре Haswell и более поздних так вовсе отсутствует... Ну этот так, к сведению.

Или вот еще... Адресная бюрократия. Так называемый "защищенный режим"... Как было прекрасно в реальном... Куда хочешь смотришь, указывай только адрес... Нет, теперь его глобальная таблица дескрипторов, со всеми его дополнительными атрибутами привилегий и прочего флуда, который ничего, кроме постоянного ужирательства процессорного времени на постоянную работу с памятью, пусть и в кэше, но тратит миллионы тактов; ничего и не защищает. Это такая защита от дурака. Типа если разбирается - у него будет работать программа, а ежели нет, то и фиг с ним. Для тех, кто еще не в тарелке: между обращением программы и памятью поставили дополнительный набор простыней - Глобальная таблица дескрипторов GDR, локальная таблица дескрипторов LDR, и для страничной памяти (paging)- страница памяти (адресует 4 кб данных), таблица страниц памяти (адресует 4 мб данных), каталог таблиц страниц памяти (адресует 4 гб данных). Все их системным программистам приходится писать самим. Конечно, контекст многих задач настолько непредсказуем, что без динамического прописывания селекторов (указателей на дескриптор в таблице, который в свою очередь является прописанным указателем на сегмент или страницу в памяти, этакий посредник) не обойтись. Однако внесение изменений в таблицу глобальную создает опасность для системы остаться у разбитого корытца, ибо правило гласит : сначала таблица, потом исполнение, иначе #GP. Поэтому и существуют локальные таблицы дескрипторов, которые можно изменять уже поспокойнее, не забыв прописать в таблице прерываний на передачу обработки прерывания программы обработчиком в составе текущего приложения. Для ассемблерщика это яд, пустая трата времени, но мы должны этот путь пройти, ибо как гласит AMD64: сначала реальный, за ним защищенный, затем длинный и уж только потом 64-й. Вот! Там же лежит небольшой кусок кода экстренного перехода из реального в 64-разрядный режим (оригинальные комментарии оставлены как есть):

mydata segment para


pGDT32 label fword ; Used by LGDT.
dw gdt32_limit ; GDT limit ...
dd gdt32_base ; and 32-bit GDT base
pIDT32 label fword ; Used by LIDT.
dw idt32_limit ; IDT limit ...
dd idt32_base ; and 32-bit IDT base



pGDT64 label tbyte ; Used by LGDT.
dw gdt64_limit ; GDT limit ...
dq gdt64_base ; and 64-bit GDT base
pIDT64 label tbyte ; Used by LIDT.
dw idt64_limit ; IDT limit ...
dq idt64_base ; and 64-bit IDT base
mydata ends ; end of data segment

code16 segment para use16 ; 16-bit code segment

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;
; 16-bit code, real mode
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;
;
; Initialize DS to point to the data segment containing
; pGDT32 and PIDT32. Set up a real-mode stack pointer, SS:SP,
; in case of interrupts and exceptions.
;

cli; установка флага, запрещающего внешние прерывания
mov ax, seg mydata; запись в общий 16-й регистр указателя сегмента метки "mydata"
mov ds, ax; запись в 16-й регистр сегмента данных
mov ax, seg mystack; запись в общий 16-й регистр указателя сегмента метки "mystack"
mov ss, ax; запись в 16-й регистр сегмента стека.
mov sp, esp0; запись в 16-й регистр указателя стека нового указателя стека с CPL=0

;
; Use CPUID to determine if the processor supports long mode. ;

mov eax, 80000000h ; Extended-function 8000000h.
cpuid ; Is largest extended function
cmp eax, 80000000h ; any function > 80000000h?
jbe no_long_mode ; If not, no long mode.
mov eax, 80000001h ; Extended-function 8000001h.
cpuid ; Now EDX = extended-features flags.
bt edx, 29 ; Test if long mode is supported.
jnc no_long_mode ; Exit if not supported.

;
; Load the 32-bit GDT before entering protected mode.
; This GDT must contain, at a minimum, the following
; descriptors:
; 1) a CPL=0 16-bit code descriptor for this code segment.
; 2) a CPL=0 32/64-bit code descriptor for the 64-bit code.
; 3) a CPL=0 read/write data segment, usable as a stack
; (referenced by SS).
;
; Load the 32-bit IDT, in case any interrupts or exceptions
; occur after entering protected mode, but before enabling
; long mode).
;
; Initialize the GDTR and IDTR to point to the temporary
; 32-bit GDT and IDT, respectively.
;

lgdt ds:[pGDT32]; загрузка 32-й временной глобальной таблицы дескрипторов
lidt ds:[pIDT32]; загрузка 32-й временной таблицы прерываний

;
; Enable protected mode (CR0.PE=1).

mov eax, 000000011h; запись в общий 32-й регистр особого значения
mov cr0, eax; запись в 32-й регистр управления значения общего 32-го регистра

;
; Execute a far jump to turn protected mode on.
; code16_sel must point to the previously-established 16-bit
; code descriptor located in the GDT (for the code currently
; being executed).
;

db 0eah ;Far jump... Этот дальний прыг ну просто необходим, дабы сбросить конвеер, на котором
dw offset now_in_prot;to offset... уже сидят следующие команды, организованные по старым правилам
dw code16_sel ;in current code segment. реального режима. А нам нужен защищенный режим.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;
; At this point we are in 16-bit protected mode, but long
; mode is still disabled.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;

now_in_prot:

;
; Set up the protected-mode stack pointer, SS:ESP.
; Stack_sel must point to the previously-established stack
; descriptor (read/write data segment), located in the GDT.
; Skip setting DS/ES/FS/GS, because we are jumping right to
; 64-bit code.
;

mov ax, stack_sel
mov ss, ax; ну вот мы уже загружаем новые, временные, правда, но правила
mov esp, esp0

;
; Enable the 64-bit page-translation-table entries by
; setting CR4.PAE=1 (this is _required_ before activating
; long mode). Paging is not enabled until after long mode
; is enabled.
;

mov eax, cr4; загрузка содержимого 32-го регистра управления в общий 32-й регистр
bts eax, 5; установка 5 бита в состояние "1"
mov cr4, eax; загрузка содержимого общего 32-го регистра в 32-й регистр управления

;
; Create the long-mode page tables, and initialize the
; 64-bit CR3 (page-table base address) to point to the base
; of the PML4 page table. The PML4 page table must be located
; below 4 Gbytes because only 32 bits of CR3 are loaded when
; the processor is not in 64-bit mode.
;

mov eax, pml4_base ; Pointer to PML4 table (<4GB). Грузим указатель на нашу долгожданную 64-ю
mov cr3, eax ; Initialize CR3 with PML4 base. таблицу.

;
; Enable long mode (set EFER.LME=1).
;

mov ecx, 0c0000080h ; EFER MSR number. Это особый дополнительный регистр в AMD64, который
rdmsr ; Read EFER. отвечает в том числе за работу длинного режима. Первые две
bts eax, 8 ; Set LME=1. строчки его считывают, вторые - переписывают и возвращают.
wrmsr ; Write EFER. 0c0000080h - это его особый адрес.

;
; Enable paging to activate long mode (set CR0.PG=1)
;

mov eax, cr0 ; Read CR0. Уже второй раз обращаемся в регистр управления, устанавливаем бит "1" и
bts eax, 31 ; Set PE=1. возвращаем на место.
mov cr0, eax ; Write CR0.

;
; At this point, we are in 16-bit compatibility mode
; ( LMA=1, CS.L=0, CS.D=0 ).
; Now, jump to the 64-bit code segment. The offset must be
; equal to the linear address of the 64-bit entry point,
; because 64-bit code is in an unsegmented address space.
; The selector points to the 32/64-bit code selector in the
; current GDT.
;

db 066h; Вот и мы уже изменяем размер операндов с 16-х на 32-е
db 0eah; Вот и мы опять прыгаем далеко со сбросом конвеера
dd start64_linear; Прыгаем на это смещение.
dw code64_sel; В этот чудесный сегмент.
code16ends ; End of the 16-bit code segment

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;
;;
;;; Start of 64-bit code
;;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;

code64 para use64
start64: ; At this point, we're using 64-bit code

;
; Point the 64-bit RSP register to the stack’s _linear_
; address. There is no need to set SS here, because the SS
; register is not used in 64-bit mode.
;

mov rsp, stack0_linear

;
; This LGDT is only needed if the long-mode GDT is to be
; located at a linear address above 4 Gbytes. If the long
; mode GDT is located at a 32-bit linear address, putting
; 64-bit descriptors in the GDT pointed to by [pGDT32] is
; just fine. pGDT64_linear is the _linear_ address of the
; 10-byte GDT pseudo-descriptor.
;
; The new GDT should have a valid CPL0 64-bit code segment
; descriptor at the entry-point corresponding to the current
; CS selector. Alternatively, a far transfer to a valid CPL0
; 64-bit code segment descriptor in the new GDT must be done
; before enabling interrupts.
;

lgdt [pGDT64_linear]

;
; Load the 64-bit IDT. This is _required_, because the 64-bit
; IDT uses 64-bit interrupt descriptors, while the 32-bit
; IDT used 32-bit interrupt descriptors. pIDT64_linear is
; the _linear_ address of the 10-byte IDT pseudo-descriptor.
;

lidt [pIDT64_linear]

;
; Set the current TSS. tss_sel should point to a 64-bit TSS
; descriptor in the current GDT. The TSS is used for
; inner-level stack pointers and the IO bit-map.
;

mov ax, tss_sel
ltr ax

;
; Set the current LDT. ldt_sel should point to a 64-bit LDT
; descriptor in the current GDT.
;

mov ax, ldt_sel
lldt ax

;
; Using fs: and gs: prefixes on memory accesses still uses
; the 32-bit fs.base and gs.base. Reload these 2 registers
; before using the fs: and gs: prefixes. FS and GS can be
; loaded from the GDT using a normal “mov fs,foo” type
; instructions, which loads a 32-bit base into FS or GS.
; Alternatively, use WRMSR to assign 64-bit base values to
; MSR_FS_base or MSR_GS_base.
;

mov ecx, MSR_FS_base
mov eax, FsbaseLow
mov edx, FsbaseHi
wrmsr

;
; Reload CR3 if long-mode page tables are to be located above
; 4 Gbytes. Because the original CR3 load was done in 32-bit
; legacy mode, it could only load 32 bits into CR3. Thus, the
; current page tables are located in the lower 4 Gbytes of
; physical memory. This MOV to CR3 is only needed if the
; actual long-mode page tables should be located at a linear
; address above 4 Gbytes.
;

mov rax, final_pml4_base ; Point to PML4
mov cr3, rax ; Load 64-bit CR3

;
; Enable interrupts.
;

sti ; Enabled INTR снятие флага, запрещающего внешние прерывания

<insert 64-bit code here>

Часть вопросов так и осталась просто вопросами, особенно та часть, что относится к 64-ому коду. Разработчики особо не старались создавать готовый код, снабдив некоторые места внятными метками. Пока мы не научимся их писать, дальнейшее продвижение маловероятно. Обратите внимание на оператор db и seg. Оператор в отличие от команд не является указанием для процессора, это указание ассемблеру, поэтому счетчик команд никак не реагирует на него, кроме одного случая: если после оператора db стоит опкод какой-либо команды. Да и вообще, когда читаешь описание той или иной хрени, обязательно нарвешься на какого-то толкиенутого с его "резервированием местом в памяти", вместо "ручная запись в итоговый код следующей байтной или битной последовательности определенной длины". Для чего нужна эта ручная запись? Ну так дело все в мнемониках: на деле одной мнемонике может соответствовать от одного до нескольких опкодов с некоторыми исполнительными различиями. В большинстве случаев, ассемблер верно трактует контекст и выставляет нужный опкод в нужном месте, однако в критичных местах лучше перестраховаться и вместо

JMP Segment:Smeschenie

записать

db 0eah;
dd Smeschenie;
dw Segment;

Оператор seg возвращает значения сегмента памяти, в котором находится указанная величина... Почитай про операторы, очень интересно. Пока все, мой друг. Обещаю не бросать тебя на очень долго. До скорого...

[продолжение следует]
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 2
Комментарии
  1. Старый комментарий
    Есть же тег ASM.
    Запись от castaway размещена 10.03.2016 в 21:41 castaway вне форума
  2. Старый комментарий
    Аватар для JIuXOMAHT
    Цитата Сообщение от castaway
    Есть же тег ASM.
    И что он делает?
    Запись от JIuXOMAHT размещена 10.03.2016 в 23:22 JIuXOMAHT вне форума
 
Новые блоги и статьи
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 08.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru