+// 16bit trampoline for enabling irqs from 32bit mode.
+ASM16(
+ " .global trampoline_checkirqs\n"
+ "trampoline_checkirqs:\n"
+ " rep ; nop\n"
+ " lretw"
+ );
+
+static void
+check_irqs32()
+{
+ extern void trampoline_checkirqs();
+ struct bregs br;
+ br.flags = F_IF;
+ br.code.seg = SEG_BIOS;
+ br.code.offset = (u32)&trampoline_checkirqs;
+ call16big(&br);
+}
+
+static void
+check_irqs16()
+{
+ asm volatile(
+ "sti\n"
+ "nop\n"
+ "rep ; nop\n"
+ "cli\n"
+ "cld\n"
+ : : :"memory");
+}
+
+
+/****************************************************************
+ * Threads
+ ****************************************************************/
+
+#define THREADSTACKSIZE 4096
+
+struct thread_info {
+ struct thread_info *next;
+ void *stackpos;
+};
+
+struct thread_info MainThread;
+
+void
+thread_setup()
+{
+ MainThread.next = &MainThread;
+ MainThread.stackpos = NULL;
+}
+
+struct thread_info *
+getCurThread()
+{
+ u32 esp = getesp();
+ if (esp <= BUILD_STACK_ADDR)
+ return &MainThread;
+ return (void*)ALIGN_DOWN(esp, THREADSTACKSIZE);
+}
+
+// Briefly permit irqs to occur.
+void
+yield()
+{
+ if (MODE16) {
+ // In 16bit mode, just directly check irqs.
+ check_irqs16();
+ return;
+ }
+ if (! CONFIG_THREADS) {
+ check_irqs32();
+ return;
+ }
+ struct thread_info *cur = getCurThread();
+ if (cur == &MainThread)
+ // Permit irqs to fire
+ check_irqs32();
+
+ // Switch to the next thread
+ struct thread_info *next = cur->next;
+ asm volatile(
+ " pushl $1f\n" // store return pc
+ " pushl %%ebp\n" // backup %ebp
+ " movl %%esp, 4(%%eax)\n" // cur->stackpos = %esp
+ " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
+ " popl %%ebp\n" // restore %ebp
+ " retl\n" // restore pc
+ "1:\n"
+ : "+a"(cur), "+c"(next)
+ :
+ : "ebx", "edx", "esi", "edi", "cc", "memory");
+}
+
+// Last thing called from a thread (called on "next" stack).
+static void
+__end_thread(struct thread_info *old)
+{
+ struct thread_info *pos = &MainThread;
+ while (pos->next != old)
+ pos = pos->next;
+ pos->next = old->next;
+ free(old);
+ dprintf(DEBUG_thread, "\\%08x/ End thread\n", (u32)old);
+}
+
+void
+run_thread(void (*func)(void*), void *data)
+{
+ ASSERT32();
+ if (! CONFIG_THREADS)
+ goto fail;
+ struct thread_info *thread;
+ thread = memalign_tmphigh(THREADSTACKSIZE, THREADSTACKSIZE);
+ if (!thread)
+ goto fail;
+
+ thread->stackpos = (void*)thread + THREADSTACKSIZE;
+ struct thread_info *cur = getCurThread();
+ thread->next = cur->next;
+ cur->next = thread;
+
+ dprintf(DEBUG_thread, "/%08x\\ Start thread\n", (u32)thread);
+ asm volatile(
+ // Start thread
+ " pushl $1f\n" // store return pc
+ " pushl %%ebp\n" // backup %ebp
+ " movl %%esp, 4(%%edx)\n" // cur->stackpos = %esp
+ " movl 4(%%ebx), %%esp\n" // %esp = thread->stackpos
+ " calll *%%ecx\n" // Call func
+
+ // End thread
+ " movl (%%ebx), %%ecx\n" // %ecx = thread->next
+ " movl 4(%%ecx), %%esp\n" // %esp = next->stackpos
+ " movl %%ebx, %%eax\n"
+ " calll %4\n" // call __end_thread(thread)
+ " popl %%ebp\n" // restore %ebp
+ " retl\n" // restore pc
+ "1:\n"
+ : "+a"(data), "+c"(func), "+b"(thread), "+d"(cur)
+ : "m"(*(u8*)__end_thread)
+ : "esi", "edi", "cc", "memory");
+ return;
+
+fail:
+ func(data);
+}
+
+void
+wait_threads()
+{
+ ASSERT32();
+ if (! CONFIG_THREADS)
+ return;
+ while (MainThread.next != &MainThread)
+ yield();
+}
+
+
+/****************************************************************
+ * String ops
+ ****************************************************************/
+