Глава 19 - практика: дополнительные сведения
Мы используем cookie-файлы, чтобы получить статистику, которая помогает нам улучшить сервис для Вас с целью персонализации сервисов и предложений. Вы можете прочитать подробнее о cookie-файлах или изменить настройки браузера. Продолжая пользоваться сайтом без изменения настроек, вы даёте согласие на использование ваших cookie-файлов.
speech bubble

Глава 19 - практика: дополнительные сведения

Самоучитель по языку ассемблер(assembler)

 

Содержание:

 

ВНИМАНИЕ! Данный самоучитель был написан много лет назад и его автор не сможет ответить на ваши вопросы. Курс по assembler предназначен для саморазвития. Если у вас хватает квалификации, можете предложить внести правки.

 

Здесь кратко будут даны сведения которые я не смог по смыслу включить ни в одну из глав! Считаю нужным рассмотреть вопрос написания загрузчика для OS! ну и некоторые другие связанные с этим вопросы. Здесь будет применяться новый для вас компилятор nasm! Эта глава для обучения не обязательна! - она для интересующихся!

Загрузчик

А начиналось всё с того что один парень - next решил изучать вопросы связанные с написанием OS, и выложил об этом объявление в сети. Я его нашёл и мы вмести сделали загрузчик и даже простенькую ОСЬ! Которая тем не менее уже может читать файлы! Проект http://nextos.ntagil.ru там есть вся документация и нужные программы.

Отличительная особенность здесь то что компилировали загрузчик с помощью nasm! Он бесплатен и удобнее для этих целей! Ну рассматривать теоретические аспекты написания загрузчиков не позволяет объем, а вот текст всё же приведу, его изучение поможет вам глубже понять принципы применения тех или иных команд! А вообще там всё ясно из комментариев! Тестируется под эмулятором....ну на сайте всё есть!

Листинг:

[BITS 16] ;так мы говорим что сегменты у нас будут 16-ти разрядные
[ORG 0x0]; адресация с нулевого смещения
;1-st segment (of 16):
;01,0Kb - table of interrupt vectors | 0000 - 03FF
;00,5Kb - 256byte data & 256byte stack | 0400 - 05FF
;00,5Kb - bootsector (1-st bootloader) | 0600 - 07FF
;18,0Kb - 1-st cyl. head 0/1 36 sectors | 0800 - 4FFF
; 0x0800 - 0x09FF - bootloader
; 0x0A00 - 0x1BFF - FAT(1)
; 0x1C00 - 0x2DFF - FAT(2)
; 0x2E00 - 0x49FF - root catalog
; 0x4A00 - 0x4FFF - files
;64,0Kb - kernel | 40000 - 50000

;for compatibility
;with fat12 fs
jmp short entry
nop
OEM db 'OS NeWoS' ; OEM ID
BytesPerSector dw 512 ; Bytes per sector
ClusterSize db 1 ; Sectors per cluster
ReservedSectors dw 1 ; Reserved sectors at beginning
FATCopies db 2 ; Fat copies
RootSize dw 224 ; Root directory entries
TotalSectors dw 2880 ; Total sectors on disk
Media db 0F0h ; Media descriptor byte
FATSize dw 9 ; Sectors per FAT
TrackSize dw 18 ; Sectors per track
Heads dw 2 ; Heads
HiddenSectors dd 0 ; Special hidden sectors
BigTotal dd 0 ; Big total number of sectors
Drive db 0 ; Physical drive number
q db 0
ExtendedBPB db 29h ; Extended boot record signature
SerialNumber dd 0 ; Volume serial number
VLabel db 'OS Disk ' ; Volume label
FSID db 'FAT12 ' ; File system ID

;точка входа
entry:
;moving ourself
;into 0x600
cli
xor ax, ax ;set regs:
mov es, ax ;es, ds - 0x0000
mov ds, ax ;
mov sp, 0x5ff ;set stak
sti

;bios грузит нас по адресу 0x0000:0x7c00 а мы перемещаем сами себя туда куда нужно нам!
mov si, 0x7c00 ;from 0x0000:0x7c00
mov di, 0x0600 ;to 0x0000:0x0600
mov cx, 0x0200 ;512 bytes
rep movsb ;moving
;начали выполнение! хоть написано 0х60 :) на самом деле это преобразуется в 0x600! :)
jmp 0x60:start ;main programm runs at 0x0060:0x0000-0x0060:0x0200 (0600-0800)
;start of main programm
start:
;set video mode
call vid_mod
;read from disk
;читаем сразу первый цилиндр! через bios! тут будет и фат и корень!
push es
mov ch, 0x0 ;1-st cylinder (36 sectors)
xor bx, bx ;to address
mov es, bx ;
mov bx, 0x800 ;0x0000:0x0800
mov dx, 0x0000 ;fd0 & head0
read:
mov ax, 0x0212 ;read cylinder
mov cl, 0x01 ;from 1-st sector
int 0x13 ;go
cmp dh, 0x0 ;head = 0?
jne rd_ok ;no - end, yes - next_head
mov dx, 0x0100 ;head = 1
mov bx, 0x2C00 ;0x0000:0x2C00
jmp read ;read
rd_ok:
pop es
;call stop_flop ;stop floppy
;//////////////root dir scan/////////////////////////////
cld
mov si, 0x2E00 ;find in root catalog
mov di, loader ;file
add di, 0x600 ;
mov cx, 12 ;how much symbols
next: ;go!
pusha ;save regs
repe cmpsb ;compare
jcxz ura ;found - good, no - next block
popa ;restore regs
add si, 32 ;take next block
cmp si, 0x4A00 ;if not at the end
jb next ;compare again
mov ax, 0x0e21 ;not found - error
mov bx, 0x000a ;
int 0x10 ;print '!'
jmp $ ;and halt
ura: ;found!
popa ;restore regs, address in si
mov ax,1301h ;тут выводим строку с именем лоадера
xor bx,bx
mov cx,11
mov bl,1fh
xor dx,dx
mov bp,0x600+loader
int 0x10
;////////////////copy to f_info file information//////////////////
mov di, f_info ;from
add di, 0x0600 ;to
mov cx, 0x10 ;32 bytes
rep movsw ;copy
;//////////////////////FAT ANALUIZE////////////////////////////////
mov ax,0x4000;установим es для чтения вт. загрузчика
mov es,ax
mov di,0x0A00
mov cx,[0x600+f_beg] ;тут номер начального кластера
mov bx,03h ;на это умножаем
dalee: mov ax,cx
call read_ls ;читаем кластер с номером в ax
mul bx ;умножаем на 3 и делим на 2 для
shr ax,1 ;правильного извлечения 12 бит
add di,ax ;извлекаем номер следующего кластера
mov ax,[di] ;он помещается в ax
test cx,1 ;тут проверка на чётность
jnz nechet
and ax,0x0fff ;чётное - накладываем маску
jmp short ok
nechet: shr ax,4 ;нечетное - сдвигаем вправо на 4
ok: mov di,0x0A00
mov cx,ax
cmp ax,0x0fff ;проверка на конец цепочки кластеров
jne dalee
;//////////////////////go to kernel/////////////////////////////////
jmp 0x4000:0x0000
;///////////////////////////////////////////////////////////////////
jmp $ ;здесь мы никогда не окажемся
;procedures////////////////////////////////////////////////////////
;set video mode
vid_mod:
mov ax, 0x0003 ;video mode normal text
int 0x10
mov ax, 0x0500 ;video page first page
int 0x10
ret
;stop floppy drive
stop_flop:
pusha
mov dx, 0x3f2
xor al, al
out dx, al
popa
ret
;read logical sector ax - номер кластера
read_ls:
pusha
xor dx,dx
add ax,31
div word [0x600+TrackSize]
mov bx,ax ;bx=ax=ax/18 bx понадобится при расчёте головки
inc dl ;сектор равен остаток +1
push dx ;сохраняем номер сектора
xor dx,dx
div word [0x600+Heads] ;теперь ax=ax/2 (то есть ax/36) - это дорожка track
mov si,ax ;сохраним т.к. понадобится при расчёте головки
;теперь пытаемся разместить сектор и дорожку в cx в правильном порядке
mov ch,al
shr ax,2
mov cl,al
and cl,0x0c0
pop ax ;теперь в ax номер сектора
add cl,al
;размещение закончено теперь определим головку
mov ax,si
mul word [0x600+Heads]
sub bx,ax ;тут будет либо 0 либо 1
;вызываем прерывание для чтения
mov dh,bl
mov dl,0
mov ax,0x0201 ;читаем один сектор
mov bx,[0x600+bufer] ;es:bx = 0x4000:0x0000 - абс.адрес = 0x40000
int 0x13 ;прочитали, если облом то зависнем
jnc exit
mov ax, 0x0e21 ;print '!'
mov bx, 0x000a
int 0x10
exit:
add word [0x600+bufer],512 ;увеличиваем адрес для следующего сектора
popa
ret
;////////////////////////////////////////////////////////////////////
;free space
TIMES 462 - ($-$$) db 0x00
;указатель на память ядра
bufer dw 0x0000
;info about file(from root catalog)
f_info:
f_name: db 0, 0, 0, 0, 0, 0, 0, 0
f_ext: db 0, 0, 0
f_attr: db 0
f_rez: db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
f_time: dw 0
f_date: dw 0
f_beg: dw 0
f_size: dw 0, 0
;name of file to load (11 symbols)
loader: db "LOADER RUN"
free_bytes: db 0x0, 0x0, 0x0
;signature of bootable discs
db 0x55, 0xaa

Если захотите скомпилировать то наберите

nasmw -fbin boot.asm -o bsec.bin

И всё пишите bsec.bin в первый сектор дискетки и тестируйте!

Процесс описан на сайте поэтому рисунков не будет! Ещё бы ведь задача загрузчика не картинки выводить а грузить ядро OS!

У нас загрузчик грузит вторичный loader!

[BITS 16]
[ORG 0x40000]
_start:
cli
mov ax, cs
mov ds, ax
mov ss, ax
mov es, ax
mov sp, _start
sti
;/////////////служебная процедура установки шрифта///////////////////
mov bp, cyr_binary ; es:bp указывает на новую таблицу
mov ah, 0x11 ; 0x11 - функция генерации символов
mov al, 0x00 ; подфункция 0x00 - загрузить пользовательский шрифт для текстового режима
mov cx, 0xff ; 0xff - число изменяемых символов
mov dx, 0x00 ; 0x00 - изменить кодировку начиная с ascii-кода символа
mov bh, 0x10 ; 0x10 - число байт в образце символа
mov bl, 0x00
int 0x10
;////////////////////////////////////////////////////////////////////
mov ax,1301h ;выводим приветствие
mov cx,20
mov bl,1fh
xor dx,dx
inc dh
mov bp,msgks
int 10h
inc dh ;сообщаем о переходе в защищенный режим
mov bp,msgpm
int 10h
;/////////////остановка мотора у флопика////////////////////////////
mov dx, 0x3f2
xor al, al
out dx, al
;///////////////////////////////////////////////////////////////////
;Установим базовый вектор контроллера прерываний (главного) в 0x20
cli
mov al,00010101b
out 0x20,al
mov al,0x20
out 0x21,al
mov al,00000100b
out 0x21,al
mov al,00001101b
out 0x21,al
;////////перемещение таблицы дескрипторов на 0x32000////////////////
push 0x3200
pop es
mov si,gdt
xor di,di
mov cx,6
rep movsd
;///////////////////////////////////////////////////////////////////
;Загрузка регистра GDTR:
lgdt [gd_reg]
;Включение A20
in al, 0x92
or al, 2
out 0x92, al
;Установка бита PE регистра CR0
mov eax, cr0
or al, 1
mov cr0, eax
jmp 0x8: dword _protected ;прыжок для загрузки регистра cs
[BITS 32]
_protected:
;Загрузим регистры DS и SS селектором сегмента данных
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
mov esp, 0xA0000-4
;////////////измерим объем оперативной памяти/////////////
xor eax,eax
xor ecx,ecx
mov edi,0x100000
next: inc ecx
xchg [edi],eax
xchg [edi],eax
cmp eax,0
jne ex
add edi,0x100000
jmp next
;////////////в ecx объем в MB (поместим в переменную ядра)///////////
ex: mov [0x200000],ecx
;переместим ядро на тот адрес по которому оно скомпоновано
cld
mov esi, kernel_binary
mov edi, 0x000000
mov ecx, 0x4000 ;Размер ядра в двойных словах (16384 Кбайт)
rep movsd
;Ядро скопировано, передаем управление ему
jmp 0x000000
gdt:
dw 0, 0, 0, 0 ;Нулевой дескриптор
db 0xFF ;Сегмент кода с DPL=0
db 0xFF ;Базой=0 и Лимитом=4 Гб
db 0x00
db 0x00
db 0x00
db 10011010b
db 0xCF
db 0x00
db 0xFF ;Сегмент данных с DPL=0
db 0xFF ;Базой=0 и Лимитом=4Гб
db 0x00
db 0x00
db 0x00
db 10010010b
db 0xCF
db 0x00
;Значение, которое мы загрузим в GDTR:
gd_reg:
dw 8192
dd 0x32000
msgks: db "Ядро начало загрузку",0
msgpm: db "Система входит в PM! ",0
cyr_binary:
;шрифты русские!
incbin 'cpDOS866.fnt'
;а так бы подключаем само ядро! оно уже на си!
kernel_binary:
incbin 'c:\SpecProg\MyOs\Test\kernel\binary\kernel.bin'

Компиляция

nasmw -fbin loader.asm -o loader.run

Loader.run надо поместить в корень дискеты!

 

Си и ассемблер

При написании оси мы использовали язык C/C++ для самого ядра! Здесь я приеду вам служебные процедуры по обработке строк! Чтобы вы знали как совмещать си и асм!

/////////////////работа со строками/////////////////////////////////////////////////////
unsigned char strcmp(char* s1, char* s2)
{
char retval = 0;
_asm{mov esi,s1};
_asm{mov edi,s2};
_asm{
push ax
push esi
push edi
strcmpl:lodsb
mov ah,[edi]
inc edi
cmp al,ah
jne short strcmpf0
or al,al
jnz strcmpl
mov retval,1
jmp short strcmpf1
strcmpf0:mov retval,0
strcmpf1:pop edi
pop esi
pop ax
};
return retval;
}

int strlen(char* s)
{
int retval = 0;
_asm{mov edx,s};
_asm{
push ecx
push edi
mov edi,edx
mov ecx,-1 //это чтоб сканировать строку в 2^32 байт длинной :)
xor al,al//пока не ноль
repnz scasb
mov eax,-2//ноль не учитываем потому и два, а миниус чтоб длину сделать положительной
sub eax,ecx//вычитаем и получаем нормальную длину строки
pop edi
pop ecx
mov retval,eax
};
return retval;
}
Для комментирования необходимо авторизоваться