Replace 32bit->16bit farcall system with regular calls.

Instead of always "far calling" to 16bit mode, use a regular call.
When actually needing to "far call", transition to the 16bit C code
that does far calling.  This reduces the overhead to the check_irqs
and wait_irq code.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
diff --git a/src/romlayout.S b/src/romlayout.S
index 666f763..676658f 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -183,22 +183,22 @@
 
         retl
 
-// Far call a 16bit function from 32bit mode.
-// %eax = address of struct bregs
-// Clobbers: %e[bcd]x, %e[ds]i, flags, segment registers, idt/gdt
-        DECLFUNC __farcall16_from32
-        .global __farcall16big_from32
+// Call a 16bit SeaBIOS function from SeaBIOS 32bit C code.
+// %ebx = calling function
+// Clobbers: %ecx, %edx, flags, segment registers, idt/gdt
+        DECLFUNC __call16
+        .global __call16big
         .code32
-__farcall16_from32:
+__call16:
         movl $1f, %edx
         jmp transition16
-__farcall16big_from32:
+__call16big:
         movl $1f, %edx
         jmp transition16big
 
         // Make call.
         .code16gcc
-1:      calll __farcall16
+1:      calll *%ebx
         // Return via transition32
         movl $(2f + BUILD_BIOS_ADDR), %edx
         jmp transition32
diff --git a/src/stacks.c b/src/stacks.c
index da5daa9..044d9ea 100644
--- a/src/stacks.c
+++ b/src/stacks.c
@@ -112,40 +112,69 @@
     return eax;
 }
 
-// Call a function with a specified register state.  Note that on
-// return, the interrupt enable/disable flag may be altered.
-inline void
-farcall16(struct bregs *callregs)
-{
-    if (!MODESEGMENT && getesp() > BUILD_STACK_ADDR)
-        panic("call16 with invalid stack\n");
-    asm volatile(
-#if MODE16 == 1
-        "calll __farcall16\n"
-        "cli\n"
-        "cld"
-#else
-        "calll __farcall16_from32"
-#endif
-        : "+a" (callregs), "+m" (*callregs)
-        :
-        : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
-}
-
-inline void
-farcall16big(struct bregs *callregs)
+// Call a 16bit SeaBIOS function from a 32bit SeaBIOS function.
+static inline u32
+call16(u32 eax, void *func)
 {
     ASSERT32FLAT();
     if (getesp() > BUILD_STACK_ADDR)
         panic("call16 with invalid stack\n");
     asm volatile(
-        "calll __farcall16big_from32"
+        "calll __call16"
+        : "+a" (eax)
+        : "b" ((u32)func - BUILD_BIOS_ADDR)
+        : "ecx", "edx", "cc", "memory");
+    return eax;
+}
+
+static inline u32
+call16big(u32 eax, void *func)
+{
+    ASSERT32FLAT();
+    if (getesp() > BUILD_STACK_ADDR)
+        panic("call16big with invalid stack\n");
+    asm volatile(
+        "calll __call16big"
+        : "+a" (eax)
+        : "b" ((u32)func - BUILD_BIOS_ADDR)
+        : "ecx", "edx", "cc", "memory");
+    return eax;
+}
+
+// Far call 16bit code with a specified register state.
+void VISIBLE16
+_farcall16(struct bregs *callregs)
+{
+    ASSERT16();
+    asm volatile(
+        "calll __farcall16\n"
+        "cli\n"
+        "cld"
         : "+a" (callregs), "+m" (*callregs)
         :
         : "ebx", "ecx", "edx", "esi", "edi", "cc", "memory");
 }
 
 inline void
+farcall16(struct bregs *callregs)
+{
+    if (MODE16) {
+        _farcall16(callregs);
+        return;
+    }
+    extern void _cfunc16__farcall16(void);
+    call16((u32)callregs, _cfunc16__farcall16);
+}
+
+inline void
+farcall16big(struct bregs *callregs)
+{
+    extern void _cfunc16__farcall16(void);
+    call16big((u32)callregs, _cfunc16__farcall16);
+}
+
+// Invoke a 16bit software interrupt.
+inline void
 __call16_int(struct bregs *callregs, u16 offset)
 {
     if (MODESEGMENT)
@@ -204,68 +233,48 @@
         : "ebx", "edx", "esi", "edi", "cc", "memory");
 }
 
-// 16bit trampoline for enabling irqs from 32bit mode.
-ASM16(
-    "  .global trampoline_checkirqs\n"
-    "trampoline_checkirqs:\n"
-    "  rep ; nop\n"
-    "  lretw"
-    );
-
-static void
+// Low-level irq enable.
+void VISIBLE16
 check_irqs(void)
 {
-    if (MODESEGMENT) {
-        asm volatile(
-            "sti\n"
-            "nop\n"
-            "rep ; nop\n"
-            "cli\n"
-            "cld\n"
-            : : :"memory");
-        return;
-    }
-    extern void trampoline_checkirqs();
-    struct bregs br;
-    br.flags = F_IF;
-    br.code.seg = SEG_BIOS;
-    br.code.offset = (u32)&trampoline_checkirqs;
-    farcall16big(&br);
+    asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory");
 }
 
 // Briefly permit irqs to occur.
 void
 yield(void)
 {
-    if (MODESEGMENT || !CONFIG_THREADS) {
+    if (MODESEGMENT) {
         // Just directly check irqs.
         check_irqs();
         return;
     }
+    extern void _cfunc16_check_irqs(void);
+    if (!CONFIG_THREADS) {
+        call16big(0, _cfunc16_check_irqs);
+        return;
+    }
     struct thread_info *cur = getCurThread();
     if (cur == &MainThread)
         // Permit irqs to fire
-        check_irqs();
+        call16big(0, _cfunc16_check_irqs);
 
     // Switch to the next thread
     switch_next(cur);
 }
 
-// 16bit trampoline for waiting for an irq from 32bit mode.
-ASM16(
-    "  .global trampoline_waitirq\n"
-    "trampoline_waitirq:\n"
-    "  sti\n"
-    "  hlt\n"
-    "  lretw"
-    );
+void VISIBLE16
+wait_irq(void)
+{
+    asm volatile("sti ; hlt ; cli ; cld": : :"memory");
+}
 
 // Wait for next irq to occur.
 void
 yield_toirq(void)
 {
     if (MODESEGMENT) {
-        asm volatile("sti ; hlt ; cli ; cld": : :"memory");
+        wait_irq();
         return;
     }
     if (CONFIG_THREADS && MainThread.next != &MainThread) {
@@ -273,12 +282,8 @@
         yield();
         return;
     }
-    extern void trampoline_waitirq();
-    struct bregs br;
-    br.flags = 0;
-    br.code.seg = SEG_BIOS;
-    br.code.offset = (u32)&trampoline_waitirq;
-    farcall16big(&br);
+    extern void _cfunc16_wait_irq(void);
+    call16big(0, _cfunc16_wait_irq);
 }
 
 // Last thing called from a thread (called on "next" stack).