52

I know that BIOS loads its first instruction from 0xFFFFFFF0, but why this specific address? I've a bunch of questions and hope you can help me with some of them, at least.

My questions:

  • Why is the first BIOS instruction located at the "top" of a 4 GB RAM?
  • What would happen if my computer only has 1 GB of RAM?
  • What about systems with more than 4 GB of RAM (for example, 8 GB, 16 GB, etc.)?
  • Why is the stack initialized with some value (in this case, a value located at 0xFFFFFFF0)?

I've read about that this afternoon, and I still don't get it.

  • 28
    One question per question please. – Lightness Races in Orbit Oct 19 '15 at 12:42
  • 4
    I like how the accepted answer doesn't even mention segmented memory or addressing modes at all, and the only place the A20 line is even touched is in the comments. – imallett Oct 20 '15 at 7:17
  • Atmel AVRs start execution from address 0, while Freescale HCS08's start from 0xFFFE, iirc. Each processor family has its own characteristics. – Nick T Oct 20 '15 at 14:20
  • 2
    @imallett I like how you decide to complain about it here rather than asking the poster to update their answer with further information. I also like how you think that this knowledge that OP would be able to spot even though the purpose of asking a question is to get knowledge about stuff that one might now fully understand. – MonkeyZeus Oct 20 '15 at 18:18
  • 2
    @MonkeyZeus to date, 9 other commenters have already done that, and it still hasn't changed. My comment, while sarcastic, was not vacuous; it is a warning to future netizens, as well as to the OP. – imallett Oct 21 '15 at 0:14
57

0xFFFFFFF0 is where an x86-compatible CPU starts executing instructions when it's powered on. That's a hardwired, unchangeable (without extra hardware) aspect of the CPU and different types of CPUs behave differently.

Why the first BIOS instruction is located at the "top" of a 4 GB RAM?

It's located at the "top" of 4 GB address space - and on power-on the BIOS or UEFI ROM is set to respond to reads of those addresses.

My theory on why this is:

Just about everything in programming works better with contiguous addresses. The CPU designer does not know what a system builder will want to do with the CPU, thus it's a bad idea for the CPU to require addresses smack in the middle of the space be required for various purposes. It's better to keep that "out of the way" at the top or bottom of the address space. Of course, keep in mind this decision was made when the 8086 was new, which did not have an MMU.

In the 8086, interrupt vectors existed at memory location 0 and above. Interrupt vectors need to be at known addresses and were desired to be in RAM for flexibility - yet it was not possible for the CPU designer to know how much RAM was going to be in a system. So starting from 0 and working up made sense for those (because no system in 1978 when the 8086 was invented would have 4 Gbytes of RAM - so expecting RAM to be at 0xFFFFFFF0 was not a good idea), and then ROM would have to be at the upper boundary.

Of course, starting with at least the 80286, interrupt vectors could be moved to a different starting location other than 0, but modern 64-bit x86 CPUs still boot up in 8086 mode, so everything still works the old way for compatibility (as ridiculous as it sounds in 2015 to still need your x86 CPU to be able to run DOS).

So since interrupt vectors start from 0 and work upward, ROM would have to start from the top and work downward.

What would happen if my computer has only 1 GB of RAM?

A 32-bit CPU has 4,294,967,296 addresses, numbered 0 (0x00000000) to 4294967295 (0xFFFFFFFF). ROM can live in some addresses and RAM can live in others. With the CPU's MMU this can even be switched on the fly. RAM does not have to live at all addresses.

With only 1 GB of RAM, some addresses will not have anything responding when they are read or written to. This can cause invalid data to be read when such addresses are accessed or a system lockup.

What about systems with more than 4 GB of RAM (e.g: 8 GB, 16 GB, etc.)?

Keeping it somewhat simple: 64-bit CPUs have more addresses (which is one of the things that makes them 64-bit - e.g. 0x0000000000000000 through 0xFFFFFFFFFFFFFFFF) for example, so the extra RAM "fits". Assuming the CPU is in long mode. Until then the RAM is there, just not addressable.

Why is stack initialized with some value (in this case, a value located at 0xFFFFFFF0)?

I can't immediately find anything on what x86 assigns the stack pointer at power-on, but it would eventually have to be reassigned by an initialization routine anyway once that routine finds out how much RAM is in the system. (@Eric Towers in the comments below reports that it is set to zero on power up.)

| improve this answer | |
  • 7
    It's best to think of the address space as a big space in which things can be assigned by hardware. When the CPU reads/writes memory, it actually performs a communication over a bus, and hardware can make sure things like RAM or ROM is responding at specific address ranges. So such hardware would have to make sure a ROM responds at 0xFFFFFFF0 when the CPU is reset. There is no inherent obligation that ROM appears right after RAM. It can appear wherever the hardware tells it too, depending on the capabilities of such hardware. – LawrenceC Oct 19 '15 at 2:21
  • 4
    It's possible to have "holes" or unassigned spaces that aren't used by ROM, RAM, or anything - typically accessing those will cause a system lockup. – LawrenceC Oct 19 '15 at 2:22
  • 16
    This answer assumes that the CPU can use 32 address bits while in 16 bit mode. But in 16 bit mode it can only use 20 address bits. The address 0xFFFFFFF0 is not reachable until after the CPU has been switched to 32 bit mode. Last time I looked closely at BIOS code, the entry point was at 0xFFFF0. – kasperd Oct 19 '15 at 7:27
  • 6
    @MichaelKjörling your calculation is wrong. Shifted segment and offset are not ORed, they are added. Thus logical FFFF:FFF0 is physical (1)0FFE0 (where leading 1 is present if A20 is enabled). – Ruslan Oct 19 '15 at 9:58
  • 9
    @kasperd There's a hack in place - the memory manager has the high 12 bits set to 1 until the first long jump takes place. So yes, logically, you're working with 0xFFFF0, but in reality, it maps to 0xFFFFFFF0. I expect this was done for compatibility with the 8086 - both it and more modern CPUs appear to use 0xFFFF0, but the 32-bit CPUs actually access 0xFFFFFFF0 (mapped to BIOS ROM). – Luaan Oct 19 '15 at 13:24
27

It isn't located at the top of RAM; it is located in ROM whose address is at the top of the memory address space, along with any memory on expansion cards, like Ethernet controllers. It is there so that it won't conflict with RAM, at least until you have 4 GB installed. Systems that have 4 GB or more of RAM can do two things to resolve the conflict. Cheap motherboards simply ignore the parts of RAM that conflict with where the ROM is located. Decent ones remap that RAM to appear to have an address above the 4 GB mark.

I'm not sure what you are asking about the stack. It certainly isn't initialized to be in ROM. When the CPU resets, it is initially in "real mode", where it acts just like the original 8086 and uses 16-bit segmented addressing, allowing it only to access 1 MB of memory. The BIOS code is located at top of that 1 MB. The BIOS picks somewhere in RAM to set up the stack and loads and executes the first sector of the first bootable drive. It is up to the OS to switch into 32 or 64-bit mode once it takes over and set up its own stacks (one per task/thread).

| improve this answer | |
  • 1
    Thank you so much for the answer, but @LawrenceC give more details on his answer and helped me on how the whole thing works. Anyway, thank you! I give you an upvote :3 – Fernando Paladini Oct 19 '15 at 2:33
14

First, this has nothing to do with RAM, really. We're talking about address space here - even if you only have 16 MiB of memory, you still have the full 32 bits of address space on a 32-bit CPU.

This already answers your first question, really - at the time this was designed, real world PCs had nowhere near the full 4 GiB of memory; they were more in the range of 1-16 MiB of memory. The address space was, for all intents and purposes, free.

Now, why 0xFFFFFFF0 exactly? The CPU doesn't know how much of the BIOS there is. Some BIOSes may only take a few kilobytes, while others may take full megabytes of memory - and I'm not even getting into the various optional RAMs. The CPU must be hardwired to some address to start on - there's noöne to configure the CPU. But this is only a mapping of the address space - the address is mapped directly into the BIOS ROM chip (yes, this means you don't get access to the full 4 GiB of RAM at this point if you do have that many - but that isn't anything special, many devices require their own range in address space). On a 32-bit CPU, this address gives you full 16 bytes to do the very basic initialization - which is enough to setup your segments and, if needed, address mode (remember, x86 boots in 16-bit real mode - the address space isn't flat) and do a jump to the real boot "procedure". At this point, you don't use RAM at all - it's all just mapped ROM. In fact, RAM isn't even ready to be used at this point - that's one of the jobs of the BIOS POST! Now, you might be thinking - how does a 16-bit real mode access the address 0xFFFFFFF0? Sure, there's segments, so you have 20-bit address space, but that still isn't good enough. Well, there's a trick to it - the 12 high bits of the address are set until you execute your first long jump, giving you access to the high address space (while rejecting access to anything lower than 0xFFF00000 - until you execute a long jump).

All this are the things that are mostly hidden from programmers (not to mention users) on modern operating systems. You usually don't have any access to anything so low level - some things are already beyond salvage (you can't switch CPU modes willy-nilly), some are exclusively handled by the OS kernel.

So a nicer view comes from old-school coding on MS DOS. Another typical example of device memory being directly mapped to address space is direct access to video memory. For example, if you wanted to write text to the display fast, you wrote directly to address B800:0000 (plus offset - in 80x25 text mode, this meant (y * 80 + x) * 2 if my memory serves me right - two bytes per character, line by line). If you wanted to draw pixel-by-pixel, you used a graphics mode and the start address of A000:0000 (typically, 320x200 at 8 bits per pixel). Doing anything high-performance usually meant diving into device manuals, to figure out how to access them directly.

This survives to this day - it's just hidden. On Windows, you can see the memory addresses mapped to devices in the Device manager - just open properties of something like your network card, go to the Resources tab - all the Memory Range items are mappings from device memory to your main address space. And on 32-bit, you'll see that most of those devices are mapped above the 2 GiB (later 3 GiB) mark - again, to minimize conflicts with user-useable memory, though this is not really an issue with virtual memory (applications don't get anywhere near the real, hardware address space - they have their own virtualized chunk of memory, which might be mapped to RAM, ROM, devices or the page file, for example).

As for the stack, well, it should help to understand that by default, stack grows from the top. So if you do a push, the new stack pointer will be at 0xFFFFFEC - in other words, you're not trying to write to the BIOS init address :) Which of course means that the BIOS init routines can use the stack safely, before remapping it somewhere more useful. In old-school programming, before paging became the de facto default, the stack usually started on the end of RAM, and "stack overflow" happened when you started overwriting your application memory. Memory protection changed a lot of this, but in general, it maintains backwards compatibility as much as possible - note how even the most modern x86-64 CPU can still boot MS DOS 5 - or how Windows can still run many DOS applications that have no idea about paging.

| improve this answer | |
  • 3
    Excellent answer, just to expand and say that modern processors are starting to drop hacks like A20 line masking, so support for older edge-cases is dying. – Basic Oct 20 '15 at 19:10
  • 2
    To the last paragraph: BIOS can't use the stack "freely": it can't write to the ROM (to which 0xFFFFFFEC would be mapped). This means not only no push but for example no call either. These must wait until RAM is ready. – The Vee Nov 12 '18 at 9:22
7

In addition to the other points mentioned, it may be helpful to understand what an address is. While newer architectures complicate things, historically a machine would on each memory cycle output the desired address on 20 to 32 wires (depending upon the architecture, with some special tricks to note whether it needed to a pair or foursome of bytes simultaneously); various parts of the memory system would examine the state of those wires and activate themselves when they saw certain combinations of high and low values.

If a machine with 32 address wires only needed to use 1MB of RAM and 64KB of ROM [quite plausible for some embedded controllers] it might activate the RAM for all addresses where the top address wire was low and the ROM for all addresses where it was high. The bottom 20 address wires would then be tied to the RAM to select one of 1,048,576 bytes and the bottom 16 would be wired to the ROM as well, to select one of 65,536 bytes. The remaining 11 address wires would simply be not be connected to anything.

On such a machine, accesses to addresses 0x00100000-0x001FFFFF would be equivalent to accesses to RAM addresses 0x00000000-0x000FFFFF. Likewise with addresses 0x000200000-0x0002FFFFF, or 0x7FF00000-0x7FFFFFFFF. Addresses above 0x80000000 would all read ROM, with a 64K pattern repeating throughout the space.

Even though the processor has a 4,294,967,296-byte address space, there's no need to have hardware recognize that many distinct addresses. Putting the reset vector near the top of the address space is a design that will work well regardless of how much or how little RAM and ROM the system has and avoids the need to fully decode the address space.

| improve this answer | |
  • Good point - You won't find any 64-bit hardware that will support anything close to the addressable 64-bit memory space (or even 1x10^-12 of it). – Basic Oct 20 '15 at 19:20
3

My teory is because we are using negative logic the digital one (1) is no tension at all (O volts) We only have to put tension on the last 4 bits at initialization so the program counter (or Instruction pointer) lacates at 1111 1111 1111 1111 1111 1111 1111 0000. We don't have to address the upper 28 bits since most (old) cpu's) were 16 bits and the lower nibbles can be addressed by a single address chip in the old days. Now since we have 64 bits with compatibility to 32 bits and 32 bits to 16 bits, the hardware couild have been improved but the method stays. Also bioses aren't always 64 bits or 32 bits programmed. My opinion is also since memories aren't always the same, the bios must be located at the same first segment. The way we see the bios addressed isn't the real address all the time. Just a taught of me ...

| improve this answer | |
2

upon RESET an 8088/8086 compatible cpu executes the instructions at 0FFFF0, which is 16 bytes below the 1 megabyte limit. normally the ROM at this location (in PC implementations) would be the BIOS, so at the end of the BIOS ROM, there is a jump to the start of the BIOS rom.

shown here: start vector and 'date' signature behind it, IBM 5150 PC 8KB eprom dump bios date: 10/19/1981

00001FEE  FF                db 0xff
00001FEF  FF                db 0xff
00001FF0  EA5BE000F0        jmp word 0xf000:0xe05b
00001FF5  3130              xor [bx+si],si
00001FF7  2F                das
00001FF8  3139              xor [bx+di],di

note that the addressing are of an 8KB $2000 rom, which places the start address (the absolute far JMP, to whichever other location, in this case within the 8KB rom itself, although not the lowest possible address within that rom) at $FFFF:$0 segmented or $FFFF0 linear.

as for compatibility: if some 'future' or current processor 'expects' it to have a whole lot more F's in front of the address, that doesn't matter. for compatibility of newer cpus in older systems the additional address lines remain unconnected and therefore the data on the databus is exactly the same. as long as the least significant bits remain FFFF0.

(in a system with just 1mb ram and the rom positioned in the end of that ram, and nothing else, it'll happily 'think' it's talking to the higher address yet get the exact same data, because those implementations have never heard of address lines higher than A19)

take note that the world is not just 'pcs'... the ibm pc was an 'accident', these processors were never specifically designed for 'pcs' and go into a whole lot of more things than just pcs (such as satellites, weapons sytems, etc). 32 and 64bit protected mode are usually -not- desired. (virtual 8086 mode is a lot more interesting as a reason to pick a newer (386+) version for example). therefore there is a lot more to 'backwards compatibility' than just 'will it run dos'.

| improve this answer | |
1

The motherboard ensures that the instruction at the reset vector is a jump to the memory location mapped to the BIOS entry point. This jump implicitly clears the hidden base address present at power up. All of these memory locations have the right contents needed by the CPU thanks to the memory map kept by the chipset. They are all mapped to flash memory containing the BIOS since at this point the RAM modules have random crap in them.

| improve this answer | |
0

the iAPx86 family is a generic chipset that went into a whole lot of other things besides the ibm pc (which kinda randomly decided to simply buy intels chipset (although not the complete set as intel intended it to be used) and build a pc out of it.

having the start address at the top (the start address should always be rom) removes the need to decode the additional address lines (you can simply take an 8088, some bus demultiplexing logic, and a 512 bytes (yes that's bytes, not kilo or mega ;) rom, hook it up from $0000-$0200 and the start address will still be $001F0 (topmost bits all '1', least significant nibble all zeros). simply leaving the rest of the address bus unconnected.

the vast majority of cpu families have reset vectors or a starting address at the highest address locations as in systems which don't just have rom but ram as well you normally place the ram at the bottom and the rom at the top. (if the system is supposed to be expandable in either aspect, leaving a gap in the middle ;)

i/o has it's own selection pin and instructions (IN/OUT) and thus is not part of normal address space in most intel iAPx86 implementations.

but anyway. short story: 1111111111111110000 is always 'some 1's followed by 4 zeroes. thus the highest address-16 you have in your rom. if the cpu wants '111111111111110000' and you only have '111110000' lines connected, that's still the same to the cpu. no need to decode the rest of the bus pins whatsoever.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.