上一章我们分别将每一个显示字符打入显示缓冲区,这样太麻烦,每次修改显示不同内容得重写,所以我们这一章设一个专门存放字符串的数据区,当要显示时,再用指令统一取出来。
8086处理器除法指令有两种类型。
div cl ;除以cx寄存器低位的8位cl
div byte [0x0023] ;除以0x0023处的1字节数据
div cx
div word [0x0230]
jmp near start
mytext db 'L',0x07,'a',0x07,'b',0x07,'e',0x07,'l',0x07,' ',0x07,'o',0x07,\
'f',0x07,'f',0x07,'s',0x07,'e',0x07,'t',0x07,':',0x07
number db 0,0,0,0,0
start:
mov ax,0x7c0 ;设置数据段基地址
mov ds,ax
mov ax,0xb800 ;设置附加段基地址
mov es,ax
cld
mov si,mytext
mov di,0
mov cx,(number-mytext)/2 ;实际上等于 13
rep movsw
因为计算机启动时从硬盘启动,那么ROM-BIOS将读取主引导扇区的内容,将它加载到内存地址0x0000:0x7c00处。而每一条mov指令,如果不指定段寄存器的话,都采用"DS指令右移4位+操作数地址"作为目标地址。此处如果将数据段寄存器ds设为0x7c0,则由于这段代码的初始地址就是0x7c00,则可以从这个地方开始,下面自动用ds计算偏移,不用每次都指定地址了。
附加段基址为0xb800,因为文本模式的显示缓冲区首地址就是0xb800,我们要控制movsw指令向显示缓冲区写数据,来显示数据到屏幕上。
上述代码显然可看出,35行为数据段,而718行为代码段。按照数据代码分开储存的原则,这样显然是不对的。但是看到有指令jmp将运行顺序跳过了数据区,则可以。
第3行的**\符号**,应该为不中断换行符,将下一行的内容同样也作为本行,接到后面,这样就不用在同一行中打印太多内容。
movsb以字节为单位传送,movsw以字(2字节)为单位传送。
需要设置以下参数:源数据串地址、目标地址、传送计数、正向/反向传送指定标志。
每次移动后,DI或SI均+1或+2(正向),-1或-2(反向);无论正反,CX均-1。
单纯的movsw或movsb只能执行一次,如果将整个区间全都读入,则需要用rep指令,意为不断重复每一步移动过程,直到CX为0。
;得到标号所代表的偏移地址
mov ax,number
;计算各个数位
mov bx,ax
mov cx,5 ;循环次数
mov si,10 ;除数
digit:
xor dx,dx
div si
mov [bx],dl ;保存数位
inc bx
loop digit
;显示各个数位
mov bx,number
mov si,4
重复转到标号处执行。loop指令做两件事
对于第11行,相当于把余数所在的dl的内容赋值给bx中地址指向的存储单元。注意,如果要用寄存器来提供偏移地址,只能使用BX、SI、DI、BP,使用其他寄存器都是非法的。
自增指令inc,相当于C语言中的++。可以指定目标格式。下面的格式都是合法的。
inc al
inc byte [bx] ;将(默认)DS:BX地址处的内容(字节)+1
inc word [label_a] ;将指定位置处的字+1
和inc相对的指令是指令dec,自减,其他都和inc相同。
cbw: Convert Byte to Word
cwd: Convert Word to Double-word
cbw意思是将AL中的有符号数扩展到整个AX,如AL中为01001111,则cbw之后AX为0000000001001111;如果AL为10001101,则执行cbw后AX为1111111110001101.
swd是将AX中的数扩展到DX:AX中。
INTEL8086处理器只允许以下四种基址变址寄存器组合
[bx+si]
[bx+di]
[bp+si]
[bp+di]
这些组合可以用于任何带有内存操作数的指令,其他像[bx+ax]、[ax+cx]等都是非法的。