汇编语言有关在屏幕区显示字符的四种方法(推荐)
李忠老师的《x86汇编语言:从实模式到保护模式》中第五章到第七章的部分,每一章在讲述知识点的同时,分别使用了三种不同的显示字符的方法,加上调用BIOS的10h中断的方法,这里做出一次简单梳理:
一:第五章,最基础的直接用mov的方法
代码如下:
;代码清单5-1 ;文件名:c05_mbr.asm ;文件说明:硬盘主引导扇区代码 ;创建日期:2011-3-3121:15 movax,0xb800;指向文本模式的显示缓冲区 moves,ax ;以下显示字符串"Labeloffset:" movbyte[es:0x00],'L' movbyte[es:0x01],0x07 movbyte[es:0x02],'a' movbyte[es:0x03],0x07 movbyte[es:0x04],'b' movbyte[es:0x05],0x07 movbyte[es:0x06],'e' movbyte[es:0x07],0x07 movbyte[es:0x08],'l' movbyte[es:0x09],0x07 movbyte[es:0x0a],'' movbyte[es:0x0b],0x07 movbyte[es:0x0c],"o" movbyte[es:0x0d],0x07 movbyte[es:0x0e],'f' movbyte[es:0x0f],0x07 movbyte[es:0x10],'f' movbyte[es:0x11],0x07 movbyte[es:0x12],'s' movbyte[es:0x13],0x07 movbyte[es:0x14],'e' movbyte[es:0x15],0x07 movbyte[es:0x16],'t' movbyte[es:0x17],0x07 movbyte[es:0x18],':' movbyte[es:0x19],0x07 movax,number;取得标号number的偏移地址 movbx,10 ;设置数据段的基地址 movcx,cs movds,cx ;求个位上的数字 movdx,0 divbx mov[0x7c00+number+0x00],dl;保存个位上的数字 ;求十位上的数字 xordx,dx divbx mov[0x7c00+number+0x01],dl;保存十位上的数字 ;求百位上的数字 xordx,dx divbx mov[0x7c00+number+0x02],dl;保存百位上的数字 ;求千位上的数字 xordx,dx divbx mov[0x7c00+number+0x03],dl;保存千位上的数字 ;求万位上的数字 xordx,dx divbx mov[0x7c00+number+0x04],dl;保存万位上的数字 ;以下用十进制显示标号的偏移地址 moval,[0x7c00+number+0x04] addal,0x30 mov[es:0x1a],al movbyte[es:0x1b],0x04 moval,[0x7c00+number+0x03] addal,0x30 mov[es:0x1c],al movbyte[es:0x1d],0x04 moval,[0x7c00+number+0x02] addal,0x30 mov[es:0x1e],al movbyte[es:0x1f],0x04 moval,[0x7c00+number+0x01] addal,0x30 mov[es:0x20],al movbyte[es:0x21],0x04 moval,[0x7c00+number+0x00] addal,0x30 mov[es:0x22],al movbyte[es:0x23],0x04 movbyte[es:0x24],'D' movbyte[es:0x25],0x07 infi:jmpnearinfi;无限循环 numberdb0,0,0,0,0 times203db0 db0x55,0xaa
这里采用的最基础的做法,就是对字符进行一个一个的处理。先将显示缓存区的地址0xb800赋给es寄存器,然后通过movbyte[es:0x00],'L'的形式,来处理后续的字符。这种方法较为简单,这里不再赘述。
二:第六章,采用了批量处理的方法
代码如下:
;代码清单6-1 ;文件名:c06_mbr.asm ;文件说明:硬盘主引导扇区代码 ;创建日期:2011-4-1222:12 jmpnearstart mytextdb'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,'',0x07,'o',0x07,\ 'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07 numberdb0,0,0,0,0 start: movax,0x7c0;设置数据段基地址 movds,ax movax,0xb800;设置附加段基地址 moves,ax cld movsi,mytext movdi,0 movcx,(number-mytext)/2;实际上等于13 repmovsw ;得到标号所代表的偏移地址 movax,number ;计算各个数位 movbx,ax movcx,5;循环次数 movsi,10;除数 digit: xordx,dx divsi mov[bx],dl;保存数位 incbx loopdigit ;显示各个数位 movbx,number movsi,4 show: moval,[bx+si] addal,0x30 movah,0x04 mov[es:di],ax adddi,2 decsi jnsshow movword[es:di],0x0744 jmpnear$ times510-($-$$)db0 db0x55,0xaa
这里采用的办法是批量传送,后续用loop循环挨个处理。这样的写法明显比上一种写法要高明一些,减少了工作量。这段代码中值得注意的地方是 movsi,mytext (其中mytext是声明的字符的地址),这里值得留意的原因之一是在做显示时间的编码中,有过下列这样的写法,所以会格外的留心。
org7c00h start1: movax,cs;置其他段寄存器值与CS相同 movds,ax;数据段 moves,ax movbl,10h movbp,Message1 movah,02h int1ah xorax,ax moval,ch divbl addal,0x30 mov[es:bp+2],al addah,0x30 mov[es:bp+3],ah xorax,ax moval,cl divbl addal,0x30 mov[es:bp+5],al addah,0x30 mov[es:bp+6],ah xorax,ax moval,dh divbl addal,0x30 mov[es:bp+8],al addah,0x30 mov[es:bp+9],ah movdh,3 movdl,0 movax,1301h;功能号 movbp,Message1 movcx,MessageLength1 movbx,0007h int10h ;ret Message1: db'00:00:00' MessageLength1equ($-Message1) times510-($-$$)db0;用0填充引导扇区剩下的空间 db55h,0aah;引导扇区结束标志
(上面的那段代码的功能是调用BIOS中断显示系统时间)这段代码中对于“00:00:00”的处理方法,代码二中批量处理si处的mytext字段有异曲同工之妙,这里mark一下。
关于代码二中显示数字的方法,是用到了loop循环。先将数字按照“除以10”的方法得到每一位的值,然后将其加上0x30(有关ASCII的知识可解释这一点是为什么),然后将最终值赋予 依次递增的显存地址对应的内容,直到将之前处理的每一位数字都显示出来,over.
三:第七章,使用栈来操作
这一章的代码的特殊之处在于通过将字符串按照一个一个的顺序分别取到之后,将其按照顺序压栈,然后再依次出栈再处理而显示。
;代码清单7-1 jmpnearstart messagedb'1+2+3+...+100=' start: movax,0x7c0;设置数据段的段基地址 movds,ax movax,0xb800;设置附加段基址到显示缓冲区 moves,ax ;以下显示字符串 movsi,message movdi,0 movcx,start-message @g: moval,[si] mov[es:di],al incdi movbyte[es:di],0x07 incdi incsi loop@g ;以下计算1到100的和 xorax,ax movcx,1 @f: addax,cx inccx cmpcx,100 jle@f ;以下计算累加和的每个数位 xorcx,cx;设置堆栈段的段基地址 movss,cx movsp,cx movbx,10 xorcx,cx @d: inccx xordx,dx divbx ordl,0x30 pushdx cmpax,0 jne@d ;以下显示各个数位 @a: popdx mov[es:di],dl incdi movbyte[es:di],0x07 incdi loop@a jmpnear$ times510-($-$$)db0 db0x55,0xaa
对于代码段四,第一部分显示“1+2+3+4+...+100=”的部分是沿用了上面的代码二中的做法,使用loop循环处理。
而下面处理数字的部分,是一种新的处理方式。这里是将数字依次“除以10”得到每一位的数之后,将其加上0x00(原因:ASCII显示字符需要)压入栈中,然后在下一个循环中,依次出栈并且处理使得其能够显示出来。
四:调用BIOS的10h中断来显示字符
以上,无论是最简单的mov的做法,还是movbw的做法,异或压栈出栈的做法,都难免分别处理每一个字符的圈子。这里介绍一种调用BIOS中断的做法,直接处理一串字符串,较为简单,可参考性高。
org07c00h;告诉编译器程序加载到7c00处 movax,cs movds,ax moves,ax callDispStr;调用显示字符串例程 jmp$;无限循环 DispStr: movax,BootMessage movbp,ax;es:bp=串地址 movcx,16;cx=串长度 movax,01301h;ah=13,al=01h movbx,000ch;页号为0(bh=0)黑底红字(bl=0Ch,高亮) movdl,0 int10h;10h号中断 ret BootMessage: db"Hello,OSworld!" times510-($-$$)db0;填充剩下的空间,使生成的二进制代码恰好为 dw0xaa55;结束标志
这里的做法是调用BIOS的10h中断来显示“Hello,OSworld!”,其中bp为字符串地址,cx为串长度,ah为功能号,al指示光标置于串尾,bx指示页号为0然后字符显示属性为黑底红字,dh为行号,dl为列号(如果不做处理的话,默认dh,dl皆为0,即在第0行第0列显示),参数设置完之后则调用10h中断显示字符串。
总结:以上的四种方法,通过学习不仅了解显示的方法,更重要的是对汇编语言有了更多的认识。以上方法在实际操作中介于方便与否,大多采用的直接调用BIOS的10h中断来操作。
以上所述是小编给大家介绍的汇编语言有关在屏幕区显示字符的四种方法,希望对大家有所帮助!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。