找到bug了
犯了个超低级错误
printf是不可重入函数。。。
加了开关中断宏就好了
现在OK了
新代码如下
#include <stdio.h>
#include <windows.H>
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")
#define DISABLE_INTERRUPT() FlagEn=0
#define ENABLE_INTERRUPT() FlagEn=1
#define TASK_STK_SIZE 2048
#define TASKS_N 10
#define OS_TICKS_PER_SEC 100
typedef unsigned int OS_STK;
typedef unsigned long INT32U;
typedef struct os_tcb
{
OS_STK *OSTCBStkPtr;
struct os_tcb *OSTCBNext;
}OS_TCB;
OS_STKTaskStk[TASKS_N][TASK_STK_SIZE];
HANDLE mainhandle;
CONTEXT Context;
BOOLEAN FlagEn = 1;
OS_TCB OSTCB[3];
OS_TCB *OSTCBCur;
void OSIntCtxSw(void)
{
OS_STK *sp;
sp = (OS_STK *)Context.Esp;//得到主线程当前堆栈指针
//在堆栈中保存相应寄存器。
*--sp = Context.Eip;//先保存eip
*--sp = Context.EFlags;//保存efl
*--sp = Context.Eax;
*--sp = Context.Ecx;
*--sp = Context.Edx;
*--sp = Context.Ebx;
*--sp = Context.Esp;//此时保存的esp是错误的,但OSTCBCur保存了正确的
*--sp = Context.Ebp;
*--sp = Context.Esi;
*--sp = Context.Edi;
OSTCBCur->OSTCBStkPtr = (OS_STK *)sp;//保存当前esp
OSTCBCur = OSTCBCur->OSTCBNext;//得到当前就绪最高优先级任务的tcb
sp = OSTCBCur->OSTCBStkPtr;//得到重新执行的任务的堆栈指针
//恢复所有处理器的寄存器
Context.Edi = *sp++;
Context.Esi = *sp++;
Context.Ebp = *sp++;
Context.Esp = *sp++;//此时上下文中得到的esp是不正确的
Context.Ebx = *sp++;
Context.Edx = *sp++;
Context.Ecx = *sp++;
Context.Eax = *sp++;
Context.EFlags = *sp++;
Context.Eip = *sp++;
Context.Esp = (unsigned long)sp;//得到正确的esp
SetThreadContext(mainhandle, &Context);//保存主线程上下文
}
void CALLBACK OSTickISR(unsigned int a,unsigned int b,unsigned long c,unsigned long d,unsigned long e)
{
if(!FlagEn)
return;//如果当前中断被屏蔽则返回
SuspendThread(mainhandle);//中止主线程的运行,模拟中断产生.但没有保存寄存器
if(!FlagEn)
{//在suspendthread完成以前,flagEn可能被再次改掉
ResumeThread(mainhandle);//模拟中断返回,主线程得以继续执行
return;//如果当前中断被屏蔽则返回
}
GetThreadContext(mainhandle, &Context);//得到主线程上下文,为切换任务做准备
OSIntCtxSw();//由于不能使用中断返回指令,所以此函数是要返回的
ResumeThread(mainhandle);//模拟中断返回,主线程得以继续执行
}
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos)
{
INT32U *stk;//console 下寄存器为32位宽
stk = (INT32U *)ptos; /* Load stack pointer */
*--stk = (INT32U)pdata; /* Simulate call to function with argument */
*--stk = (INT32U)0x00000000;//子程序是从当前esp+4处取得传入的参数,所以此处要空出4个字节
*--stk = (INT32U)task; /* Put pointer to task on top of stack */
*--stk = (INT32U)0x00000202;/* EFL = 0X00000202*/
*--stk = (INT32U)0xAAAAAAAA; /* EAX = 0xAAAAAAAA */
*--stk = (INT32U)0xCCCCCCCC; /* ECX = 0xCCCCCCCC */
*--stk = (INT32U)0xDDDDDDDD; /* EDX = 0xDDDDDDDD */
*--stk = (INT32U)0xBBBBBBBB; /* EBX = 0xBBBBBBBB */
*--stk = (INT32U)0x00000000; /* ESP = 0x00000000 esp可以任意,因为 */
*--stk = (INT32U)0x11111111; /* EBP = 0x11111111 */
*--stk = (INT32U)0x22222222; /* ESI = 0x22222222 */
*--stk = (INT32U)0x33333333; /* EDI = 0x33333333 */
return ((OS_STK *)stk);
}
void task1(void *pParam)
{
while(1)
{
DISABLE_INTERRUPT();
printf("A");
ENABLE_INTERRUPT();
Sleep(100);
}
}
void task2(void *pParam)
{
while(1)
{
DISABLE_INTERRUPT();
printf("B");
ENABLE_INTERRUPT();
Sleep(100);
}
}
void task3(void *pParam)
{
while(1)
{
DISABLE_INTERRUPT();
printf("C");
ENABLE_INTERRUPT();
Sleep(100);
}
}
void VCInit(void)
{
HANDLE cp,ct;
Context.ContextFlags = CONTEXT_CONTROL;
cp = GetCurrentProcess();//得到当前进程句柄
ct = GetCurrentThread();//得到当前线程伪句柄
DuplicateHandle(cp, ct, cp, &mainhandle, 0, TRUE, 2);//伪句柄转换,得到线程真句柄
}
void OSStartHighRdy(void)
{
_asm{
mov ebx, [OSTCBCur];//OSTCBCur结构的第一个参数就是esp
mov esp, [ebx];//恢复堆栈
popad;//恢复所有通用寄存器,共8个
popfd;//恢复标志寄存器
ret;//ret 指令相当于pop eip 但保护模式下不容许使用eip
;//永远都不返回
}
}
int main(void)
{
VCInit();
OSTCB[0].OSTCBStkPtr = OSTaskStkInit(task1, NULL, &TaskStk[0][TASK_STK_SIZE-1]);
OSTCB[0].OSTCBNext = &OSTCB[1];
OSTCB[1].OSTCBStkPtr = OSTaskStkInit(task2, NULL, &TaskStk[1][TASK_STK_SIZE-1]);
OSTCB[1].OSTCBNext = &OSTCB[2];
OSTCB[2].OSTCBStkPtr = OSTaskStkInit(task3, NULL, &TaskStk[2][TASK_STK_SIZE-1]);
OSTCB[2].OSTCBNext = &OSTCB[0];
OSTCBCur = OSTCB;
timeSetEvent(1000/OS_TICKS_PER_SEC, 0, OSTickISR, 0, TIME_PERIODIC);
OSStartHighRdy();
return 0;
}