ASM解释器:如何存储局部变量?

时间:2011-12-25 10:20:29

标签: c# assembly local

对于我的作业,我需要在C#中编写一个非常小的虚拟16位汇编程序 - 解释器。 它使用字节数组(64k)模拟RAM,使用变量(A,B,C,...)模拟寄存器。 现在我需要一种保存局部变量的方法,google说它们是在Stack上分配的。

但是我不清楚的是,当它们被分配到堆栈上时(使用push ...),解释器在以后使用时如何访问它们?

请参阅以下两行:

pi INT 3
mov A, pi

在第一行中,pi在堆栈上分配,在第二行中使用pi,但是解释器应该如何知道堆栈中pi在哪里访问其数据? (我的Stack也是一个字节数组,有2个辅助函数(push,pop),还有一个指向堆栈顶部的指针)

4 个答案:

答案 0 :(得分:1)

通常没有单独的堆栈内存,而是堆栈在常规RAM中,因此您只有堆栈指针可以跟踪它。

通常,通过将堆栈指针复制到另一个寄存器,然后移动堆栈指针为变量腾出空间,在子例程的开头分配局部变量:

mov bp, sp ;copy stack pointer
sub sp, 4 ;make room for two integer variables

使用堆栈指针的副本来访问局部变量:

mov A, [bp-2] ;get first integer
mov B, [bp] ;get second integer

当您离开子例程时,您将恢复堆栈指针以取消分配局部变量:

mov sp, bp ;restore stack
ret ;exit from subroutine

您在问题中使用的语法通常用于声明全局变量,而不是局部变量:

.data
pi int 3 ;declare a label and allocate room for an int in the program
.code
mov A, pi ;use the address of the label to access the int

答案 1 :(得分:1)

通常,堆栈数据是通过stack pointer相对访问的,myadd: push bp ; we'll be accessing stack through bp (can't do that through sp because there's no sp-relative memory addressing in 16-bit mode), so, let's save bp first mov bp, sp ; bp is equal to the stack pointer mov ax, dword ptr [bp + 4] ; load ax with 1st parameter stored at bp+4 (sp+4) add ax, dword ptr [bp + 6] ; add to ax 2nd parameter stored at bp+6 (sp+6) pop bp ; restore bp ret ; near return to the caller at address stored at sp (address after call myadd), the result/sum is in ax 是一个CPU寄存器,指向存储在堆栈中的最后一个元素。您可以将其视为模拟CPU内存的索引。每当你将某些东西推到堆栈上时,堆栈指针就会根据该东西的大小递减,并且在递减之后,某些东西会被存储在地址的仿真内存中。无论何时从堆栈中弹出一些东西,都会从存储在堆栈指针中的地址中获取值,然后堆栈指针会根据该内容的大小递增。这就是CPU堆栈在许多不同CPU中的工作方式。

如果您正在实现CPU仿真器或CPU指令仿真器/解释器,那么您并不关心变量。您关心的是操作CPU寄存器和内存的CPU指令,因为您的程序是根据CPU指令表示的。它们(指令)必须跟踪存储在堆栈中的所有loacal变量,即它们相对于堆栈指针当前值的位置。

例如,如果你考虑一个简单的子程序,在栈上添加两个16位整数值,它可能看起来就像这样。 16位x86汇编:

    push word 2 ; prepare/store 2nd parameter on the stack
    push word 1 ; prepare/store 1st parameter on the stack
    call myadd ; near call, pushes address of next instruction (add), jumps to myadd
    add sp, 4 ; remove myadd's parameters (1 and 2) from the stack
    ; ax should now contain 3

调用者可能看起来像这样:

{{1}}

答案 2 :(得分:0)

'google说它们是在Stack'上分配的

这是在真实计算机中实现的方式,但这不是全部。

如果您想要虚拟解释器,则需要使用名为“Hash Table”的数据结构。

这是一个家庭作业问题。所以没有直接答案:P 但是下面的代码将解释如何使用哈希表。将变量名称和值存储在哈希表中。

using System;
using System.Collections;

class Program
{
    static Hashtable GetHashtable()
    {
    // Create and return new Hashtable.
    Hashtable hashtable = new Hashtable();
    hashtable.Add("Area", 1000);
    hashtable.Add("Perimeter", 55);
    hashtable.Add("Mortgage", 540);
    return hashtable;
    }

    static void Main()
    {
    Hashtable hashtable = GetHashtable();

    // See if the Hashtable contains this key.
    Console.WriteLine(hashtable.ContainsKey("Perimeter"));

    // Test the Contains method. It works the same way.
    Console.WriteLine(hashtable.Contains("Area"));

    // Get value of Area with indexer.
    int value = (int)hashtable["Area"];

    // Write the value of Area.
    Console.WriteLine(value);
    }
}

答案 3 :(得分:0)

答案是:这取决于。作为语言设计者,您应该定义什么是可见性(如果定义了变量名,源代码的哪一部分是可用的名称?)和隐藏(如果在另一个对象的可见区域中定义了另一个具有相同名称的对象,哪个名称胜出?)变量规则。不同的语言有不同的规则,只需比较Javascript和C ++。

所以,我会这样做。 (1)引入 namespace 的概念:在源文件的某个点可见的名称列表。 (请注意,这与C ++的命名空间概念不同。)命名空间应该能够将名称解析为某个适当的对象。 (2)当解释器从一个过程更改为另一个过程,从一个文件更改为另一个文件,从一个块到另一个块,看到声明或块结束等时,实现更改名称空间的规则。

这些步骤基本上对大多数语言都有效,而不仅仅是汇编程序。

(我认为,Google对“堆栈分配”的引用是指在一个单独的子例程中处理每个子例程,并在本地重新定义命名空间的概念,因此“在堆栈上”,因此它将在程序时自动弹出饰面。)