💻 第三章 体验一次手工汇编
📄 一句话概括

手工汇编=查操作码表+摆地址:把每条汇编指令翻成机器码、按字节连续装填,这一步就是汇编器干的事。

📝 本节和前面几节的关系

前三节把语言、寄存器、执行循环都讲清了,这一节全部合起来用:亲手做一次汇编器的活。

一、手工汇编的流程

第一步:查操作码表,把每条助记符指令翻成机器码——3E 80、D3 02、76; 第二步:按字节连续摆放地址,从 0x0000 起——每摆一个字节地址加一; 第三步:逐格核对,装填完成。 整件事一句话:照着表填空,按地址摆位。

二、为什么说这是汇编器干的事

你手工做的两步(翻译+定位),正是汇编器程序自动完成的——它读入汇编源码,输出机器码,还能给出每行的地址。 现代工具链里这一步只是开始:汇编器之后还有链接器(把多个模块拼起来)和装载器(放进内存)。

三、手工汇编的意义

做一遍才知道:高级语言的编译过程再复杂,终究要落到【地址加字节】这一层。 【⚠️ 2026 现状】现在当然不再手敲机器码,但【翻译+定位】这两个动作,一切编译器都在做。

四、动手 Demo:手工汇编器:翻译加定位

把三条汇编指令翻成机器码,再按地址连续装填——两步走完看结果。

# 第一步: 翻译  第二步: 定位
asm = ["LD A, 128", "OUT (2), A", "HALT"]
opcode = {"LD A, 128": [0x3E, 0x80],
          "OUT (2), A": [0xD3, 0x02],
          "HALT": [0x76]}
addr = 0
for ins in asm:
    code = opcode[ins]
    print(hex(addr), ins, "->", " ".join("%02X" % b for b in code))
    addr += len(code)

实跑输出:

--------------------------------------------------------------------
手工汇编器——把人话翻成机器码
--------------------------------------------------------------------

动手把一串汇编指令【人肉翻译】成机器码,再装填进内存:

源程序(汇编):
    1. LD A, 128
    2. OUT (2), A
    3. HALT

逐条汇编结果:
  汇编指令       机器码     长度(字节)
  -------------------------------------
  LD A, 128      3E 80      2
  OUT (2), A     D3 02      2
  HALT           76         1

装填进内存(地址从 0x0000 起连续摆放):
  地址       字节     来源
  -------------------------------
  0x0000     3E       LD A, 128
  0x0001     80       LD A, 128
  0x0002     D3       OUT (2), A
  0x0003     02       OUT (2), A
  0x0004     76       HALT

合计 5 个字节 —— 与第二章手敲进内存的完全一致。
手工汇编的功夫全在查表:操作码查手册、操作数照抄,一条条摆对地址。

输出里每一行左边是地址、右边是机器码——真实汇编器还会生成一张【符号表】记录每个标号的位置。 装填顺序不能乱:0x0000 起连续摆,中间留空会导致 PC 取到空洞。

30 秒实验

  1. 给 opcode 字典再加一条指令(比如 JP 0x0000,机器码 C3 00 00),体会【带地址的指令】要摆多少字节。
  2. 算一算三条指令共占几个字节(答案:5)——与第二章手敲进内存的字节数对照。

常见误区

  1. 以为手工汇编是【背下所有机器码】——不需要,查表即可,关键是流程与地址账。
  2. 以为汇编器只做翻译——它同时负责地址分配(定位),两件事一起干。
自测三问(点开对答案) 1. 手工汇编的两个步骤是什么? —— 查表翻译(助记符到机器码)+按地址定位(连续装填)。 2. 汇编器与我们手工做的是同一件事吗? —— 是——它自动完成翻译与定位,还能输出符号表。 3. 三条指令共占几个字节? —— 5 个:LD A,128 占 2、OUT 占 2、HALT 占 1。

相关笔记