【问题标题】:How to assign a value from a pointer to a variable permanently如何从指向变量的指针中永久赋值
【发布时间】:2019-12-21 14:36:45
【问题描述】:

我正在为 Z80 处理器构建一个非常基本的反汇编程序。我的程序首先从二进制文件中读取 8 位指令,并将它们存储在向量 bytes 中。该向量被传递给函数disassemble(vector<int>& bytes),该函数遍历bytes 向量并将每个字节解析为其(它们的)对应指令。每条指令都添加到向量assembly

void disassemble(std::vector<int>& bytes)
{
    std::vector<char*> assembly;
    char instruction[20];
    int xx, yy;
    for (auto it = bytes.begin(); it != bytes.end(); ++it) {
        switch (*it)
        {
            case 0x00: // nop
                assembly.push_back("nop");      
                break;
            case 0x01: // ld bc, $xxyy
                yy = *(++it);
                xx = *(++it);
                sprintf_s(instruction, "ld bc, $%02X%02X",xx,yy);
                assembly.push_back(instruction);
                break;
            default:
                assembly.push_back("Instruction not implemented.");
                break;
        }
    }
    std::cout << "instructions: " << assembly.size() << std::endl;
    for (int i = 0; i < assembly.size(); i++)
    {
        std::cout << assembly.at(i) << std::endl;
    }
}

到目前为止,我已经实现了两条指令:

  • 0x00: 没有
  • 0x01: ld bc, $xxyy(其中yy和xx分别代表接下来的两个字节)

例如,下面的二进制代码: 00 01 0D C9 00 00 01 D5 C5 01 05 5C 00 00 应该反汇编到(我添加的cmets):

nop ; 00
ld bc, $C90D; 01 0D C9
nop ; 00
nop ; 00
ld bc, $C5D5 ; 01 D5 C5
ld bc, $5C05 ; 01 05 5C
nop ; 00
nop ; 00

实际输出:

instructions: 8
nop
ld bc, $5C05
nop
nop
ld bc, $5C05
ld bc, $5C05
nop
nop

如你所见,当代码完成后,所有的 0x01 指令都被解析为ld bc, $5C05。在调试过程中,我观察到assembly 向量填充,并且ld bc, $C90D 被正确解释。但是,当解析下一条 ld 指令时,前一条会更新为ld bc, $C5D5。解析最后的 ld 指令时也会发生同样的情况。

我猜这与将 xx 和 yy 分配给迭代器有关,该迭代器会随着每个新的 ld 指令不断更新。如何在不更新assembly 的元素的情况下增加迭代器?我想坚持使用迭代器而不是使用for(int i=0; i&lt;bytes.size(); i++),因为我以前从未使用过它们并且喜欢学习新事物。但如果这是必须做的,那就这样吧。谢谢!

【问题讨论】:

    标签: c++ pointers vector iterator


    【解决方案1】:

    向量中的每个条目都是相同的——指向instruction 的指针。如果您希望向量中的每个条目不同,则必须 push_back 不同 值。

    最合乎逻辑的解决方法是将assembly 更改为std::vector&lt;std::string&gt;

    【讨论】:

    • 这不是我所期待的罪魁祸首!我将assembly 更改为std::vector&lt;std::string&gt; 并添加了以下内容:assembly.push_back((std::string) instruction); 并且它有效。谢谢!
    【解决方案2】:

    之前的代码只是将每个 ld 指令分配为相同的 char 数组('instruction')。您最后写入该缓冲区的任何指令对于您插入的每个 ld 指令都是相同的(因为它们都引用相同的指令)。一个简单的修复将是沿着这些思路......

    std::vector<std::string> assembly;
    

    但是,由于我过去曾编写过自己的 z80 汇编器和反汇编器,因此我建议您稍作停顿,更好地了解指令集!

    最好将指令集视为一个表格,例如http://clrhome.org/table/

    例如,如果您查看指令 0x40 -> 0x47,您会注意到它们与 0x48 -> 0x4F 惊人地相似(0x50 -> 0x57 等也是如此)。与其拥有 256 个变量的 switch 语句(加上用于扩展操作码和未定义操作码的表),不如尝试识别那些重复的模式,并进行相应的处理。即

    const char* const registers[] = {"B", "C", "D", "E", "H", "L", "(HL)", "A"};
    
    void printLD(uint8_t op)
    {
      assert( op >= 0x40 && op <= 0x7F );
      printf("LD %s, %s\n", registers[(op >> 4) - 4], registers[op & 0xF]);
    }
    void printADD(uint8_t op)
    {
      assert( op >= 0x80 && op <= 0x87 );
      printf("ADD A, %s\n", registers[op & 0xF]);
    }
    void printADC(uint8_t op)
    {
      assert( op >= 0x88 && op <= 0x8F );
      printf("ADC A, %s\n", registers[op & 0xF]);
    }
    

    您会很快注意到指令以 8 块为一组进行组织,因此最好打开第一个半字节,然后再推迟到某个函数,例如

    void printOp(uint8_t op)
    {
      uint8_t hi = op >> 4;
      uint8_t lo = op & 4;
      switch(hi)
      {
      case 0x4:
      case 0x5:
      case 0x6:
      case 0x7: printLD(op); break;
      case 0x8: if(lo < 8) printADD(op) else printADC(op); break;
      }
    }
    

    从长远来看,它将为您节省大量时间,并使调试变得更加容易。 (IIRC 在 Z80 指令集中有大约 1400 个左右的操作码,这将产生一些非常严峻的 switch 语句!)

    【讨论】:

      猜你喜欢
      • 2011-07-06
      • 1970-01-01
      • 2021-03-22
      • 1970-01-01
      • 1970-01-01
      • 2020-07-27
      • 1970-01-01
      • 1970-01-01
      • 2015-03-10
      相关资源
      最近更新 更多