汇编语言编程中如何实现文本居中显示?
汇编语言编程中如何实现文本居中显示?
嘿,我看你在DOS汇编里做菜单居中的时候卡壳了,之前直接硬写光标列号的方法确实不好使——毕竟每个菜单文本的长度都不一样,固定一个列号肯定没法让所有内容都居中。咱换个思路,动态计算每个字符串的居中起始位置,这样不管字符串多长都能自动对齐到屏幕中间,给你具体的实现方案:
核心思路
DOS下默认是80列25行的文本模式,要实现居中:
- 先算出当前要输出的字符串长度(因为你的字符串都是用
$结尾的,刚好可以遍历到$来统计长度) - 用「屏幕总列数 - 字符串长度」得到左右留白的总宽度,再除以2就是左边需要空的列数(也就是光标要定位的起始列)
- 把光标移到这个起始列,再输出字符串就行
具体代码实现
先给你加两个实用的子程序:一个算字符串长度,一个做居中输出,然后把你的菜单输出逻辑改成调用这个居中子程序就行。
1. 字符串长度计算子程序
专门用来统计以$结尾的字符串长度,结果存在cx寄存器里:
; 输入:dx = 字符串的偏移地址 ; 输出:cx = 字符串长度(不含结尾的$) strlen proc push ax push si mov si, dx ; 把字符串地址放到si寄存器,用来遍历 xor cx, cx ; 初始化长度计数器为0 strlen_loop: mov al, [si] ; 取当前字符 cmp al, '$' ; 判断是不是结束符$ je strlen_exit ; 是就退出循环 inc cx ; 不是就长度+1 inc si ; 指针移到下一个字符 jmp strlen_loop strlen_exit: pop si pop ax ret strlen endp
2. 居中输出子程序
调用上面的长度计算子程序,动态算出光标位置,然后输出字符串:
; 输入:dx = 要输出的字符串偏移地址 ; 功能:将字符串居中输出到当前行 center_print proc push ax push bx push cx push dx ; 先把用到的寄存器压栈保存,避免破坏原有数据 call strlen ; 先算字符串长度,结果存在cx里 mov ax, 80 ; DOS默认屏幕总列数是80,如果是40列就改成40 sub ax, cx ; 总列数 - 字符串长度 = 左右留白的总宽度 shr ax, 1 ; 右移一位相当于除以2,得到左边的留白列数 mov dl, al ; 把起始列号放到dl寄存器(供光标定位中断用) mov ah, 02h ; 调用BIOS的10h中断,功能号02h是设置光标位置 mov bh, 0 ; 页号设为0(DOS下一般只用页0) int 10h pop dx ; 恢复之前保存的字符串偏移地址 mov ah, 09h ; 调用DOS的21h中断,功能号09h是输出字符串 int 21h pop cx pop bx pop ax ; 恢复寄存器 ret center_print endp
3. 修改你的菜单输出逻辑
把原来直接调用int 21h输出的代码,改成调用上面的center_print子程序就行:
displaymenu: call centerclear ; 保留你原来的清屏子程序 ; 用居中输出代替直接输出 lea dx, labelmenu call center_print lea dx, labelcal call center_print lea dx, labelconv call center_print lea dx, labelext call center_print
额外优化(可选)
如果想让代码适配不同的屏幕列数(比如有些老系统是40列),可以加个获取当前屏幕列数的子程序,代替硬写的80:
; 输出:ax = 当前屏幕的总列数 get_screen_cols proc push bx mov ah, 0fh ; BIOS 10h中断的0fh功能是获取当前视频模式 int 10h ; 返回值:al=每行列数,ah=当前视频模式,bh=当前页 xor ah, ah ; 把ah清零,让ax里只存列数 pop bx ret get_screen_cols endp
然后把center_print里的mov ax,80改成call get_screen_cols就行。
为啥你之前的方法不行?
你之前直接写mov dl,39是80列的中间位置,但这个位置只有当字符串长度为1的时候才会完全居中——比如你的MAIN MENU长度是9,那起始列应该是(80-9)/2=35.5,取整35或者36才对,硬写39肯定会偏右,所以动态计算才是正确的路子。
备注:内容来源于stack exchange,提问作者D Ex




