• Please review our updated Terms and Rules here

Compaq Portable BIOS Disassembled Source Code

cr1901

Veteran Member
Joined
Dec 28, 2011
Messages
817
Location
NJ
This is a start, but I took the Compaq Portable BIOS and disassembled it to study differences compared to a genuine PC BIOS. There are some notable differences when compared to a genuine IBM BIOS or, say, one of the earlier ERSO derivatives, including:

  • Different offsets for the IVT initialization table.
  • Different offsets for certain interrupt vectors.
  • 'COMPAQ' signature near the end of the BIOS
  • Lack of BIOS date (seems they forgot to put it in?)
  • Data tends to be placed after each logical code section, rather than at IBM compatible offsets.
  • Deliberately seems more difficult to analyze than a standard BIOS (use of bx as the base register for variables in ROM in place of a constant offset).
  • Accessing I/O ports which are meaningless on an IBM PC (0xB4-0xB8, 0xD4-0xD8).
  • More to come!

The BIOS was disassembled with freeware IDA to save myself some time and frustration. It's not wonderfully commented but it will at least assemble without error, and only one warning. I can only work with IDA for so long before I need to take a break, so I'll come back to this at a future time. Most of the code sections are accounted for, but I wouldn't assume it will compile to an identical ROM image just yet (i.e. data interpreted as code may very well be in the BIOS).

I hope this might be useful to someone for the time being.
 

Attachments

  • portable.zip
    21 KB · Views: 3
Still have some unreferenced data blocks in there -- keep going ;-)

More than a few lol... hence it is a start :p. I'm still missing the floppy drive parameters and CRT table at least.
 
COMPAQ Portable BIOS Disassembly v2...

  • All data and code sections have been separated.
  • All but three data sections have been identified by function, and have labels generated to relevant data. For the most (err, some :p) part, literal addresses as operands in the code sections have been replaced with offset operators pointing to these labels.
  • Important labels have been given user friendly names, or names based on context.
  • Version 3 should be the final version where all remaining labels/references are accounted for.

Ya know, this makes me wonder offhand... when referencing a label, why does displacement addressing, ie
Code:
mov ax, es:[my_label + bx]
not require an offset operator, but any other attempt to take the address of a label requires it?

Looking at this a bit more, there is absolutely no doubt in my mind that Compaq deliberately coded this to be hard to disassemble and analyze... my favorite thing I've seen so far is the POST routine calling a subroutine which 'falls through' (lacks a 'ret') into another subroutine (whose starting address is called at other places in the code), which in turn contains the return statement.
 

Attachments

  • portable_v2.zip
    21.7 KB · Views: 5
Last edited:
I wouldn't be so quick to conclude that they did it intentionally to be hard to disassemble. I think it's more likely they did it to be as intentionally different from the IBM BIOS as possible, since they knew they were going to have to defend not copying it outright.

Or, maybe you're right and they DID copy major sections but wanted to obscure that fact through goofy coding :)

Or, could be a few other reasons: Want to shave a few cycles here and there, incompetence, etc. ;-)
 
Looking at this a bit more, there is absolutely no doubt in my mind that Compaq deliberately coded this to be hard to disassemble and analyze... my favorite thing I've seen so far is the POST routine calling a subroutine which 'falls through' (lacks a 'ret') into another subroutine (whose starting address is called at other places in the code), which in turn contains the return statement.

That's not at all uncommon practice among assembly coders. Consider the following flow to display a byte in 2 ASCII characters.

Save input value
Shift right 4 bits
Call Nibble
Restore value
Mask off 4 low-order bits
Nibble:
Convert 4 bits to ASCII
CharacterOut:
Display character
return

Note that the "CharacterOut" routine can be called from other places, but that the "Nibble" routine is only explicitly called once; the call to CharacterOut is implied by falling into it and that the second call to Nibble is implied by falling into it. Note that there's only one RET instruction for what amounts to three subroutines.

Similarly, if there's a CALL just before a RET in a code, the pair can be replaced by a simple JMP. Again, no attempt to obfuscate; just the realization on the part of the programmer that there's no good reason to waste stack space and cycles.
 
Back
Top