ITEEDU

关于CX寄存器中所谓"显示字符个数"这个参数的含义前面并未说明,通过这个程序我们对这个参数就能有所了解,这个参数表示待显示的字符所需重复显示的个数。运行这个程序之后还有一个问题值得注意:即当一串大写字母"A"出现在屏幕上后,光标究竟在什么地方?

前面使用0EH功能输出字符串时光标总是在最后一个字符的后面,而09H功能却正相反,光标在第一个字符下面闪烁,也就是说09H功能不会自动移动光标。这就给我们出了这样一个难题:如果需要在屏幕上光标位置处显示一个有属性的字符串,程序DISPSTR.ASM能行吗?

DISPSTR.ASM
        data  segment
              assume    ds:data
         msg  db        'Take care,I want to sleep.',0
        data  ends
        code  segment
              assume    cs:code
        main  proc      far
              mov       ax,data	;初始化DS寄存器
              mov       ds,ax
              mov       bh,0	;显示页号送入BH寄存器
              mov       bl,00010100b	;属性字节送入BL寄存器
              mov       cx,1	;显示一个字符
              mov       si,offset msg	;SI寄存器指向字符串MSG
    outloop:
              lodsb     ;从字符串中取得一个字符
              or        al,al	;已到字符串结尾了吗?
              jz        end_out	;已到结尾,转END_OUT
              mov       ah,09h	;利用10H中断的09H功能
              int       10h	;输出AL寄存器中的字符以及BL中的属性
			
              jmp       outloop	;转至OUTLOOP继续输出下一个字符
    end_out:
              mov       ah,4ch	;结束进程
              int       21h
        main  endp
        code  ends
              end       main

表面看来这个程序是使用0EH功能的翻版,应该没有问题,可运行这个程序后除了能看到一个蓝底红色的句号外并没有看到任何文字,这就是09H功能没有移动光标的结果。其实每个字符都被显示了,只不过它们都显示在同一个地方,所以除了最后的一个句号外我们看不到其它的文字。我们可以在指令"INT 10H"后面插入一个"等待":

;**等待**********************
mov   ah,0    ;*
int   16h    ;*
;****************************

然后运行这个程序,可以看到字符一个个的显示在同一个位置,光标始终不移动。BIOS特别提供了一个功能调用用于移动光标:

功能号:02H
用 途:设置光标位置
参 数:DH=行号(0-24)
DL=列号(0-79)
BH=显示页号
调 用:INT 10H
返 回:无

程序DISPSTR1.ASM就是应用了这个功能修改之后的结果:

DISPSTR1.ASM
        data  segment
              assume    ds:data
         msg  db        'Take care,I want to sleep.',0
         col  db        0		;光标所在列
        data  ends
        code  segment
              assume    cs:code
        main  proc      far
              mov       ax,data	;初始化DS寄存器
              mov       ds,ax
			
              mov       bh,0	;显示页号送入BH寄存器
              mov       dx,0	;设置光标位置
              mov       ah,02h	;移动光标至(0,0)处
              int       10h
  next_line:
              mov       bl,00010100b	;属性字节送入BL寄存器
              mov       cx,1	;每次显示一个字符
              mov       si,offset msg	;SI寄存器指向字符串MSG
    outloop:  lodsb     ;取得一个字符
              or        al,al	;已到字符串结尾了吗?
              jz        end_out	;已到结尾,转END_OUT
              mov       ah,09h	;利用10H中断的09H功能
              int       10h	;输出AL寄存器中的字符
			
              inc       dl	;准备将光标移动到下一列
              mov       ah,2	;移动光标
              int       10h
;*等待***	*********	**************	
              mov       ah,0    ;*
              int       16h    ;*
;********	*********	**************	
              jmp       outloop	;转至OUTLOOP输出下一个字符
    end_out:  inc       byte ptr col	;光标所在列加1
              mov       ah,02h	;选择10H中断的2号功能
              inc       dh	;光标所在行加1
              cmp       dh,24	;已到最底行了吗?
              jz        exit	;已到最底行,转EXIT结束程序
              mov       dl,col	;准备将光标移动到下一行第"COL"列
              int       10h	;移动光标
              jmp       next_line	;在下一行重新输出字符串
       exit:  mov       ah,4ch	;结束进程
              int       21h
        main  endp
        code  ends
              end       main

程序的运行结果倒是很有意思的,不过明眼人一眼就能看出这个程序有一个小"埋伏":它并没有在"当前光标所在位置"处显示字符串。09H功能确实能在当前光标位置处显示字符,但由于程序并不知道当前光标位置究竟在哪儿,所以在程序中无法设定DH和DL寄存器的值。要想解决这个问题,只能想办法找到"当前光标位置",这就要用到10H中断的03H功能。

功能号:03H
用 途:取得当前光标位置
参 数:BH=显示页号
调 用:INT 10H
返 回:DH=当前光标所在行号
DL=当前光标所在列号
CX=光标类型

程序DISPSTR1.ASM才是真正达到"在当前光标位置处显示有属性的字符串"这个目标的程序:

DISPSTR2.ASM
        data  segment
              assume    ds:data
         msg  db        'Take care,I want to sleep.',0
        data  ends
        code  segment
              assume    cs:code
        main  proc      far
              mov       ax,data	;初始化DS寄存器
              mov       ds,ax
			
              mov       bh,0	;显示页号送入BH寄存器
              mov       ah,03h	;利用10H中断的03H功能
              int       10h	;取得当前光标所在位置
			
              mov       bl,00010100b	;字符属性字节送入BL寄存器
              mov       cx,1	;每次显示一个字符
              mov       si,offset msg	;SI寄存器指向字符串
    outloop:
              lodsb     ;从字符串中取得一个字符
              or        al,al	;已到字符串结尾了吗?
              jz        end_out	;已到结尾,转END_OUT
              mov       ah,09h	;利用10H中断的09功能
              int       10h	;输出AL寄存器中的字符
              inc       dl	;光标所在列加1
              mov       ah,2	;利用10H中断的02H功能
              int       10h	;将光标移动到下一列
;*等待*	*********	**************
              mov       ah,0    ;*
              int       16h    ;*
;******	*********	**************	
              jmp       outloop	;转至OUTLOOP输出下一个字符
    end_out:
              mov       ah,4ch	;结束进程
              int       21h
        main  endp
        code  ends
              end       main

这里有必要说明一下返回CX中"光标类型"的含义。大家都知道文本屏幕上的光标的形状是可变的,最明显的就是当我们使用DOS中的编辑器"EDIT"时,在插入状态下光标是一粗横线,而按下"Insert"键后光标会变成一个方块。所以所谓光标类型其实就是指光标的高度。