显示字符串
注意:此段代码来自https://blog.csdn.net/qq_29542611/article/details/79329508,本文主要对题目进行解析
从前面我们可以知道,从0b800H开始,写入的低字节是文字,高字节是颜色,可以立刻在屏幕上显示出来。
分析显示和内存中的位置的关系,因为dh中存放了行号,dl中存放了列号,同样从前面在80×25彩色字符模式下,显示器可以显示25行,每行80个字符,每个字符可以有256种属性(背景色、前景色、闪烁、高亮等组合信息)
我们可以知道,对于0b800h的偏移地址是dh*160+dl*2
这个数很容易超过255,所以
然后就可以写代码了
assume cs:code
data segment
db 'Welcome to masm!',0
data ends
code segment
start:
mov dh,8
mov dl,3
mov cl,2
mov ax,data
mov ds,ax
mov si,0
call show_str
mov ax,4c00h
int 21h
;低字节是文字,高字节是颜色
;dh*160+dl*2
show_str:
push ax
push bx
push cx
push dx
push es
push si
push di
mov ax,0b800h
mov es,ax
;乘数一个默认放在ax中
mov al,dh
mov ah,0
;另一个放在8位reg这里是bl中
mov bl,160
mul bl
;结果在ax
mov di,ax
mov al,dl
mov ah,0
mov bl,2
mul bl
add di,ax
mov dl,cl
set_str:
mov cl,[si]
;将下一个读入的字符放入cx
mov ch,0
jcxz ok
mov ax,[si]
mov es:[di],ax
mov es:[di+1],dl
add di,2
inc si
jmp short set_str
ok:
;先出栈
pop di
pop si
pop es
pop dx
pop cx
pop bx
pop ax
ret
code ends
end start
解决除法溢出的问题
assume cs:code,ds:data,ss:stack
data segment
data ends
stack segment
db 16 dup(0)
stack ends
code segment
start:
; 设置被除数
mov dx, 1234H
mov ax, 5679H
; 设置除数
mov bx, 0004H
; 调用函数
call divdw
; 程序返回
jmp finish
; 功能 : 不会产生溢出的 divdw 函数
; 参数 :
; 被除数 : dx 高 16 位 , ax 低 16 位
; 除数 : bx
; 返回 :
; 商 : dx 高 16 位 , ax 低 16 位
; 余数 : cx
; 公式 :
; X / n -> 商 S(32bit) 余 Y(16bit)
; X = H * 65536 + L
; S_H = H / n
; S_L = (rem(H / n) * 65536 + L) / n
; 上一行公式的商为最终结果的低 16 位 , 余数即为最终的余数
divdw:
; 首先计算商的高 16 位
; 注意这里我们的除数是 16 位的
; 如果进行 div 运算的时候默认会将被除数当成 32 位
; 其中高 16 位在 dx 中 , 低 16 位在 ax 中
; 因此首先要将 ax 的值保存起来 , 然后将 dx 的值移动到 ax 中
; 然后将 dx 清零 , 计算完成后还要恢复 ax
push ax
mov ax, dx
xor dx, dx
div bx ; 进行除法运算 , ( ax 保存商 , dx 保存余数 )
; 我们接下来要使用上一个除法运算得到的余数
; 除法运算需要使用 ax , 但是现在 ax 保存着商
; 因此我们需要将 ax 再进行保存
; 这里先使用一个暂时不用的寄存器进行保存 , (si)
mov si, ax ; 将商保存
; 现在 dx 中存放的是被除数高 8 位除以除数得到的余数
; 根据公式 , 下一次的除法运算
; 要将第一次的除法运算的商左移 16 位在加上被除数的低 16 位作为新的被除数
; 但是我们知道 , 在 div 指令中 , 32 位的除法
; 刚好是 dx 中存放高 16 位 , ax 中存放低 16 位 , 因此直接进行除法运算即可
pop ax ; 设置新的被除数的低 16 位 , 也就是旧的被除数的低 16 位数
div bx ; 进行除法运算
; 商保存在 ax 中 , 余数保存在 dx 中
; 这个时候得到的余数就是真正的余数
; 商 就是真正的商的低 16 位 , 而真正的商的高 16 位被我们临时保存在了 si 中
; 现在我们需要将余数存在 cx 中 , 商的高 16 位存在 dx 中 , 然后就可以返回了
mov cx, dx
mov dx, si
ret
finish:
mov ax,4cH
int 21H
code ends
end start
数值显示
;实验10 问题3 数值显示
;编程,将data段中的数据以十进制的形式显示出来。
assume cs:code
data segment
dw 123,12666,1,8,3,38
data ends
;存放word型数据对应的ASCII码,word型数据最大为65536 对应的10进制只有5位
;字符串 0 结尾占一位,所以string 段 用6位就够了
string segment
db 6 dup (0)
string ends
code segment
start:
mov cx,6
mov ax,data
mov ds,ax
mov bx,0
mov dh,8
s:
;下面循环中有更改cs,ds 故将其放入临时区
push cx
push ds
mov ax,ds:[bx]
call dtoc
mov dl,3
mov cl,2
mov ax,string
mov ds,ax
call show_str
inc dh
add bx,2
pop ds
pop cx
loop s
mov ax,4c00h
int 21h
;名称:dtoc
;功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符。
;参数:(ax)=word型数据,ds:si指向字符串的首地址
;返回:si ,修改si的值,si指向字符串的首地址
dtoc:
push ax
push bx
push cx
push dx
push es
push di
mov bx,string
mov es,bx
mov di,5
mov si,di
get_num:
;用32位除 16位 ax存商 dx余数
mov dx,0
mov bx,10
div bx
;将余数转ASCII码值
add dx,48
mov es:[di],dl
dec di
mov cl,al
mov ch,0
jcxz dtoc_ok
mov si,di
jmp short get_num
dtoc_ok:
pop di
pop es
pop dx
pop cx
pop bx
pop ax
ret
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
;参数:(dh)行号(取值范围0~24),(dl)=列号(取值范围0~79),
; (cl)=颜色,ds:si指向字符串的首地址
;返回:无
show_str:
push ax
push bx
push cx
push dx
push es
push si
push di
;显存起始位置,目的地址
mov ax,0b800H
mov es,ax
;确定 di = dh*160+dl*2,ax 、bx、di 没有用户数据被占用 可以进行使用
;mov di,(dh)*160+(dl)*2
mov al,dh
mov ah,0
mov bl,160
mul bl
mov di,ax
mov al,dl
mov ah,0
mov bl,2
mul bl
add di,ax
mov dl,cl ;存放颜色
str_set:
mov cl,ds:[si]
mov ch,0
jcxz show_ok ;控制结束条件
mov ax,ds:[si]
mov es:[di],ax ;设置字母
mov es:[di+1],dl;设置颜色
add di,2
inc si
jmp short str_set
show_ok:
pop di
pop si
pop es
pop dx
pop cx
pop bx
pop ax
ret
code ends
end start