You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

汇编语言编程中如何实现文本居中显示?

汇编语言编程中如何实现文本居中显示?

嘿,我看你在DOS汇编里做菜单居中的时候卡壳了,之前直接硬写光标列号的方法确实不好使——毕竟每个菜单文本的长度都不一样,固定一个列号肯定没法让所有内容都居中。咱换个思路,动态计算每个字符串的居中起始位置,这样不管字符串多长都能自动对齐到屏幕中间,给你具体的实现方案:

核心思路

DOS下默认是80列25行的文本模式,要实现居中:

  1. 先算出当前要输出的字符串长度(因为你的字符串都是用$结尾的,刚好可以遍历到$来统计长度)
  2. 用「屏幕总列数 - 字符串长度」得到左右留白的总宽度,再除以2就是左边需要空的列数(也就是光标要定位的起始列)
  3. 把光标移到这个起始列,再输出字符串就行

具体代码实现

先给你加两个实用的子程序:一个算字符串长度,一个做居中输出,然后把你的菜单输出逻辑改成调用这个居中子程序就行。

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

火山引擎 最新活动