File System Issues

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Armature
Member
Member
Posts: 33
Joined: Wed Apr 25, 2018 2:44 pm
Libera.chat IRC: idk

File System Issues

Post by Armature »

Hey, this is my first time here and I was wondering if anyone could give me some help setting up the file system. So far I've set up the BPB with the FAT12 File system and written some small amount of code that looks for the root directory in a set location and searches the table for the kernel file. I then write the code to the boot sector of a floppy drive and insert the floppy drive into a windows computer but the computer reports back that the disk doesn't have a filesystem. I'm not sure what I've done wrong but here is my actual code.

Code: Select all

org 0x7C00
jmp near Boot
nop

bpbBytesPerSector:  	DW 512
bpbSectorsPerCluster: 	DB 1
bpbReservedSectors: 	DW 1
bpbNumberOfFATs: 	DB 2
bpbRootEntries: 	DW 224
bpbTotalSectors: 	DW 2880
bpbMedia: 	        DB 0xF0
bpbSectorsPerFAT: 	DW 9
bpbSectorsPerTrack: 	DW 18
bpbHeadsPerCylinder: 	DW 2
bpbHiddenSectors: 	DD 0
bpbTotalSectorsBig:     DD 0
bsDriveNumber: 	        DB 0
bsUnused: 	        DB 0
bsExtBootSignature: 	DB 0x29
bsSerialNumber:	        DD 0xa0a1a2a3
bsVolumeLabel: 	        DB "TSO         "
bsFileSystem: 	        DB "FAT12   "	

Boot:
	cli
	mov ax, 0x0000
	mov es, ax
	mov dx, ax
	mov ss, ax
	mov sp, 0x7C00
	mov bp, 0x0500
	sti

ReadDisk:
	mov ah, 0x02
	mov al, 13	;how many sectors to read
	mov cl, 19	;Starting sector
	mov ch, 0	;cylinder
	mov dh, 0	;head
	mov dl, 0x80	;Floppy Drive
	mov bx, 0x7E00	;ES:BX 0x0000:0x7E00
	int 0x13
	jc _DiskReadError
	mov cx, 8
	mov di, 0x7E00
	mov si, KERNEL
	jmp ReadFAT

_DiskReadError:
	mov si, READERR
	jmp BPrint

ReadFAT:
	cmp si, di
	jne _NotFound
	inc si
	inc di
	dec cx
	cmp cx, 0
	je _LoadEntry
	jne ReadFAT

_LoadEntry:
	mov si, KERF
	jmp BPrint

_NotFound:
	mov si, KERNF
	jmp BPrint


BPrint:
	mov ah, 0x0E
	_BPrintLoop:
	lodsb
	cmp al, 0
	je _BDone
	int 0x10
	jmp _BPrintLoop
	_BDone:
	cli 
	hlt
	
READERR db '!!ATTENTION!! - Disk Read Error. The system has been halted to prevent damage.', 0x0A, 0x0D, 0
KERNF db '!!ATTENTION!! - No Kernel [TerSysVI.bin] found. The system has been halted to prevent damage.', 0x0A, 0x0D, 0
KERNEL db 'TerSysVI BIN'
KERF db 'Kernel found. Loading Kernel from disk...', 0x0A, 0x0D, 0

times 510 - ($-$$) db 0
dw 0xAA55
This was compiled by nasm and when emulated in QEMU, I get the Disk read error.

Any help would be great. Thanks.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: File System Issues

Post by BenLunt »

Armature wrote:

Code: Select all

Boot:
	cli
	mov ax, 0x0000
	mov es, ax
	mov dx, ax
	mov ss, ax
	mov sp, 0x7C00
	mov bp, 0x0500
	sti
I am guessing that the "dx" above should have been "ds".
Armature wrote:

Code: Select all

ReadDisk:
	mov ah, 0x02
	mov al, 13	;how many sectors to read
	mov cl, 19	;Starting sector
	mov ch, 0	;cylinder
	mov dh, 0	;head
	mov dl, 0x80	;Floppy Drive
A value of 0x80 is not a floppy drive... (hint: dl < 0x80 == floppy. dl >= 0x80 == hard drive)
Armature wrote:

Code: Select all

	mov bx, 0x7E00	;ES:BX 0x0000:0x7E00
	int 0x13
	jc _DiskReadError
	mov cx, 8
	mov di, 0x7E00
	mov si, KERNEL
	jmp ReadFAT

_DiskReadError:
	mov si, READERR
	jmp BPrint

ReadFAT:
	cmp si, di
	jne _NotFound
	inc si
	inc di
	dec cx
	cmp cx, 0
	je _LoadEntry
	jne ReadFAT
Does the loop above actually do anything? si and di will never be the same. If it was, it will loop until cx == 0 and then go to _LoadEntry. However, since it doesn't, it goes directly to _NotFound anyway. Plus, you are checking that the address of KERNEL and 0x7E00 ever match. You need to be checking the two bytes *at* those addresses.

Your code actually does nothing after it "tries" to read 13 sectors from the *end of* the 1st track. Head = 0, Cyl = 0, Sector = 19 is an non-existant sector on a 1.44 standard floppy disk. This type of floppy has 80 cylinders, 2 sides per cylinder (tracks), 18 sectors per track = 2880 sectors.

I have an out-dated version of my FAT boot sector at https://github.com/fysnet/FYSOS/blob/ma ... at1x_f.asm that you might wish to study. I warn you though, if you simply copy and paste it, you will not get what you think you are getting. i.e.: it won't work right out of the box. You must study it and learn what it is doing so that you can adapt it, or better yet, write your own to work.

Ben
- www.fysnet.net/osdesign_book_series.htm
Octocontrabass
Member
Member
Posts: 5586
Joined: Mon Mar 25, 2013 7:01 pm

Re: File System Issues

Post by Octocontrabass »

Armature wrote:I then write the code to the boot sector of a floppy drive and insert the floppy drive into a windows computer but the computer reports back that the disk doesn't have a filesystem.
Use a hex editor to compare your boot sector with a working boot sector. You've probably made a mistake somewhere in the BPB.
Armature
Member
Member
Posts: 33
Joined: Wed Apr 25, 2018 2:44 pm
Libera.chat IRC: idk

Re: File System Issues

Post by Armature »

Octocontrabass wrote:
Armature wrote:I then write the code to the boot sector of a floppy drive and insert the floppy drive into a windows computer but the computer reports back that the disk doesn't have a filesystem.
Use a hex editor to compare your boot sector with a working boot sector. You've probably made a mistake somewhere in the BPB.
ive checked with several others and all the values appear to be present and correct.
Armature
Member
Member
Posts: 33
Joined: Wed Apr 25, 2018 2:44 pm
Libera.chat IRC: idk

Re: File System Issues

Post by Armature »

BenLunt wrote:
Armature wrote:

Code: Select all

Boot:
	cli
	mov ax, 0x0000
	mov es, ax
	mov dx, ax
	mov ss, ax
	mov sp, 0x7C00
	mov bp, 0x0500
	sti
I am guessing that the "dx" above should have been "ds".
Armature wrote:

Code: Select all

ReadDisk:
	mov ah, 0x02
	mov al, 13	;how many sectors to read
	mov cl, 19	;Starting sector
	mov ch, 0	;cylinder
	mov dh, 0	;head
	mov dl, 0x80	;Floppy Drive
A value of 0x80 is not a floppy drive... (hint: dl < 0x80 == floppy. dl >= 0x80 == hard drive)
Armature wrote:

Code: Select all

	mov bx, 0x7E00	;ES:BX 0x0000:0x7E00
	int 0x13
	jc _DiskReadError
	mov cx, 8
	mov di, 0x7E00
	mov si, KERNEL
	jmp ReadFAT

_DiskReadError:
	mov si, READERR
	jmp BPrint

ReadFAT:
	cmp si, di
	jne _NotFound
	inc si
	inc di
	dec cx
	cmp cx, 0
	je _LoadEntry
	jne ReadFAT
Does the loop above actually do anything? si and di will never be the same. If it was, it will loop until cx == 0 and then go to _LoadEntry. However, since it doesn't, it goes directly to _NotFound anyway. Plus, you are checking that the address of KERNEL and 0x7E00 ever match. You need to be checking the two bytes *at* those addresses.

Your code actually does nothing after it "tries" to read 13 sectors from the *end of* the 1st track. Head = 0, Cyl = 0, Sector = 19 is an non-existant sector on a 1.44 standard floppy disk. This type of floppy has 80 cylinders, 2 sides per cylinder (tracks), 18 sectors per track = 2880 sectors.

I have an out-dated version of my FAT boot sector at https://github.com/fysnet/FYSOS/blob/ma ... at1x_f.asm that you might wish to study. I warn you though, if you simply copy and paste it, you will not get what you think you are getting. i.e.: it won't work right out of the box. You must study it and learn what it is doing so that you can adapt it, or better yet, write your own to work.

Ben
- http://www.fysnet.net/osdesign_book_series.htm

ive gone through and modified the code and ive started to read through yours too
Armature
Member
Member
Posts: 33
Joined: Wed Apr 25, 2018 2:44 pm
Libera.chat IRC: idk

Re: File System Issues

Post by Armature »

Ive figured out what the issue was. I was jumping four bytes using

Code: Select all

jmp near boot
nop
instead of the three using

Code: Select all

jmp short boot
nop
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: File System Issues

Post by BenLunt »

Armature wrote:Ive figured out what the issue was. I was jumping four bytes using

Code: Select all

jmp near boot
nop
instead of the three using

Code: Select all

jmp short boot
nop
Good. Something specific to the assembler, but still, good. However, does it actually boot? I mean, does it find the next stage (kernel in your case), load, and jump to it?

Ben
Armature
Member
Member
Posts: 33
Joined: Wed Apr 25, 2018 2:44 pm
Libera.chat IRC: idk

Re: File System Issues

Post by Armature »

BenLunt wrote:
Armature wrote:Ive figured out what the issue was. I was jumping four bytes using

Code: Select all

jmp near boot
nop
instead of the three using

Code: Select all

jmp short boot
nop
Good. Something specific to the assembler, but still, good. However, does it actually boot? I mean, does it find the next stage (kernel in your case), load, and jump to it?

Ben
so far it boots and reads the disk but I haven't checked to see if it finds or loads the kernel yet.
Armature
Member
Member
Posts: 33
Joined: Wed Apr 25, 2018 2:44 pm
Libera.chat IRC: idk

Re: File System Issues

Post by Armature »

I just tried to find the kernel on the floppy rive using the code but it didn't find it. I did change the code to this :

Code: Select all

org 0x7C00
jmp near Boot
nop

bpbOEM			db "AAAAAAAA"			
bpbBytesPerSector:  	DW 512
bpbSectorsPerCluster: 	DB 1
bpbReservedSectors: 	DW 1
bpbNumberOfFATs: 	DB 2
bpbRootEntries: 	DW 224
bpbTotalSectors: 	DW 2880
bpbMedia: 		DB 0xf0
bpbSectorsPerFAT: 	DW 9
bpbSectorsPerTrack: 	DW 18
bpbHeadsPerCylinder: 	DW 2
bpbHiddenSectors: 	DD 0
bpbTotalSectorsBig: 	DD 0
bsDriveNumber: 		DB 0
bsUnused: 		DB 0
bsExtBootSignature: 	DB 0x29
bsSerialNumber:		DD 0xa0a1a2a3
bsVolumeLabel: 		DB "OUR FLOPPY "
bsFileSystem: 		DB "FAT12   "

BPrint:
	mov ah, 0x0E
	_BPrintLoop:
	lodsb
	cmp al, 0
	je _BDone
	int 0x10
	jmp _BPrintLoop
	_BDone:
	ret


Boot:
	cli
	mov ax, 0x0000
	mov es, ax
	mov ds, ax
	mov ss, ax
	mov sp, 0x7C00
	mov bp, 0x0500
	sti

ReadDisk:
	mov si, READING
	call BPrint
	mov ah, 0x02
	mov al, 13	;how many sectors to read
	mov cl, 1	;Starting sector
	mov ch, 1	;cylinder
	mov dh, 0	;head
	mov dl, 0x00	
	mov bx, 0x7E00	;ES:BX 0x0000:0x7E00
	int 0x13
	jc _DiskReadError
	mov cx, 0x0008
	jmp ReadFAT

_DiskReadError:
	mov si, READERR
	call BPrint
	cli
	hlt

ReadFAT:
	mov di, [0x7E00]
	mov si, [KERNEL]
	cmp si, di
	jne _NotFound
	inc si
	inc di
	dec cx
	cmp cx, 0x0000
	je _LoadEntry
	jne ReadFAT

_LoadEntry:
	mov si, KERF
	call BPrint
	cli
	hlt

_NotFound:
	mov si, KERNF
	call BPrint
	cli	
	hlt
	
READING db 'Reading Disk...', 0x0A, 0x0D, 0
READERR db '!!ATTENTION!! - Disk Read Error. The system has been halted to prevent damage.', 0x0A, 0x0D, 0
KERNF db '!!ATTENTION!! - No Kernel [TerSysVI.bin] found. The system has been halted to prevent damage.', 0x0A, 0x0D, 0
KERNEL db 'TerSysVI BIN'
KERF db 'Kernel found. Loading Kernel from disk...', 0x0A, 0x0D, 0

times 510 - ($-$$) db 0
dw 0xAA55
When testing on real hardware, the disk is read and then it moves to ReadFAT: but it shows that the file can't be found.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: File System Issues

Post by BenLunt »

Armature wrote:I just tried to find the kernel on the floppy rive using the code but it didn't find it. I did change the code to this :

Code: Select all

org 0x7C00
jmp near Boot
nop
Just a reminder that you changed the "near" to "short" earlier.
Armature wrote:

Code: Select all

BPrint:
	mov ah, 0x0E
	_BPrintLoop:
	lodsb
	cmp al, 0
	je _BDone
	int 0x10
	jmp _BPrintLoop
	_BDone:
	ret
It is a good idea to set BX to zero as well.
Armature wrote:

Code: Select all

ReadDisk:
	mov ah, 0x02
	mov al, 13	;how many sectors to read
	mov cl, 1	;Starting sector
	mov ch, 1	;cylinder
	mov dh, 0	;head
	mov dl, 0x00	
	mov bx, 0x7E00	;ES:BX 0x0000:0x7E00
	int 0x13
This reads from the second cylinder, first track, first sector. This is LBA = 36. What is at LBA 36? The last sector of the Root? It is a good idea to calculate the LBA from the BPB and then call a small routine to convert the LBA (dx:ax) into CHS (dx and cx). See line 216 of the URL I pointed you to before.
Armature wrote:

Code: Select all

ReadFAT:
	mov di, [0x7E00]
	mov si, [KERNEL]
	cmp si, di
	jne _NotFound
	inc si
	inc di
So you are grabbing a word from address 0x07E00 (ds:0x7E00) and a word from address ds:KERNEL. You then compare these two words and if equal, you increment si and di one byte offset. Do you see what you are doing? Comparing two bytes but only incrementing one byte.

Better to point SI to KERNEL and DI to current entry, CX := 11, and use repz scasb. If zero flag set, you found your entry. If not, you move SI back to KERNEL (push/pop) and increment DI to the next entry. See line 345 of the URL I pointed you to before.

Your boot code needs a lot more content to it. You need to load the root directory, found by calculating the LBA from the items in the BPB, calculate the count of root entries to search, calculate the LBA of the first FAT, etc.

You have a good start, now study and continue to add to it.

Ben
Armature
Member
Member
Posts: 33
Joined: Wed Apr 25, 2018 2:44 pm
Libera.chat IRC: idk

Re: File System Issues

Post by Armature »

Hey, I've read through your code and some other tutorials on how to convert LBA to CHS but I'm left really confused. I understand the maths behind the code and how the CHS are calculated but what I don't understand is the storing of the variables calculated. The outputs are stored in 16 bit registers when the interrupt using the 8bit registers to read and write data. Would you be able to explain this to me please?
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: File System Issues

Post by BenLunt »

Armature wrote:Hey, I've read through your code and some other tutorials on how to convert LBA to CHS but I'm left really confused. I understand the maths behind the code and how the CHS are calculated but what I don't understand is the storing of the variables calculated. The outputs are stored in 16 bit registers when the interrupt using the 8bit registers to read and write data. Would you be able to explain this to me please?
For Floppy Drives, 8-bit registers only might be okay. However, CHS (Cylinder/Head/Sector) values are stored in three 8-bit registers, though each 8-bit register is *not* exclusive to only one of the values.

Code: Select all

 CH = low eight bits of cylinder number
 CL = sector number 1-63 (bits 0-5)
         high two bits of cylinder (bits 6-7, hard disk only)
 DH = head number
The indication that the high two bits are hard-drives only is simply because a floppy disk doesn't have that many cylinders. You still have to place the high two bits of the cylinder (which for floppy disks will be 00b) into the high two bits of CL.

Let's show an example. CX is the 16-bit register, consisting of CH and CL. Therefore, shown is the 16-bit representation of the cylinder and sector bits: (assuming that your version of this forum uses fixed width fonts for all code blocks)

Code: Select all

  11111100 00000000
  54321098 76543210
  CCCCCCCC CCSSSSSS
Where 'C' = Cylinder and 'S' = Sector. However, the 10-bit cylinder value is actually:

Code: Select all

  76543210 98xxxxxx
  CCCCCCCC CCxxxxxx
To get the actual Cylinder number from CX above, you can do something like:

Code: Select all

  ; CX = 10-bit cylinder number in CHS form (zero based)
  mov  dh,cl
  shr  dh,6
  mov dl,ch
 ; DX now is 10-bit cylinder number (zero based)
and DX will now contain a valid 10-bit cylinder number. Convert from a 10-bit number back to CHS, you could do something like:

Code: Select all

  ; DX = 10-bit cylinder number (zero based)
  ; AL = 6-bit sector number (1 based)
  mov  ch,dl
  mov cl,dh
  shl  cl,6
  and  al,3Fh
  or  cl,al
  ; CX now is 10-bit cylinder number in CHS form (zero based)
The code that I directed you to might not be near as clear on what it is doing, but this is because a boot sector needs all of the room it can get. For boot sectors, if you can do the conversion in 16 bytes and have unclear code, compared to 32 bytes to have clear code, you do the 16-byte version and include a well-documented comment block indicating what you are doing.

Does this help?
Ben
Armature
Member
Member
Posts: 33
Joined: Wed Apr 25, 2018 2:44 pm
Libera.chat IRC: idk

Re: File System Issues

Post by Armature »

BenLunt wrote:
Armature wrote:Hey, I've read through your code and some other tutorials on how to convert LBA to CHS but I'm left really confused. I understand the maths behind the code and how the CHS are calculated but what I don't understand is the storing of the variables calculated. The outputs are stored in 16 bit registers when the interrupt using the 8bit registers to read and write data. Would you be able to explain this to me please?
For Floppy Drives, 8-bit registers only might be okay. However, CHS (Cylinder/Head/Sector) values are stored in three 8-bit registers, though each 8-bit register is *not* exclusive to only one of the values.

Code: Select all

 CH = low eight bits of cylinder number
 CL = sector number 1-63 (bits 0-5)
         high two bits of cylinder (bits 6-7, hard disk only)
 DH = head number
The indication that the high two bits are hard-drives only is simply because a floppy disk doesn't have that many cylinders. You still have to place the high two bits of the cylinder (which for floppy disks will be 00b) into the high two bits of CL.

Let's show an example. CX is the 16-bit register, consisting of CH and CL. Therefore, shown is the 16-bit representation of the cylinder and sector bits: (assuming that your version of this forum uses fixed width fonts for all code blocks)

Code: Select all

  11111100 00000000
  54321098 76543210
  CCCCCCCC CCSSSSSS
Where 'C' = Cylinder and 'S' = Sector. However, the 10-bit cylinder value is actually:

Code: Select all

  76543210 98xxxxxx
  CCCCCCCC CCxxxxxx
To get the actual Cylinder number from CX above, you can do something like:

Code: Select all

  ; CX = 10-bit cylinder number in CHS form (zero based)
  mov  dh,cl
  shr  dh,6
  mov dl,ch
 ; DX now is 10-bit cylinder number (zero based)
and DX will now contain a valid 10-bit cylinder number. Convert from a 10-bit number back to CHS, you could do something like:

Code: Select all

  ; DX = 10-bit cylinder number (zero based)
  ; AL = 6-bit sector number (1 based)
  mov  ch,dl
  mov cl,dh
  shl  cl,6
  and  al,3Fh
  or  cl,al
  ; CX now is 10-bit cylinder number in CHS form (zero based)
The code that I directed you to might not be near as clear on what it is doing, but this is because a boot sector needs all of the room it can get. For boot sectors, if you can do the conversion in 16 bytes and have unclear code, compared to 32 bytes to have clear code, you do the 16-byte version and include a well-documented comment block indicating what you are doing.

Does this help?
Ben
This makes a lot more sense now thank you. now that CX is both the cylinder and sector number, can we just call int 0x13 and sector will be read into the memory?
Armature
Member
Member
Posts: 33
Joined: Wed Apr 25, 2018 2:44 pm
Libera.chat IRC: idk

Re: File System Issues

Post by Armature »

Right, I think I finally got it. This is the code I've written:

Code: Select all

div [bpbSectorsPerTrack]	;When dividing, the answer is stored in AX but the reaminder is stored in the DX register
	add dx, 0x01				;dx should now be the sector in CHS format
	mov [ABSOLUTESECTOR], dx		;Store the value in mem address pointed to by label
	mov ax, [LBA]
	mul [bpbHeadsPerCylinder]			;multiply the ax register by the number of heads on the medium
	div [bpbSectorsPerTrack]		;divide the ax register by the number of sectors per track
	mov [ABSOLUTECYLINDER], ax		;move what should be the Cylinder into the mem location at the label
	mov ax, [LBA]
	div [bpbSectorsPerTrack]
	div [bpbHeadsPerCylinder]
	mov [ABSOLUTEHEAD], dx			;the remainder, which was stored into dx should be the absolute head
How does it look?

EDIT: By the way I moved the calculated root directory LBA into AX before I called this.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: File System Issues

Post by BenLunt »

That's a whole lot of code for a little bit of output.

Remember:

Code: Select all

; Sector   = (LBA mod SPT)+1
; Head     = (LBA  /  SPT) mod Heads
; Cylinder = (LBA  /  SPT)  /  Heads
This requires at most two divides. Period.

Divide LBA with SPT gives you the (LBA mod SPT) for the Sector as well as the (LBA / SPT) for the remaining two statements. Then divide that result with Heads and you get both the Head and the Cylinder values.

Again, the code I pointed to uses two divides and shows how to divide a 32-bit value by a 16-bit value in 16-bit real mode. If you are only going to use CHS for floppies, you can forget about the 32-bit divide.

Code: Select all

     ; ax = LBA
     mov  cx,nSecPerTrack    ; sectors per track 
     div  cx                 ; ax = ax / cx  with dx = remdr.
     push dx                 ; save sectors
     mov  cx,nHeads          ; heads per cylinder
     div  cx                 ; ax = ax / cx  with dx = remdr.
     pop  cx                 ; restore sector
     inc  cx                 ; sectors are one (1) based
     ; cx = sector
     ; ax = cylinder
     ; dx = head
To get back to my point on smaller code is better code in a boot sector, the above can be optimized to:

Code: Select all

     ; ax = LBA
     div  nSecPerTrack    ; ax = ax / cx  with dx = remdr.
     push dx              ; save sectors
     div  nHeads          ; ax = ax / cx  with dx = remdr.
     pop  cx              ; restore sector
     inc  cx              ; sectors are one (1) based
     ; cx = sector
     ; ax = cylinder
     ; dx = head
Thirteen (13) bytes. Your code, on the other hand, would be nearly fifty (50) bytes. In boot sectors, 40 bytes is a ballpark sized space to use for other items such as error checking and string space.

Ben
Post Reply