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