编程运行环境
选择DOS方式,但现在一般用win10系统,不能进入DOS,只好安装dosbox代替。将写好的源程序保存为asm文件,打开dosbox编译链接并运行。编译链接程序需另行下载,注意与自己的电脑相对应选择32位或64位。
编程目标
在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串’welcome to masm!’。
相关知识
内存地址空间中,B8000H~BFFFFH共32KB的空间即为80×25彩色字符模式显示缓冲区。写入这个地址空间的内容将会出现在显示器上,显示器可以显示25行,每行80个字符,每个字符可以有256种属性。
一个字符在显示缓冲区中要占两个字节,偶地址存放字符,奇地址存放字符的属性。通常情况下,B8000H~B8F9FH中的4000个字节的内容将出现在显示器上。
分析
- 需要定义存放字符串的内存空间,我们定义数据段来存放字符串,用BX指向其偏移地址。
- 我们需要将数据写入显示缓冲区,要取得其段地址,由上述可知该显示缓冲区的段地址为B800H,用es来存放该段地址。
- 显示器上的一行80个字符要对应显示缓冲区的160个字节,则行首的偏移地址从0000H开始每行增加A0H。
- 在25行80列屏幕中间显示的是3行16列的字符串,则行的偏移地址从6E0H开始,用BP指向行,增量为A0H;列的偏移地址从40H开始,用SI指向列,增量为2。
- 我们不难想到用二重循环来解决这个问题,外层循环3次,内层16次循环,需要注意的是每行显示的属性不同,则在外循环中要改变属性存放在寄存器中。具体见源码注释。
源码示例1
assume cs:code,ds:data,ss:stack
data segment
db 'welcome to masm!'
data ends
stack segment
db 10 dup (0)
dw 02H,24H,71H ;字符的属性:02H为黑底绿字,24H为绿底红字,71H为白底蓝字
stack ends
code segment
start: mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,10 ;将栈顶指向堆栈中存放的字符属性
mov ax,0b800H
mov es,ax ;将显示缓冲区的段地址送入ES
mov bp,6e0H ;BP用来定位显示的行,初始为第11行(从0行记起)
;AL作为中间变量存放字符的属性,注意栈操作只能为字型数据
mov cx,3
s0: mov dx,cx ;DX暂存外循环变量的值
mov bx,0 ;BX用来定位内存的偏移地址
mov si,40H ;SI用来定位显示的列,初始为第64列(从0列记起)
pop ax ;从堆栈中取得字符属性,AL部分有效
mov cx,16
s: mov ah,[bx]
mov es:[bp][si],ah ;存放字符的ASCII码
mov es:[bp][si].1,al ;存放字符的属性
inc bx
add si,2
loop s
add bp,0a0H
mov cx,dx ;DX恢复外循环变量的值
loop s0
mov ax,4c00H
int 21H
code ends
end start
- 汇编源程序的编码安排很灵活,以上程序可以将显示缓冲区的段与存放属性的段相互换。也可以有完全不同的方式,我们需要程序具有修改自身指令并执行的功能,则可以用到几个转移指令。现在我们利用这一点来修改程序数据,详见如下代码。
源码示例2
assume cs:code,ds:data
data segment
db 'welcome to masm!'
db 0 ;定义一个值为0的字节用来结束字符串的读取
data ends
code segment
start: mov ax,data
mov ds,ax
mov ax,0b800H
mov es,ax ;将显示缓冲区的段地址送入ES
mov bp,6e0H ;BP用来定位显示的行,初始为第11行(从0行记起)
mov al,24H ;AL作为中间变量存放字符的属性
; 字符的属性:02H为黑底绿字,24H为绿底红字,71H为白底蓝字
mov cx,3
s0: mov dx,cx ;DX暂存外循环变量的值
mov bx,0 ;BX用来定位内存的偏移地址
mov si,40H ;SI用来定位显示的列,初始为第64列(从0列记起)
s: mov cl,[bx]
mov ch,0
jcxz s2 ;CX内容的值为零跳转,不为零则继续向下执行
mov es:[bp][si],cl ;存放字符的ASCII码
mov byte ptr es:[bp][si].1,02H ;存放字符的属性
s1: inc bx
add si,2
jmp short s
t: add bp,0a0H
mov cx,dx ;DX恢复外循环变量的值
loop s0
s2: mov di,offset s1
dec di
mov cs:[di],al ;将AL的内容复制到程序中更改字符属性
mov al,71H ;修改AL中存放的属性
jmp short t
code ends
end start
以上程序不必考虑字符串的长度,仅仅关心结束标记,利用转移指令修改源程序的某个数值,要做到的是精确定位。
运行结果
编程最重要的还是思想和方法,仔细看这个程序还是有很大缺陷的,关于程序的缺陷以后会详细介绍。