
//linkboy专用
//嵌入式操作系统 - votex

//此调度器是一个协作式操作系统,分为内核和外层,内核部分运行各个组件的驱动程序,采用
//定时器驱动,驱动执行频率为1000Hz(每隔1ms扫描所有驱动进程)
//外层部分反复扫描全部的任务代码,每个任务具有独立的栈空间,通过"schedule"调用进行切换.

//...注意事项
// * 凡是修改 SP 的地方都需要加上关中断保护

//...版本记录
//2012 初始版本
//2013.2.18 把内核驱动从静态代码改为动态注入方式
//2013.2.28 增加了事件退出保护沙箱,在图形界面配置中不需要保护代码

//2013.6.16 定时器中断 注释掉 //MCU TIFR = 0b0000_0010, 需要测试系统是否稳定

//2013.10.31
//把调度器改成了适合图形化编程的方式,每个事件脉冲流退出后不会自动重新执行,
//可以动态添加事件脉冲流.

//2014.1.14
//增加了快速驱动运行机制, 可以注入100us的驱动到内核中; 以前的版本最小的驱动运行间隔为1000us
//主要为了支持电机调速, LED灯光亮度控制等高速驱动的运行

//2014年1月18日 半夜 11:58
//给调度器增加了一个警戒线, 只要进程池被冲破就会触发报警, 把TCB中重要的数据放到后边,数组放前边

//2014年10月12日 上午9:52
//调试编码旋钮, 发现旋转过快系统卡住, 一开始以为是中断干扰, 后来发现是因为正反旋转事件中
//调用了延时指令, 任务创建过快导致线程池满, 于是在 CreateTask 中增加了判断, 
//如果线程池满的话就调度一次, 直到释放掉现有的某个线程.

//2015年3月2日 晚上9:31
//增加了事件资源计数器, 这样可以识别一个事件是否正在执行, 防止事件重入导致代码前后冲突; 图形界面
//中每个事件自动生成一个计数器, 创建一个进程时就会被进程中的计数器指针锁住, 直到进程退出为止.
//用旋钮调节音调程序测试通过, 效果非常好.

//2015年3月26日 下午
//准备把操作系统移植到MEGA644上, 并且兼容MEGA32
// * //MCU.SFIOR.2(bit) = 1; 这里不对, 设置为1反而是禁止上拉电阻

unit OS
{
	//---------------------------------------------------
	public void OS_init()
	{
		#asm "CLI"
		
		//关闭看门狗
		CloseWatchdog();
		
		//端口初始化
		//注意!! 这个是MEGA32的设置, MEGA324不一样.
		//一个解决思路: 寄存器名前边加上限制, 如 M32_SFIOR, M32_MCUCR, M324_MCUCR, ...
		
		//初始化端口
		//这里不对吧??? 应该是设置为0才是上拉电阻有效(2015.3.26)
		//MCU.SFIOR.2(bit) = 1;
		
		//这里可以不需要初始化, 因为复位的时候硬件初始化
		//MCU.DDRA = 0x00;
		//MCU.PORTA = 0x00;
		//MCU.DDRB = 0x00;
		//MCU.PORTB = 0x00;
		//MCU.DDRC = 0x00;
		//MCU.PORTC = 0x00;
		//MCU.DDRD = 0x00;
		//MCU.PORTD = 0x00;
		
		//定时器初始化(与任务无关项)
		TimerInit();
		
		//内核驱动时间表初始化
		DriverInit();
		
		//OS内核初始化
		KernelInit();
		
		tick = 0;
	}
	//=================================================================================
	[uint8*RUN_NUMBER] TickList;
	[uint8*RUN_NUMBER] MaxTickList;
	[uint16*RUN_NUMBER] DriverHandleList;
	
	//..........................
	//注意 DriverNumber 由操作系统使用,用于调度边界控制,
	//而 RUN_NUMBER 仅仅用于定义足够大的存储区,不参与代码逻辑
	
	//内核驱动程序总数
	uint8 DriverNumber;
	//所需的最小存储边界
	public const uint8 RUN_NUMBER = 0;
	//..........................
	
	[uint16*RUN100us_NUMBER] Driver100usHandleList;
	
	//..........................
	//注意 Driver100usNumber 由操作系统使用,用于调度边界控制,
	//而 RUN100us_NUMBER 仅仅用于定义足够大的存储区,不参与代码逻辑
	
	//快速内核驱动程序总数
	uint8 Driver100usNumber;
	//所需的最小存储边界
	public const uint8 RUN100us_NUMBER = 0;
	//..........................
	
	uint8 tick;
	
	public uint8 DebugTick;
	
	//=================================================================================
	//提供如下定义
	//函数	* CloseWatchdog()
	//函数	* TimerInit()
	//宏定义	* 函数定时中断修饰
	//宏定义	* 关闭定时中断响应
	//宏定义	* 打开中断定时响应
	#include "$chip$.txt"
	
	//定时器初始化, 这个是通过宏定义替换的函数, 具体映射到对应的单片机定时器上
	//void TimerInit()
	//=================================================================================
	
	//---------------------------------------------------
	//中断服务函数,注意刚开始不允许中断,如果被中断(串口等)
	//延时后下一个tick可能已经来临从而重新进入这里,造成覆盖.
	//千万别用或运算! 会清掉其他一切定时器中断标志
	//MCU TIFR | 0b0000_0010; ERROR!
	MACRO_TIMER_TICK
	void OS_run()
	{
		MACRO_TIMER_OFF();
		
		//注意这里暂时不清掉中断标志,测试系统是否稳定
		//MCU TIFR = 0b0000_0010;
		#asm "SEI"
		
		//运行快速内核驱动列表
		RunDriver100usList();
		
		//遍历组件驱动列表,逐个运行
		tick + 1;
		if( tick >= 10 ) {
			RunDriverList();
			tick = 0;

			DebugTick += 1;
		}
		#asm "CLI"
		
		MACRO_TIMER_ON();
	}
	//---------------------------------------------------
	//初始化内核驱动时间表
	void DriverInit()
	{
		DriverNumber = 0;
		for( uint8 i = 0; i < RUN_NUMBER; i + 1 ) {
			TickList[i] = 0;
			MaxTickList[i] = 0;
			DriverHandleList[i] = 0;
		}
		Driver100usNumber = 0;
	}
	//---------------------------------------------------
	//注入组件驱动程序到内核进程
	public void CreateDriver( uint16 Handle, uint8 MaxTick )
	{
		DriverHandleList[DriverNumber] = Handle;
		MaxTickList[DriverNumber] = MaxTick;
		DriverNumber + 1;
	}
	//---------------------------------------------------
	//注入快速组件驱动程序到内核进程
	public void CreateDriver100us( uint16 Handle )
	{
		Driver100usHandleList[Driver100usNumber] = Handle;
		Driver100usNumber + 1;
	}
	//---------------------------------------------------
	//遍历组件驱动列表,逐个运行
	void RunDriverList()
	{
		for( uint8 i = 0; i < DriverNumber; i + 1 ) {
			TickList[i] + 1;
			if( TickList[i] == MaxTickList[i] ) {
				TickList[i] = 0;
				D_CALL( DriverHandleList[i] );
			}
		}
	}
	//---------------------------------------------------
	//遍历快速组件驱动列表,逐个运行
	void RunDriver100usList()
	{
		for( uint8 i = 0; i < Driver100usNumber; i + 1 ) {
			D_CALL( Driver100usHandleList[i] );
		}
	}
	//---------------------------------------------------
	//动态调用封装器,两种方式都测试通过
	void D_CALL( uint16 Handle )
	{
		#asm "ldd r30,y+0"
		#asm "ldd r31,y+1"
		
		//第一种方式: 通过专用指令ICALL间接调用 R30 R31 所指向的地址
		#asm "ICALL"
		
		//第二种方式: 通过 ret 和堆栈调用 R30 R31 指向的地址
//		#asm "push r30"
//		#asm "push r31"
//		#asm "ret"
	}
	//=================================================================================
	//任务控制块
	struct TCB
	{
		//任务使用的存储空间,用于堆栈基址,局部变量和调用函数地址的保护
		//堆栈数据格式(初始状态):
		//位置索引: 0    1    2   ~~   L-8  L-7  L-6  L-5  L-4  L-3  L-2  L-1
		//单元参数: *    *    *   ~~   R29  R28  PCE  PCH  PCL  SHE  SDH  SDL
		//堆栈指针: *    *    *   ~~   /^\   *    *    *    *    *    *    * 
		[uint8*STACK_LENGTH] Stack;
		
		//进程占用的资源(事件代码)计数器, 当计数器不等于0时表示对应的事件正在被占用
		->uint8 e_res_count;
		
		//进程是否被使用
		bool open;

		//当前进程所绑定的事件指针
		uint16 EventProc;
		
		//任务的当前SP指针
		uint16 SP;
	}
	[struct TCB * TASK_NUMBER] AppTCB;
	
	//每个任务占据的存储空间大小
	const uint16 STACK_LENGTH = 200;
	
	//当前任务号
	uint16 CurrentTaskIndex;
	
	//所需的最小存储边界
	const uint16 TASK_NUMBER = 5;
	//...................
	
	//堆栈指针
	uint16 SP = #addr 0x5D;
	
	//---------------------------------------------------
	//调度器内核初始化
	void KernelInit()
	{
		CurrentTaskIndex = 0;
		for( uint8 i = 0; i < TASK_NUMBER; i + 1 ) {
			AppTCB[i].open = false;
		}
	}
	//---------------------------------------------------
	//开始运行
	public void Start()
	{
		SP = AppTCB[0].SP;
		#asm "POP R29"
		#asm "POP R28"
		#asm "RETI"
	}
	//---------------------------------------------------
	//手工触发执行一次指定编号的事件
	public void TrigEvent( uint8 i )
	{
		#asm "PUSH R28"
		#asm "PUSH R29"
		
		AppTCB[CurrentTaskIndex].SP = SP;
		
		CurrentTaskIndex = i;
		
		//准备切换到继续的进程
		uint16 BSP = AppTCB[CurrentTaskIndex].SP;
		
		//一定要关中断,因为 SP 是一个16位变量,不允许访问期间被中断
		#asm "CLI"
		SP = BSP;
		#asm "SEI"
		
		#asm "POP R29"
		#asm "POP R28"
		#asm "RET"
	}
	//---------------------------------------------------
	//注入一个事件到调度器事件列表
	public uint8 CreateTask( uint16 Proc )
	{
		//临时辅助变量, 用于引用类型数据的底层访问
		uint16 NP;
		->uint8 P;
		
		//搜索一个空闲的进程, 如果线程池满了的话, 就自动调度一次进行释放
		uint8 i = 0;
		forever {
			if( !AppTCB[i].open ) {
				break;
			}
			i + 1;
			//i % TASK_NUMBER;
			if( i == TASK_NUMBER ) {
				i = 0;
				Schedule();
			}
		}
		//绑定当前进程的事件指针
		AppTCB[i].EventProc = Proc;

		//设置事件的运行起点
		AppTCB[i].Stack[STACK_LENGTH - 4] = Proc.0(uint8);
		AppTCB[i].Stack[STACK_LENGTH - 5] = Proc.8(uint8);
		AppTCB[i].Stack[STACK_LENGTH - 6] = 0;
		
		//设置事件的沙箱,保证事件安全退出
		uint16 SandboxHandle = #addr EventSandbox;
		AppTCB[i].Stack[STACK_LENGTH - 1] = SandboxHandle.0(uint8);
		AppTCB[i].Stack[STACK_LENGTH - 2] = SandboxHandle.8(uint8);
		AppTCB[i].Stack[STACK_LENGTH - 3] = 0;
		
		//设置事件变量空间
		P -> AppTCB[i].Stack[0];
		#asm "LDD r30,y+&P"
		#asm "LDD r31,y+&P+1"
		#asm "STD y+&NP,r30"
		#asm "STD y+&NP+1,r31"
		AppTCB[i].Stack[STACK_LENGTH - 7] = NP.0(uint8);
		AppTCB[i].Stack[STACK_LENGTH - 8] = NP.8(uint8);
		
		//设置进程的事件资源计数器
		if( i != 0 ) {
			AppTCB[i].e_res_count -> AppTCB[CurrentTaskIndex].e_res_count;
			AppTCB[i].e_res_count + 1;
		}
		
		//设置堆栈指针起点,先指向堆栈结尾,再移动6个PC指针(保护沙箱 和 事件起始点) 和 2个软件堆栈指针的空间
		P -> AppTCB[i].Stack[STACK_LENGTH - 1 - 8];
		#asm "LDD r30,y+&P"
		#asm "LDD r31,y+&P+1"
		#asm "STD y+&NP,r30"
		#asm "STD y+&NP+1,r31"
		AppTCB[i].SP = NP;
		
		AppTCB[i].open = true;

		return i;
	}
	//---------------------------------------------------
	//进行一次调度, 把当前局部变量基指针保存到当前任务的堆栈区,
	//然后切换到下一个任务,弹出对应的局部变量基指针和PC,运行下一个任务
	public void Schedule()
	{
		#asm "PUSH R28"
		#asm "PUSH R29"
		
		AppTCB[CurrentTaskIndex].SP = SP;
		
		//搜索下一个就绪的进程
		forever {
			CurrentTaskIndex + 1;
			CurrentTaskIndex % TASK_NUMBER;
			if( AppTCB[CurrentTaskIndex].open ) {
				break;
			}
		}
		//准备切换到继续的进程
		uint16 BSP = AppTCB[CurrentTaskIndex].SP;
		
		//一定要关中断,因为 SP 是一个16位变量,不允许访问期间被中断
		#asm "CLI"
		SP = BSP;
		#asm "SEI"
		
		#asm "POP R29"
		#asm "POP R28"
		#asm "RET"
	}
	//---------------------------------------------------
	//立即结束指定的事件
	public void DeleteTask( uint16 Proc )
	{
		//遍历事件进程池
		uint8 i = 0;
		loop( TASK_NUMBER ) {
			if( AppTCB[i].open && AppTCB[i].EventProc == Proc ) {
				AppTCB[i].e_res_count = 0;
				AppTCB[i].open = false;
			}
			i + 1;
		}
	}
	//---------------------------------------------------
	//事件运行沙箱, 确保事件安全退出
	void EventSandbox()
	{
		AppTCB[CurrentTaskIndex].e_res_count - 1;
		
		//占用用户事件空间,跳过6个存储单元(两个PC指针),防止在下边的代码中干扰事件堆栈初始化
		#asm "CLI"
		SP - 6;
		#asm "SEI"
		
		AppTCB[CurrentTaskIndex].open = false;
		Schedule();
	}
	//---------------------------------------------------
	//警戒线溢出报警延时函数
	void SafelineDelay()
	{
		loop( 250 ) loop( 250 ) {}
	}
}







