@@ -174,36 +174,39 @@ static void bios_cfg_read_entry(void *buf, uint16_t entry, uint32_t len)
}
}
+/* Return top of memory using BIOS function E801. */
static uint32_t get_e801_addr(void)
{
- uint32_t eax, ebx, ecx, edx;
+ uint16_t eax, ebx, ecx, edx;
uint32_t ret;
- eax = 0xe801;
ebx = 0;
ecx = 0;
edx = 0;
asm("int $0x15\n"
- : "+a"(eax)
- : "b"(ebx), "c"(ecx), "d"(edx));
+ : "=a"(eax), "+b"(ebx), "+c"(ecx), "+d"(edx)
+ : "a"(0xe801));
- /* Output could be in AX/BX or CX/DX */
- if ((uint16_t)ecx || (uint16_t)edx) {
- if (!(uint16_t)edx) {
- /* Add 1 MB and convert to bytes */
- ret = (ecx + 1024) << 10;
- } else {
- /* Add 16 MB and convert to bytes */
- ret = (edx + 256) << 16;
- }
+ /* Not SeaBIOS, but in theory a BIOS could return CX=DX=0 in which case
+ * we need to use the result from AX & BX instead.
+ */
+ if (ecx == 0 && edx == 0) {
+ ecx = eax;
+ edx = ebx;
+ }
+
+ if (edx == 0) {
+ /* This is for machines with <= 16MB of RAM, which probably
+ * would never be the case, but deal with it anyway.
+ * ECX = extended memory between 1M and 16M, in kilobytes
+ * Convert it to bytes and return.
+ */
+ ret = ((uint32_t)ecx + 1024 /* 1M in K */) << 10;
} else {
- if (!(uint16_t)ebx) {
- /* Add 1 MB and convert to bytes */
- ret = (eax + 1024) << 10;
- } else {
- /* Add 16 MB and convert to bytes */
- ret = (ebx + 256) << 16;
- }
+ /* EDX = extended memory above 16M, in 64K units.
+ * Convert it to bytes and return.
+ */
+ ret = ((uint32_t)edx + 256 /* 16M in 64K units */) << 16;
}
return ret;