📄 一句话概括
用拨码开关按地址把机器码逐字节敲进内存,逐格验收后开跑——数码管一亮,手工造的计算机正式活了。
📝 本节和前面几节的关系
硬件完工、程序通道打通、外设接好——万事俱备,这一节把程序敲进去,让机器跑起第一条指令。
一、敲程序:逐字节装填
测试程序只有 5 个字节:把 0x80 装进寄存器 A -> 把 A 写到端口 2(数码管)-> 暂停。 用拨码开关按地址敲:先拨地址 0x0000,再拨内容 0x3E,按下写入键;地址加一,重复——5 个字节全部进内存。 这一步在第三章会升级成【手工汇编】:那时你会知道 0x3E 这些字节为什么是这些字节。
二、验收:逐格检查
敲完不急着跑:把每个地址的内容读出来逐格核对——写错一个字节,机器就跑飞。 验收通过后按下复位(CPU 指向 0x0000),把时钟切成单步模式——准备逐拍观礼。
三、开跑与调试心法
单步模式下每按一次走一拍:取指 0x3E、取数 0x80、执行 OUT 写端口 2——数码管亮出图案,HALT 收工。 调试心法:机器永远按指令办事,【错的一定是敲进去的某个字节】。错了别慌,回到逐格验收查那张内存表。 这套【装填、验收、单步、定位】的流程,就是一切调试技术的祖先——今天的断点调试,本质还是它。
四、动手 Demo:内存验收单与执行观礼
把 5 字节测试程序的内存验收单打出来,再顺着执行一遍。
# 测试程序: 点亮数码管
mem = {0x0000: 0x3E, 0x0001: 0x80, 0x0002: 0xD3,
0x0003: 0x02, 0x0004: 0x76}
for addr in sorted(mem):
print(hex(addr), format(mem[addr], "08b"), hex(mem[addr]))
print("复位 -> PC=0x0000 -> 单步执行 -> 数码管亮")
实跑输出:
--------------------------------------------------------------------
输入测试程序并调试——逐格检查内存
--------------------------------------------------------------------
程序通过拨码开关按地址逐个字节敲进内存,敲完先【逐格验收】:
测试程序(点亮数码管)装入内存后的样子:
地址 内容(二进制) 内容(十六进制) 这是什么
----------------------------------------------------------------
0x0000 00111110 0x3E 把下一个数装进 A
0x0001 10000000 0x80 要装的数(128)
0x0002 11010011 0xD3 输出到 I/O 端口
0x0003 00000010 0x02 端口 2(数码管)
0x0004 01110110 0x76 暂停(HALT)
逐格验收通过后,按下复位、撤掉步进,时钟一开——
CPU 从 0x0000 开始取指执行,A=0x80 -> 写端口 2 -> 数码管亮出图案
调试心法:错了别慌,把时钟调成单步,一次看一个节拍——
机器永远按指令办事,错的一定是敲进去的某个字节。
0x3E 是【装载寄存器 A】的机器码、0xD3 是【输出到端口】的机器码——第三章手工汇编逐一拆解它们。 HALT(0x76)让 CPU 停拍休息——程序结束要有收尾指令,跟函数要有 return 一个道理。
30 秒实验
- 把 5 字节程序在纸上手工【敲】一遍(写地址、写内容),体验逐字节装填。
- 在任意调试器里打一个断点单步执行——你用的就是本章单步时钟的软件版。
常见误区
- 以为跑错是【机器抽风】——机器不抽风,错的永远是内存里被敲错的那一个字节。
- 跳过逐格验收直接开跑——错了再回头查,花的功夫反而多一倍。
自测三问(点开对答案)
1. 测试程序的 5 个字节分别干什么? —— 装 0x80 进 A(两个字节)、写端口 2(两个字节)、HALT 收工(一个字节)。 2. 为什么必须从 0x0000 开始放程序? —— 复位后 CPU 从 0x0000 取第一条指令,这是硬件约定。 3. 调试四步是什么? —— 装填、逐格验收、单步执行、定位错字节——一切调试技术的祖先。- ☐ 下一章(第三章 体验一次手工汇编)
相关笔记
- ⌨️ 编程原理 · 索引 — 从源码到运行的完整链路在那边
- 💻 计算机是怎样跑起来的 · 索引