The hyper code runs in a special mode of the processor, hyper
mode, which is described in detail in US
patent 6,212,629.
The usual modes of a 80386-compatible processor (real, protected,
virtual
8086) are called mortal mode.
Hyper code handles processor startup and soft-reset, and features of
x86 architecture which are seldom used and / or very complex.
Implementation in hyper code allows corrections and additions to the
behaviour of the processor with just a BIOS update, while
an implementation in hardware would need a costly re-design. On serious
bugs, a BIOS update can be applied to machines already sold by their
users, but having to change the processor in the field is really
expensive,
as Intel had to find out with their Pentium FDIV bug.
The actual system BIOS can be further divided into the core BIOS code and the board specific code. The core BIOS code is produced by a BIOS company and bought by the motherboard manufacturer (OEM) in source or (more often) binary form. The OEM then writes the board specific code and links together his code with the core BIOS. Usually, a tool from the BIOS company is used to configure the BIOS for the board, compress some parts of the BIOS to save space and glue it all together to get the BIOS image.
The NexGen BIOS numbering scheme reflects all these parts mentioned above. The first four characters refer to the actual system BIOS and the following four characters refer to the hyper code:
char position | description | possible values | |
---|---|---|---|
actual system BIOS | 1 | board (bus) | 0 or v = VL, p = PCI |
2-3 | AMI WINBIOS core BIOS version (see below) | 05 = 11/11/92, 06 = 10/10/94 | |
4 | board specific code version | some letter | |
hyper code | 5-7 | board (bus) | 033 = VL, 034 = PCI |
8 | hyper code version | some letter |
I have copies of the following NexGen BIOSes:
The configuration tool is called AMIBCP (AMI BIOS Configuration
Program). I know of the following versions:
While booting, the AMI BIOS displays a version string. Here is the one for a NexGen PCI system BIOS:
51-0100-000000-00101111-101094-NXPCI-H
|| | | | | | |
|| | | | | | Keyb. BIOS Revision Level
|| | | | | BIOS type (chipset id.)
|| | | | BIOS release date
|| | | AMIBCP settings (see below)
|| | reference number (customer number)
|| version number (major/minor)
|BIOS size (128 KiB)
CPU type (586)
AMIBCP settings (0=no, 1=yes):
0 Halt on error during POST
0 Initialise CMOS RAM at every boot
1 Keyboard controller output pin 23, 24 blocked
0 Mouse support in BIOS and keyboard controller
1 Wait for in case of POST error
1 Display floppy error during POST
1 Display video error during POST
1 Display keyboard error during POST
When <Ins> (that's <Einfg> on a german keyboard) is
pressed
while booting, the CMOS data are ignored and three additional version
information lines are displayed:
000-0-0000-00-00-0000-00-00-000
| | | | | | | | |
| | | | | | | | keyboard controller pin for
| | | | | | | | turbo switch input pin
| | | | | | | mask value to switch clock low
| | | | | | data value to switch clock low
| | | | | port address to switch clock low
| | | | mask value to switch clock high
| | | data value to switch clock high
| | port address to switch clock high
| clock switching through chipset registers (0 = no, 1 = yes)
keyboard controller pin for clock switching
000-0-0000-00-00-0000-00-00-00-0
| | | | | | | | | |
| | | | | | | | | BIOS modification number
| | | | | | | | keyboard controller pin for 82335 reset
| | | | | | | mask value for cache control (disable)
| | | | | | data value for cache control (disable)
| | | | | port address for cache control (disable)
| | | | mask value for cache control (enable)
| | | data value for cache control (enable)
| | port address for cache control (enable)
| cache control through chipset registers (0 = no, 1 = yes)
keyboard controller pin for cache control
CPU-1.01 DIM-1.70
The keyboard controller pin number (for clock switching, turbo switch input pin, and cache control) is followed by a letter (H or L) which indicates that a high signal on the pin is used to switch to H(igh) or L(ow).
As you see, a lot of the fields are not used the way AMI meant them to be by NexGen!
Mode | Address range | Content |
---|---|---|
- | 00000h - 03FFFh | unused (0FFh) |
H | 04000h - 0FFF9h | hyper code (uncompressed), unused space at end
filled with 0FFh jump to x86 reset address 0FFFF000h:0FFF0h (unreal mode) = offset 1FFF0h |
0FFFAh - 0FFFBh | checksum | |
0FFFCh - 0FFFFh | hypercode version string ('033w') | |
H | 10000h - 100004h | jump to address 0FFFE4000h = offset 04000h (patched over AMIBIOS header) |
10004h - 1002Fh | rest of AMIBIOS header, includes module table | |
10030h - 12E57h | module POST (Power-On Self Test, compressed) | |
12E58h - 17F2Dh | module Runtime (compressed) | |
17F2Eh - 1C3F4h | module Setup (compressed) | |
1C3F5h - 1E05Ah | unused (00h) | |
R | 1E05Bh - 1E06Ah | core BIOS release date, jump to address 0FE98h:005Bh = offset 1E9DBh |
R/U | 1E980h - 1FFFFh | module Init (uncompressed) |
Mode | Address range | Content |
---|---|---|
R | 1E980h - | decompression code |
R | - | real / protected mode test |
R | - | keyboard, A20, CMOS test |
R | - 1FFEFh | Nx586-specific: hypercode checksum, CPU signature, cache on / off, NxVL, detect speed |
U | 1FFF0h - 1FFFFh | core BIOS release date, far jump to address 0F000h:0E05Bh = offset 1E05Bh |
Mode | Address range | Content |
---|---|---|
H | 00000h - 07FFDh | hyper code (most of it compressed), unused space
at end filled with 0FFh jump to x86 reset address 0FFFF000h:0FFF0h (unreal mode) = offset 1FFF0h |
07FFEh - 07FFFh | word 0C34h. What for? | |
08000h - 0B139h | module DIM (Device Initialization Manager) | |
0B13Ah - 0CA0Fh | unused (00h) | |
0CA10h - 0DFFFh | unused (0FFh) | |
0E000h - 0FFFFh | ESCD (Enhanced System Configuration Data, is initially 0FFh) | |
H | 10000h - 10004h | jump to address 0FFFFFFD6h = offset 1FFD6h (patched over AMIBIOS header) |
10004h - 1002Fh | rest of AMIBIOS header, includes module table | |
10030h - 13779h | module POST (Power-On Self Test, compressed) | |
1377Ah - 1945Eh | module Runtime (compressed) | |
1945Fh - 1DF89h | module Setup (compressed) | |
1DF8Ah - 1E05Ah | unused (00h) | |
R | 1E05Bh - 1E06Ah | core BIOS release date, jump to address 0FE43h:005Bh = offset 1E48Bh |
1E06Bh - 1E42Fh | unused (00h) | |
R/U | 1E430h - 1FFFFh | module Init (uncompressed) |
Mode | Address range | Content |
---|---|---|
R | 1E430h - 1E48Fh | AMI header |
R | 1E490h-1EE0Fh | decompression code |
R | 1EE10h - 1EE99h | real / protected mode test |
R | 1EE9Ah - 1F00Fh | keyboard, A20, CMOS test |
R | 1F010h - 1FFD5h | Nx586-specific: hypercode checksum, CPU signature, cache on / off, NxMC, init i82378, detect speed |
H | 1FFD6h - 1FFEFh | i82378: enable lower 64K of BIOS, jump to address 0FFFE0000h = offset 00000h |
U | 1FFF0h - 1FFFFh | core BIOS release date, far jump to address 0F000h:0E05Bh = offset 1E05Bh |
After reset, the Nx586 processor starts in hyper mode at physical
address 0FFFF0000h. At this address, the upper 64 KiB of the BIOS
(offset 10000h) is enabled. Unfortunately, the AMI WINBIOS uses this 64
KiB bank for its own purpose, so only a single jump instruction is
patched over the AMI header here and the actual hyper code is situated
in the other bank.
On a VL system, the whole 128 KiB are decoded into addresses
0E0000h-0FFFFFh (top of 1 MiB) and 0FFFE0000h-0FFFFFFFFh (top of 4 GiB).
On a PCI system, only the upper 64 KiB are decoded into addresses
0F0000h-0FFFFFh (top of 1 MiB), 0FFEF0000h-0FFEFFFFFh (top of 4 GiB - 1
MiB) and 0FFFF0000h-0FFFFFFFFh (top of 4 GiB). The lower 64 KiB are only
mapped after some register of the Intel i82378 has been properly set
up.
Therefore, on a VL system, the first instruction is a direct jump into the hyper code at address 0FFFE4000h = offset 04000h. On the PCI system instead, there is a detour to address 0FFFFFFD6h = offset 1FFD6h where the i82378 is programmed to map the lower 64 KiB of the BIOS into the address space before a jump to 0FFFE0000h = offset 00000h can be finally done.
After some tests have succeeded, hyper code copies (VL) or copies / decompresses (PCI) itself into the second level cache, bank 3. After enabling the second level cache, the copy of the hyper code gets visible at physical address 0FFFF0000h-0FFFFBFFFh (up to 48 KiB). From now on, in hyper mode, the processor can't see most of the upper 64 KiB of the BIOS (Flash) EPROM anymore! But this is no problem because in hyper mode only hyper code matters and the AMI BIOS is never looked at.
Setting up the different invisible and visible x86 registers, the Nx586 resumes to mortal mode to the x86 unreal mode reset address 0FFFF000h:0FFF0h = offset 1FFF0h. Note the BIOS EPROM is still visible in mortal mode. The first far jump reloads the CS segment descriptor cache with proper real mode values and thus ends unreal mode. Execution continues in real mode at address 0F000h:0E05Bh = offset 1E05Bh. There we find a far jump into the Init module and there a short jump which skips the decompression routine. After some basic tests and chipset initialisations, modules are decompressed into main memory as needed and excuted and we are deep inside the BIOS.
Here are the textual results of these tools, the dissected files can be downloaded here (Dissected.zip, 3396 KiB).
005q033s
005r033s
hyper code is the same as from 005q033s, 00000h-03FFFh is different
from 005q033s
Module Source Dest Comp Uncmp Contentv06d033w
--------------------------------------------
1 17F5Ch 0000h 44FCh 5D7Ah Setup
0 10030h 0000h 2DFCh 3D7Ch POST
2 12E2Ch 7500h 5130h 8B00h Runtime
3 1E980h 0000h ----- 1680h INIT
Module Source Dest Comp Uncmp Contentv06e033w
--------------------------------------------
1 17F2Ah 0000h 44C7h 5D7Ah Setup
0 10030h 0000h 2E24h 3DA2h POST
2 12E54h 7500h 50D6h 8B00h Runtime
3 1E980h 0000h ----- 1680h INIT
Module Source Dest Comp Uncmp Contentp06s034i
--------------------------------------------
1 17F2Eh 0000h 44C7h 5D7Ah Setup
0 10030h 0000h 2E28h 3DA8h POST
2 12E58h 7500h 50D6h 8B00h Runtime
3 1E980h 0000h ----- 1680h INIT
Module Source Dest Comp Uncmp Contentp06w034n
--------------------------------------------
1 193AAh 0000h 4AE8h 67B8h Setup
0 10030h 0000h 3616h 4898h POST
2 13646h 6F00h 5D64h 9100h Runtime
3 1E350h 0000h ----- 1CB0h INIT
4 08000h 8000h 314Eh 49F0h DIM
Module Source Dest Comp Uncmp Contentp06w034n.mc3
--------------------------------------------
1 193A3h 0000h 4AF2h 67C4h Setup
0 10030h 0000h 35ECh 48B8h POST
2 1361Ch 6F00h 5D87h 9100h Runtime
3 1E350h 0000h ----- 1CB0h INIT
4 08000h 8000h 313Bh 4A10h DIM
Module Source Dest Comp Uncmp Contentp06x034n
--------------------------------------------
1 193DFh 0000h 4B02h 67D0h Setup
0 10030h 0000h 35CFh 486Eh POST
2 135FFh 6F00h 5DE0h 9100h Runtime
3 1E350h 0000h ----- 1CB0h INIT
4 08000h 8000h 313Bh 4A10h DIM
Module Source Dest Comp Uncmp Contentp06y034p
--------------------------------------------
1 19464h 0000h 4B27h 6808h Setup
0 10030h 0000h 3745h 4B30h POST
2 13775h 6F00h 5CEFh 9100h Runtime
3 1E430h 0000h ----- 1BD0h INIT
4 08000h 8000h 313Bh 4A10h DIM
Module Source Dest Comp Uncmp Contentp06y034q
--------------------------------------------
1 1945Fh 0000h 4B2Ah 6808h Setup
0 10030h 0000h 374Ah 4B3Ah POST
2 1377Ah 6F00h 5CE5h 9100h Runtime
3 1E430h 0000h ----- 1BD0h INIT
4 08000h 8000h 313Bh 4A10h DIM
Module Source Dest Comp Uncmp Content
--------------------------------------------
1 1945Fh 0000h 4B2Ah 6808h Setup
0 10030h 0000h 374Ah 4B3Ah POST
2 1377Ah 6F00h 5CE5h 9100h Runtime
3 1E430h 0000h ----- 1BD0h INIT
4 08000h 8000h 313Bh 4A10h DIM
Hyper code is built from a lot of modules written in x86 assembly
language and then linked together. Linking usually glues the modules
tightly together so that only small traces remain in the object
code that indicate a module border. If alignment to paragraph (16 Byte)
or page (256 Byte) was set for a module, chances are better to identify
a module border, but you still don't know their names and purpose.
Fortunately for me, NexGen included an ID in each module that contains
the original assembler filename plus a version number of that file!
That ID is a 0-terminated ASCII string and looks like this: first, the
characters @(#), then the filename, then a white space, and at last the
version. That kind of ID is automatically created by some source code
control systems and there are tools to scan for those IDs. Especially
SCCS (source code control system) has a "what" command that does this.
There are some variations to this: sometimes, the filename includes the
extension .ASM and sometimes not. Sometimes, the white space is a tab
character and sometimes a sequence of space characters. Sometimes the
file version is present and sometimes it is omitted. So a little bit of
guesswork is needed.
As I don't have SCCS, I wrote a small tool in PERL again, which can be downloaded here (HYVERS.PL, 1 KiB). For example, its output on the hypercode of the 005Q033S BIOS is:
00521 "@(#) hcode sb.00"
00532 "@(#)hypres.asm b.03"
00F7C "@(#)033s"
01800 "@(#)retf.asm\t3.4"
01D00 "@(#)iret.asm\t3.2"
01F00 "@(#)int.asm\t3.4"
02500 "@(#)call.asm\t3.3"
02900 "@(#)excph.asm\t3.1"
02C00 "@(#)task.asm\t3.5"
03600 "@(#)iobm.asm\t2.2"
03A00 "@(#)into.asm\t3.4"
03F00 "@(#)invop.asm\t3.29"
04B00 "@(#)cache.asm\t3.7"
04E00 "@(#)decode.asm\t3.19"
06200 "@(#)sofres.asm\t2.3"
06300 "@(#)hdeb.asm\t2.11"
06C00 "@(#)io_em.asm\t2.5"
06D00 "@(#)undoc.asm\t2.3"
06E00 "@(#)emul.asm\t3.11"
07500 "@(#)emsubs.asm\t3.2"
07900 "@(#)movcr.asm\t3.1"
079A0 "@(#)nphcode.asm\t3.10"
07DA0 "@(#)fixupnp.asm\t3.4"
080E0 "@(#)fst.asm\t3.5"
08600 "@(#)fsincos.asm\t3.2"
08A80 "@(#)fpatan.asm\t3.3"
08FF0 "@(#)f2xm1.asm\t3.4"
09250 "@(#)fyl2x.asm\t3.2"
098A0 "@(#)fptan.asm\t3.2"
09B20 "@(#)fbld.asm\t3.4"
09D10 "@(#)fbstp.asm\t3.5"
09FB0 "@(#)fscale.asm\t3.3"
0A400 "@(#)fxtract.asm\t3.2"
0A580 "@(#)np_sub.asm\t3.3"
0A6A0 "@(#)np_xcp.asm\t3.3"
0A870 "@(#)np_em.asm\t3.2"
0A900 "@(#)cpustub.asm\t3.3"
Using this tool on all available hyper code versions, I created the following table that shows the progress of the hyper code source files over the different hyper code versions:
File \ Version | 033s | 033v | 033w | 034i | 034n | 034p | 034q |
---|---|---|---|---|---|---|---|
CACHE | 3.7 | 3.7 | 3.8 | ?3.9 | ?3.9 | ?3.9 | ?3.9 |
CALL | 3.3 | =3.4 | 3.4 | 3.4 | 3.4 | 3.4 | 3.4 |
CPUSTUB | 3.3 | 3.3 | 3.3 | 3.3 | 3.3 | 3.4 | 3.4 |
DECODE | 3.19 | ?3.20 | 3.22 | ?3.22 | 3.22 | 3.22 | 3.22 |
EMSUBS | 3.2 | 3.2 | 3.2 | 3.2 | 3.2 | 3.2 | 3.2 |
EMUL | 3.11 | 3.11 | ?3.12 | ?3.12 | ?3.12 | ?3.12 | ?3.12 |
EXCPH | 3.1 | =3.2 | 3.2 | 3.2 | 3.2 | 3.2 | 3.2 |
F2XM1 | 3.4 | 3.4 | 3.4 | - | - | - | - |
FBLD | 3.4 | 3.4 | 3.6 | 3.6 | 3.6 | 3.6 | 3.6 |
FBSTP | 3.5 | 3.5 | 3.12 | 3.11 | 3.11 | 3.12 | 3.12 |
FIXUPNP | 3.4 | 3.4 | 3.7 | 3.6 | 3.7 | 3.8 | 3.8 |
FPATAN | 3.3 | 3.3 | 3.3 | - | - | - | - |
FPTAN | 3.2 | 3.2 | 3.2 | - | - | - | - |
FSCALE | 3.3 | 3.3 | 3.3 | 3.3 | 3.3 | 3.3 | 3.3 |
FSINCOS | 3.2 | 3.2 | 3.2 | - | - | - | - |
FST | 3.5 | 3.5 | 3.6 | 3.6 | 3.6 | 3.6 | 3.6 |
FXTRACT | 3.2 | 3.2 | 3.2 | - | - | - | - |
FYL2X | 3.2 | 3.2 | 3.2 | - | - | - | - |
HDEB | 2.11 | ?2.12 | ?2.12 | - | - | - | - |
HYPRES | ?1 | ?2 | ?2 | ?3 | ?4 | ?5 | ?5 |
INT | 3.4 | =3.5 | 3.5 | ?3.6 | ?3.6 | ?3.6 | ?3.6 |
INTO | 3.4 | =3.5 | 3.5 | =3.5 | 3.5 | 3.5 | 3.5 |
INVOP | 3.29 | ?3.3 | ?3.4 | 3.5 | ?3.6 | ?3.6 | ?3.6 |
IO_EM | 2.5 | 2.5 | 2.5 | 2.5 | 3.8 | 3.8 | 3.8 |
IOBM | 2.2 | 2.2 | 2.2 | 2.2 | 2.2 | 2.2 | 2.2 |
IRET | 3.2 | =3.3 | ?3.4 | 3.3 | 3.3 | 3.3 | 3.3 |
MOVCR | 3.1 | ?3.3 | ?3.3 | 3.2 | 3.2 | 3.2 | 3.2 |
NP_EM | 3.2 | =3.3 | 3.3 | - | - | - | - |
NP_SUB | 3.3 | 3.3 | 3.3 | 3.3 | 3.3 | 3.3 | 3.3 |
NP_XCP | 3.3 | 3.3 | 3.4 | 3.3 | 3.3 | 3.4 | 3.4 |
NPHCODE | 3.10 | =3.11 | 3.11 | ?3.12 | 3.16 | 3.16 | 3.16 |
RETF | 3.4 | =3.5 | 3.5 | ?3.6 | ?3.6 | ?3.6 | ?3.6 |
SOFRES | 2.3 | 2.3 | 2.3 | 2.3 | 2.3 | 2.3 | 2.3 |
TASK | 3.5 | =3.6 | 3.6 | ?3.7 | ?3.7 | ?3.7 | ?3.7 |
UNDOC | 2.3 | 2.3 | 2.3 | 2.3 | 2.3 | 2.3 | 2.3 |
XESC | - | ?1 | ?2 | - | - | - | - |
Legend:
= A file version is not included in the file ID, but content is the
same as in another hyper code version and the file ID there does
contain this file version.
? Unknown, version number is guessed (incremented for each change).
- This module is not used in this hyper code version.
Peculiarities:
DECODE: Version 3.22 develops from 033w to 034i and throughout the
other PCI versions. Seems it was forgotten to increment the version
number.
INTO: Differences between VL and PCI on same file version.
IO_EM: 033v has file version 2.5 which is different from the 2.5 of the
other VL versions, also 034n has file version 3.8 which is different
from 3.8 of the other PCI versions.
NPHCODE: 034q has 3.16 which is different from the 3.16 of the other
PCI versions.
Slight differences between VL and PCI can't be avoided, and may be accounted for by conditional assembly. You don't want to have two versions of the same file when they differ only a little bit. Thus the case with the file INTO.ASM can be easily explained. But the other cases remain unclear. That is why I always use the hyper code version to refer to a version and not file versions because they are not reliable.
further topics (not yet done):
Diagnostic port
CMOS RAM
Author: Herbert Oppmann (![]() |
updated: 2022-11-13 |