当前位置: 首页 > news >正文

《汇编语言》王爽(第四版) 实验10:从屏幕显示到数值转换——构建可复用子程序库

1. 实验10的核心价值与设计思路

王爽老师的《汇编语言》实验10之所以成为经典,关键在于它完整呈现了模块化编程思想在底层开发中的实践路径。这个实验不是简单地让我们写三个孤立的功能,而是通过"显示字符串→防溢出除法→数值转换"的递进关系,教会我们如何构建可复用的底层工具链。

我最初学习时犯过一个典型错误:把三个子程序当作独立任务完成,结果在数值显示环节发现前两个模块的接口设计不兼容。比如防溢出除法子程序返回的商和余数存放位置,与数值转换子程序需要的输入格式不匹配。这让我深刻理解了接口设计的重要性——好的子程序不仅要功能正确,还要为后续调用预留合理的参数传递方式。

实验中最精妙的设计在于寄存器传参约定

  • 显示字符串使用DH(行)、DL(列)、CL(颜色)传递显示属性
  • 防溢出除法通过AX/DX/CX传递被除数和除数
  • 数值转换用AX传递待转换数字,DS:SI指向输出缓冲区

这种一致性让三个子程序可以像乐高积木一样无缝衔接。当我们需要显示一个除法结果时,调用链是这样的:

除法运算 → 数值转换 → 屏幕输出

2. 显示字符串子程序深度解析

2.1 显存映射原理

在80x25彩色文本模式下,屏幕内容对应B8000H-B8F9FH的物理内存。每个字符占用2字节:

+-----+-----+ | ASCII码 | 属性字节 | +-----+-----+

属性字节的低4位控制前景色,高4位控制背景色。比如CL=02H表示绿色前景。

2.2 精确定位算法

计算指定行列的显存偏移地址是关键步骤:

mov ax, 00A0H ; 每行80字符×2字节=160字节(A0H) mul dh ; 行号×160 → 行起始偏移 mov dh, 0 add ax, dx ; 加上列偏移 add ax, dx ; 列号×2(每个字符占2字节)

这个算法比单纯的(dh×160 + dl×2)更高效,因为避免了16位乘法。

2.3 完整代码实现

show_str: push ax push bx push cx push si push es mov ax, 0B800H mov es, ax mov al, 160 mul dh mov bx, ax ; 行偏移 mov al, 2 mul dl add bx, ax ; 行列综合偏移 mov ah, cl ; 保存颜色属性 show_loop: mov cl, [si] jcxz show_end ; 遇到0结束 mov al, cl mov es:[bx], ax ; 写入字符和属性 add bx, 2 inc si jmp show_loop show_end: pop es pop si pop cx pop bx pop ax ret

3. 防溢出除法子程序精要

3.1 溢出问题本质

当被除数的高16位(H) ≥ 除数(N)时,直接使用DIV指令会导致商无法存放在AX中而触发除法错误。例如:

被除数:000F4240H (1,000,000) 除数:000AH (10) H=000FH > N=000AH → 溢出

3.2 数学公式拆解

王爽给出的公式堪称经典:

X/N = int(H/N)×65536 + [rem(H/N)×65536 + L]/N

实际计算分三步:

  1. 计算H/N得到商Q1和余数R1
  2. 计算(R1×65536 + L)/N得到商Q2和余数R2
  3. 最终商=Q1×65536 + Q2,余数=R2

3.3 代码实现技巧

divdw: push bx mov bx, ax ; 保存L到BX mov ax, dx ; AX = H xor dx, dx ; DX = 0 div cx ; H/N → AX=Q1, DX=R1 push ax ; 保存Q1 mov ax, bx ; AX = L div cx ; (R1*65536+L)/N → AX=Q2, DX=R2 mov cx, dx ; 余数存入CX pop dx ; 高16位商来自Q1 pop bx ret

这个实现巧妙地用栈暂存中间结果,避免了使用额外内存变量。

4. 数值显示子程序实战

4.1 进制转换算法

将数字转为十进制字符串需要反复除10取余。例如转换12666:

12666 ÷ 10 = 1266...6 1266 ÷ 10 = 126...6 126 ÷ 10 = 12...6 12 ÷ 10 = 1...2 1 ÷ 10 = 0...1

逆序读取余数得到"12666"。

4.2 栈的妙用

由于余数读取顺序与显示顺序相反,使用栈结构正合适:

dtoc: push bx push cx push dx mov bx, 10 xor cx, cx ; 计数器清零 push_loop: xor dx, dx ; 被除数高16位置零 div bx ; DX:AX ÷ 10 add dl, '0' ; 余数转ASCII push dx ; 入栈 inc cx test ax, ax ; 商是否为0 jnz push_loop pop_loop: pop dx mov [si], dl inc si loop pop_loop mov byte ptr [si], 0 ; 添加结束符 pop dx pop cx pop bx ret

5. 子程序协同工作示例

现在我们组合三个子程序,实现"计算(12666+10000)/10并显示结果":

mov ax, 12666 mov bx, 10000 add ax, bx ; AX=22666 mov cx, 10 xor dx, dx ; 被除数高16位清零 call divdw ; 计算22666/10 → DX:AX=商, CX=余数 mov ax, dx ; 取商高16位(实际为0) call dtoc ; 转换商到字符串 mov ax, 2266 ; 商低16位 call dtoc ; 追加转换 mov dh, 12 ; 第12行 mov dl, 30 ; 第30列 mov cl, 0Ch ; 红底亮黄字 call show_str ; 显示"2266"

这个例子展示了如何通过合理的寄存器传参约定,让多个底层子程序像管道(pipe)一样协同工作。在实际开发中,这种模块化思想可以大幅提升代码复用率。

http://www.cnnetsun.cn/news/3381065.html

相关文章:

  • AI空间认知缺陷解析与协作场景优化方案
  • 边缘AI实战:从PyTorch到STM32的部署全链路
  • Java高性能架构设计方法
  • “不该让AI决定主键”?资深架构师紧急叫停团队Claude部署(附数据库设计辅助安全红线白皮书)
  • 你的设备数据,经得起查吗?JVS物联网平台聊聊工业合规的“可信数据底座“
  • Maven多模块项目构建:从‘parent.relativePath’报错解析到IDE兼容性实战
  • 风电运维新选择 管线探测仪 LY-1000 让地下管线一目了然
  • 从井下信号波动看 鼎讯HM-G2500 如何守护煤矿传输命脉
  • ASC1T34S 1位双电源单向总线收发器——数据手册深度解读与参数分析
  • Python构建安全升级:告别setup.py直接调用,拥抱PEP 517
  • 计算机毕业设计之基于SpringBoot教工绩效管理系统
  • Linux日志系统:systemd-journald与rsyslog配置详解
  • 计算机毕业设计之jsp校友交流论坛的网站
  • YOLOv8 Pose在动物姿态识别中的技术解析与应用实践
  • KataGo围棋AI:从AlphaZero到3D围棋思维的实战部署指南
  • YOLOv8目标检测实战:从数据采集到ONNX部署全流程指南
  • 数据工程师与数据科学家岗位分析方法论
  • Linux日志文件清理与管理的实用技巧
  • UniAR:单一视觉Tokenizer统一多模态理解与生成任务
  • 华为欧拉OpenEuler系统离线部署MySQL 8:从环境适配到生产级配置
  • 计算机毕业设计之jsp网吧管理系统
  • Hermes Agent:从意图理解到自动化工作流的工程实践
  • 纯CSS无缝跑马灯进阶:从原理到实战,打造横竖双向无限轮播
  • 海南超级AI医院:技术架构与医疗效率革命
  • Windows USB设备管理与故障修复技术详解
  • Android集成Unity时SO库加载失败:Gradle配置与解决方案详解
  • PIC18F2458驱动CMT-8540S-SMT蜂鸣器实现嵌入式声音交互
  • [Android] ResumeBuilder-一站式简历制作+满足求职需求
  • C++函数对象与Lambda表达式:从基础原理到STL实战应用
  • 企业如何选AI数字人系统?2026年深度评测与选型方案