1/* i386.s -- assembly support. */
2
3/*
4// QuickThreads -- Threads-building toolkit.
5// Copyright (c) 1993 by David Keppel
6//
7// Permission to use, copy, modify and distribute this software and
8// its documentation for any purpose and without fee is hereby
9// granted, provided that the above copyright notice and this notice
10// appear in all copies.  This software is provided as a
11// proof-of-concept and for demonstration purposes; there is no
12// representation about the suitability of this software for any
13// purpose. */
14
15/* NOTE: double-labeled `_name' and `name' for System V compatability.  */
16/* NOTE: Mixed C/C++-style comments used. Sorry! */
17
18/* Callee-save: %esi, %edi, %ebx, %ebp
19// Caller-save: %eax, %ecx
20// Can't tell: %edx (seems to work w/o saving it.)
21//
22// Assignment:
23//
24// See ``i386.h'' for the somewhat unconventional stack layout.  */
25
26
27	.text
28	.align 2
29
30	.globl _qt_abort
31	.globl qt_abort
32	.globl _qt_block
33	.globl qt_block
34	.globl _qt_blocki
35	.globl qt_blocki
36	.globl _qt_align
37	.globl qt_align
38
39/* These all have the type signature
40//
41//	void *blocking (helper, arg0, arg1, new)
42//
43// On procedure entry, the helper is at 4(sp), args at 8(sp) and
44// 12(sp) and the new thread's sp at 16(sp).  It *appears* that the
45// calling convention for the 8X86 requires the caller to save all
46// floating-point registers, this makes our life easy.  */
47
48/* Halt the currently-running thread.  Save it's callee-save regs on
49// to the stack, 32 bytes.  Switch to the new stack (next == 16+32(sp))
50// and call the user function (f == 4+32(sp) with arguments: old sp
51// arg1 (8+32(sp)) and arg2 (12+32(sp)).  When the user function is
52// done, restore the new thread's state and return.
53//
54// `qt_abort' is (currently) an alias for `qt_block' because most of
55// the work is shared.  We could save the insns up to `qt_common' by
56// replicating, but w/o replicating we need an inital subtract (to
57// offset the stack as if it had been a qt_block) and then a jump
58// to qt_common.  For the cost of a jump, we might as well just do
59// all the work.
60//
61// The helper function (4(sp)) can return a void* that is returned
62// by the call to `qt_blockk{,i}'.  Since we don't touch %eax in
63// between, we get that ``for free''.  */
64
65_qt_abort:
66qt_abort:
67_qt_block:
68qt_block:
69_qt_blocki:
70qt_blocki:
71	pushl %ebp		/* Save callee-save, sp-=4. */
72	pushl %esi		/* Save callee-save, sp-=4. */
73	pushl %edi		/* Save callee-save, sp-=4. */
74	pushl %ebx		/* Save callee-save, sp-=4. */
75	movl %esp, %eax		/* Remember old stack pointer. */
76
77qt_common:
78	movl 32(%esp), %esp	/* Move to new thread. */
79	pushl 28(%eax)		/* Push arg 2. */
80	pushl 24(%eax)		/* Push arg 1. */
81	pushl %eax		/* Push arg 0. */
82	movl 20(%eax), %ebx	/* Get function to call. */
83	call *%ebx		/* Call f. */
84	addl $12, %esp		/* Pop args. */
85
86	popl %ebx		/* Restore callee-save, sp+=4. */
87	popl %edi		/* Restore callee-save, sp+=4. */
88	popl %esi		/* Restore callee-save, sp+=4. */
89	popl %ebp		/* Restore callee-save, sp+=4. */
90_qt_align:
91qt_align:
92	ret			/* Resume the stopped function. */
93
94	.globl _qt_tramp
95	.globl qt_tramp
96_qt_tramp:
97qt_tramp:
98	movl 12(%esp), %eax	/* Load 'qt_error' address */
99	sub $4, %esp		/* Align stack pointer to 16-byte boundary */
100	jmp *%eax		/* call 'qt_error' */
101	hlt			/* 'qt_error' never returns */
102
103/* Start a varargs thread. */
104
105	.globl _qt_vstart
106	.globl qt_vstart
107_qt_vstart:
108qt_vstart:
109	pushl %edi		/* Push `pt' arg to `startup'. */
110	call *%ebp		/* Call `startup'. */
111	popl %eax		/* Clean up the stack. */
112
113	call *%ebx		/* Call the user's function. */
114
115	pushl %eax		/* Push return from user's. */
116	pushl %edi		/* Push `pt' arg to `cleanup'. */
117	call *%esi		/* Call `cleanup'. */
118
119	hlt			/* `cleanup' never returns. */
120