For educational purposes, I'm developing my own software emulator for the 8086 microprocessor with VGA/MCGA support. Although it's far from complete, it's advanced enough to start using a BIOS (though its utility will be limited for now).

I am attempting to use a ROM image from an IBM Model 30 (specifically the 33F4498 / 33F4499 from 1989).

To efficiently progress, I also rely on technical documentation, including IBM's and the book "CBios for IBM PS/2 computers and compatibles", which helps me configure the emulated CPU registers and use the ROM address table to boot correctly..

Originally, the BIOS is spread across two 32KB chips (33F4498 / 33F4499), and the chips are read simultaneously in pairs to form a 16-bit word. To simulate this behavior, I merge the contents of each chip by pairing them—first byte of the first chip with the first byte of the second chip, and so on—to create a complete 64KB BIOS file.

Here's a pseudocode representation:

merged_bios []u8
for each pair in 33F4498 (even) / 33F4499 (odd):
    merged_bios << even[i]
    merged_bios << odd[i]

I then inject this newly assembled BIOS into the emulated 1024 KiB memory starting from address 0xF0000 up to the physical limit, and I set the CPU registers so that on reset, the values point to the address:

FFFF0h - the Power-up entry point.

At this address, there is a long jump to position 0xFE05Bh.

So far, everything executes without any issues, but upon reaching the specified address, which according to the documentation should lead to the Power-On Self Test (POST) entry and its test and initialization routines, I instead encounter a jump instruction:

e93020 jmp 0x2030 offset from 0xf000:e05e

Obviously, with such an offset in the current execution position, this jump leads outside the limits of the emulated memory. I check everything concerning alignments and make sure not to have corrupted anything during the ROM merge.

I also verify that the BIOS is injected at the correct place in the emulated RAM (using a hex editor to compare the merged ROM with the code in the emulator's memory and checking if the problem might be from the emulator itself in addressing or execution issues). I test other ROM images in case the first is corrupted, and I also try other ROMs in the Model 30 as well as Model 25, but nothing changes. Even though the offsets might vary depending on the ROM used, the problem always remains at position 0xFE05Bh.

I must be missing something, and despite reviewing the documentation, I can't pinpoint what's going wrong. Does anyone have any suggestions on how to investigate this issue?

  • As an aside, github.com/SingleStepTests/8088 should be an excellent resource for testing the 8086 side of things. Obviously not the strict bus activity stuff, having been captured on an 8088, but the effect of all instructions on registers and memory. I used it semi-recently while developing an interpreter of 16-bit x86 code and it helped immensely.
    – Tommy
    Commented May 14 at 16:25
  • If you are serious about PS/2 Model 30 (non-286) emulation, this might be a helpful read, as the PS/2 mainboard has some ideosyncracies: vogons.org/viewtopic.php?f=9&t=76815 Commented May 15 at 18:08

2 Answers 2


Most of the time on x86, you have to consider segments and offsets separately.

Opcode 0xE9 is a near jump, it only touches IP. The addition wraps around, so the target is the relative offset (as a signed 16-bit integer) + the offset after the jump instruction (three bytes further), truncated to 16 bits, with CS unchanged. The destination address is 0xF000:0x008E: the segment is unchanged, and the destination offset is (0xE05E + 0x2030) & 0xFFFF.

The Intel manuals give details of the operation (see here, but some translation is required for a 16-bit CPU).

  • Wow, thank you for your explanation; it's a good lesson for me. I will revisit the near jump instruction and improve my understanding. Thanks again. ( I can't mark useful your answer :( )
    – ExecAssa
    Commented May 13 at 16:37

I am absolutely not an expert on x86 bare-metal stuff (I play in the 8-bit era) but I've read enough over the years for this to set my A20 spidey-sense tingling.

Is this the wrap-around behavior of segment:offset addressing that the address actually wraps back to zero after crossing the 1024KB line?

See this Wiki article regarding the A20 line and wrap-around. I might be completely wrong, of course, so pay attention to much more learned answers than mine.

  • Thank you for your help; it will certainly be useful for my project. @Stephen Kitt has just provided the answer I needed. Thanks again.
    – ExecAssa
    Commented May 13 at 16:38
  • 2
    Not this time around. The 8086 had no A20 gate. Commented May 14 at 15:50
  • 2
    @user3840170 but it does have address wrap-around at 1024KiB (since it only has 20 address lines). Commented May 14 at 15:54
  • You can't get the 1024kb wrap around with a segment value of f000, so unless OP is mistaken about the value in CS, this is not what's going on here. Commented May 17 at 14:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .