Page 1 of 1
Having a problem parsing my simple file table.
Posted: Fri Mar 13, 2020 2:27 pm
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
Re: Having a problem parsing my simple file table.
Posted: Sat Mar 14, 2020 3:57 am
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.
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).
Re: Having a problem parsing my simple file table.
Posted: Sat Mar 14, 2020 7:59 am
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
Re: Having a problem parsing my simple file table.
Posted: Sat Mar 14, 2020 9:22 am
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.)
Re: Having a problem parsing my simple file table.
Posted: Sat Mar 14, 2020 1:50 pm
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.
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.

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.
Re: Having a problem parsing my simple file table.
Posted: Mon Mar 16, 2020 7:20 am
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.
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?)
The ES segment prefix here is meaningless and wastes space.
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.)
Re: Having a problem parsing my simple file table.
Posted: Mon Mar 16, 2020 1:25 pm
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.
Re: Having a problem parsing my simple file table.
Posted: Tue Mar 17, 2020 1:50 am
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.
Re: Having a problem parsing my simple file table.
Posted: Tue Mar 17, 2020 1:52 pm
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.