机器码行为模拟器lw指令输出异常问题求助
模拟器说明
我用C语言实现了一款机器码行为模拟器,可接收机器码输入并展示每个执行状态下的内存与寄存器信息。使用教授提供的汇编器将汇编代码转换为机器码,模拟器会将输入的整数当作32位二进制指令处理。
正常测试案例
测试以下汇编代码时,模拟器运行完全正常:
lw 0 1 five load reg1 with 5 (uses symbolic address) lw 1 2 3 load reg2 with -1 (uses numeric address) start add 1 2 1 decrement reg1 beq 0 1 2 goto end of program when reg1==0 beq 0 0 start go back to the beginning of the loop noop done halt end of program five .fill 5 neg1 .fill -1 stAddr .fill start will contain the address of start (2)
对应的输出结果:
total of 17 instructions executed final state of machine: @@@ state: pc 8 memory: mem[ 0 ] 8454151 mem[ 1 ] 9043971 mem[ 2 ] 655361 mem[ 3 ] 16842754 mem[ 4 ] 16842749 mem[ 5 ] 29360128 mem[ 6 ] 25165824 mem[ 7 ] 5 mem[ 8 ] -1 mem[ 9 ] 2 registers: reg[ 0 ] 0 reg[ 1 ] 0 reg[ 2 ] -1 reg[ 3 ] 0 reg[ 4 ] 0 reg[ 5 ] 0 reg[ 6 ] 0 reg[ 7 ] 0 end state
异常测试案例
但测试一段加载两个字并相加的简单汇编代码时,第一条LW指令可正常工作,第二条LW指令无法正确加载目标值:
lw 0 1 five load reg1 with 5 lw 1 2 one load reg2 with 1 start add 0 1 2 adds reg1 with reg2 done halt end of program five .fill 5 one .fill 1 stAddr .fill start will contain the address of start (2)
对应的输出结果:
@@@ state: pc 1 memory: mem[ 0 ] 8454148 mem[ 1 ] 9043973 mem[ 2 ] 65538 mem[ 3 ] 25165824 mem[ 4 ] 5 mem[ 5 ] 1 mem[ 6 ] 2 registers: reg[ 0 ] 0 reg[ 1 ] 5 reg[ 2 ] 0 reg[ 3 ] 0 reg[ 4 ] 0 reg[ 5 ] 0 reg[ 6 ] 0 reg[ 7 ] 0 end state @@@ state: pc 2 memory: mem[ 0 ] 8454148 mem[ 1 ] 9043973 mem[ 2 ] 65538 mem[ 3 ] 25165824 mem[ 4 ] 5 mem[ 5 ] 1 mem[ 6 ] 2 registers: reg[ 0 ] 0 reg[ 1 ] 5 reg[ 2 ] 0 reg[ 3 ] 0 reg[ 4 ] 0 reg[ 5 ] 0 reg[ 6 ] 0 reg[ 7 ] 0 end state
核心simulate函数代码
已确认机器码本身无问题,以下是模拟器的核心simulate函数实现:
void simulate(stateType * state){ int run = 0; int count = 0; int opcode, arg0, arg1, arg2, offsetField; while (!run){ printState(state); opcode = (state->mem[state->pc] & 29360128) >> 22; arg0 = (state->mem[state->pc] & 3670016) >> 19; arg1 = (state->mem[state->pc] & 458752) >> 16; arg2 = (state->mem[state->pc] & 7) >> 0; offsetField = convertNum(state->mem[state->pc] & 65535) >> 0; printf("%d\n",opcode); printf("%d\n",arg0); printf("%d\n",arg1); printf("%d\n",arg2); printf("%d\n",offsetField); switch(opcode) { case HALT: run = 1; ++state->pc; case ADD: state->reg[arg2] = state->reg[arg0] + state->reg[arg1]; ++state->pc; break; case NAND: state->reg[arg2] = ~(state->reg[arg0] | state->reg[arg1]); ++state->pc; break; case LW: state->reg[arg1] = state->mem[state->reg[arg0] + offsetField]; ++state->pc; break; case SW: state->mem[state->reg[arg0] + offsetField] = state->reg[arg1]; ++state->pc; break; case BEQ: if (state->reg[arg0] == state->reg[arg1]){ state->pc = state->pc + offsetField + 1; } else { ++state->pc; } break; case JALR: state->reg[arg1] = state->pc + 1; state->pc = state->reg[arg0]; break; case NOOP: ++state->pc; break; } ++count; } printf("machine halted\n"); printf("total of %d instructions executed\n", count); printf("final state of machine:\n"); printState(state); }
问题排查分析
1. Opcode提取掩码错误
32位指令的操作码(opcode)通常占据**最高6位(bit31bit26)**,但当前代码中使用的掩码`29360128`(十六进制`0x1C00000`)仅覆盖了bit22bit24,仅3位,导致提取的opcode值完全错误,进而引发switch分支匹配错误(比如第二条LW指令被识别为其他指令,跳过了正确的加载逻辑)。
正确的opcode提取代码应为:
opcode = (state->mem[state->pc] & 0xFC000000) >> 26;
2. HALT分支缺少break语句
在switch的HALT分支中,设置run=1并递增pc后未添加break,导致代码会继续执行后续的ADD分支逻辑,错误地修改寄存器值并再次递增pc,引发异常行为。需添加break语句:
case HALT: run = 1; ++state->pc; break; // 添加该语句终止switch分支
3. 指令字段提取逻辑验证
当前代码中arg0、arg1的掩码和移位位数也不符合常规32位指令格式(比如arg0应为bit25bit23,掩码`0x3800000`,右移23位;arg1应为bit22bit20,掩码0x700000,右移20位),需根据目标指令集的格式修正字段提取逻辑。以LW指令格式[opcode(6)][arg0(3)][arg1(3)][offset(16)]为例,正确的字段提取代码应为:
opcode = (state->mem[state->pc] & 0xFC000000) >> 26; arg0 = (state->mem[state->pc] & 0x3800000) >> 23; arg1 = (state->mem[state->pc] & 0x700000) >> 20; offsetField = convertNum(state->mem[state->pc] & 0xFFFF);
内容的提问来源于stack exchange,提问作者Zachary Herman

