加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Windows > 正文

assembly – 用DOS显示数字

发布时间:2020-12-14 04:00:47 所属栏目:Windows 来源:网络整理
导读:我的任务是编写一个显示我的线性地址的程序 ?程序的PSP.我写了以下内容: ORG 256 mov dx,Msg mov ah,09h ;DOS.WriteStringToStandardOutput int 21h mov ax,ds mov dx,16 mul dx ; - Linear address is now in DX:AX ??? mov ax,4C00h ;DOS.TerminateWithEx
我的任务是编写一个显示我的线性地址的程序
?程序的PSP.我写了以下内容:

ORG     256

        mov     dx,Msg
        mov     ah,09h          ;DOS.WriteStringToStandardOutput
        int     21h
        mov     ax,ds
        mov     dx,16
        mul     dx              ; -> Linear address is now in DX:AX

        ???

        mov     ax,4C00h        ;DOS.TerminateWithExitCode
        int     21h
; ------------------------------
Msg:    db      'PSP is at linear address $'

我搜索了DOS api(使用Ralph Brown’s interrupt list)
并没有找到输出数字的单一功能!
我错过了吗,我该怎么办?

我想在DX中显示数字:AX为十进制数.

解决方法

DOS确实没有为我们提供直接输出数字的功能.
您必须先自己转换数字然后让DOS显示它
?使用其中一个文本输出函数.

显示AX中保存的无符号16位数

在解决转换数字的问题时,有助于了解如何进行转换
?组成数字的数字彼此相关.
我们考虑数字65535及其分解:

(6 * 10000) + (5 * 1000) + (5 * 100) + (3 * 10) + (5 * 1)

方法1:通过降低10的幂来除法

处理从左到右的数字很方便,因为它
?允许我们在提取后立即显示单个数字.

>通过将数字(65535)除以10000,我们获得一位数的商
(6)我们可以直接输出为角色.我们还得到一个剩余部分
(5535)将成为下一步的红利.
>通过将前一步骤(5535)的余数除以1000,我们得到
一个数字商(5),我们可以直接输出为一个字符.
我们还得到一个剩余部分(535),它将成为下一步的股息.
>通过将前一步骤(535)的余数除以100,我们可以直接输出为一个字符.
我们还得到一个剩余部分(35),它将成为下一步的股息.
>通过将前一步骤(35)的余数除以10,我们得到
一个数字商(3),我们可以直接输出为一个字符.
我们还得到一个余数(5),它将成为下一步的红利.
>通过将前一步骤(5)的余数除以1,我们可以直接输出为一个字符.
其余部分总是为0.(避免这个愚蠢的除法为1
需要一些额外的代码)

mov     bx,.List
.a: xor     dx,dx
    div     word ptr [bx]  ; -> AX=[0,9] is Quotient,Remainder DX
    xchg    ax,dx
    add     dl,"0"         ;Turn into character [0,9] -> ["0","9"]
    push    ax             ;(1)
    mov     ah,02h         ;DOS.DisplayCharacter
    int     21h            ; -> AL
    pop     ax             ;(1) AX is next dividend
    add     bx,2
    cmp     bx,.List+10
    jb      .a
    ...
.List:
    dw      10000,1000,100,10,1

虽然这种方法当然会产生正确的结果,但它有一些
缺点:

>考虑较小的数字255及其分解:

(0 * 10000) + (0 * 1000) + (2 * 100) + (5 * 10) + (5 * 1)

如果我们使用相同的5步过程,我们将获得“00255”.那两个领先
零是不受欢迎的,我们必须包含额外的指令才能获得
摆脱他们.
>分隔符随每一步而变化.我们必须存储一个分隔符列表
记忆.动态计算这些分频器是可能的,但介绍了一个
很多额外的分歧.
>如果我们想要应用此方法来显示更大的数字说
32位,我们最终会想要涉及的分歧
真的有问题.

因此方法1是不切实际的,因此很少使用.

方法2:除以const 10

处理从右到左的数字似乎是违反直觉的
?因为我们的目标是首先显示最左边的数字.但就像你要做的那样
发现,它工作得很漂亮.

>通过将数字(65535)除以10,我们得到一个商(6553)
在下一步成为红利.我们还得到了剩下的(5)
不能输出,所以我们必须保存在某个地方.堆栈是一个
方便的地方这样做.
>通过将上一步(6553)的商除以10,我们得到
商(655)将成为下一步的红利.我们也得到了
剩下的(3)我们还不能输出,所以我们必须保存它
某处.堆栈是一个方便的地方.
>通过将前一步(655)的商除以10,我们得到
商(65)将成为下一步的红利.我们也得到了
剩下的(5)我们还不能输出,所以我们必须保存它
某处.堆栈是一个方便的地方.
>通过将上一步(65)的商除以10,我们得到
商(6)将成为下一步的红利.我们也得到了
剩下的(5)我们还不能输出,所以我们必须保存它
某处.堆栈是一个方便的地方.
>通过将前一步骤(6)的商除以10,我们得到
商(0)表示这是最后一个分裂.我们也得到了
剩下的(6)我们可以直接作为角色输出,但是
避免这样做是最有效的,所以我们以前也是如此
将它保存在堆栈中.

此时,堆栈保持我们的5个剩余部分,每个剩余部分是一个数字
?数字在[0,9]范围内.由于堆栈是LIFO(后进先出),所以
?我们首先要POP的值是我们想要显示的第一个数字.我们使用了
?用5个POP分隔循环以显示完整的数字.但在实践中,
?因为我们希望这个例程能够处理具有的数字
?少于5位数字,我们会计算到达时的数字,然后再计算数字
?许多POP.

mov     bx,10          ;CONST
    xor     cx,cx          ;Reset counter
.a: xor     dx,dx          ;Setup for division DX:AX / BX
    div     bx             ; -> AX is Quotient,Remainder DX=[0,9]
    push    dx             ;(1) Save remainder for now
    inc     cx             ;One more digit
    test    ax,ax          ;Is quotient zero?
    jnz     .a             ;No,use as next dividend
.b: pop     dx             ;(1)
    add     dl,"9"]
    mov     ah,02h         ;DOS.DisplayCharacter
    int     21h            ; -> AL
    loop    .b

第二种方法没有第一种方法的缺点:

>因为当商变为零时我们停止,所以从来没有任何问题
丑陋的前导零.
>分隔线是固定的.这很容易.
>将此方法应用于显示更大的数字并且非常简单
这正是下一步的结果.

显示DX:AX中保存的无符号32位数

在8086,需要级联2个分区来划分32位值
?DX:AX 10.
第1分部将高股息(延伸为0)除以产生高
?商.第二分部除以低分红(延伸与分红)
?来自第一师的余数)产生低商数.这是剩下的
?从我们保存在堆栈上的第二个分区.

为了检查DX:AX中的dword是否为零,我在一个划痕中对两个OR进行了OR运算
?寄存器.

我没有计算数字,需要注册,而是选择了sentinel
?在堆栈上.因为这个哨兵得到的值(10)没有任何数字
?有([0,9]),它很好地允许确定显示循环何时必须停止.

除此之外,此代码段与上面的方法2类似.

mov     bx,10          ;CONST
    push    bx             ;Sentinel
.a: mov     cx,ax          ;Temporarily store LowDividend in CX
    mov     ax,dx          ;First divide the HighDividend
    xor     dx,dx          ;Setup for division DX:AX / BX
    div     bx             ; -> AX is HighQuotient,Remainder is re-used
    xchg    ax,cx          ;Temporarily move it to CX restoring LowDividend
    div     bx             ; -> AX is LowQuotient,9]
    push    dx             ;(1) Save remainder for now
    mov     dx,cx          ;Build true 32-bit quotient in DX:AX
    or      cx,ax          ;Is the true 32-bit quotient zero?
    jnz     .a             ;No,use as next dividend
    pop     dx             ;(1a) First pop (Is digit for sure)
.b: add     dl,02h         ;DOS.DisplayCharacter
    int     21h            ; -> AL
    pop     dx             ;(1b) All remaining pops
    cmp     dx,bx          ;Was it the sentinel?
    jb      .b             ;Not yet

显示DX:AX中保存的带符号32位数字

程序如下:

首先通过测试符号位找出带符号的数字是否为负数.
如果是,则取消数字并输出“ – ”字符,但要注意不要
?在此过程中销毁DX:AX中的数字.

该片段的其余部分与无符号数字相同.

test    dx,dx          ;Sign bit is bit 15 of high word
    jns     .a             ;It's a positive number
    neg     dx             ;
    neg     ax             ; | Negate DX:AX
    sbb     dx,0           ;/
    push    ax dx          ;(1)
    mov     dl,"-"
    mov     ah,02h         ;DOS.DisplayCharacter
    int     21h            ; -> AL
    pop     dx ax          ;(1)
.a: mov     bx,10          ;CONST
    push    bx             ;Sentinel
.b: mov     cx,9]
    push    dx             ;(2) Save remainder for now
    mov     dx,ax          ;Is the true 32-bit quotient zero?
    jnz     .b             ;No,use as next dividend
    pop     dx             ;(2a) First pop (Is digit for sure)
.c: add     dl,02h         ;DOS.DisplayCharacter
    int     21h            ; -> AL
    pop     dx             ;(2b) All remaining pops
    cmp     dx,bx          ;Was it the sentinel?
    jb      .c             ;Not yet

我是否需要针对不同数量大小的单独例程?

在需要偶尔显示AL,AX或DX:AX的程序中,您可以
?只包括32位版本,并使用下一个小版本wrappers
?尺寸:

; IN (al) OUT ()
DisplaySignedNumber8:
    push    ax
    cbw                    ;Promote AL to AX
    call    DisplaySignedNumber16
    pop     ax
    ret
; -------------------------
; IN (ax) OUT ()
DisplaySignedNumber16:
    push    dx
    cwd                    ;Promote AX to DX:AX
    call    DisplaySignedNumber32
    pop     dx
    ret
; -------------------------
; IN (dx:ax) OUT ()
DisplaySignedNumber32:
    push    ax bx cx dx
    ...

或者,如果您不介意使用AX和DX寄存器
这个贯穿始终的解决方案:

; IN (al) OUT () MOD (ax,dx)
DisplaySignedNumber8:
    cbw
; ---   ---   ---   ---   -
; IN (ax) OUT () MOD (ax,dx)
DisplaySignedNumber16:
    cwd
; ---   ---   ---   ---   -
; IN (dx:ax) OUT () MOD (ax,dx)
DisplaySignedNumber32:
    push    bx cx
    ...

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读