Ładowanie pliku - FAT16

0

Pisze bootloader z obsługą FAT16.
Ma załadować jakiś plik i skoczyć do niego.
Dlaczego bootloader nie może znaleźć pliku z jądrem ???

org	0x7C00

mov si, M4
call print

	jmp	start
	OEM_Name		            db	"MyOperatingSystem"	; nazwa systemu
	BytesPerSector	            dw	 0x0200	; bajtow na sektor - 512
	SectorsPerCluster	            db	0x04	; sektorow na klaster - 4
	ReservedSectors	            dw	0x0001	; zarezerwowanych sektorow - 1
	FATCopies		            db	0x02		; liczba kopii FAT - 2
	MaxRootDirEntries	            dw	0x0200	; liczba wpisow w katalogu glownym - 512
	NumberOfSectorsSmall	    dw	 0xFFF0	; liczba sektorow dla malych partycji - 65520
	MediaDescriptor		    db	0xF8		; F8 - dysk twardy, F0 - dyskietka - 248
	SectorsPerFAT		    dw	 0x0040	; liczba sektorow przypadajacych na jedna FAT - 64
	SectorsPerTrack		    dw	 0x0020	; liczba sciezek - 32
	NumberOfHeads		    dw	 0x0040	; liczba glowic - 64
	HiddenSectors		    dd	0x00000000	; liczba ukrytych sektorow - 0
	NumberSectorsHuge	    dd	0x00000000	; Liczba sektorow dla duzych dyskow 
                                                                        ;(NumberOfSectorsSmall jakby nie styklo) - 0
	LogicalDriveNumber	    dw	0x0080	; numer napedu - 128
	ExtendedSignature	    db	0x29		; sygnaturka - 41
	SerialNumber		    dd	0x5678D47D; numer seryjny - 5667028
	VolumeName		    db	"CursedOS   "; etykietka
	FATName			    db	"FAT16   "	; nazwa wersji fata

print:					; Funkcja sluzy wyswietlaniu
 .pisz:
	lodsb	
	or	al, al
	jz	.napisalem
	mov	ah, 0x0E
	int	0x10
	jmp	.pisz
 .napisalem:
	ret

start:		; tu zaczynamy impreze
	cli					; wylaczamy obsluge przerwan - bedziemy ustawiac rejestry segmentowe
	push    word 0x0000	    ; adres do naszego wykorzystania - OD - 0
	pop	ss				    ; ustawiamy tam segment stosu
	push	word 0xFFF0     ; a wielkosc ustawiamy na najwieksza - DO - 65520
	pop	sp				    ; ustawiamy wskażnik stosu
	mov	ax, cs				; tak
	mov	ds, ax
	mov	es, ax				; ladujemy do dwoch rejestrow segmentowych
	mov	fs, ax
	mov	gs, ax
	sti					    ; i odpalamy obsluge przerwan
	
; W tym miejscu za pomoca przerwania 13h i funkcji 0x08h pobierzemy dane o dysku i zapiszemy je w pamieci
	mov	ah, 0x08			            ; funkcja 0x08h przerwania numer 13
	mov	dl, byte [LogicalDriveNumber]	; numer dysku
	int	13h                             ; wywolanie przerwan

; te zamotanie to dlatego ze z byte musialem word zrobic                  
	inc	ch				        ; zwiekszamy ch o 1
	xor	ax, ax                  ; zerojemy AX
	mov	al, ch                  ; przenosimy wartosc ch do al
	mov	word [maxTrack], ax		; wrzucamy tutaj parametry - ???
	inc	dh                      ;
	mov	al, dh                  ;
	mov	word [maxHead], ax      ;
	mov	al, cl                  ;
	mov	word [maxSector], ax    ;

; Obliczamy wielkosc Root Directory i zapisujemy w CX zeby pasowalo do funkcji ReadSectors
	mov	ax, 0x0020			        ; 32-bajtowy opis kazdego pliku w katalogu
	xor	dx, dx				        ; czyscimy DX (DX:AX mul costam)
	mul	word [MaxRootDirEntries]	; mnozymy przez maksymalna liczbe wpisow otrzymujac wielkosc :)
	div	word [BytesPerSector]		; i dzielimy przez liczbe bajtow na sektor otrzymujac ilosc sektorow ktore to zajmuje
	xchg ax, cx				        ; wsadzamy do cx wartosc ax -  wymienia zawartości operandów między sobą

; Obliczamy polozenie Root Directory i zapisujemy w AX z tego samego powodu co wyzej ;)
	mov	ax, word [SectorsPerFAT]	; wrzucamy liczbe sektorow na jedna tablice FAT
	xor	dx, dx                      ; zerowanie dx
	mul	word [FATCopies]		    ; mnozymy przez liczbe tablic FAT
	add	ax, word [ReservedSectors]	; dodajemy liczbe sektorow
	mov	word [DataSector], ax		; zapisujemy to w pamieci
	add	word [DataSector], cx		; i dodajemy wielkosc root dir otrzymujac poczatek sekcji z danymi

; Odczytujemy ten sztycht
	mov	bx, 0x7E00			; odczytujemy po 512 bajtow wiec zaczynamy impreze od 0x0000:0x7E00 (obszar zaraz za bootloaderem)
	call ReadSectors

; Tutaj zaczynamy szukac naszego pliku i sprawdzac czy wogole on tam jest
	mov	cx, word [MaxRootDirEntries]	; bedziemy odczytywac nie wiecej razy niz jest mozliwe. W CX jest licznik dla instrukcji loop
	xor	di, di
	mov	di, 0x7E00		                ; adres poczatku obszaru przeszukiwanego
	xor	si, si
	mov	si, KernelName			        ; adres nazwy do wyszukania

SearchForKernel:
	push cx				    ; zeby nam czasem licznik sie nie zepsul
	mov	cx, 0xB				; 11 znakow ma kernelek
	push di				    ; di / si jest zmieniane przez cmpsb to trzeba zachowac
	push si
	rep	cmpsb				; sprawdz ten sztycht - Opis : Porównuje bajt o adresie DS:SI lub DS:ESI z bajtem o adresie ES:DI lub ES:EDI 
	                        ; i w zależności od wyniku ustawia odpowiednie flagi (takie same jak CMP ). 
							; Następnie, w zależności od DF (destination flag, wskaźnika kierunku) zmniejsza (gdy DF=1) lub zwiększa (gdy DF=0) SI i DI (lub ESI i EDI). 
	pop	si				    ; polozylismy to podnosimy
	pop	di

	
	je	LoadKernel			; jesli nazwa pasuje to ladujemy kernela
	pop	cx				    ; jesli nie to bierzemy licznik ktory schowalismy
	add	di, 0x20			; przestawiamy na nastepny wpis w RootDirectory (kazdy ma 32b)
	loop SearchForKernel	; i patrzymy na nastepny
	jmp	KernelNotFound		; jesli nie ma no to skaczemy ze nie ma ;)

LoadKernel:

    mov si, M1
	call print

	mov	ax, [di+0x1A]			; na tej pozycji jest numer klastra gdzie lezy nasz kernelson
	mov	word [cluster], ax		; zapisujemy pierwszy klaster

; tutaj zaladujemy tablice FAT
	mov	cx, word [SectorsPerFAT]	; ladujemy do CX liczbe sektorow ktora zajmuje FAT
	mov	ax, word [ReservedSectors]	; a do ax poczatek
	mov	bx, 0x7E00			; ladujemy to zamiast wczesniej odczytanego RootDir - bo nam juz niepotrzebny
	call	ReadSectors

	push	word 0x0100			; ustawiamy wartosc ES
	pop	es


	mov	ax, word [cluster]
	xor	bx, bx				; adres pod ktory wrzucamy (zaczynamy od 0x0100:0x0000)
	

FATLoop:
	call	ClusterToSector			; zamieniamy numer klastra na adres
	mov	cl, byte [SectorsPerCluster]	; ilosc sektorow do odczytania
	call	ReadSectors			; odczytujemy pierwszy sektor
	
	push	bx
	mov	ax, word [cluster]
	shl	ax, 1				; mnozymy klaster razy 2 otrzymujac offset z ktorego odczytamy
	mov	bx, ax				; wrzucamy do bx
	mov	ax, [bx+0x7E00]			; odczytujemy wartosc z tablicy FAT
	mov	word [cluster], ax		; wrzucamy to do numeru klastra bez wzgledu na to co tam jest
	pop	bx

	cmp	ax, 0xFFFF			; porownujemy wartosc odczytana, jesli jest 0xFFFF to znaczy ze to ostatni klaster pliku
	jne	FATLoop				; wiec jesli nie pasuje to czytamy nastepny
	
	 mov si, M3
	 call print
	  
	jmp	0x0100:0x0000			; i odpalamy odczytany kernel
	
KernelNotFound:

mov si, M2
call print

	jmp $

; --------
; Potrzebne funkcyjki
; --------

; --------
; ClusterToSector
; Funkcja przyjmuje numer klastra i przelicza go na adres pliku (w AX)
; --------

ClusterToSector:
	push	dx				; kladziemy uzywane zmienne na stos
	push	cx
	sub	ax, 2				; odejmujemy 2
	xor	dx, dx
	xor	cx, cx
	mov	cl, byte [SectorsPerCluster]	; mnozymy przez liczbe sektorow na klaster
	mul	cx
	add	ax, word [DataSector]		; i dodajemy miejsce poczatku sektora z danymi majac polozenie pliku
	pop	cx
	pop	dx				; przywracamy zmienne
	ret

; --------
; SectorCHS - konwersja adresu w postaci numeru sektora liczac od poczatku na CHS (czyli cylinder head sector)
; Funkcja przyjmuje w rejestrze AX numer
; --------

SectorCHS:
	push	bx				; kladziemy ax na stos
	push	ax
	xor	bx, bx
	mov	ax, word [maxSector]
	mul	word [maxHead]
	xor	dx, dx
	xchg	ax, bx				; tu mamy obliczenie cylindra
	pop	ax
	div	bx
	mov	byte [absoluteTrack], al
	mov	ax, dx

	xor	dx, dx				; zerujemy do dzielenia. Dzielenie wyglada tak: (DX:AX div costam) stad musimy wyzerowac dx...
	div	word [maxSector]		; dzielimy przez maksymalna liczbe sektorow otrzymujac tym samym w ax numer glowicy a w dx sektor
	mov	byte [absoluteHead], al		; zapisujemy ;)
	inc	dl
	mov	byte [absoluteSector], dl
	pop	bx
	ret					; i wracamy

;-------
; ReadSectors - odczytuje cx sektorow z dysku zaczynajac od ax pod adres es:bx
; Przyjmowane argumenty:
;	cx - liczba sektorow do odczytu
;	ax - poczatek obszaru do odczytu
;	es:bx - adres w pamieci pod ktory odbywa sie odczyt
;-------

ReadSectors:
	push	ax
	push	bx
	push	cx				; zapisujemy je na stosie bo bedziemy zmieniac a sa nam potrzebne
	call	SectorCHS			; zamieniamy adres na CHS
	mov	ah, 0x02			; czytanie sektorow z dysku
	mov	al, 0x01			; ilosc - 1
	mov	ch, byte [absoluteTrack]	; sciezka
	mov	cl, byte [absoluteSector]	; sektor
	mov	dh, byte [absoluteHead]		; glowica
	mov	dl, byte [LogicalDriveNumber]	; dysk
	int	13h				; czytamy
	pop	cx
	pop	bx
	pop	ax
	add	bx, word [BytesPerSector]	; przelaczamy sie na nastepny sektor
	inc	ax
	loop	ReadSectors			; i powtarzamy az sie nie skonczy ilosc sektorow do odczytania
	ret					; jesli koniec to wracamy



; -------
; Zmienne
; -------

absoluteTrack	db	0x00		; sciezka (cylinder)
absoluteHead	db	0x00		; glowica
absoluteSector	db	0x00		; sektor
maxTrack	dw	0x0000
maxHead		dw	0x0000		; miejsce na dane o nosniku
maxSector	dw	0x0000
DataSector	dw	0x0000		; polozenie sektora z danymi (jego poczatku)
cluster		dw	0x0000

KernelName	db	"krenel.bin" ; nazwa szukanego pliku

M1 db "Ladowanie kernel'a...", 13, 10, 0
M2 db "Nie odnaleziono pliku...", 13, 10, 0
M3 db "Zaladowano poprawnie...", 13, 10, 0
M4 db "Szukam...", 13, 10, 0


times 510-($-$$) db	0x00
dw	0xAA55

Dodam jeszcze paczkę z plikami potrzebnymi po kompilacji

0

Dlatego że krenel.bin?

0

Nie rozumiem ???
Chodzi Ci o nazwę - są takie same.

0

Masz jakiekolwiek pojęcie o systemie FAT16?
Stricte - o tym, jak przechowywane są tam nazwy plików?

0

Dopiero zacząłem naukę FAT16 więc mam pojęcie ogólne :).
Jeśli chodzi o kod to nie wiem gdzie jest błąd.

1

nie wiem gdzie jest błąd.

Sprawdź, jak w FAT16 trzymane są nazwy plików...

0

czy chodzi Ci o to że zamiast

KernelName db "krenel.bin" ; nazwa szukanego pliku
powinno być KernelName db "KRENEL BIN"

1

Pomijając, że chyba masz na myśli plik kernel.bin, to w FAT (bez LFN (long file name)) jego nazwa brzmiałaby:
kernel bin (dwie spacje, bo cała nazwa pliku musi mieć 11 znaków (format 8+3)).
#Edit albo KERNEL BIN - dawno nie "bawiłem się" z FAT-em i nie wiem czy jest to zapisywane wielkimi, czy małymi literami.

0

Spotkałem się z zapisem KERNEL BIN ale czytałem że FAT 16 nie rozróżnia wielkości liter i sprawdze ja dawałem tylko jedną spacje

0

czytałem że FAT 16 nie rozróżnia wielkości liter

Zapewne chodzi o to, że Ty - jako programista - nie powinieneś zwracać uwagi na wielkość liter, a zapisane od środka może być nawet jako KeRnEL BIn.

ja dawałem tylko jedną spacje

Wtedy nazwa nie spełniałaby formatu 8+3 (8 znaków nazwy pliku, 3 znaki rozszerzenia).

0

Niestety dalej nie znajduje pliku

0

Wrzuć aktualny kod + może daj obraz dyskietki, z której korzystasz.

0

Tutaj masz wszystko z czego korzystam (lub korzystałem wcześniej - cały katalog na żywca)

0

Masz może to merge.exe na 32-bitową wersję Windowsa? :P

0

program merge.exe kompilowałem z tych źródeł

 program merge;
 
{$APPTYPE CONSOLE}
 
var
  final, f: file of byte;
  buf, q: byte;
begin
  if (paramcount < 3) or (paramstr(1) = 'help') then begin
    writeln('uzycie: merge.exe <1 plik> <2 plik> <3...> <plik wynikowy>');
    readln;
  end else begin
    assignfile(final, paramstr(paramcount));
    rewrite(final);
    for q := 1 to paramcount -1 do begin
      writeln(paramstr(q));
      assignfile(f, paramstr(q));
      reset(f);
      while not eof(f) do begin
        blockread(f, buf, sizeof(buf));
        blockwrite(final, buf, sizeof(buf));
      end;
      closefile(f);
    end;
    closefile(final);
  end;
  writeln('-> ' + paramstr(paramcount));
end.

używałem wcześniej przy wcześniejszej wersji bootloadera zanim ktoś polecił mi spróbować zaimplementować system plików wiec jest na razie nie używany ale go nie kasuje i tak(może się kiedyś przydać)

programu merge.exe używałem gdy nie szło mi z linkerem - inna historia

0

Więc on jedynie łączy te 3 pliki (boot.bin, main.bin, kernel.img) ze sobą?
Nie tędy droga.
Jedyne co z tego zostanie poprawnie stworzone, to sam bootloader - nic więcej, bo:
1.Powinieneś kompilować kod C do pliku binary.
2.Jeżeli wyłącznie połączysz te pliki ze sobą, nie zostanie utworzony poprawny system plików FAT.
Poczytaj o VFD (virtual floppy driver) oraz skorzystaj z normalnego linkera.

0

Kiedy Ci tłumaczę Ci że , nie używam już merge.exe bo i tak nie działał a poza tym jak bootloader ma znaleźć plik z jądrem kiedy są połączone w jeden.
Był mi on (merge.exe potrzebny kiedy próbowałem kompilować kody prostych os'ów z różnych kursów np. tego

https://github.com/luksow/OS

To co napisałem ma znałeść w katalogu z boot loaderem plik z jądrem systemu .

0

Mea culpa - źle zrozumiałem Twojego posta :F
Ale nadal mi się wydaje, że po prostu źle generujesz ten ostateczny plik dyskietki.
Skompiluj całość u siebie i podeślij jedynie ten jeden ostateczny plik, który uruchamiasz w Bochsie/QEMU/czymkolwiek (chyba, że w ogóle problem jest z jego wygenerowaniem or something :P).

0

Może i źle ale co nie wiem niestety...
Tak jak dałem ci cały kod w załączniku to w tym katalogu jest folder(File_After_Compilation) z plikiem img - bootloaderem i plikiem bin - kernel'em .
Nie łącze ich w jedno tylko chce by bootloader sam wykrył plik z jadrem który znajduje się w tym samym katalogu a plik odpalam w VirtualBox ale pod Qmenu powinien też zadziałać - sprawdziłem

0

A! Ok - teraz sytuacja jest nieco jaśniejsza.
Wciąż jednak chyba nie rozumiesz w jaki sposób to działa - musisz te dwa pliki połączyć w jedną całość, lecz nie poprzez żadne merge czy linkery (tzn.linker jest potrzebny jedynie do tego, by odpowiednio zlinkować dane kernela, a nie do stworzenia obrazku dyskietki).
U siebie na przykład mam taki skrypt (fragment), który kopiuje wszystkie niezbędne pliki na dyskietkę, a później tworzy obraz dyskietki i zapisuje go do pliku xFlo.img:

echo Linkowanie
cd ..\..\output
..\programs\ld -T ../code/link.ld tutaj jest lista plików, nieistotne

..\programs\vfd close A:
..\programs\vfd open A: /W ..\xFlo.img

copy stage2.o A:\stage2.bin >nul
copy system.obj A:\system.bin >nul

..\programs\vfd close A:

W tej chwili, plik xFlo.img to już jest cały obraz dyskietki w systemie plików FAT, i właśnie ten plik uruchamiany jest przez Bochsa bądź QEMU.

xFlo.img to odpowiednio przygotowany wcześniej plik, którego pierwsze 512 bajtów to bootloader, a cała reszta to zera, tak więc w Windowsie widziany jest jako dodatkowa, pusta "stacja dyskietek". Każda modyfikacja tej "dyskietki" powoduje wykrycie tego przez program/sterownik VFD i także modyfikację tego pliku. To jest coś na wzór archiwum.

0

Ja myślałem że gdy zaimplementuje system plików FAT16 to nie będę musiał się bawić z linkerem(jakoś nie chciał połączyć plików - były jakieś błędy)
Nie wiem jak pisać te całe skrypty linkera i liczyłem że bootloader sam znajdzie w katalogu plik z kernelem.
Acha i jeszcze jedno

Jeżeli skompilowane pliki main.c i boot.asm połączyć plik.img i rozmiar tego pliku będzie równy dokładnie 512 bajtów to virtual box będzie potrafił go odczytać - uruchomić
I teraz nie wiem co zrobić bo gdybym chciał pisać proste jądro ale rozmiar byłby choć o bajt za dużo to wywala błąd i nie wiem co z tym fantem zrobić.
Nie będę gnieździł się w 512 bajtach i wiem że muszę odczytać drugi sektor dysku ale nie wiem jak to zrobić

To cytat z innego forum na którym pisałem i chodzi mi o to że gdy obraz dyskietki będzie miał więcej niż 512 bajtów to virtual box go nie uruchomi.

0

Bootloader jest kompletnie niezależny od miejsca, w którym go uruchomiłeś, jak i od samego systemu hosta.
Sugerowałbym przerobienie podstaw podstaw z osdev wiki.

0

Więc jak je połączyć by virtual box się nie oburzał.
Treść erorra

Fehlercode:
VBOX_E_IPRT_ERROR (0x80BB0005)
Komponente:
Medium
Interface:
IMedium {29989373-b111-4654-8493-2e1176cba890}
Callee:
IVirtualBox {3b2f08eb-b810-4715-bee0-bb06b9880ad2}
Callee RC:
VBOX_E_OBJECT_NOT_FOUND (0x80BB0001)

0

Napisałem - skorzystaj z VFD.

0

a to jest ....

0

Virtual Floppy Drive.

0

Wirtualny napęd dyskietek - ustawiłem w virtual boxie już dawno by traktował obraz jako dyskietkę i nic - wyżej opisane błedy

0

Po pół minuty dalej nie wiem jak uruchomić te pliki co ma jestem w zakładce DRIVER i

The VFD driver is installed.
Failed to start the VFD driver.
Nastąpiło zablokowanie ładowania sterownika

Bede szukał jak to obsługiwać

0

Uruchamiasz vfdwin, wchodzisz do Driver, instalujesz sterownik oraz go startujesz (zainstalowany musi być tylko raz, uruchamiany co restart systemu).
Przechodzisz do Drive0 i przypisujesz odpowiednią literkę (np.A:/) do tej stacji.
Do całej reszty najlepiej napisać sobie automatyczny skrypt korzystający z interfejsu konsolowego (vfd.exe); masz vfdhelp.txt oraz fragment mojego pliku batch, który podesłałem w paru postach wcześniej.
Powinieneś dać sobie z tym radę...

#Edit nie jestem pewien, czy VFD działa na systemach 64-bitowych, może to być problemem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1