stevewoods1986 wrote:This is making me confused.
Trust me, that happens to everyone when they are learning this stuff. It's all a great big mess, but sadly, it's a great big mess that can't be avoided at some point in any x86 PC boot loader project. It is a big part of why we usually recommend using an existing boot loader instead of rolling your own, though doing one yourself at least it does give a lot of interesting experience - albeit experience which becomes almost entirely irrelevant the moment the OS switches into p-mode.
It might be worth while reviewing
Real Mode memory addressing in detail, since I suspect that's part of the problem. I don't know how much of this you already know or not, and most of the problem parts aren't specific to reading from disk, or even to the PC in general, but are a direct consequence of the design of the x86 processor and how they manage memory in real mode.
Basically, the 8086 (the original Intel CPU of the x86 series from 1978) and its 8/16 bit version, the 8088 (which is what was used in the original IBM PC in 1981) used a system called 'segmented memory', in which two 16-bit values - a segment address, and an offset from that segment address - are combined to form a 20-bit physical memory address, giving a 1 MiB address space (and some change due to the way the segments and offsets overlap).
OK, so I am pretty sure if you've done any x86 assembly at all, you know that much. What you probably don't know is the historical reasons for it, and the actual details of how it works - if you are like most of us starting out, you have some idea of what it means but don't understand it as well as you think you do.
You will note that normally two 16-bit values, if used together, would form a 32-bit value. This is what most people would have expected the designers to do, and is how the earlier 8-bit architectures like the Intel 8080, the Zilog Z80 (which was mostly a clone of the 8080), the Motorola 6800, and the MOStek 6502 got a 16-bit (64KiB) address space. A number of other 16-bit systems such did just that, though for the most part chip designers skipped 16-bit designs and went straight to 32-bit ones such as the Motorola 68000. Intel, however, went with a rather more complicated affair that reduced the address space significantly.
Now, you might well wonder why in Eris' name they would do something so daft, but the designers had Good Reasons At The Time to do this.
First off - and this is a key to understanding a lot of what came later - Intel wasn't intending the 8086 to be a home computer system, and they didn't expect it to be used for very long. Intel had two different markets in mind - embedded microcontrollers, which they had more or less invented with the 4004 ten years earlier, and the emerging professional workstations, which they intended to get a jump on with a brand-new and ultra-powerful 32-bit design, the i432. They were also planning a few new series of microcontrollers, but in 1978, they had what was seen as an immediate need to extend the life of the 8080, especially given they way Zilog had caught them off-guard with the Z80. They had thrown together the 8080A in 1977, but they needed a good stopgap to buy them three or four years, and they knew other manufacturers were planning 16 and even 32 bit systems, so they wanted to beat those to the market.
Note that they were deliberately ignoring the 'microcomputer' market at this time, because they figured it was a dead end - what sort of weirdo wants a computer in their own home, anyway? - and that if they left that market to Zilog et al., they would be too focused on those absurd toys to focus on the vastly more important microcontrollers (they were right about that part - even ignoring the maker market, which they mostly have, the market for microcontrollers for things like toasters and traffic signs is about twenty times what the PC and mobile markets are combined, and that remains Intel's bread and butter) and maybe not notice what was going on with workstations. After all, if you were going to take on up and coming giants like the
Xerox Alto and the
Lisp Machine, you needed to be on your game, right?
(I so wish that LispMs hadn't come crashing down... but that was all on the companies that made them, not the technology they used - sure, let's keep building hand built wire-wrapped systems, who needs microprocessors, anyway!) and besides it is thirty-five years too late to do anything about it.)
This sort of leads into the next part, which is that they were assuming that most of the programming would be in assembly, by professional programmers who had already used the 8080. Thus, they wanted to make porting assembly code from the 8080 to the 8086 as easy as possible, which is no mean feat. They chose segmentation primarily to make it so that programs written for a 16-bit address space could be ported without having to change how the addressing was handled - as long as it used 'short' (PC relative 8-bit) and 'near' (16-bit segment offset) addresses within a single segment, the program would need a lot fewer alterations.
This was to become known as the 'tiny' memory model, and when MS-DOS came along, it had a separate executable file format, .COM, just for programs like that.
Finally, using a full 32-bit address space would have required more pins on the chip's Dual-Inline Package (DIP), and additional address lines for all the memory components. While they could have done what Motorola did around the same time with the 68K - namely, just not give it a full set of address pins, figuring they could work in a 24-bit (16MiB) address space - they opted to go with a 20-bit space, since after all, who would be able to afford a full megabyte of memory for a microcontroller?
Segments were not an invention of Intel's; but after they were used in the IBM System/360 in 1966, with unpleasant consequences, most of the industry realized that they were not a particularly wise idea and avoided them. Intel, however, figured it wouldn't hurt, since they anticipated that the 8086 would only be used for maybe five or six years. Isn't the irony wonderful?
The particular approach they used took the segment nase, shifted upwards by one byte, and added the segment offset to that to get the absolute address. So, for 7c00:0000, the absolute address is:
For 7c00:0200 (not 7c00:0010 - I made a mistake earlier, I meant to say you needed advance the loading location by 512 bytes, not 16 bytes), it would be
However, you will note that these segments overlap at a period of 16, meaning that
and
and
and even
This arrangement is, not to put too fine a point on it, a real PITA to really learn properly, and even experienced x86 coders frequently get confused by it if they aren't paying close attention.
Since the registers were all 16-bit, including the instruction pointer, there was no way to represent an absolute address in the registers - it wouldn't fit. Thus, all addressing had to be done from a segment base. By default, the instruction pointer (IP, also called the Program Counter or PC in other systems) is always from a segment offset in the Code Segment (CS) register, and the stack pointer (SP) is similarly always from Stack Segment (SS). With data, a 16-bit address defaults to an offset from the Data Segment (DS), but can be used with the Extra Segment (ES).
A basic assumption of all of this was that most addresses would be local to the segment pointed to by DS ('near' addresses); some instructions could
only work on near addresses, just as some could only use AX as an implicit register target (the original version of IMUL was like this). To indicate a different base segment, the instruction opcodes had to have fields indicating the type of addressing (short, near, or far) and if far, which segment to use for the base.
This doesn't even touch the mess that is 16-bit and 32-bit protected mode segmentation; fortunately, in 32-bit p-mode, you actually have 32-bit address registers, so for a 4GiB address space you only need to set all the segments to a single base for each task record's virtual memory space, and treat it as if it were a sensible flat memory instead. Unfortunately, you still need to deal with real mode until you've switched to p-mode, so you do need to know about this stuff.
Wicked-Pedo has a
much more extensive explanation of this, as does the OS Dev wiki on the page explaining
Real Mode, though this should be enough to get you started - or terminally confused.