一、基础概念
1.1 标志位(C/Z/V/S)
标志位用于记录运算结果的状态,供条件转移指令判断使用。
标志位定义
| 标志位 | 全称 | 含义说明 |
|---|---|---|
| C | Carry 进位 | - 加法:最高位产生进位,C=1;无进位,C=0 - 减法:最高位产生借位,C=1;无借位,C=0 |
| Z | Zero 零 | - 运算结果为 0,Z=1 - 运算结果不为 0,Z=0 |
| V | oVerflow 溢出 | - 有符号数运算结果溢出,V=1 - 有符号数运算结果不溢出,V=0 |
| S | Sign 符号 | - 运算结果为正数(最高位为0),S=0 - 运算结果为负数(最高位为1),S=1 |
标志位示例:二进制加法
0101 (二进制数1) + 0011 (二进制数2) ------- 1000 (二进制和)
- 进位标志位(C):无进位产生,C=0
- 溢出标志位(V):0101(+5)与 0011(+3)均为正数,结果 1000 符号位为 1(负数),发生有符号数溢出,V=1(4 位有符号数范围为 −8~+7,5+3=8 超出上界)
- 零标志位(Z):结果不为 0,Z=0
- 符号标志位(S):结果 1000 符号位为 1,S=1(结果被解释为负数)
进位 (C) 和溢出 (V) 的区别
进位是无符号数运算的溢出,溢出是有符号数运算的溢出,二者相互独立:
- 示例 1:无进位无溢出
text 0011 (3) + 0011 (3) ------- 0110 (6) 正确
- 无进位,
C=0;无溢出,V=0
- 无进位,
- 示例 2:有进位无溢出
text 1111 (-1) + 0111 (7) ------- 1 0110 (6) 正确 --- 进位
- 有进位,
C=1;不产生溢出,V=0
- 有进位,
- 示例 3:有进位有溢出
text 1100 (-4) + 1000 (-8) ------- 1 0100 (截断后为+4,实际结果为-12) 溢出 -- 进位
- 有进位,
C=1;产生溢出:V=1(负数 + 负数 = 正数,违反有符号数运算规则)
- 有进位,
1.2 指令格式与寻址方式
TEC-2008 是 16 位教学计算机,支持单字和双字指令。
指令格式结构
| 8位操作码 | 4位目的寄存器 DR | 4位源寄存器 SR |
| 8位 I/O 端口地址 | |
| 8位相对转移指令偏移量 | |
| ↓ | |
| 立即数 / 直接地址 / 变址偏移量(双字指令的第二个字) | |
说明
- 单字指令:16 位,最高 8 位为操作码;最低 8 位分两种用法:
- 运算指令:拆分为 4 位目的寄存器 + 4 位源寄存器
- IO/转移指令:作为完整 8 位端口地址或转移偏移量
- 双字指令:32 位,第二个字用于存放 16 位立即数、直接地址或变址偏移量。
支持的寻址方式
- 寄存器寻址
- 寄存器间接寻址(
[寄存器],以寄存器值为内存地址) - 立即数寻址
- 直接地址寻址
- 变址寻址
- 相对寻址
- 堆栈寻址
- IO 端口地址
二、完整指令系统
2.1 指令分类
TEC-2008 共支持 29 条基本指令,分为 6 大类:
- 数据移动指令 4 条:
MVRR、MVRD、LDRR、STRR - 堆栈操作指令 4 条:
PUSH、POP、PSHF、POPF - 输入输出指令 2 条:
IN、OUT - 算术逻辑运算指令 11 条:
ADD、SUB、AND、XOR、TEST、CMP、OR、DEC、INC、SHL、SHR - 转移指令 6 条:
JMPA、JR、JRC、JRNC、JRZ、JRNZ - 子程序调用与返回指令 2 条:
CALA、RET
2.2 指令命名规则
| 缩写 | 全称说明 |
|---|---|
| MV | MOVE |
| LD | LOAD |
| ST | STORE |
| SH | SHIFT |
| JMP | JUMP |
| CAL | CALL |
| RET | RETURN |
| JR | JUMP RELATIVE |
| R | REGISTER |
| D | DATA |
| A | ADDRESS |
| F | FLAGS |
2.3 A 组指令
A 组指令为单字运算与相对转移指令,8 位操作码范围:000xxxxx 或 0100xxxx。
注:CZVS 列中,* 表示影响该标志位,. 表示不影响标志位,顺序为 C、Z、V、S。
| 指令格式 | 汇编语句 | 操作数个数 | CZVS | 功能说明 |
|---|---|---|---|---|
00000000 DR SR | ADD DR, SR | 2 | **** | DR ← DR + SR |
00000001 DR SR | SUB DR, SR | 2 | **** | DR ← DR - SR |
00000010 DR SR | AND DR, SR | 2 | .*.. | DR ← DR and SR |
00000011 DR SR | CMP DR, SR | 2 | **** | DR - SR(不存结果,仅更新标志位) |
00000100 DR SR | XOR DR, SR | 2 | .*.. | DR ← DR xor SR |
00000101 DR SR | TEST DR, SR | 2 | **** | DR and SR(不存结果,仅更新标志位) |
00000110 DR SR | OR DR, SR | 2 | .*.. | DR ← DR or SR |
00000111 DR SR | MVRR DR, SR | 2 | .... | DR ← SR |
00001000 DR0000 | DEC DR | 1 | **** | DR ← DR - 1 |
00001001 DR0000 | INC DR | 1 | **** | DR ← DR + 1 |
00001010 DR0000 | SHL DR | 1 | *... | DR, C ← DR * 2(逻辑左移,最高位移入C) |
00001011 DR0000 | SHR DR | 1 | *... | DR, C ← DR / 2(逻辑右移,最低位移入C) |
01000001 OFFSET | JR ADR | 1 | .... | 无条件相对跳转到 ADR |
01000100 OFFSET | JRC ADR | 1 | .... | C=1时相对跳转到 ADR |
01000101 OFFSET | JRNC ADR | 1 | .... | C=0时相对跳转到 ADR |
01000110 OFFSET | JRZ ADR | 1 | .... | Z=1时相对跳转到 ADR |
01000111 OFFSET | JRNZ ADR | 1 | .... | Z=0时相对跳转到 ADR |
2.4 B/D 组指令
B/D 组指令为内存操作、IO、堆栈、子程序等指令,8 位操作码范围:1000xxxx(B组)或 1100xxxx(D组)。
注:CZVS 列中,* 表示影响该标志位,. 表示不影响标志位,顺序为 C、Z、V、S。
| 指令格式 | 汇编语句 | 操作数个数 | CZVS | 功能说明 |
|---|---|---|---|---|
10000000 00000000 ADR(16 位) | JMPA ADR | 1 | .... | 无条件绝对跳转到 ADR |
10000001 DR SR | LDRR DR, [SR] | 2 | .... | DR ← [SR](寄存器间接寻址,从内存加载) |
10000010 I/O PORT | IN I/O PORT | 1 | .... | R0 ← [I/O PORT](从端口读入,固定存入R0) |
10000011 DR SR | STRR [DR], SR | 2 | .... | [DR] ← SR(寄存器间接寻址,写入内存) |
10000100 00000000 | PSHF | 0 | .... | 标志寄存器FLAG入栈 |
10000101 0000 SR | PUSH SR | 1 | .... | 寄存器SR的值入栈 |
10000110 I/O PORT | OUT I/O PORT | 1 | .... | [I/O PORT] ← R0(向端口输出,固定取自R0) |
10000111 DR0000 | POP DR | 1 | .... | DR ← 栈顶出栈数据 |
10001000 DR0000 DATA(16 位) | MVRD DR, DATA | 2 | .... | DR ← DATA(加载16位立即数) |
10001100 00000000 | POPF | 0 | **** | FLAG ← 栈顶出栈数据(恢复全部标志位) |
10001111 00000000 | RET | 0 | .... | 子程序返回 |
11001110 00000000 ADR(16 位) | CALA ADR | 1 | .... | 调用首地址为 ADR 的子程序 |
三、汇编程序设计示例
3.1 例 1:输出单个字符 '6'
功能:在屏幕上输出显示一个字符 '6'。
A 2000 ; 地址从16进制的2000开始(内存RAM区的起始地址) 2000: MVRD R0, 36 ; 把字符'6'的ASCII码送入R0 2002: OUT 80 ; 输出显示字符'6',80为串口数据端口地址 2003: RET ; 每个用户程序都必须用RET指令结束 2004: ; 按回车键即结束源程序的输入过程
3.2 例 2:计算 1 到 10 的累加和
功能:计算 1 到 10 的累加和,运行后用 R 命令查看 R1 中的结果。
A 2060 MVRD R1, 0000 ; 置累加和的初值为0 MVRD R2, 000A ; 最大的加数(10) SUB R3, R3 ; 预置参加累加的数为0 (2065) INC R3 ; 得到下一个参加累加的数 ADD R1, R3 ; 累加计算 CMP R3, R2 ; 判断是否累加完 JRNZ 2065 ; 未完,开始下一轮累加 RET
3.3 例 3:循环输出 ASCII 可打印字符
功能:在显示器屏幕上循环显示 95 个(包括空格字符)可打印字符。
程序代码
A 2000 ; 从内存的2000单元开始建立用户的第一个程序 2000: MVRD R1,7E ; 向寄存器传送直接数,7E是'~'的ASCII码 2002: MVRD R0,20 ; 20是空格的ASCII码,从空格开始输出 2004: OUT 80 ; 通过串行接口输出R0低位字节内容到显示器屏幕 2005: PUSH R0 ; 保存R0寄存器的内容到堆栈中 2006: IN 81 ; 读串口状态端口,bit0=1表示输出完成 2007: SHR R0 ; R0寄存器的内容右移一位,bit0移入进位标志C 2008: JRNC 2006 ; C≠1则输出未完成,继续查询 2009: POP R0 ; 从堆栈中恢复R0寄存器的原内容 200A: CMP R0,R1 ; 比较两个寄存器的内容,相等则Z=1 200B: JRZ 200E ; Z=1则跳转到程序结束 200C: INC R0 ; R0自增,取下一个字符 200D: JR 2004 ; 无条件跳转继续输出 200E: RET ; 子程序返回指令,教学计算机的用户程序必须用RET结束
运行结果
>G 2000
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmno
pqrstuvwxyz{|}~
R0=007E R1=007E R2=20F5 R3=0000 SP=2780 PC=2200 R6=0000 R7=0000 R8=0000
R9=0000 R10=0000 R11=0000 R12=0000 R13=0000 R14=2612 R15=0000 F=11000000
ASCII 码字符集
| L\H | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
|---|---|---|---|---|---|---|---|---|
| 0000 | NUL | DLE | SP | 0 | @ | P | ` | p |
| 0001 | SOH | DC1 | ! | 1 | A | Q | A | q |
| 0010 | STX | DC2 | " | 2 | B | R | b | r |
| 0011 | ETX | DC3 | # | 3 | C | S | c | s |
| 0100 | EOT | DC4 | $ | 4 | D | T | D | t |
| 0101 | ENQ | NAK | % | 5 | E | U | e | U |
| 0110 | ACK | SYN | & | 6 | F | V | f | v |
| 0111 | BEL | ETB | ' | 7 | G | W | G | w |
| 1000 | BS | CAN | ( | 8 | H | X | h | x |
| 1001 | HT | EM | ) | 9 | I | Y | i | y |
| 1010 | LF | SUB | * | : | J | Z | j | z |
| 1011 | VT | ESC | + | ; | K | [ | k | { |
| 1100 | FF | FS | , | < | L | |l | | | |
| 1101 | CR | GS | - | = | M | ] | m | } |
| 1110 | SO | RS | . | > | N | ^ | n | ~ |
| 1111 | SI | US | / | ? | O | _ | o | DEL |
3.4 例 4:数字字符输入过滤
功能:从键盘上键入多个属于 '0' 到 '9' 的数字符并在屏幕上显示,遇非数字符结束程序。
A 2040 IN 81 ; 读串口状态端口,bit1=1表示有键盘输入 SHR R0 ; 第一次右移,bit0移入C SHR R0 ; 第二次右移,bit1移入C JRNC 2040 ; 无输入则循环检测(C=0时跳转) (2044) IN 80 ; 把输入字符读到R0低位字节 MVRD R1, 00FF AND R0, R1 ; 将R0的高位字节清0 MVRD R2, 0030 ; '0'的ASCII码 MVRD R3, 0039 ; '9'的ASCII码 CMP R0, R2 ; 判输入字符是否小于'0' JRNC 2053 ; 小于'0',跳转到程序结束处 CMP R3, R0 ; 判输入字符是否大于'9' JRNC 2053 ; 大于'9',跳转到程序结束处 OUT 80 ; 输出刚输入的数字符 JMPA 2044 ; 转去等待下一个字符输入 (2053) RET
注意:以上代码结构需对照实验讲义核实,(2044) 标签之前的指令地址在笔记中未完整记录。 说明:
CMP实际执行减法(不存结果)。无符号比较时,R0 < R2发生借位,C=0;否则C=1。
3.5 例 5:内存读写与大小写转换
功能:读出指定内存中的大写字母字符,将其显示到屏幕上,转换为小写字母后再写回存储器的原存储单元。
操作步骤:用 E 命令送入 6 个字符 'A'~'F' 到内存 20F0 开始的存储区域中,运行后用 D 命令查看。
主程序
A 2080 MVRD R3, 0006 ; 指定被读数据的个数 MVRD R2, 20F0 ; 指定被读、写数据内存区首地址 (2084) LDRR R0, [R2] ; 寄存器间接寻址,读内存字符到R0 CALA 2100 ; 调用子程序,入口地址为2100 DEC R3 ; 剩余字符数减1 JRZ 208B ; 个数为0则结束程序 INC R2 ; 内存地址自增 JR 2084 ; 循环处理下一个字符 (208B) RET
子程序
A 2100 ; 子程序从2100单元开始 OUT 80 ; 输出R0中的字符 MVRD R1, 0020 ; 大写转小写的偏移量 ADD R0, R1 ; 转换为小写字母 STRR [R2], R0 ; 写回原内存单元 (2105) IN 81 ; 查询串口输出状态 SHR R0 JRNC 2105 ; 输出未完成则等待 RET ; 子程序返回
3.6 例 6:子程序调用:大小写转换
功能:主程序完成从键盘读入字符并显示,调用子程序将大写字母转为小写并再次显示。
主程序
A 2040 2040: IN 81 ; 检测键盘输入 2041: SHR R0 2042: SHR R0 2043: JRNC 2040 2044: IN 80 ; 读取输入字符到R0 2045: OUT 80 ; 显示原字符 2046: PUSH R0 ; 保存R0 2047: IN 81 ; 等待输出完成 2048: SHR R0 2049: JRNC 2047 204A: POP R0 ; 恢复R0 204B: CALA 2050 ; 调用转换子程序 204D: JMPA 2040 ; 循环读取 204F: RET
子程序
2050: MVRD R1,20 ; 大写转小写偏移量 2052: ADD R0,R1 ; 转换字符编码 2053: OUT 80 ; 显示小写字符 2054: RET ; 子程序返回
运行结果
>G 2040 AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz
说明:程序无输入判断逻辑,会持续运行。
3.7 例 7:批量内存读写测试
功能:将 A~F 写入内存 2240 开始的区域,并显示出来。
程序录入(错误版本)
>A 2220
2220: MVRD R3, 6
2222: MVRD R2, 223f
2224: MVRD R1, 40
2226: INC R2
2227: INC R1
2228: STRR R2, R1 ; 错误:寄存器间接寻址必须加[]
^Error
2228: STRR [R2], R1 ; 正确写法
2229: LDRR R0, [R2]
222A: out 80
222B: IN 81
222C: SHR R0
222D: JRNC 202b ; 错误:相对跳转地址超出范围
^Error
222D: JRNC 222b ; 正确写法
222E: DEC R3
222F: JRNC 2226 ; 错误:应使用JRNZ判断循环
2230: RET
运行前内存
>D 2240 2240 0000 0000 0000 0000 0000 0000 0000 0000
运行结果
>G 2220 ABCDEF
运行后内存
>D 2240 2240 0041 0042 0043 0044 0045 0046 0000 0000
3.8 输入输出轮询机制
1. 输出完成等待循环
串口状态定义:81端口bit0=1表示输出完成。
(2028) IN 81 ; 读取串口输出状态 SHR R0 ; bit0移入进位标志C JRNC 2028 ; C=0表示未完成,循环等待
测试建议:去掉这三行后,字符可能输出不完整或乱码。
2. 键盘输入检测循环
串口状态定义:81端口bit1=1表示有键盘输入。
(2044) IN 81 ; 读取键盘输入状态 SHR R0 ; 第一次右移 SHR R0 ; 第二次右移,bit1移入C JRNC 2044 ; C=0表示无输入,循环检测
测试建议:去掉这4行后,程序会直接读取旧数据,无法正常响应按键。
3. PUSH/POP 保护寄存器的作用
OUT 80 输出后需轮询 IN 81,该指令会覆盖R0中的字符数据,因此用堆栈保存恢复:
PUSH R0 ; 保存待显示的字符 IN 81 ; 读取状态,覆盖R0 SHR R0 JRNC 2028 POP R0 ; 恢复原字符
四、程序调试与问题排查
4.1 常见错误与修正
错误示例:跳转指令写错
批量内存读写程序中,将JRNZ写成JRNC,导致循环只执行一次。
错误版本反汇编
>U 2220 2220: MVRD R3, 0006 2222: MVRD R2, 223F 2224: MVRD R1, 0040 2226: INC R2 2227: INC R1 2228: STRR [R2], R1 2229: LDRR R0, [R2] 222A: OUT 0080 222B: IN 0081 222C: SHR R0 222D: JRNC 222B 222E: DEC R3 222F: JRNC 2226 ; 错误:应该用JRNZ 2230: RET
错误原因
DEC R3 后 R3≠0,Z=0;无借位C=1,JRNC判断C=0不成立,不跳转。
修正方法
>A 222F 222F: JRNZ 2226
4.2 其他常见错误
- STRR 指令遗漏括号:寄存器间接寻址必须加
[],错误STRR R2, R1→正确STRR [R2], R1 - 相对跳转地址越界:JR类指令偏移量为8位,超出范围需用JMPA绝对跳转
五、常用命令说明
A 地址:从指定地址开始汇编输入程序U 地址:反汇编指定地址开始的指令D 地址:查看指定地址开始的内存内容E 地址:修改指定地址开始的内存内容G 地址:从指定地址开始运行程序R:查看当前寄存器的状态


评论区
评论加载中...