Вы здесь: HomeСтатьиОС своими рукамиПишем загруочный сектор для ОС

Пишем загруочный сектор для ОС

В  данном посте хочу рассказать как можно запись исполняемый код в загрузочный сектор не задев при этом служебной информации о фат12(которая используется на дискете). Для большей ясности рекомендую прочесть статью на   харбе , прочитать про FAT а также на нашем сайте уже писалось по поводу загрузочного сектора.

И так приступим. Наш загрузчик должен быть компактен, чтоб его размер не превышал 256 байт(хотя можно и чуть больше,  я в данном примере копирую в сектор 432 (1b0h) байта то есть такой размер  кода может быть в загрузочном секторе , но реально 256 байт хватает с головой чтоб загрузить с дискеты файл и запустить его ). Для начала просто напишем код код который выводит на экран "Hello world!" . Для уменьшения кода я написал простенький макрос который используется в 2х местах _rwsecto, этот макрос как видите принимает два параметра, первый отвечает за запись/чтение второй буфер куда или от куда считывать.

macro _rwsector rw,buf{
    ;2 чтение,  3 запись
    mov ah,rw     ;//функция чтения/записи
    mov al,1      ;//кол секторов 
    mov bx,buf    ;//буфер куда читаем
    mov ch,0      ;//номер дорожки
    mov cl,1      ;//номер сектора
    mov dh,0      ;//сторона головки
    mov dl,0      ;//сам дисковод А=0 B=1
    int 13h
}

org 100h    ;//генерируем СОМ файл, загружаемый с 0x100
use16
jmp start
qwe db  0ch dup (0)
fun:
jmp pisem
;//-------------
pisem:
mov ax,07b3h
mov  ds,ax
        mov si,msg    
         cld ;//направление для строковых команд
         mov  ah, 0x0E  ;//номер функции BIOS
    pnext:
             lodsb ;//загружаем очередной символ в al
             cmp al,'$' ;// $ символ означает конец строки
             jz   next
             int  0x10 ;//вызываем функцию BIOS
             jmp  pnext
      next:
    
jmp $  ;//  организовываем вечный цикл
;//==================
msg db "Hello World!", '$' ; переменная которую выведем на экран 
;//==================
start:  ;// начало кода который записывает данные в загрузочный сектор
mov ax,cs ;
mov es,ax ;//также определяем es так как чтение кластера дискеты будет проходить по адресу es:ds  
     _rwsector 2,buf ;//макрос который считывает в буфер загрузочный сектор дискеты

;//пишем сначала джамп а потом  идем на +03аh и там наш код пишем
;//просто после первых 3 байт идет описание дискеты оно занимает 0а3h , дальше все наше))
;/если эту информацию повредить то windows или dos не смогут определить какая там файловая 
;//система и предложат ее отформатировать
mov si,buf
mov ah,0ebh
mov [si],ah
mov ah,03ch
mov [si+1],ah
mov ah,90h
mov [si+2],ah
;//далее пишем сам код
mov cx,1b0h ;
mov di,buf   ;//куда писать будем
add di,03eh ;//отступаем чтоб не повредить инфу о дискете
mov si,fun ;//указываем начало кода который нужно вписать 

rep movsb ;//и пишем
_rwsector 3,buf ;// затем преобразованный сектор пишем обратно на дискету

;================

mov ah,0x4C   ;//эта функция завершает программу     
mov al,0      ;//код возврата 0     
int 0x21      ;//вызываем ядро операционной системы
buf db  512 dup (0)  ;// буфер для хранения информации считанной с сектора

Данный код должен быть выполнен на os Windows или в DOSe с тем учетом что в ПК иметься дискета (3,5" и объемом 1,4 Мб) под буквой А. Можно (даже рекомендуется) все делать на виртуальной машине. Затем перезагрузить машину(ПК) и загрузиться с дискетки. Если все сделали верно то должно вывести "Hello world!!" как на рисунке ниже:

ОС на ассемблере. Загрузочный сектор

 Теперь давайте разберемся как загрузиться с дискеты а затем загрузить нужный нам файл в память и передать управление этому участку памяти куда мы загрузили наш файл. 
Прежде всего давайте рассмотрим как вообще распределяется память в реальном режиме у ПК:

  • от 00000h до A0000h основная память  640Кбайта ( векторы прерываний, ядро ос,программы и прочее ) 
  • от A0000h до C0000h графика EGA
  • от C0000h до D0000h видео память.
  • от D0000h до F0000h системная область
  • от F0000h до FFFFFh системное ПЗУ

 Исходя из того что в самом начале памяти размещены векторы прерываний то мы будим грузиться в память по адресу 0800h. Теперь рассмотрим пример программы которая будет грузить в память с дискеты файл io.bin а затем выполнит его. Давайте сначала разберем код файла io.bin :

mov ax,cs
mov ds,ax
pisem:
mov si,msg
print: 
		mov al,[si]  
		cmp al,0
		je next 
		
		mov ah,0x0e  
		int 0x10 
		mov al,[si]
		
		jmp pnext
       next:
endfun:
jmp endfun
pnext:
inc si ; i
jmp print
msg db "files is load!", 0 

Пример кода программы которая будет писать в загрузчик код, который ищет файл и загружает его в оперативную память:

;описываем макросы
macro _rwsector rw,kols,buf,nomd,noms,nomg,disk{
	;2 чтение,  3 запись
	mov ah,rw      ;функция чтения/записи
	mov al,kols		;кол секторов 
    mov bx,buf		;буфер куда читаем ES:BX 
    mov ch,nomd		;номер дорожки
    mov cl,noms		;номер сектора
    mov dh,nomg		;сторона головки
    mov dl,disk		;сам дисковод А=0 B=1
    int 13h
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
macro _searchfile buf,nfile,nomer{
    ;макрос ищет файл если нашел то возвращает его относительный адрес
    ;
	local yes
	local no
	local exit
	local search
	_rwsector 2,1,buf,0,2,1,nomer ; чтение каталога(списка) файлов
	
	mov cx,512
	mov bx,0
	
	xor bx,bx
	mov bx,buf
	
	search:
	mov si,nfile
	mov di,bx
	cld 
	mov cx,11
	repe CMPSB ;сравниваем пока строки равны, идет сравнение 6ти элементов 
	je yes
		

	add bx,32
		
	cmp bx,buf+512
	jne search
	 
	mov ax,0
	
	jmp exit
	
	yes:

 		
		mov ah,[bx+27]
		mov al,[bx+26]
		
	exit:
}

macro _nextsector sector,buf{;макрос смотрит таблицу относительных 
	;секторов и возвращает значение по этому адресу(чтоб узнать, 
	;конец файла или нет)
	;значение возвращаем в ах
	;метод читает относительный адрес в карте 
	;ax - номер относительного сектора на карте
	;будем учитывать что все находится в буфере buf(карта)
	local nechet
	local next
        push sector
        
        
        ;загружаем карту сперва
        _rwsector 2,9,buf,0,2,0,0 ;
        
        mov cx,2
		xor dx,dx
		
		div cx
		
		pop bx
		add ax,bx
		add ax,dx
		;ах = относительный сектор
		;читаем карту диска и снова вычисляем относительный адрес
		
		push dx
		push ax
		
		
		pop bx; номер кластера 
		pop dx
		
		
		mov ah,[buf+bx-1];идет следом
	        mov al,[buf+bx-2] 	;идет 
		
		cmp dx,1
		je nechet
	    ;если нет остачи то AB *C=*CAB если нет то A* BC= BCA*
	    
	    shr ax,4
		
		jmp next
		;---------
		nechet:
		shl ax,4
		shr ax,4
		
		
	    next:
	    ;грузим в память сектор

	
	
	
	}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;==================================
macro _loadsector buf,nomer,disk{
	local ost
	local next
	push buf ;куда грузим
	push nomer
	xor dx,dx
	
	pop ax
	add ax,14
	mov cx,18
	div cx
	;ah-остаток
	
	mov cl,dl
	push cx
	mov ah,0
	mov cl,2
	div cl
	pop cx
	cmp ah,1
	je ost
		mov dh,1
		jmp next
	ost:
		mov dh,0
	next:
	
	add al,ah
	
	mov ch,al
	
	
       pop buf
       mov ah,2 		;функция чтения
       mov al,1		;кол секторов 
        
   
       mov dl,disk		;сам дисковод А=0 B=1
       int 13h
}

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
macro _loadfile2 fname,buf,fbuf{
	;fname - имя файла, переменная 11 байт
	;buf-буфер для чтения информационных секторов, желательно иметь 
	;размер 12*512byte=6144 byte
	;fbuf - буфер для загрузки файла
	;ах возвращает код операции fff все норм, 0-файл на найден
	
	local exit
	local go
	
	
	_searchfile buf,fname,0
	
	 cmp ax,0
	 je exit	
	
	mov bx,fbuf
		
	go:
	
	push bx
	push ax
	_loadsector bx,ax,0
	pop ax
	_nextsector ax,buf
	pop bx
	
	cmp ax,0fffh
	je exit
	
	add bx,512
	jmp go
	exit:
	
}

org 100h    ;генерируем СОМ файл, загружаемый с 0x10
use16
jmp start
qwe db  0bh dup (0)
fun:
jmp pisem
;-------------
; код который выполниться при загрузке первого сектора
pisem:
;
	mov ax,07b3h
	mov  ds,ax
	mov ax,0800h ;сюда грузим имя файла
	mov es,ax	 ; а затем от туда его вынимаем 
	cld
	xor di,di
	mov si,nfile
	mov cx,11
	rep movsb 
	mov ds,ax
	_loadfile2 0,buf,0	
	jmp dword 0800h:0000  
	exit:
jmp $


nfile db "IO      BIN",'!$';имя нашего файла который грузим в ОП
;==================
start:
mov ax,cs
mov es,ax
 

_rwsector 2,1,buf,0,1,0,0


;пишем сначала джамп а потом затем идем на +1а3 и там наш код пишем

mov si,buf
mov ah,0ebh
mov [si],ah
mov ah,03ch
mov [si+1],ah
mov ah,90h
mov [si+2],ah

mov cx,1b0h ;
mov di,buf
add di,03eh
mov si,fun

rep movsb
_rwsector 3,1,buf,0,1,0,0
;================
mov ah,0x4C   ;эта функция завершает программу     
mov al,0      ;код возврата 0     
int 0x21      ;вызываем ядро операционной системы
buf db  512 dup (0)  ; буфер для хранения информации считанной с сектора

Теперь давайте разберемся с макросами. Слегка изменили макрос _rwsector , теперь он стал гибче и принимает больше параметров. _rwsector макрос который ищет файл, он принимает буфер для того чтоб грузить туда таблицу файлов, имя файла, и номер диска. Макрос читает таблицу файлов, и если файл найден то возвращает его относительный адрес, то есть относительный адрес первого сектора файла. Затем идет макрос _rwsector который принимает буфер и относительный сектор, он проверяет относительный сектор файла если его номер равен 0fffh то это последний сектор в файле если нет то смотрим второй и так пока не достигнем последний сектор. Макрос _rwsector загружает сектор по относительному адресу, принимает относительный адрес сектора а также буфер куда грузить и номер диска. И последний макрос _loadfile2 принимает имя файла, затем буфер служебный буфер куда будет грузиться сам файл, этот макрос содержит все выше перечисленные макросы и с помощью них загружает в память файл, также макрос в AX возвращает код операции если 0 то файл не найден.

Добавить комментарий


Защитный код
Обновить