王爽汇编实验10 子程序3例

本文介绍了三个实用的汇编语言子程序:显示字符串、进行长字除法运算及整数到字符串转换的方法。详细解释了每个子程序的功能、参数传递方式及内部实现逻辑。

子程序1:

                   ;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
                   ;参数:(dh)=行号(取值范围0 ~ 24),(dl)=列号(取值范围0 ~ 79),(cl)=颜色,    

                   ;ds:si指向字符串的首地址
                   ;返回: 无

show_str:   PUSH      AX
                   PUSH      CX
                   PUSH      SI
                   PUSH      BX
                   PUSH      ES
                   PUSH      DI
                   MOV       BL,CL                                         ;记录下颜色参数
                   MOV       AX,0b800h
                   MOV       ES,AX
                   MOV       AX,0A0h                                     ;行号间隔
                   MUL       DH      
                   MOV       DI,AX                                         ;用参数乘以行号间隔,得到目的行偏移地址,结果存在DI中
                   MOV       AX,2                                           ;列号间隔
                   MUL       DL
                   ADD       DI,AX                                          ;用参数乘以列号间隔,得到目的列偏移地址
                                                                                   ;结果加在行号偏移地址中      

show_str1: MOV       CL,[SI] 
                   MOV       CH,0
                   JCXZ      show_str_ok
                   MOV       ES:[DI],CL                                  ;向目标地址写入字符
                   MOV       ES:[DI+1],BL                              ;向目标地址写入颜色
                   INC       SI                                                ;转向下个字符
                   ADD       DI,2
                   JMP       SHORT show_str1                       ;继续下个字符判断
show_str_ok:pop       DI
                      pop       ES
                      pop       BX
                      Pop       SI
                      Pop       CX
                      Pop       AX
                      RET            

 

子程序2:

;名称:divdw
;功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型.
;参数:(ax)=dword型数据的低16位
;     (dx)=dword型数据的高16位
;     (cx)=除数
;返回:(dx)=结果的高16位,
;     (ax)=结果的低16位,
;     (cx)=余数
;使用公式如下:

;X/N=INT(H/N)*65536+[REM(H/N)*65536+L]/N
;X:被除数,范围:0,FFFFFFFF
;N:除数,范围:  0,FFFF
;H:X高16位,范围:0,FFFF
;L:X低16位,范围:0,FFFF
;int():描述性运算符,取商,比如:int(38/10)=3
;rem():描述性运算符,取余数,比如:rem(38/10)=8

 

divdw:          ;子程序开始
      push bx
      push ax
      mov ax,dx         ;被除数为X的高16位
      sub dx,dx          ;因为H取值最大FFFF,而除数取值最大FFFF,所以先设H为32位,高16位为0000
      div cx                 ;H/N,商在AX中,余数在DX中
      mov bx,ax          ;保存H/N的商
      pop ax               ;REM(H/N)取余数在DX中*65536意思是*10000H,那么DX就是被除数中的高16位,
                                ;+L X的低16位,自然存在AX中
      div cx                 ;[REM(H/N)*65536+L]/N 商在AX中,余数在DX中 (ax)=结果的低16位
      mov cx,dx          ;(cx)=余数
      mov dx,bx         ;(dx)=结果的高16位 (H/N的商)
      pop bx              ;还原调用的bx
      ret                    ;子程序结束

 

子程序3:

;名称:dtoc
;功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符.
;参数:(ax)=word型数据,ds:si指向字符串的首地址
;返回:无
;方法:用10 除以(ax),每次余数为一位的值,每次的商是下一次的被除数,直到商为0.
;     每次的商加上30H,为十进制数的ASCII码

 

dtoc:               ;子程序开始
             push dx
             push bx
             push si
             push ax
             push cx
             mov bx,0                              ;计数,一共有多少个字符
dtoc_1: mov cx,10                            ;除数
             sub dx,dx                           ;进行16位的除法,DX存余数
             div cx                                  ;AX存商,DX存余数
             mov cx,ax
             jcxz dtoc_2                        ;如果商为0,除法结束
             add dx,30H                       ;得到十进制数的ASCII码
             push dx                            ;将十进制数的ASCII码入栈,否则直接写入的是逆序字符串
             inc bx
             jmp short dtoc_1
dtoc_2: add dx,30H
             push dx
             inc bx                               ;将最后一个结果数入栈,商为0,余数未知
             mov cx,bx                        ;用于确定LOOP循环次数
dtoc_w: pop ax
             mov ds:[si],al                  ;字符操作
             inc si                               ;最后一次循环SI+1
             loop dtoc_w                    ;字符循环写入ds:si地址
dtoc_ok:mov byte ptr ds:[si],0    ;字符串以0为结尾符
             pop cx
             pop ax
             pop si
             pop bx
             pop dx
             ret          ;子程序结束

<think>我们根据用户要求,需要提供关于汇编语言》实验10的总结或相关内容。参考站内引用内容,特别是引用[2]和引用[3],它们详细描述了实验10的任务和代码实现。实验10要求编写一个子程序,能够在指定位置(行号、列号)用指定颜色显示一个以0结尾的字符串。主要任务:1.在屏幕的指定位置显示字符串。2.字符串以0结尾。3.通过寄存器传递参数:DH=行号(0~24),DL=列号(0~79),CL=颜色属性,DS:SI指向字符串首地址。子程序分析(参考引用[3]的代码):步骤1:计算指定行对应的显存起始地址。-显存地址从B8000H开始(文本模式)。-每行80个字符,每个字符占2个字节(ASCII码+属性),所以每行占160字节(0xA0字节)。-行偏移地址=行号×0A0H步骤2:计算指定列对应的显存地址偏移。-列偏移地址=列号×2(因为每个字符占2字节)步骤3:计算目标地址:显存段地址(0B800H)加上行偏移地址和列偏移地址。步骤4:循环将字符串中的每个字符及其属性写入显存对应位置,直到遇到0结束。需要注意的细节:-需要保存寄存器的值(如cx),因为子程序中可能改变它。-使用乘法指令计算行偏移地址(mul指令,这里用8位乘法,结果在ax中)。-列偏移地址用移位或者乘法(乘以2)实现。根据引用[3]的代码,我们可以写出总结:实验10总结:1.目标:编写一个通用的显示字符串子程序show_str,它可以在屏幕的任意位置以指定颜色显示一个以0结尾的字符串。2.参数传递:DH=行号(0~24)DL=列号(0~79)CL=颜色属性DS:SI=字符串首地址3.显存地址计算:-显存起始段地址:0B800H-每行160字节(0A0H),所以行偏移地址=行号×0A0H-列偏移地址=列号×2-目标地址偏移量=行偏移地址+列偏移地址-因此,目标地址的段内偏移为:DI=(DH*0A0H)+(DL*2)4.子程序流程:(1)计算目标地址的偏移量(存入DI):movax,0B800Hmoves,ax;ES指向显存段moval,0A0Hmuldh;AX=行偏移地址(注意:DH为8位,乘法结果在AX中)movdi,ax;DI=行偏移地址moval,2muldl;AX=列偏移地址(乘以2,因为每个字符占2字节)adddi,ax;DI=行偏移+列偏移(2)循环显示每个字符:put:movcl,[si];取字符movch,0;高位置0jcxzok;如果CX为0,结束(因为字符串以0结尾)moves:[di],cl;将字符放入显存(偶地址)moves:[di+1],cl;这里应该是放入属性,但注意:属性在下一个字节,所以是[di+1]放属性值(由调用者传入的cl,但这里代码有误?)注意:引用[3]中的代码在属性处理上有问题。正确的做法应该是将属性值(即传入的cl)放在每个字符后面的属性字节。但是引用[3]的代码中,在循环体内使用了以下指令:pushcx;保存颜色属性(因为后面要使用cx寄存器,但这里保存的是整个cx,而cl是属性,ch是0?)movcl,[si]movch,0jcxzokmoves:[di],cl;放字符popcx;恢复之前保存的cx(即颜色属性),注意这里弹出的是原来push的cx(包括颜色属性和字符?)moves:[di+1],cl;放属性incsiadddi,2jmpshortput这样写实际上有问题:因为pushcx时,cx的值是调用者传入的颜色属性(在cl中,ch可能是0也可能不是,但调用时我们一般只设置cl)。然后我们在循环体内先保存cx(即颜色属性),然后取字符串字符,判断是否为0,不是则放入字符,然后popcx(此时pop出来的是颜色属性)放入属性位置。但是,在循环中,我们每处理一个字符都会push一次cx(颜色属性)然后pop一次。这样效率不高,且容易出错(因为push和pop必须在同一个循环内配对)。实际上,我们可以优化:在进入循环之前,我们就将颜色属性保存在一个寄存器中(比如bl),然后在循环中直接使用。改进后的代码:movbl,cl;将颜色属性保存到bl中put:movcl,[si]movch,0jcxzokmoves:[di],cl;字符moves:[di+1],bl;属性incsiadddi,2jmpshortput这样避免了循环内频繁push/pop。5.注意事项:-子程序中不要使用固定数值,应该通过参数传递。-要保护寄存器(除了结果寄存器,其他寄存器最好保持原值),在子程序开头和结尾进行入栈和出栈操作。-乘法指令使用:mul指令(8位乘法,被乘数在AL中,乘数为寄存器或内存,结果在AX中)。因此,一个完整的子程序实现(带寄存器保护)如下:show_str:pushaxpushbxpushcxpushdxpushsipushdipushesmovax,0B800Hmoves,ax;设置显存段;计算行偏移moval,0A0H;每行160字节muldh;AX=AL*DH(行号)->行偏移movdi,ax;行偏移存入DI;计算列偏移moval,2muldl;AX=2*DL(列号)adddi,ax;此时DI指向显存中指定位置(偏移地址)movbl,cl;将颜色属性存入BLs:movcl,[si];取字符movch,0jcxzok;若为0,跳出循环moval,[si];也可以直接用al,但这里用moves:[di],[si]不行,因为不能内存到内存moves:[di],al;写入字符moves:[di+1],bl;写入属性incsiadddi,2jmpsok:popespopdipopsipopdxpopcxpopbxpopaxret注意:在子程序中我们保护了所有使用的寄存器(除了ax用于返回值,但这里没有返回值,所以我们保护了所有)。另外,在循环中我们使用了jmps,可以改为:s:movcl,[si]orcl,cl;也可以这样判断是否为0,而不使用chjzokmoves:[di],clmoves:[di+1],blincsiadddi,2jmps或者:s:lodsb;从[si]加载一个字节到al,并si自增oral,aljzokmovah,bl;属性movwordptres:[di],ax;一次写入两个字节(字符和属性)adddi,2jmps这样更高效。总结:实验10是学习汇编语言中屏幕显示操作的重要实验,重点在于掌握显存地址的计算和子程序的设计(参数传递、寄存器保护)。通过本实验,可以加深对接口设计和硬件操作的理解。根据以上总结,我们生成以下相关问题:相关问题:1.为什么在显存中,每个字符需要占用两个字节?2.在显示字符串子程序中,如何计算指定行和列的显存地址?3.实验10子程序设计中,为什么要保存寄存器?如果不保存,可能会产生什么问题?4.在改进版的子程序中,如何避免循环内频繁使用push/pop操作?5.除了使用0作为字符串结束标志,还有哪些常见的字符串结束标志?</think> 以下是汇编语言》实验10的总结报告,综合实验要求、核心知识点和实现细节: ### 实验10总结报告 实验10的核心任务是**编写通用字符串显示子程序**,实现在屏幕指定位置(行号、列号)以指定颜色显示以`0`结尾的字符串,涉及关键知识点如下: #### 一、核心功能需求 1. **接口定义** - 输入参数: - `DH` = 行号(0~24) - `DL` = 列号(0~79) - `CL` = 颜色属性(高4位背景色/低4位前景色) - `DS:SI` = 字符串首地址(以`0`结尾)[^2][^3] - 输出:在文本模式(80×25)下显示字符串 2. **显存映射原理** - 显存起始地址:`0B8000H`(需设置`ES=0B800H`) - 字符存储格式:每个字符占2字节(ASCII码 + 属性字节) - 行偏移计算:每行160字节(80字符×2字节)[^3] #### 二、关键实现步骤 1. **计算显存目标地址** ```nasm mov ax, 0B800H mov es, ax ; ES指向显存段 mov al, 160 ; 每行字节数=160 mul dh ; AX = 行偏移量(行号×160) mov di, ax ; DI=行基址 add di, dl ; 加上列偏移 add di, dl ; 相当于列号×2(每个字符占2字节) ``` 2. **字符循环写入与结束判断** ```nasm show_char: mov cl, [si] ; 取字符 jcxz exit ; 检测到0则退出(CX=0) mov es:[di], cl ; 写入字符ASCII码 mov es:[di+1], bl ; 写入颜色属性(BL=预存的颜色) add di, 2 ; 显存指针+2 inc si ; 字符串指针+1 jmp show_char exit: ret ``` 3. **寄存器保护设计** 子程序需保护现场(尤其`CX`),防止破坏调用者环境[^3]: ```nasm show_str proc push ax push bx push cx push si push di push es ... ; 核心逻辑 pop es pop di ... ; 恢复寄存器 ret show_str endp ``` #### 三、关键技术难点 1. **坐标转换算法** 显存偏移地址 = `行号×160 + 列号×2` - 乘法优化:使用`MUL`指令计算行偏移,避免硬编码地址[^3] 2. **属性与字符分离存储** - 显存中字符与属性连续存储(偶地址=ASCII,奇地址=颜色) - 需预存颜色值(如存入`BL`)避免被覆盖 3. **字符串终止检测** 利用`JRCXZ`指令判断`CX=0`(需先`MOV CH,0`) #### 四、优化方向 1. **减少乘法操作**:预计算行偏移表 2. **增强兼容性**:支持多页显存切换 3. **错误处理**:增加行列号越界检查 > 实验10深入训练了**参数传递、显存操作、子程序设计**等汇编核心能力,是后续开发复杂I/O功能的基础[^1][^2]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值