/*
 *	ucontext.h
 *
 *	ユーザーコンテキスト(System V互換)
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015-2016 Naoyuki Sawa
 *
 *	* Sun May 17 15:20:04 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 *	* Tue Nov 29 23:01:43 JST 2016 Naoyuki Sawa
 *	- 既存のP/ECE用の実装に加えて、Win32用とWin64用の実装を追加しました。
 *	  既存のP/ECE用の実装は、変更していません。
 *	- Win32用の実装については、下記のページに実装例が有りますが、これは正しく動作しません。
 *	  「CodeProject - Unix ucontext_t Operations on Windows Platforms」(https://www.codeproject.com/tips/4225/unix-ucontext-t-operations-on-windows-platforms)
 *	  上記の実装例は、GetThreadContext(),SetThreadContext() APIを利用していますが、それらのAPIは実行中のスレッド自身に対して正しい値を返さない仕様だからです。
 *	  (もし上手く動いたように見えても、それはたまたま上手く動いたように見えているだけです。)
 *	  従って今回は、GetThreadContext(),SetThreadContext() APIを使用せずに、アセンブラで実装する事にしました。
 *	  アセンブラで実装するにあたり、Win32とWin64それぞれで、setjmp()/longjmp()の生成コードを見て、それらが行っている処理を参考にして実装しました。
 *	- Win64のアセンブラを書くのは今回が初めてで、呼び出し規約についていくつかの記事を参考にさせて頂きました。
 *	  今回参考にさせて頂いた記事を、ucontext.cの中の「Win64用の実装時に参考にした資料」に引用しておきました。
 *	- Win64用のgetcontext(),setcontext(),makecontext()は、/clip/libclip.x64/ucontext_x64.asmにて実装しました。
 *	  詳細は、ucontext.cの中の「Win64」の実装箇所のコメントを参照して下さい。
 */
#ifndef __UCONTEXT_H__
#define __UCONTEXT_H__
#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus
/****************************************************************************
 *	
 ****************************************************************************/
//stack_tは本来signal.hで定義すべきだが、P/ECEにはsignal.hが無いので、ucontext.hに含めて定義する事にした。
typedef struct {					// P/ECE│ Win32│ Win64│
	void*			ss_sp;			//+ 0, 4│+ 0, 4│+ 0, 8│スタックのベースアドレス
     //	int			ss_flags;		//      │      │      │フラグ
	size_t			ss_size;		//+ 4, 4│+ 4, 4│+ 8, 8│スタックのバイト数
} stack_t;						//= 8   │= 8   │=16   │
/*--------------------------------------------------------------------------*/
#ifdef  PIECE
//P/ECE
typedef struct mcontext {
	void*			r0;			//+ 0, 4│%r0
	void*			r1;			//+ 4, 4│%r1
	void*			r2;			//+ 8, 4│%r2
	void*			r3;			//+12, 4│%r3
	void*			sp;			//+16, 4│%sp
	void*			pc;			//+20, 4│%pc
} mcontext_t;						//=24
#endif//PIECE
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if     (defined(_WIN32) && !defined(_WIN64))
//Win32
typedef struct mcontext {
	void*			ebx;			//+ 0, 4│ebx
	void*			esi;			//+ 4, 4│esi
	void*			edi;			//+ 8, 4│edi
	void*			ebp;			//+12, 4│ebp
	void*			esp;			//+16, 4│esp
	void*			eip;			//+20, 4│eip
} mcontext_t;						//=24
#endif//(defined(_WIN32) && !defined(_WIN64))
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if     (defined(_WIN32) &&  defined(_WIN64))
//Win64
typedef struct mcontext {
	void*			rbx;			//+  0, 8│rbx
	void*			rsi;			//+  8, 8│rsi
	void*			rdi;			//+ 16, 8│rdi
	void*			rbp;			//+ 24, 8│rbp
	void*			rsp;			//+ 32, 8│rsp
	void*			rip;			//+ 40, 8│rip
	void*			r12;			//+ 48, 8│r12
	void*			r13;			//+ 56, 8│r13
	void*			r14;			//+ 64, 8│r14
	void*			r15;			//+ 72, 8│r15
	void*			xmm6[2];		//+ 80,16│xmm6
	void*			xmm7[2];		//+ 96,16│xmm7
	void*			xmm8[2];		//+112,16│xmm8
	void*			xmm9[2];		//+128,16│xmm9
	void*			xmm10[2];		//+144,16│xmm10
	void*			xmm11[2];		//+160,16│xmm11
	void*			xmm12[2];		//+176,16│xmm12
	void*			xmm13[2];		//+192,16│xmm13
	void*			xmm14[2];		//+208,16│xmm14
	void*			xmm15[2];		//+224,16│xmm15
} mcontext_t;						//=240
#endif//(defined(_WIN32) &&  defined(_WIN64))
/*--------------------------------------------------------------------------*/
typedef struct ucontext {				// P/ECE│ Win32│   Win64│
	struct ucontext*	uc_link;		//+ 0, 4│+ 0, 4│+  0,  8│現在のコンテキストが終了したとき、続いて切り替わるコンテキストへのポインタ(現在のコンテキストがmakecontext()で生成されたものの場合)
     //	sigset_t		uc_sigmask;		//      │      │        │このコンテキストでブロックされているシグナル群
	stack_t			uc_stack;		//+ 4, 8│+ 4, 8│+  8, 16│このコンテキストが用いているスタック
	mcontext_t		uc_mcontext;		//+12,24│+12,24│+ 24,240│保存されているコンテキストのマシン特有の表現形式
} ucontext_t;						//=36   │=36   │=264    │
/****************************************************************************
 *	
 ****************************************************************************/
int getcontext(ucontext_t* ucp);
int setcontext(const ucontext_t* ucp);
void makecontext(ucontext_t* ucp, void (*func)(), int argc, ...);
int swapcontext(ucontext_t* oucp, const ucontext_t* ucp);
#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus
#endif//__UCONTEXT_H__
