【传输篇】地牢里的无情快递员:数据移动指令与方块降临的序曲
前言:总线物流与“手短腿长”的物理限制
在前面的【运算篇】里,我们完成了 4-bit ALU 内部算术与逻辑指令的闭环。通过
ADD/ADC、NOT以及位移指令SHL/SHR,我们构筑了完整的通用计算和图形位掩码机制。然而,在纸面上跑通这些核心算法后,我们必须面对一个更底层的工程现实:如果数据无法跨越内部寄存器的物理边界,所有的计算结果都只是无效的电平振荡。CPU 内部的累加器(A)和暂存器(B、X、Y)算得再精确,只要这些比特不能流向 RAM、ROM 或外设接口,外界对它的运算状态就一无所知。要想让《俄罗斯方块》的字模数据从只读存储器(ROM)加载到活动缓冲区(RAM),并最终投射到负责点阵屏渲染的显存区(VRAM),我们必须引入数据移动指令(Data Movement Instructions)。
在 4-bit 微处理器架构中,这类指令的设计面临着极端的硬件物理限制,我们可以将其总结为:数据总线极窄,而地址总线极宽。
这是整台机器最硬核的架构冲突:
- 数据宽度(手短):由于核心逻辑是 4-bit 架构,我们的累加器、内部数据寄存器以及数据总线(Data Bus),物理宽度只有可怜的 4 位,单次吞吐的数值区间死死限制在
0b0000 - 0b1111(十进制 0-15)。- 寻址空间(腿长):为了容纳《俄罗斯方块》的游戏主逻辑和基础字模,程序计数器(PC)和地址总线(Address Bus)必须达到 12 位,从而撑起
0x000到0xFFF(共 4096 个 Nibbles,折合 2KB)的物理寻址货架。这种“4位数据总线 vs 12位地址总线”的不对称设计,直接决定了底层物流的残酷成本。
在现代高级语言中,一条简单的赋值语句
a = b在编译后往往只需要一个时钟周期。但在我们的 4-bit 硬件环境里,为了搬运区区 4 位(0.5字节)的数据货物,CPU 的译码器和总线控制器必须付出成倍的代价——通过多次时钟周期的连续“取指折返跑”,在总线上分段拼凑出 3 倍于数据长度的 12 位目标地址。这种由于总线复用和多字节指令带来的时钟周期赤字,就是 4-bit 系统为了换取生存领土,向寻址空间交纳的“尊严税”。
本篇作为【传输篇】的开端,我们将从硬件电路与时序成本的角度,彻底拆解微处理器内部的三大核心数据移动律令——
MOV、LDI与LDA/STA。看它们是如何在算力赤字的边缘调配比特,拉开游戏方块真正降临的序曲。
MOV 指令:地牢内部的“分赃艺术”
- 注记符:
MOV - 硬编码:
0x01(对应你代码中的case 0x01) - 类型:数据移动(Data Movement)
- 指令周期:1(1周期取指兼内部电平直导)
- 影响标志:无(纯粹的资产搬运,不触发灵魂审判)
指令格式
在 4-bit ISA 体系中,MOV指令采用 寄存器直接寻址(Register Direct Addressing) 模式:MOV Rd, Rs
- Opcode (4-bit):
0x01标识这是一次内部寄存器之间的数据传送。 - Operand (4-bit):高 2 位指定目标寄存器
Rd(A/B/X/Y),低 2 位指定源寄存器Rs。
指令分析
在 4-bit 架构的严苛约束下,外部总线是极其昂贵的公共资源。每当程序计数器(PC)需要跨越物理边界去翻阅外部 RAM 的货架时,整个系统都不得不放慢时钟的步伐去等待电平稳定。此时,
MOV指令便成了我们手中成本最低、速度最快的资产闪转腾挪工具。
MOV的核心奥义在于它的“纯粹性”。从硬件电路来看,MOV属于典型的组合逻辑直通——当控制单元(CU)译码出0x01时,它只是冷酷地合上了源寄存器输出端与目标寄存器输入端之间的三态门(Buffer)。在区区 1 个时钟周期内,比特在硅片内部的微米级导线上完成了灵魂的复制。它不经过 ALU,不需要进位链的等待,甚至连代表机器情绪的标志位(Flags)也懒得惊动。在《俄罗斯方块》的全局调度中,
MOV扮演着“战术中转站”的角色。由于我们的累加器 A 是全村唯一的合法劳动力(所有算术和位移运算必须以它为主体),这就导致 A 极易陷入严重的“生存过载”。如果每一个中间结果都要用STA运回内存,那算力账本将当场赤字。通过MOV,我们可以把算到一半的方块坐标迅速“偷渡”到 X 或 Y 寄存器中寄宿,让 A 腾出手来应付下一轮肉搏。这是地牢内部不交寻址尊严税的特权阶层。
实战场景
1. 保护现场:给过载的累加器腾出双手
在计算方块消除行的得分时,累加器 A 刚算完“当前消除行数”,紧接着又要去计算“连击翻倍分”。
; 目标:在不惊动内存的情况下,暂存 A 里的消除行数 ; 此时 A = 0011 (假设消了 3 行) MOV B, A ; B = 0011 (厂区内部秘密交接,A 的值复制到 B) LDI A, 2 ; A = 0010 (让 A 腾出手来加载翻倍系数) MUL A, B ; A = 0010 * 0011 = 0110 (最终得分 6,B 中原值完好)这一连串动作展示了MOV如何作为战术缓冲,仅用 1 个周期就完成了现场保护,避免了昂贵的内存读写开销。
2. 跨界跳板:指针拼凑的黄金配角
在 4-bit 机器里,如果要实现间接寻址,我们需要用 X 和 Y 寄存器拼凑成一个高低位指针。MOV是唯一的穿针引线者。
; 假设当前算出的低位显存偏移量在累加器 A 中 (比如 0x05) ; 我们需要把它送入指针寄存器 X MOV X, A ; X = 0005 (X 成功接棒,成为低位指针) LDI Y, 8 ; Y = 1000 (固定高位 0x800,指向显存区) LDX A, [XY] ; 此时 A 盲读出 0x85 地址的显存像素在没有 12 位原生指针的荒原上,MOV让 4 位寄存器之间能够无缝交接,完成了对长腿地址的分布式合围。
3. 无痕备份:不触发审判的影子转移
有时候我们需要复制一份状态快照,但绝不想触发任何条件跳转分支。
; 假设此时 A = 0000,且前一次运算刚好点亮了 Zero 标志位 (Z=1) ; 我们需要把 A 搬走,但接下来的逻辑还要依赖这个 Z 标志 MOV Y, A ; Y = 0000 (数据成功备份) ; 此时硬件状态:Z 标志依然保持为 1! JZ PREVIOUS_ZERO ; 顺利执行基于前一步结果的跳转由于MOV纯粹是物理导线的电平导通,它对状态寄存器(PSW)完全免疫。这种“不留痕迹”的移动,是实现复杂嵌套逻辑时最安全的影子备份。
总结
在 4-bit 的逻辑地牢里,内卷不是一种罪恶,而是一种智慧。 我们利用
MOV指令在几个寄存器之间疯狂倒腾资产,把公共总线的开销压到了零。这种在厂区内部私下分赃的纯内循环,是地牢神机在时钟赤字下跑赢时间的底层保障。
LDI 指令:空手套白狼的“虚空造物”
- 注记符:
LDI - 硬编码:
0x02(对应你代码中的case 0x02) - 类型:数据移动(Data Movement)
- 指令周期:2(1周期取指 + 1周期数据锁存)
- 影响标志:无(纯粹的数字捏造,不惊动灵魂审判)
指令格式
在 4-bit ISA 体系中,LDI指令采用 立即寻址(Immediate Addressing) 模式:LDI Rd, #imm
- Opcode (4-bit):
0x02标识这是一次将立即数强行装入寄存器的操作。 - Operand (4-bit):高 2 位指定目标寄存器
Rd(A/B/X/Y),低 2 位在指令编码层面被复用或紧跟其后,直接携带 4 位的原始数据(#imm)。
指令分析
在传统的计算机原理中,数据应当安分地躺在内存的货架(RAM)里,等待 CPU 拿着地址去提货。但在 4-bit 的赤字账本下,每一次去 RAM 提货都需要耗费巨额的时钟周期。这时候,
LDI指令便成了我们手中空手套白狼的“物质捏造器”。
LDI的本质是一场精妙的“指令流偷渡”。从硬件电路来看,当控制单元(CU)在第一个时钟周期将指令从 ROM 刷进指令寄存器(IR)时,它惊讶地发现,这条指令的下半身居然直接绑着货物!在第二个时钟周期,控制单元根本不需要向总线经理申请内存地址,而是直接拉通了一条内部“暗道”,将指令寄存器低 4 位的物理电平,强行灌进了目标寄存器的锁存器(Latch)中。在《俄罗斯方块》的生存博弈里,
LDI是整场游戏运行的“第一推动力”。无论是初始化方块降落的起始 X 坐标、给循环计数器设定死刑倒计时,还是强行往标志位里刷入初始状态,都必须依赖LDI。然而,虚空造物并非没有代价,在 4 位的物理法律限制下,你用尽全身力气,最多也只能凭空捏出15 (1111b)这一粒微小的尘埃。它是如此的“手短”,以至于我们要想去访问 12 位的长腿内存,必须用好几条LDI去连续捏碎块,才能拼凑出一个完整的远方地址。
实战场景
1. 虚空造物:游戏初始状态的无中生有
当一个新方块在屏幕顶端诞生时,我们必须凭空给它赋予初始的物理坐标。
; 目标:将新方块的起始 X 坐标设为 5,Y 坐标设为 0 ; 此时 RAM 里空空如也 LDI A, 5 ; A = 0101 (虚空捏出 X 坐标 = 5) MOV B, A ; B = 0101 (将 5 暂存到暂存器 B) LDI A, 0 ; A = 0000 (虚空捏出 Y 坐标 = 0) ; 接下来可以通过 STA 将 A 和 B 运往游戏缓冲区这一连串动作展示了LDI如何在不依赖任何前置数据源的情况下,直接在寄存器荒原上建立起物理世界的秩序。
2. 积沙成塔:给 12 位长腿地址“画骨”
我们的寄存器只有 4 位,但 ROM 和外部设备的地址长达 12 位。要想去远方提货,必须用LDI连续拼凑指针。
; 目标:拼凑出地址 0x300(按键输入端口),让 X 和 Y 寄存器合体作为指针 ; 12位地址拆分:高4位=0x3, 中4位=0x0, 低4位=0x0 LDI Y, 3 ; Y = 0011 (高位指针锁死在 0x3 区域) LDI X, 0 ; X = 0000 (低位指针归零) ; 指针 XY 此时合体,合围目标指向 0x300 端口在没有 12 位原生立即数寄存器的绝望世界里,LDI就像搬砖的泥瓦匠,通过多次搬运、层层堆叠,硬生生帮 CPU 搭建起了通往远方外设的桥梁。
3. 刑期裁决:无情循环计数器的死刑宣告
游戏里方块需要随着时间自然下落,或者在消除行时需要进行固定次数的循环扫描。LDI是唯一的“监斩官”。
LOOP_START: ; ... (执行一轮显存扫描或方块消行检测) ... DEC X ; 计数器 X 减 1 JNZ LOOP_START; 如果没减到 0,继续循环在这段经典的循环中,如果最开始不使用LDI X, 10(宣告执行 10 次死刑),计数器就会陷入不可控的未知状态。LDI赋予了程序精准掌控循环生命周期的能力。
总结
在 4-bit 的逻辑地牢里,空手套白狼不是欺骗,而是一种特权。 我们利用
LDI指令绕过了对 RAM 货架的漫长盘问,直接把字面量变成了机器的心跳。它是最锋利的冷兵器,虽然每次只能刺出 4 位远的距离,但其不需要总线跑腿的极高性价比,让它成为了地牢里最不可或缺的生存工具。
LDA / STA 指令:跨越国境线的“残酷折返跑”
- 注记符:
LDA(Load Absolute) /STA(Store Absolute) - 硬编码:
LDA为0x03,STA为0x04(对应你代码中的case 0x03与case 0x04) - 类型:数据移动(Data Movement)
- 指令周期:5(1周期取操作码 + 3周期取 12 位地址 + 1周期读/写内存)
- 影响标志:无(纯粹的异地物流,不引发灵魂审判)
指令格式
在 4-bit ISA 体系中,LDA与STA采用 绝对寻址(Absolute Addressing) 模式。由于地址长达 12 位,在硬件层面它们属于极其奢靡的多字节(多 Nibble)特权指令:
LDA [addr] ; 将 12 位绝对地址内存格位的数据加载至累加器 A
STA [addr] ; 将累加器 A 里的数据强行按入 12 位绝对地址内存格位
指令分析
如果说
MOV是厂区内部的私下分赃,LDI是凭空捏造的物质魔术,那么LDA/STA就是跨越国境线的残酷折返跑。它们是这台地牢神机里最沉重、最挥霍生命、但同时也是唯一能够突破硅片边界、沟通现实世界的“终极物流工具”。请凝视这条指令在硬件电路里长达 5 个周期的悲壮宿命。当控制单元(CU)在第一周期咬下
0x03或0x04的操作码时,它就明白,接下来的 3 个周期,CPU 必须把双手老老实实地背在身后,让程序计数器(PC)在总线上当场跑 3 趟折返跑,像个在狭窄货架间疯狂折返的快递员一样,分批把 12 位长腿地址的碎块(高4位、中4位、低4位)一点一点接过来、锁存住、拼起来。直到第 5 个周期,拼凑完成的物理电平才会轰然撞向外部存储器。75% 的生命,都活生生地葬送在了问路的路上。这种对时钟周期的疯狂压榨,就是 4-bit 机器为了向远方寻址空间索要领土,所付出的最沉重的“尊严税”。然而,即便税率高得令人发指,在《俄罗斯方块》的骨骼机理里,
LDA/STA依然是绝对的无冕之王。因为它是实现 MMIO(内存映射输入输出) 的唯一合法杠杆。在这台机器里,没有独立的 I/O 端口指令,我们通过总线路由,把神圣的 1KB 物理疆域粗暴地割让了出去——0x080 - 0x0DF绑架给了屏幕的 VRAM 显存镜像,0x300绑架给了玩家的物理按键。这意味着,当你执行LDA [0x300]时,CPU 实际上正把触角伸出次元壁,在摸索玩家有没有按下方向键;而当你执行STA [0x080]时,算好的比特方块才真正跨越了空间的隔离,化为了屏幕上亮起的赛博灯火。
实战场景
1. 异地提货:打破次元壁的“触觉感知”
当主游戏循环在等待玩家输入时,CPU 必须定期前往被绑架的0x300端口,打听现实世界中玩家的意图。
; 目标:读取玩家按键,探测是否有下落加速指令 ; 0x300 拆分为 3 个 Nibble 地址:3, 0, 0 LDA [0x300] ; 历经 5 个周期疯狂折返,A 终于拿到了按键状态 ; 此时若 A = 0001b(假设代表向下键被按下) ANI A, 1 ; 用位与指令过滤噪音 JNZ SPEED_DROP; 标志位亮起,方块瞬间进入自由落体这一连串动作展示了LDA如何充当 CPU 延伸到现实世界里的“视神经”,通过 5 周期的痛苦折返,抓回了外界的物理信号。
2. 跨界送货:让方块在 VRAM 显存里“真正降降临”
方块在 CPU 内部无论用ADD算得多么精确,不把结果运到显存区,对玩家来说就是不存在的。STA负责完成最后的临门一脚。
; 假设 A 寄存器里已经算出了当前行第一个像素格子的最终命运:1010b ; 我们需要把它运往 VRAM 显存的开端 0x080(第 1 行第 1 格) STA [0x080] ; 5周期的时钟赤字爆裂开来! ; 4位比特跨越外部总线,直接夺舍了屏幕上的 4 颗 LED 灯这是全篇最动人的一幕。当STA砸向0x080的那一瞬间,死板的指令流终于变成了流动的画面,方块在这一刻跨越了次元壁,真正降临。
3. 矩阵走私:从 ROM 只读密室里的字模搬运
俄罗斯方块有 7 种形态,每种形态的 4 旋转矩阵都死死地冻结在 ROM 固区(例如0x400开端)。我们必须用LDA把它们走私到 RAM 缓冲区里,方块才能开始运动。
; 目标:从 ROM 0x401 地址抠出一个 T 型方块的初始比特皮肤 LDI Y, 4 ; 指针高位 LDI X, 0 ; 指针中位(配合自研的变址加载逻辑,或者直接绝对寻址) LDA [0x401] ; 直接从 ROM 的冰冷固件里提货,A = 0110b (方块字模) STA [0x010] ; 转身又是 5 个周期,将数据倾倒进 RAM 的游戏活动缓冲区这是无情物流网络的最佳写照。数据在 ROM、寄存器、RAM 之间通过LDA和STA完成了惊心动魄的接力,赋予了死代码流动的生命。
总结
在 4-bit 的逻辑地牢里,物流的代价就是生命的流逝。 我们明知道
LDA/STA每跑一趟就要挥霍掉整整 5 个宝贵的时钟周期,但为了让那 200 颗像素点亮,为了让玩家的执念传进硅片,我们必须老老实实地向地址空间交纳这笔“尊严税”。这种在空间隔离与时间赤字之间的悲壮折返,构成了古典微处理器最硬核的工业美学。
🏁 结尾收官整活
本篇【传输篇】到这里,三大无情快递员的底牌已经全部扒干净了。数据移动指令不是什么高端的魔法,它们只是硅片导线上冰冷的开关切换、总线经理不知疲倦的跑腿问路。
但正是由于
MOV的内部合伙、LDI的虚空造物以及LDA/STA的跨界大搬运,我们终于把 4-bit 地牢里的五官、肌肉、血液全部连成了一个活生生的有机体。货已经运到了,显存也划分完了。接下来,我们将迎来整部连载最烧脑、最考验逻辑架构的核心战役。地牢里亮起了灯,但谁来控制方块掉落的节奏?这里没有操作系统,没有时间片轮转,甚至没有哪怕一个微秒的硬件定时器中断。我们到底要如何用一条简陋的状态机分支,去和真实的物理时间死磕到底?
我们下一站【控制篇·终章】:赛博木偶戏,看我们如何冷酷地手搓主游戏循环,让时钟钟摆精确起吊!
