8051模拟器的制作方法

发布于 2024-08-12 02:44:16 字数 668 浏览 2 评论 0原文

出于学习目的,我打算开始构建一个 8051 微控制器仿真器。我很擅长使用 C/C++/C# 进行编程。这不是课堂项目等,而是我这边的一个学习计划。

我确实发现了很多讨论这个问题的问题。然而,我想在更细粒度的层面上打破它,以便我可以在真正开始编写代码之前知道我需要关注哪些领域。

我最初的要求是:

  1. 文本编辑器(可以使用编辑框控件),用户可以在其中编写汇编代码

  2. 验证语法是否为正确

  3. 有小窗口,在运行时显示寄存器值。

  4. 当用户启动程序时,指令应逐步更新寄存器窗口。

除了 GUI 元素之外,我更感兴趣的是如何模拟微控制器。

根据我的理解,我可以进一步分解它:

  1. 我需要一个指令查找表或其他一些方法来存储可用指令并验证语法。任何关于如何实现这一点的指示,请告诉我。

  2. 如何模拟 8051 的每条指令?

  3. 对于寄存器,我可以根据类型使用无符号整数并更新表。

    对于寄存器
  4. 由于微控制器的 RAM 内存有限,我如何检查代码长度或者在内存中执行的代码,以避免缓冲区溢出或其他问题。

如果有一些开源项目详细介绍了如何从头开始构建模拟器,我们将不胜感激。

For learning purpose i intend to start building a 8051 microcontroller emulator. I am comfortable programming in C/C++/C#. This is no class project etc but a learning initiative from my side.

I did found quite a lot of questions discussing this. However, I wanted to break it bit more on a granular level so that I can know which areas I need to focus before i actually start writing the code.

My initial requirements are:

  1. text editor(can use editbox control) where the user can write assembly code

  2. Validate if the syntax is correct

  3. Have small window which shows the register values at run time.

  4. when user starts the program, the instructions should be step by step updating the register windows.

More than the GUI element i am more interested to know how to emulate the microcontroller.

The way I understand I can further break it down:

  1. I need to have a look up table for instructions or some other way to store available instructions and validate the syntax. Any pointers how to implement this, please let me know.

  2. How do I emulate each instruction for 8051?

  3. For registers, i can have the use un/signed integers based on the type and update the table.

  4. Since microcontroller has limited RAM memory, how do I keep a check of the code length or rather the code which is executing in the memory to avoid and buffer overflow or other issues.

If there are some opensource projects which detail how an emulator is built ground-up, would appreciate.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

摘星┃星的人 2024-08-19 02:44:16

我认为您对该项目的范围有点不清楚,至少与标题相关。

模拟器执行二进制代码,仅此而已。模拟器不包括编辑器(这是一种开发工具)或汇编器(同上)。汇编器负责进行语法检查和翻译,这样模拟器就只需执行相对简单的工作即可执行预先验证的合法代码。

听起来你想构建一个完整的 IDE。这将围绕编辑器、汇编器和模拟器封装大量 GUI。我会将这一步留作最后一步。


至于您关于模拟器本身的问题:

您可以使用最多(例如)64K 字节的数组作为模拟器的工作内存。您可以在程序中使用变量来模拟寄存器。我会使用 unsigned char * 来模拟程序计数器,并使用 int 来模拟大多数其他东西...

操作非常简单:从 0 开始程序计数器(或预先确定的引导位置),并启动一个循环,通过该指针获取指令,并将与该指令相关的任何操作应用于寄存器和内存。一个简单的实现将围绕一个巨大的 switch 语句,其中包括所有可能的指令代码。

正如我所说,您的模拟器不需要担心非法指令,因为汇编器不应该产生任何指令。如果程序(即主循环)遇到非法操作,您可能会停止它。

同样,您的模拟器不必担心范围、索引或大小溢出...这也是汇编器的问题,或者可能是链接器的问题(如果您有的话)。


更新:以下几点:

模拟器框架

I think you're a little unclear on the scope of this project, at least as related to the title.

An emulator executes binary code, and nothing else. The emulator doesn't include an editor (that's a development tool) nor an assembler (ditto). It's the assembler's responsibility to do the syntax check and translation, that way the emulator has only the relatively easy job of executing pre-validated, legal code.

It sounds like you want to build a complete IDE. This would wrap a lot of GUI around the editor, assembler and emulator. I would leave that step as the last one.


As for your questions regarding the emulator itself:

You can use an array of up to (e.g.) 64K bytes as the emulator's working memory. You use variables in your program to emulate the registers. I'd use an unsigned char * to emulate the program counter, and ints for most other stuff...

The operation is pretty simple: Start the program counter at 0 (or a pre-determined boot location), and start a loop which fetches instructions via that pointer, and apply to registers and memory whatever operation is associated with the instruction. A simple implementation would center around a huge switch statement that includes all possible instruction codes.

As I said, your emulator shouldn't need to worry about illegal instructions, because the assembler shouldn't produce any. You could have your program (i.e. the main loop) halt if it hits an illegal operation.

Similarly, your emulator doesn't have to worry about range, index or size overruns... that's also the assembler's problem, or maybe the linker's, if you have one.


Update: A few pointers from right here in SO:

Emulator Framework

吃不饱 2024-08-19 02:44:16

最近我为AVR芯片组装了一个仿真器,它也是一个小型8位微控制器。源代码位于 GitHub 上,名称为 ghewgill/emulino。最有趣的文件是 cpu.c 其中包含每个的实现CPU指令。关键行位于 cpu_run() 中(省略一些细节):

while (state == CPU_RUN) {
    u16 instr = Program[PC++];
    Instr[instr](instr);
}

这会从 PC 寄存器指向的程序存储器加载一个 16 位字,然后将其用作指令跳转表的索引(这是一个 64k 的函数指针数组 - 实际的表是由脚本在编译时生成的)。该函数将是该源文件中的 do_XXX() 函数之一,并且可以在执行实际指令之前进行进一步的指令解码。例如,do_ADD() 函数:

static void do_ADD(u16 instr)
{
    trace(__FUNCTION__);
    // ------rdddddrrrr
    u16 r = (instr & 0xf) | ((instr >> 5) & 0x10);
    u16 d = ((instr >> 4) & 0x1f);
    u8 x = Data.Reg[d] + Data.Reg[r];
    Data.SREG.H = (((Data.Reg[d] & Data.Reg[r]) | (Data.Reg[r] & ~x) | (~x & Data.Reg[d])) & 0x08) != 0;
    Data.SREG.V = (((Data.Reg[d] & Data.Reg[r] & ~x) | (~Data.Reg[d] & ~Data.Reg[r] & x)) & 0x80) != 0;
    Data.SREG.N = (x & 0x80) != 0;
    Data.SREG.S = Data.SREG.N ^ Data.SREG.V;
    Data.SREG.Z = x == 0;
    Data.SREG.C = (((Data.Reg[d] & Data.Reg[r]) | (Data.Reg[r] & ~x) | (~x & Data.Reg[d])) & 0x80) != 0;
    Data.Reg[d] = x;
    Cycle++;
}

它执行实际的加法运算 (Data.Reg[d] + Data.Reg[r]),然后设置所有不同的基于结果的条件标志。

Recently I put together an emulator for the AVR chip, which is also a small 8-bit microcontroller. The source is on GitHub as ghewgill/emulino. The most interesting file is cpu.c which contains the implementations for each CPU instruction. The key lines are in cpu_run() (omitting some details):

while (state == CPU_RUN) {
    u16 instr = Program[PC++];
    Instr[instr](instr);
}

This loads a 16-bit word from the program memory pointed to by the PC register, then uses that as in index into an instruction jump table (which is a 64k array of function pointers - the actual table is generated by a script at compile time). This function will be one of the do_XXX() functions in that source file, and may do further instruction decoding before executing the actual instruction. For example, the do_ADD() function:

static void do_ADD(u16 instr)
{
    trace(__FUNCTION__);
    // ------rdddddrrrr
    u16 r = (instr & 0xf) | ((instr >> 5) & 0x10);
    u16 d = ((instr >> 4) & 0x1f);
    u8 x = Data.Reg[d] + Data.Reg[r];
    Data.SREG.H = (((Data.Reg[d] & Data.Reg[r]) | (Data.Reg[r] & ~x) | (~x & Data.Reg[d])) & 0x08) != 0;
    Data.SREG.V = (((Data.Reg[d] & Data.Reg[r] & ~x) | (~Data.Reg[d] & ~Data.Reg[r] & x)) & 0x80) != 0;
    Data.SREG.N = (x & 0x80) != 0;
    Data.SREG.S = Data.SREG.N ^ Data.SREG.V;
    Data.SREG.Z = x == 0;
    Data.SREG.C = (((Data.Reg[d] & Data.Reg[r]) | (Data.Reg[r] & ~x) | (~x & Data.Reg[d])) & 0x80) != 0;
    Data.Reg[d] = x;
    Cycle++;
}

This does the actual addition operation (Data.Reg[d] + Data.Reg[r]), then sets all the various condition flags based on the result.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文