MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计之一.
#include <stdint.h> // 包含标准整数类型定义
#include <stdio.h> // 包含输入输出函数
#include <string.h> // 包含字符串操作函数
#include <stdarg.h> // 包含可变参数列表处理函数
#include <assert.h> // 包含断言宏
// 包含MicroPython特定的emit接口和xtensa汇编相关的头文件
#include "py/emit.h"
#include "py/asmxtensa.h"
#if MICROPY_EMIT_INLINE_XTENSA
// 定义内联xtensa汇编的上下文结构体
struct _emit_inline_asm_t {
asm_xtensa_t as; // xtensa汇编器的上下文
uint16_t pass; // 汇编的遍历次数
mp_obj_t *error_slot; // 错误信息存储位置的指针
mp_uint_t max_num_labels; // 最大标签数量
qstr *label_lookup; // 标签查找表
};
// 内联xtensa汇编错误信息的生成函数
static void emit_inline_xtensa_error_msg(emit_inline_asm_t *emit, mp_rom_error_text_t msg) {
*emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg); // 将错误信息封装成异常对象并存储
}
// 内联xtensa汇编错误异常的生成函数
static void emit_inline_xtensa_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) {
*emit->error_slot = exc; // 直接将异常对象存储到错误信息位置
}
// 创建一个新的内联xtensa汇编上下文
emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels) {
emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t); // 分配内存给新的上下文对象
memset(&emit->as, 0, sizeof(emit->as)); // 初始化汇编器上下文
mp_asm_base_init(&emit->as.base, max_num_labels); // 初始化基础汇编器
emit->max_num_labels = max_num_labels; // 设置最大标签数量
emit->label_lookup = m_new(qstr, max_num_labels); // 分配标签查找表内存
return emit; // 返回新创建的上下文对象
}
// 释放内联xtensa汇编上下文占用的内存
void emit_inline_xtensa_free(emit_inline_asm_t *emit) {
m_del(qstr, emit->label_lookup, emit->max_num_labels); // 释放标签查找表内存
mp_asm_base_deinit(&emit->as.base, false); // 反初始化基础汇编器
m_del_obj(emit_inline_asm_t, emit); // 释放上下文对象内存
}
// 开始一个新的内联xtensa汇编遍历
static void emit_inline_xtensa_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) {
emit->pass = pass; // 设置当前遍历类型
emit->error_slot = error_slot; // 设置错误信息存储位置
if (emit->pass == MP_PASS_CODE_SIZE) {
// 如果是代码大小计算遍历
memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); // 清空标签查找表
}
mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); // 开始基础汇编器遍历
asm_xtensa_entry(&emit->as, 0); // 进入xtensa汇编器
}
// 结束当前的内联xtensa汇编遍历
static void emit_inline_xtensa_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) {
asm_xtensa_exit(&emit->as); // 退出xtensa汇编器
asm_xtensa_end_pass(&emit->as); // 结束基础汇编器遍历
}
// 计算内联xtensa汇编中参数的数量
static mp_uint_t emit_inline_xtensa_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) {
if (n_params > 4) {
// 如果参数数量超过4个
emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("只能有最多4个参数用于Xtensa汇编")); // 生成错误信息
return 0;
}
for (mp_uint_t i = 0; i < n_params; i++) {
// 遍历所有参数
if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
// 如果参数不是标识符
emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("参数必须是顺序的寄存器a2到a5")); // 生成错误信息
return 0;
}
const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i])); // 获取参数字符串
if (!(strlen(p) == 2 && p[0] == 'a' && (mp_uint_t)p[1] == '2' + i)) {
// 如果参数不是a2到a5的寄存器
emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("参数必须是顺序的寄存器a2到a5")); // 生成错误信息
return 0;
}
}
return n_params; // 返回参数数量
}
// 为内联xtensa汇编中的标签分配一个标识符
static bool emit_inline_xtensa_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) {
assert(label_num < emit->max_num_labels); // 断言标签编号在有效范围内
if (emit->pass == MP_PASS_CODE_SIZE) {
// 如果是代码大小计算遍历
// 检查标签是否重复
for (uint i = 0; i < emit->max_num_labels; i++) {
if (emit->label_lookup[i] == label_id) {
return false; // 如果标签重复,返回false
}
}
}
emit->label_lookup[label_num] = label_id; // 将标签编号和标识符关联
mp_asm_base_label_assign(&emit->as.base, label_num); // 分配标签编号
return true; // 返回true表示成功
}
// 定义寄存器名称和编号的映射结构体
typedef struct _reg_name_t {
byte reg; // 寄存器编号
byte name[3]; // 寄存器名称字符串