什么是程序
X86
X86在调用函数的时候传递在参数是从栈中取出的,需要哪些参数提前按一定顺序入栈即可。第一个出栈的就对应第一个参数,依次类推。函数返回值存在eax中。
ARM
arm函数调用参数传递顺序是从r0~r3,第一个参数在r0中,第二个参数在r1中,依次类推。参数超过4个,则要先入栈,从第五个参数开始从栈中取。函数返回值放在r0中。
手写RTOS(课程回顾)
基于模板https://gitee.com/zeng-hanhan/hand-write-rtos
模板已经实现
1)自定义串口输出打印函数,2)定时器中断功能,3)可在模拟器运行 。。。。。
创建目录: rtos 文件:task.c task.h
工程目录结构:
核心
task.c文件:
实现创建任务函数
第一次运行任务,需要初始化栈,将 返回地址 设置为 任务函数指针。
中断结束就跳转到 该地址 执行。
void create_task(task_function *f, void *param, char *stack, int stack_len){
int *top = (int *)(stack + stack_len);
/* 伪造现场 */
top -= 16;
// r4~r11
top[0] = 0;
top[1] = 0;
top[2] = 0;
top[3] = 0;
top[4] = 0;
top[5] = 0;
top[6] = 0;
top[7] = 0;
// r0~r3
top[8] = (int)param;
top[9] = 0;
top[10] = 0;
top[11] = 0;
// r12 lr
top[12] = 0;
top[13] = 0;
// 返回地址 任务入口
top[14] = (int)f;
//psr
top[15] = (1<<24);//指令集
/* 记录栈的位置 */
task_stacks[task_count++] = (int)top;
}
开始任务函数: 将全局变量 task_running 置为1 ,实际在中断中开启执行任务
定时器中断时检测该变量,是否创建任务,避免还没创建任务就在中断运行。
void task_start(void){
task_running = 1;
while(1);
}
中断
中断函数为汇编,
保存现场后调用c函数 SysTick_Handler
SysTick_Handler_asm PROC
;保存r4~r11
STMDB SP!,{R4 - R11}
STMDB SP!,{LR}
MOV R0, LR ;LR是特殊值
ADD R1, SP, #4
BL SysTick_Handler ;不破坏r4~r11
LDMIA SP!, {R0}
LDMIA SP!, {R4 - R11}
BX R0
ENDP
SysTick_Handler 函数:
int cur_task:全局变量 当前任务
int is_task_running(void): 函数 判断是否有任务已经创建
int get_stack (int task_index): 获取任务的栈
void set_task_stack(int task,int sp): 修改 task 的栈顶为sp
均在 task.c 实现
判断要 ”创建任务” 还是 ”启动第一个任务” 还是 ”切换任务”
如果还没创建任务直接返回, 相当于啥也不做
启动第一个任务设置当前任务 cur_task = 0,
跳到 StartTask_asm运行,从当前任务的栈中恢复寄存器
效果是把伪造的现场写入寄存器后结束中断并从伪造的 返回地址(任务入口函数) 运行。
** 切换任务 **在中断发生就已经保存了所有寄存器
只需要获取下一个任务栈,把栈和特殊的 lr 值交给 StartTask_asm 汇编函数 恢复下一个任务的寄存器 开始下一个任务的执行。
void SysTick_Handler(int LR,int old_sp)
{
int stack, pre_task, new_task;
SCB_Type * SCB = (SCB_Type *)SCB_BASE_ADDR;
/* clear exception status */
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;
/* 如果还没创建任务 */
if(!is_task_running()){
return;
}
/* 启动第一个任务或者切换 */
if(cur_task == -1){
/* 启动第一个任务 */
cur_task = 0;
/* 从栈恢复寄存器 */
//
stack = get_stack(cur_task);
StartTask_asm(stack, LR); /* 开始任务需要把栈中的值读入到 寄存器 用汇编做 */
}
else{
/* 切换任务 */
pre_task = cur_task;
new_task = get_next_task(); //下一个任务
if(pre_task != new_task){
/* 保存pre_task:r4~r11
* 在汇编中已经保存
*/
/* 更新sp */
set_task_stack(pre_task, old_sp);
/* 切换new_task */
stack = get_stack(new_task);
cur_task = new_task;
StartTask_asm(stack, LR);
}
}
StartTask_asm
从任务栈恢复寄存器
StartTask_asm PROC
;从任务的栈 把r4-r11读出来写入寄存器
;r0存有任务的栈 r1有LR(特殊的值)
LDMIA R0!, {R4 - R11}
;更新SP
MSR MSP, R0
;触发硬件中断返回;
BX R1
ENDP
ROC
;从任务的栈 把r4-r11读出来写入寄存器
;r0存有任务的栈 r1有LR(特殊的值)
LDMIA R0!, {R4 - R11}
;更新SP
MSR MSP, R0
;触发硬件中断返回;
BX R1
ENDP