最近重新温习了一下丢下多时的汇编,汇编之美在于:只要遵守游戏规则,你就可以无拘无束,天马行空;汇编之丑在于如果不遵守游戏规则,你就寸步难行,焦头烂额。就像下面一个简简单单的hello world,用任何一种高级于汇编的语言,整个程序数行代码搞定,而汇编却暗藏玄机。程序用masm在windows xp sp3下编译,链接,运行通过。
DATAS SEGMENT
STRING DB 'HELLO, WORLD!'
DATAS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS
START:
MOV AX,DATAS
MOV DS,AX
; 调用字符串显示子程序
mov dh, 10 ; 字符串显示在窗口的第10行
mov dl, 30 ; 字符串显示在窗口的第30列
mov cl, 10011010b ; 颜色属性字节格式:7~0位分别对应“BL R G B I R G B”,及“闪烁-背景(RGB)-高亮-前景(RGB)”
mov si, 0 ; 字符串首字符的偏移地址
CALL show_str
; 程序结束
MOV AH,4CH
INT 21H
; 字符串显示子程序
;传入参数:
; 行号 dh
; 列号 dl
; 颜色属性 cl
; 字符串段地址 ds
; 字符串首字符偏移地址 si
; 返回值 无
show_str: MOV AX, 0
; 备份当前寄存器状态
push ax
push bx
push cx
push dx
push si
push es
; 映射显存起始段基地址给es,便于后面存放显示内容
mov ax,0b800h
mov es,ax
; 计算显示字符其实位置相对于es段基地址的偏移量
dec dh ; 行号从0开始,显示在第n行,在代码上其实是第n-1行
mov al,160 ; 每行显示80个字符,每个字符1个字节,字符后面的颜色属性值占1个字节
mul dh ; 行偏移地址
mov bx,ax
dec dl
mov al,2
mul dl ; 列偏移地址
add bx,ax ; 字符串的总偏移地址
mov al,cl
mov ch,0
cpy : mov cl,[si]
jcxz done ; 当字符串全部显示完毕时,退出,此时cx的值为0
mov es:[bx],cl ;显存偶地址存放字符
mov es:[bx+1],al ;显存奇地址存放颜色属性
add bx,2
inc si
jmp short cpy
done: pop es ; 还原寄存器状态
pop si
pop dx
pop cx
pop bx
pop ax
ret
CODES ENDS
END START
程序的基本逻辑在于两点:
1,计算机系统中,设备内存(如BIOS,显存,各种输入输出设备的缓存)虽然在物理上是独立的,但是在逻辑上都映射在系统内存的地址空间上,CPU在某个存储器地址上进行读写操作,实际上是直接对该地址所映射的物理存储器上进行读写操作。因此,要在屏幕上显示字符串,只要把字符串复制到显存所在的地址空间区域就可以了。
2,在显存中,单个ascii码字符显示需要2个字节,偶数位上的字节是字符本身,后面紧跟的奇数位上保存颜色属性值。