深入实现逻辑与移位指令:AND、OR、XOR、NOT、SHL、SHR、ROL、ROR。这些指令是位操作的基石,在I/O控制、数据打包、图形编程中无处不在。我们将实现每种指令的实际应用场景。
8位CPU最擅长的事情之一就是位操作。每一个位都有明确的含义——一个LED、一个开关、一个像素。逻辑和移位指令让你精确控制每一位。
// 场景:控制8个LED,每个LED对应GPIO的一个位
// 打开LED 3 (bit3)
LDA R1, $FF07 // 读取GPIO数据寄存器
ORI R1, #0x08 // 设置bit3 = 1
STA R1, $FF07 // 写回GPIO
// 关闭LED 5 (bit5)
LDA R1, $FF07
ANDI R1, #0xDF // 清除bit5 (0xDF = 1101_1111)
STA R1, $FF07
// 翻转LED 0 (bit0)
LDA R1, $FF07
XORI R1, #0x01 // 翻转bit0
STA R1, $FF07
// 测试LED 2是否亮
LDA R1, $FF07
ANDI R1, #0x04 // 提取bit2
JZ led_off // Z=1说明bit2=0(灭)
// LED 2亮
// 组合多个字段
// 高4位=颜色(0-15), 低4位=亮度(0-15)
// 从颜色和亮度合成字节:
SHL R1 // 颜色左移4位
SHL R1
SHL R1
SHL R1
OR R1, R2 // 合并亮度(R2)
// R1 = (color << 4) | brightness
移位是最高效的乘/除2的幂的运算:
// 乘以4 = 左移2次
SHL R1
SHL R1
// 乘以10 = (×8) + (×2)
// R1 × 10
MOV R2, R1 // 保存原始值
SHL R1 // R1 × 2
MOV R3, R1 // R3 = R1 × 2
SHL R1 // R1 × 4
SHL R1 // R1 × 8
ADD R1, R3 // R1 × 8 + R1 × 2 = R1 × 10
循环移位(ROL/ROR)在CRC校验计算中是核心操作:
// 简化的CRC-8计算
// 对每个数据字节:
// 8次循环: ROL data, 如果进位=1则XOR多项式
CRC_LOOP:
ROL R1 // 循环左移,C ← MSB
JNC NO_XOR // 无进位则跳过XOR
XORI R1, #0x07 // CRC-8多项式
NO_XOR:
DEC R2 // 计数器-1
JNZ CRC_LOOP // 循环8次
从一个8位值中提取bit5-bit3(3位字段),写出指令序列。即:result = (value >> 3) & 0x07。
实现一个8位值的位反转(bit0↔bit7, bit1↔bit6, ...)。最少需要几条指令?
用SHL/SHR实现软件UART发送:将一个8位值逐位输出到GPIO的某一位。画出时序图。
达成条件:
奖励:位操作是8位编程的精髓。掌握它们,你就能精确控制硬件的每一个引脚、数据的每一个位。
位操作远不止简单的掩码。以下是一些8位编程中的经典技巧:
在某些性能敏感的场景,可以用位操作代替分支:
// 将R1钳制到0-15范围(无分支)
// 如果R1 > 15,则R1 = 15
LDI R2, #15
CMP R1, R2 // R1 - 15
JNN no_clamp // 如果 < 15, 跳过
MOV R1, R2 // 否则 R1 = 15
no_clamp:
// 用位操作实现取绝对值
// 如果R1是负数(N=1),取反+1
// 如果R1是正数(N=0),不变
// 技巧:利用符号位扩展
// mask = R1[7] ? 0xFF : 0x00
// result = (R1 XOR mask) - mask
// 正数: XOR 0 - 0 = 原值
// 负数: XOR FF - FF = ~R1 + 1 = -R1
计算一个字节中有多少个1(population count):
// 计算 R1 中1的个数,结果在 R2
LDI R2, #0 // 计数器
LDI R3, #8 // 循环8次
COUNT_LOOP:
SHL R1 // 左移,MSB→C
JNC no_count // C=0则不计数
INC R2 // C=1则计数+1
no_count:
DEC R3
JNZ COUNT_LOOP
在优先级编码器等场景中,需要找到最低位的1的位置:
// 找R1中最低位的1的位置,结果在R2
// 技巧:R1 AND (-R1) = 只保留最低位的1
MOV R2, R1
NEG R2 // R2 = -R1 (取反+1)
AND R2, R1 // R2 = R1 & (-R1)
// 现在R2只有一个1,位于最低位
// 接下来用移位计数确定位置
| 场景 | 指令 | 说明 |
|---|---|---|
| 乘以2 | SHL | 无符号乘2 |
| 除以2 | SHR | 无符号除2 |
| 有符号除以2 | 算术右移 | 保持符号位(需要额外逻辑) |
| 串行数据发送 | SHL + C | 逐位输出到GPIO |
| 串行数据接收 | SHR + C | 逐位从GPIO读入 |
| CRC校验 | ROL + 条件XOR | 循环校验计算 |
| 伪随机数 | XOR + SHL/SHR | LFSR线性反馈移位寄存器 |
| 图形旋转 | ROL/ROR | 精灵图旋转 |
| 灰度转换 | 位提取+SHL | 颜色位提取和重组 |
逻辑运算遵循布尔代数定律,理解这些定律是优化代码的关键:
| 定律 | AND形式 | OR形式 |
|---|---|---|
| 交换律 | A & B = B & A | A | B = B | A |
| 结合律 | (A&B)&C = A&(B&C) | (A|B)|C = A|(B|C) |
| 分配律 | A&(B|C) = (A&B)|(A&C) | A|(B&C) = (A|B)&(A|C) |
| 德摩根 | ~(A&B) = ~A|~B | ~(A|B) = ~A&~B |
| 吸收律 | A&(A|B) = A | A|(A&B) = A |
| 互补律 | A&~A = 0 | A|~A = 0xFF |
XOR有很多独特的性质,使它在密码学和图形编程中特别有用:
// XOR交换两个变量(不需要临时变量)
// R1 ← R1 ^ R2
// R2 ← R2 ^ R1 = R2 ^ R1 ^ R2 = R1
// R1 ← R1 ^ R2 = R1 ^ R2 ^ R1 = R2
XOR R1, R2
XOR R2, R1
XOR R1, R2
// 现在 R1 和 R2 的值已经交换!
在通信协议和文件格式中,数据经常以位域的形式打包。逻辑和移位指令是处理位域的主要工具:
// 将两个4位值打包成一个8位字节
// R1 = 高4位, R2 = 低4位
ANDI R1, #0x0F // 确保只有4位
ANDI R2, #0x0F
SHL R1 // 左移4次
SHL R1
SHL R1
SHL R1
OR R1, R2 // 合并 = (high << 4) | low
// 从8位字节解包两个4位值
// R1 = 打包字节
MOV R2, R1 // 复制
ANDI R1, #0x0F // R1 = 低4位
SHR R2 // 右移4次
SHR R2
SHR R2
SHR R2
// R2 = 高4位
| A | B | A&B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
AND用于:清零特定位(掩码)、测试特定位、提取位域
常用掩码:0x01=test bit0, 0x0F=低4位, 0xF0=高4位, 0x7F=去掉bit7, 0xFE=去掉bit0
| A | B | A|B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
OR用于:设置特定位为1、合并位域
常用值:0x01=set bit0, 0x80=set bit7, 0xFF=all bits
| A | B | A^B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
XOR用于:翻转特定位、清零寄存器(A^A=0)、简单加密