- 主题:请教一个C51编程的问题!
如下,我打算用函数指针调用内存代码:
U8 lcall_mem[10];
void (far *lcall_code)();
void test()
{
lcall_mem[0]= 0x22; // C51的RET指令机器代码
lcall_code = (void (far *)())lcall_mem;
lcall_code();
}
按理说以上这段话就相当于调用了一个空函数,然后正常返回,然而我的代码实际编译运行的时候却直接崩溃,
甚至根本没有调用这个test函数也是如此,这是什么原因?
--
FROM 111.9.5.*
没学过C51,问了一下ChatGPT,回答如下。
你的代码看起来像是尝试在C51环境下通过函数指针调用内存中的机器码指令。这个代码崩溃的原因可能有以下几个方面:
1. **内存区域问题**:
你把指令放在了`lcall_mem`数组中,而该数组是放在数据存储区(data memory)中的。C51的数据存储区与代码存储区是分开的,直接在数据存储区执行代码是不可行的。
2. **函数指针类型**:
`far`关键字指定了函数指针指向的是代码存储区中的一个地址,但`lcall_mem`是一个数据存储区的地址,两者不匹配。
3. **编译器和硬件限制**:
一些编译器和硬件环境可能会对代码执行位置有严格的限制,不允许在数据存储区执行代码。
解决这个问题的一种方法是使用`code`存储类型来定义内存数组,确保数组被放置在代码存储区。修改后的代码如下:
unsigned char code lcall_mem[10];
void (code *lcall_code)();
void test()
{
// 注意: 你需要确保在C51编译器中使用指令将lcall_mem放到代码区
lcall_mem[0] = 0x22; // C51的RET指令机器代码
lcall_code = (void (code *)())lcall_mem;
lcall_code();
}
但是,这个代码仍然可能存在其他潜在问题:
- 编译器优化和内存分配:需要确保`lcall_mem`被分配到代码存储区的确切位置并且没有被编译器优化掉。
- 安全性和稳定性:直接执行内存中的指令是非常危险的操作,可能会导致不可预测的行为,尤其是在嵌入式系统中。
最后,还需要根据你的具体编译器和硬件手册,确认如何正确地在代码存储区放置和执行自定义机器码。某些系统可能有特定的机制或函数来处理这种情况。
【 在 moonwalker 的大作中提到: 】
: 如下,我打算用函数指针调用内存代码:
: U8 lcall_mem[10];
: void (far *lcall_code)();
: ...................
--
FROM 101.224.229.*
唉,不过还是谢谢!
我的本意,就是在内存里存放可执行代码,以便随时下载修改,放在代码区就没意义了。
【 在 pinocchio 的大作中提到: 】
: 没学过C51,问了一下ChatGPT,回答如下。
: 你的代码看起来像是尝试在C51环境下通过函数指针调用内存中的机器码指令。这个代码崩溃的原因可能有以下几个方面:
: 1. **内存区域问题**:
: ...................
--
FROM 111.9.5.*
根据你的要求又追问了一下,回答如下。
为了在C51环境下实现动态执行内存中的代码,我们需要绕过一些C51体系结构和编译器的限制。直接在数据存储区执行代码可能是不被支持的。但是,可以通过以下方法尝试实现你的目标:
1. **使用RAM执行代码(非标准方法)**:
有些高端单片机支持在RAM中执行代码,这取决于具体的硬件架构。需要检查你的MCU是否支持这个特性,并且需要特定的方法将代码从闪存复制到RAM,然后从RAM执行。
2. **使用特定的汇编代码**:
通过在C51代码中嵌入汇编指令,可以直接执行某些存储在数据存储区的指令。这通常涉及自定义的汇编器和连接器脚本。
3. **模拟动态代码执行**:
模拟代码执行可以通过插入一些条件跳转和函数指针来实现,但这仍然需要某种程度的静态代码预定义。
### 示例:通过汇编实现
假设你的目标MCU确实支持从RAM执行代码,可以通过汇编方式实现:
#include <intrins.h>
// 定义RAM中的可执行代码段
unsigned char xdata lcall_mem[10];
void (*lcall_code)();
void test()
{
// 设置机器码,注意实际操作可能需要更多指令
lcall_mem[0] = 0x22; // RET指令
// 将函数指针指向lcall_mem
lcall_code = (void (*)()) lcall_mem;
// 禁用中断
_push_(EA); // 保存中断状态
EA = 0; // 关闭中断
// 调用代码段
(*lcall_code)();
// 恢复中断
_pop_(EA); // 恢复中断状态
}
void main()
{
test();
while (1);
}
### 注意事项:
1. **中断处理**:
关闭中断可以防止在执行自定义代码时被中断打断,恢复中断可以保证系统的正常运行。
2. **RAM执行代码支持**:
确保目标MCU支持从RAM执行代码,不同的MCU对这部分的支持有所不同。
3. **安全性和调试**:
动态执行代码涉及很多潜在风险,调试时需要非常谨慎,确保每个步骤都正确执行。
### 总结
上述方法仅供参考,根据不同的MCU和开发环境,具体实现可能有所不同。在C51环境中执行动态代码非常复杂,需要对目标硬件有深入的了解,通常推荐查阅目标MCU的数据手册及相关技术文档。
- 来自 水木社区APP v3.5.7
【 在 moonwalker 的大作中提到: 】
: 唉,不过还是谢谢!
: 我的本意,就是在内存里存放可执行代码,以便随时下载修改,放在代码区就没意义了。
--
FROM 223.104.5.*
按你的办法试过了,确实不行。
我就是不调用这个函数,只要编译到代码里,程序还是会崩,看来是编译器不支持。
不过还是很感谢你帮我查资料!
【 在 pinocchio 的大作中提到: 】
: 根据你的要求又追问了一下,回答如下。
: 为了在C51环境下实现动态执行内存中的代码,我们需要绕过一些C51体系结构和编译器的限制。直接在数据存储区执行代码可能是不被支持的。但是,可以通过以下方法尝试实现你的目标:
: 1. **使用RAM执行代码(非标准方法)**:
: ...................
--
修改:moonwalker FROM 111.9.5.*
FROM 111.9.5.*
可惜可惜
- 来自 水木社区APP v3.5.7
【 在 moonwalker 的大作中提到: 】
: 按你的办法试过了,确实不行。
: 我就是不调用这个函数,只要编译到代码里,程序还是会崩,看来是编译器不支持。
: 不过还是很感谢你帮我查资料!
--
FROM 202.120.11.*
理论上肯定可行。
首先先要查c51编译器规范,一个函数地址代表了什么。栈,代码,数据等都有严格的定义。
其次,你要hack你的系统,让系统能够运行ram区的代码,或者你指定内存地址处的代码。
【 在 moonwalker 的大作中提到: 】
: 如下,我打算用函数指针调用内存代码:
:
: U8 lcall_mem[10];
: ...................
--来自微微水木3.5.14
--
FROM 183.193.16.*