📄 powerpc_mach.s
字号:
/* powerpc_mach.s -- assembly support. *//* * QuickThreads -- Threads-building toolkit. * Copyright (c) 1993 by David Keppel * * Permission to use, copy, modify and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice and this notice * appear in all copies. This software is provided as a * proof-of-concept and for demonstration purposes; there is no * representation about the suitability of this software for any * purpose. * PowerPC-Mach thread switching module. * Darwin (MacOS X) assembly * * NOTICE: Syntax for register names is not the GNU one. Register are * named "rx" and "fx", not "%rx" and "%fx" as usual for the GNU "as" tool. * Darwin "as" tool is based on GNU "as" but follows the "official" PowerPC * syntax. * * * This software is largely based on the original PowerPC-Linux porting * developed by Ken Aaker <kenaaker@silverbacksystems.com> * * Marco Bucci <marco.bucci@inwind.it> * December 2002 * *//* * * PowerPC Register convections: * * r0 volatile * r1 SP * r2 system reserved * r3-r4 volatile for parameter passing and function return * r5-r10 volatile for parameter passing * r11-r12 volatile * r13-r14 non volatile registers * f0 volatile * f1 volatile for parameter passing and function return * f2-f13 volatile for parameter passing * f14-f31 non volatile * * cr2-cr4 non volatile * * * See on the heather file for more documentation. * * * * IMPLEMENTATION NOTES * * * 1) Condition register saving * On most machines, the condition code register is caller-save. * On the PPC, the condition code register is callee-save, so the * thread context switch must preserve it. * * * 2) Floating point registers saving * On resuming a thread, floating point registers are or not restored just * depending on which block routine suspended the thread (i.e. regardless * whether "qt_block", "qt_blocki" or "qt_abort" is used to resume it). * This behaviour is obtained by implementing "qt_block" by means af a nested * call to "qt_blocki". As a result, the blocking of a thread always goes * and returns through "qt_blocki and, if a thread was blocked by "qt_block", * its execution resumes from the floating point restoring code on exit * of "qt_block". * * Thanks to David Keppel that explained me this "simple" trick. * * * 3) C languace code debugging * This software was developed and debugged using the Metrowerks * Code Warrior PPC integrated assembler. It can be still used with the * Code Warrior compiler by means of the file "powerpc_mach_asm_debug.c" * that include it. * In order to avoid "copy and paste" bugs, and make easyer the maintaining, * I made the minimal changes, so you can find some strange code as: * * #if 0 * .if 0 * C code here * .endif * #endif * * This is just to embed some C code that is needed by the Code Warrior * integrated assembler. * * * 4) Assembly constants generation * Constants used in the assembly code are generated by running * the C code in the sequel (commented). It uses the C macros declared in * the C heather in order to guarantee that the C interface and the assebly * code are "aligned". I avoided the use of an assebler preprocessor since * they are not so standard and moreover using macro espressions makes the * assembly debugging more difficult. * *#include <iostream>#include "powerpc_mach.h"int main(){ using namespace std; int i; cout << ".set LR_SAVE, " << PPC_LR_SAVE << endl; cout << ".set CR_SAVE, " << PPC_CR_SAVE << endl; cout << ".set BLOCKI_FSIZE, " << QUICKTHREADS_BLOCKI_FRAME_SIZE << endl; cout << ".set BLOCK_FSIZE, " << QUICKTHREADS_BLOCK_FRAME_SIZE << endl; cout << endl; for(i=0; i<12; i++) cout << ".set PAR_" << i << ", " << PPC_PAR(i) << endl; cout << endl; i = 13; cout << ".set GPR_SAVE_" << i << ", " << QUICKTHREADS_BLOCKI_GPR_SAVE(i) << endl; cout << endl; for(i=31; i>13; i--) cout << ".set FPR_SAVE_" << i << ", " << QUICKTHREADS_BLOCK_FPR_SAVE(i) << endl; cout << endl; cout << ".set VARGS_BKOFF, " << QUICKTHREADS_VARGS_BKOFF << endl; cout << endl << endl << endl; for(i=31; i>13; i--) cout << "\tstfd\tf" << i << ",FPR_SAVE_" << i << "(r1)" << endl; cout << endl; for(i=31; i>13; i--) cout << "\tlfd \tf" << i << ",FPR_SAVE_" << i << "(r1)" << endl; cout << endl << endl << endl; return 0;} * * * */ #if 0 .text .align 4 .globl qt_block .globl _qt_block .globl qt_blocki .globl _qt_blocki .globl qt_abort .globl _qt_abort .globl qt_start .globl _qt_start .globl qt_vstart .globl _qt_vstart.set LR_SAVE, 8.set CR_SAVE, 4.set BLOCKI_FSIZE, 128.set BLOCK_FSIZE, 192.set PAR_0, 24.set PAR_1, 28.set PAR_2, 32.set PAR_3, 36.set PAR_4, 40.set PAR_5, 44.set PAR_6, 48.set PAR_7, 52.set PAR_8, 56.set PAR_9, 60.set PAR_10, 64.set PAR_11, 68.set GPR_SAVE_13, 52.set FPR_SAVE_31, 184.set FPR_SAVE_30, 176.set FPR_SAVE_29, 168.set FPR_SAVE_28, 160.set FPR_SAVE_27, 152.set FPR_SAVE_26, 144.set FPR_SAVE_25, 136.set FPR_SAVE_24, 128.set FPR_SAVE_23, 120.set FPR_SAVE_22, 112.set FPR_SAVE_21, 104.set FPR_SAVE_20, 96.set FPR_SAVE_19, 88.set FPR_SAVE_18, 80.set FPR_SAVE_17, 72.set FPR_SAVE_16, 64.set FPR_SAVE_15, 56.set FPR_SAVE_14, 48/* various offsets used by "qt_varg" */.set P_T, PAR_0.set P_STARTUP, PAR_1.set P_USERF, PAR_2.set P_CLEANUP, PAR_3 /* the offset used to move back the linkage area to be adiacent to * the variant argument list before calling "userf(...) */.set VARGS_BKOFF, 16 /* skip "t", "startup", "userf" and "cleanup" */ /* location where "t" and "cleanup" are saved (with respect of * the stack frame base) */.set P_T_SAVE, -4.set P_CLEANUP_SAVE, -8#endif/* Block the current thread saving all integer non volatile registers and * start a new thread. */#if 0.if 0#endifvoid *qt_blocki (void *helper, void *a0, void *a1, void *newthread);asm void *qt_blocki (void *helper, void *a0, void *a1, void *newthread){#if 0.endif#endif#if 0qt_blocki:_qt_blocki:#endif/* prolog code */ stwu r1,-BLOCKI_FSIZE(r1) /* allocate the stack frame */ mflr r0 /* return addr in r0 */ mfcr r11 /* CR in r11 */ stw r0,LR_SAVE+BLOCKI_FSIZE(r1) /* save return addr in the stack */ stw r11,CR_SAVE+BLOCKI_FSIZE(r1) /* save CR in the stack */ stmw r13,GPR_SAVE_13(r1) /* save non-volatile reg *//* call helper(qt_t *old, void *a0, void *a1) */ mtlr r3 /* "helper" addr in the link reg */ mr r3,r1 /* current thread (i.e. the SP) in arg "old" */ mr r1,r6 /* swap to the new thread (i.e. to its SP) */ blrl /* jump to "helper" *//* the "helper" return value is returned (since r3 is not changed) *//* epilog code: return to the new thread's "qt_blocki" caller */ lmw r13,GPR_SAVE_13(r1) /* restore non-volatile reg */ lwz r0,LR_SAVE+BLOCKI_FSIZE(r1) /* recover return addr */ lwz r11,CR_SAVE+BLOCKI_FSIZE(r1) /* recover CR */ mtlr r0 /* return address in the link reg */ mtcr r11 /* restore CR */ addi r1,r1,BLOCKI_FSIZE /* free the stack frame */ blr /* return */#if 0.if 0#endif}#if 0.endif#endif/* Abort the current thread and start a new thread. */#if 0.if 0#endifvoid qt_abort (void *helper, void *a0, void *a1, void *newthread);asm void qt_abort (void *helper, void *a0, void *a1, void *newthread){#if 0.endif#endif#if 0qt_abort:_qt_abort:#endif/* prolog code *//* there is no prolog. It will never come back *//* call helper(qt_t *old, void *a0, void *a1) */ mtlr r3 /* "helper" addr in the link reg */ mr r1,r6 /* swap to the new thread (i.e. to its SP) *//* we don't need to set "old", we can pass just garbage. Actually, since r3 is not changed, "old" is set to "helper" (don't care) */ blrl /* call "helper" *//* the "helper" return value is returned (since r3 is not changed) *//* epilog code: return to the new thread's "qt_blocki" caller */ lmw r13,GPR_SAVE_13(r1) /* restore non-volatile reg */ lwz r0,LR_SAVE+BLOCKI_FSIZE(r1) /* recover return addr */ lwz r11,CR_SAVE+BLOCKI_FSIZE(r1) /* recover CR */ mtlr r0 /* return address in the link reg */ mtcr r11 /* restore CR */ addi r1,r1,BLOCKI_FSIZE /* free the stack frame */ blr /* return */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -