Having a problem parsing my simple file table.

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.
Post Reply
psimonson1988
Member
Member
Posts: 40
Joined: Tue Jul 16, 2019 8:40 pm

Having a problem parsing my simple file table.

Post by psimonson1988 »

This file table of mine I'm parsing it right the first time. But the second time loading the kernel, it doesn't parse properly or causes an error to occur. Spitting out a "File not found" message. I checked the floppy image and both the kernel entry is in the file table and the kernel is at 0x1400 in the floppy image. Which I believe is sector 10. Cannot figure out why it works once and then it doesn't work anymore after that. Because they are using the same code. My custom file table is laid out like this in C.

Code: Select all

typedef struct table {
  unsigned char filename[8];
  unsigned char extension[3];
  unsigned char _reserved;
  unsigned short start_sector;
  unsigned short sector_count;
} table_t;
Table and file loading are all done in include/file.inc.
NOTE: I rewrote it in NASM for first and second stage boot loaders. The problem lies in load_file.

How do I rewrite my load_file function to parse 7 sectors of 16 bytes each?

Code: Select all

; =========================================================
; FILE.INC - File table and file loading.
; Date: 03/12/2020
; Author: Philip R. Simonson
; =========================================================

; no parameters
load_table:
	mov ax, root_segment
	mov es, ax
	mov ax, 16
	mov bx, word [iRootSize]
	mul bx
	mov bx, word [iSectSize]
	xor dx, dx
	div bx
	mov cx, ax
	mov ax, 1
	xor bx, bx
.loop:
	push ax
	push bx
	push cx
	call read_sector
	mov si, op_progress
	call print_string
	pop cx
	pop bx
	add bx, [iSectSize]
	pop ax
	inc ax
	loopnz .loop
	ret

; read file from disk.
; ===========================================
; ax = loaded table segment
; [es:bx] = location to read data to.
; cx = how many sectors to read.
; ===========================================
read_file:
	push ax
	push bx
	push cx
	call read_sector
	pop cx
	pop bx
	add bx, [iSectSize]
	pop ax
	inc ax
	loopnz read_file
	ret

; call load table before this function.
; ===========================================
; ax = loaded table segment
; si = filename string
; ===========================================
load_file:
	mov ax, root_segment
	mov es, ax
	xor bx, bx
.check:
	mov al, [es:bx]
	cmp al, 0x0
	je .error
	mov cx, 11
	lea di, [es:bx]
	mov si, filename
	rep cmpsb
	je .match
	add bx, 16
	jmp short .check
.match:
	mov ax, word [es:bx+0x0c]
	mov cx, word [es:bx+0x0e]
	mov bx, load_segment
	mov es, bx
	xor bx, bx
	call read_file
	mov si, op_success
	call print_string
	mov ax, load_segment
	mov ds, ax
	mov es, ax
	jmp load_segment:0x0000
	ret
.error:
	mov si, op_notfile
	call print_string
	ret
I believe I may have made a mistake somewhere. Remember, they both use the same functions. Not sure where I went wrong. Thanks in advance for any help in resolving this issue.

Here is the 7 sectors of my simple file system:

Code: Select all

00000200  73 74 61 67 65 32 20 20  62 69 6e 00 08 00 02 00  |stage2  bin.....|
00000210  6b 65 72 6e 65 6c 20 20  62 69 6e 00 0a 00 10 00  |kernel  bin.....|
00000220  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00001000
One last thing the code: BOOT32

- Phil
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: Having a problem parsing my simple file table.

Post by Octocontrabass »

I found some problems:

Code: Select all

	cli
	mov byte [iBootDrive], dl
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, 0x7c00
	sti
You're storing DL to memory using DS, but you haven't set DS yet. You don't know what value DS has at this point, so you must set DS before using it to access memory.

You're using the value of CS to set the other segment registers, but you don't know what value CS has at this point. You must know the value you're writing to the segment registers. It's a good idea to use a far JMP to set CS to a known value as well, but you can skip that if you're careful about which instructions you use.

You don't need to disable interrupts here: interrupts can't happen between a MOV to SS and the following instruction.

Code: Select all

	lodsb
	or al, al
	jz .done
	mov ah, 0x0e
	mov bx, 7
	mov cx, 1
	int 0x10
You're using string instructions (LODSB), but you call this function before initializing the direction flag. You probably want to have a CLD instruction somewhere in your code.

You don't need to set CX for INT 0x13 AH=0x0E, so removing that instruction can save you a few bytes.

Code: Select all

root_segment dw 0x0ee0

Code: Select all

	mov ax, root_segment
	mov es, ax
This sets ES to the address of that word, rather than 0xEE0 as you probably intended.


I haven't looked at all of your code yet, but why are you using non-zero segment registers? Calculating addresses is much simpler when the segment registers are all set to zero, and you can still address 64kB, which should be more than enough for now (though be careful not to overwrite the IVT or BDA - the first 0x500 bytes of RAM).
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: Having a problem parsing my simple file table.

Post by PeterX »

Octocontrabass wrote: You're using string instructions (LODSB), but you call this function before initializing the direction flag. You probably want to have a CLD instruction somewhere in your code.
I don't do that in my code, too. You think some BIOS might set the DF? Then I should add CLD.

Greetings
Peter
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: Having a problem parsing my simple file table.

Post by Octocontrabass »

PeterX wrote:You think some BIOS might set the DF?
Yes. The contents of most registers are undefined when the BIOS passes control to your bootloader. (Even the value of DL is undefined, if the BIOS is not PnP-aware, but it's unlikely you'll want to support such old hardware anyway.)
psimonson1988
Member
Member
Posts: 40
Joined: Tue Jul 16, 2019 8:40 pm

Re: Having a problem parsing my simple file table.

Post by psimonson1988 »

Octocontrabass wrote:I found some problems:

Code: Select all

	cli
	mov byte [iBootDrive], dl
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, 0x7c00
	sti
You're storing DL to memory using DS, but you haven't set DS yet. You don't know what value DS has at this point, so you must set DS before using it to access memory.

You're using the value of CS to set the other segment registers, but you don't know what value CS has at this point. You must know the value you're writing to the segment registers. It's a good idea to use a far JMP to set CS to a known value as well, but you can skip that if you're careful about which instructions you use.

You don't need to disable interrupts here: interrupts can't happen between a MOV to SS and the following instruction.

Code: Select all

	lodsb
	or al, al
	jz .done
	mov ah, 0x0e
	mov bx, 7
	mov cx, 1
	int 0x10
You're using string instructions (LODSB), but you call this function before initializing the direction flag. You probably want to have a CLD instruction somewhere in your code.

You don't need to set CX for INT 0x13 AH=0x0E, so removing that instruction can save you a few bytes.

Code: Select all

root_segment dw 0x0ee0

Code: Select all

	mov ax, root_segment
	mov es, ax
This sets ES to the address of that word, rather than 0xEE0 as you probably intended.


I haven't looked at all of your code yet, but why are you using non-zero segment registers? Calculating addresses is much simpler when the segment registers are all set to zero, and you can still address 64kB, which should be more than enough for now (though be careful not to overwrite the IVT or BDA - the first 0x500 bytes of RAM).
Okay, I'm pretty sure that I have fixed all of the things you said were wrong so far. Hopefully there aren't too many more mistakes. :D
EDIT: Now it reads in all sectors from disk drive, but hangs when it tries to enable the A20 line. Not sure why it is hanging up though.
EDIT: Please help with enabling the A20 line, I did something wrong and it hangs while trying to enable it.
PS: I'm new to the A20 line and 32 bit protected mode, trying to learn them. I've done kernel coding in 32 bit protected mode, but I haven't wrote a bootloader for 32 bit yet. That's what I'm trying to do.
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: Having a problem parsing my simple file table.

Post by Octocontrabass »

You never did answer my question: why are you using non-zero segment registers?

I found a few more mistakes, too.

Code: Select all

	xor ax, ax
	mov ds, ax
	mov es, ax
	mov ss, ax
You're setting the segment registers to zero, which is good! But you need to add an ORG statement (something like "org 0x7c00" at the top of your code) to tell the assembler that your code is located at offset 0x7c00 relative to the start of the segment.

Code: Select all

	mov sp, 0xffff
Your stack is not properly aligned. It looks like you want to set SP to 0 here. (I notice you're loading some data from the disk near this address. How large may your stack grow before it overlaps that data?)

Code: Select all

	lea di, [es:bx]
The ES segment prefix here is meaningless and wastes space.

Code: Select all

	rep cmpsb
You should probably write the prefix here as REPE instead of REP. It doesn't change how the code is assembled by NASM, but it makes your intention more clear.

Code: Select all

	mov byte [es:di], 0x00
	mov byte [ds:si], 0xff
	cmp byte [es:di], 0xff
I can tell this code is intended to manipulate the same byte at two addresses 1MB apart, but you haven't set DI or SI, so you don't know what you're overwriting. I suspect your mystery writes here are the cause of the hang. (Also, if you're looking for a way to save some space, you can remove the redundant DS prefixes.)
psimonson1988
Member
Member
Posts: 40
Joined: Tue Jul 16, 2019 8:40 pm

Re: Having a problem parsing my simple file table.

Post by psimonson1988 »

Well I think I fixed everything that you said so far and it still doesn't print the message, "A20 is enabled." to the screen. In other words it still hangs for some reason. I'm pretty sure the problem lies in check_a20 somewhere because, if you disable it then the rest of the code works. Not sure how or why. Maybe something else like my file.inc is conflicting with a20.inc. But like I said, "I have no clue at this point.". Hopefully we figure it out though because I'm very curious as to the reason why. This is the second time I rewrote the entire first and second stage boot loaders by the way. If you want to see my first attempt to write the boot loaders it's in a folder in the same project, but I wrote it in GAS (GNU Assembler). With Intel syntax. The folder for the first attempt is call old_boot (which is unused currently).

By the way, ld is handling the linking of the two boot loaders, check the make file for linking, nasm links to elf32 and no org directive in elf. That has to be done by ld (the linker). Unless I want to redo some of the code and not use ld at all. But to get the flat binary I'm using objcopy.

Also to answer your question, "Why are you using non-zero segment registers?". Because the tutorial that I followed a very long time ago did it that way. It has always worked for me, so I thought it was the right way.
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: Having a problem parsing my simple file table.

Post by Octocontrabass »

psimonson1988 wrote:Well I think I fixed everything that you said so far and it still doesn't print the message, "A20 is enabled." to the screen.
I didn't spot any other issues. Time to pull out a debugger. See if you can find where your code is getting stuck.
psimonson1988 wrote:By the way, ld is handling the linking of the two boot loaders, check the make file for linking, nasm links to elf32 and no org directive in elf. That has to be done by ld (the linker). Unless I want to redo some of the code and not use ld at all. But to get the flat binary I'm using objcopy.
NASM is capable of directly creating a flat binary. Why bother with ld and objcopy?
psimonson1988 wrote:Also to answer your question, "Why are you using non-zero segment registers?". Because the tutorial that I followed a very long time ago did it that way. It has always worked for me, so I thought it was the right way.
Tutorials related to OS development tend to contain bad advice or be outright wrong. In this case, I'd consider nonzero segments to be bad advice: modern tools are designed to handle a flat memory model, so having nonzero segment registers adds complexity to both your code and your workflow.
psimonson1988
Member
Member
Posts: 40
Joined: Tue Jul 16, 2019 8:40 pm

Re: Having a problem parsing my simple file table.

Post by psimonson1988 »

Thank you for all your help and advice.. now it's off to debugging.
I'm not sure how to do it, but consider it solved. My file table is parsing properly now.
Post Reply