Assembly 用DOS显示数字

Assembly 用DOS显示数字,assembly,dos,x86-16,integer-division,signed-integer,8086,Assembly,Dos,X86 16,Integer Division,Signed Integer,8086,我的任务是编写一个程序,显示我的地址的线性 程序的PSP。我写了以下内容: ORG 256 mov dx,Msg mov ah,09h ;DOS.WriteStringToStandardOutput int 21h mov ax,ds mov dx,16 mul dx ; ->

我的任务是编写一个程序,显示我的地址的线性 程序的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(使用) 并且没有找到一个函数来输出一个数字! 我错过了吗?我能做什么


我想以十进制显示
DX:AX中的数字。

的确,DOS不提供直接输出数字的功能。
您必须首先自己转换数字,然后让DOS显示它 使用其中一个文本输出函数

显示AX中保留的无符号16位数字 在处理转换数字的问题时,有助于了解 构成数字的数字相互关联。
让我们考虑数字65535及其分解:

(6 * 10000) + (5 * 1000) + (5 * 100) + (3 * 10) + (5 * 1)
(0 * 10000) + (0 * 1000) + (2 * 100) + (5 * 10) + (5 * 1)
方法1:按10的幂递减除法 处理从左到右的数字很方便,因为它 允许我们在提取后立即显示单个数字

  • 通过将数字(65535)除以10000,我们得到一个单位数的商 (6) 我们可以直接输出为一个字符。我们还得到了一个余数 (5535)这将成为下一步的股息

  • 通过将上一步(5535)的余数除以1000,我们得到 我们可以直接作为字符输出的一位商(5)。 我们还得到一个余数(535),它将成为下一步的股息

  • 通过将上一步(535)的余数除以100,我们得到 我们可以直接作为字符输出的一位商(5)。 我们还得到一个余数(35),它将成为下一步的股息

  • 通过将上一步(35)的余数除以10,我们得到 我们可以直接作为字符输出的一位商(3)。 我们还得到一个余数(5),它将成为下一步的红利

  • 通过将上一步(5)的余数除以1,我们得到 我们可以直接作为字符输出的一位商(5)。 在这里,余数将始终为0。(避免这个愚蠢的除以1 需要一些额外的代码)


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

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

    (6 * 10000) + (5 * 1000) + (5 * 100) + (3 * 10) + (5 * 1)
    
    (0 * 10000) + (0 * 1000) + (2 * 100) + (5 * 10) + (5 * 1)
    
    如果我们使用相同的5步流程,我们会得到“00255”。那两个人领先 零是不需要的,我们必须包含额外的指令才能得到 摆脱他们

  • 分隔器随着每一步的变化而变化。我们必须把一份除法器的清单储存在 记忆。动态计算这些分割线是可能的,但会引入 很多额外的部门

  • 如果我们想将这种方法应用于显示更大的数字 32位,我们希望最终,相关部门将 真有问题

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

方法2:除以常数10 处理从右向左的数字似乎违反直觉 因为我们的目标是先显示最左边的数字。但当你即将 找出答案,它工作得很好

  • 通过将数字(65535)除以10,我们得到一个商(6553),该商将 成为下一步的红利。我们还得到了一个余数(5) 现在还不能输出,所以我们必须在某个地方保存。堆栈是一个 这样做很方便

  • 通过将上一步(6553)的商除以10,我们得到 将在下一步中成为红利的商(655)。我们也得到 我们还不能输出的余数(3),因此我们必须保存它 某处。堆栈是这样做的一个方便的地方

  • 通过将上一步(655)的商除以10,我们得到 将在下一步中成为红利的商(65)。我们也得到 我们还不能输出的余数(5),因此我们必须保存它 某处。堆栈是这样做的一个方便的地方

  • 通过将上一步(65)的商除以10,我们得到 将在下一步中成为红利的商(6)。我们也得到 我们还不能输出的余数(5),因此我们必须保存它 某处。堆栈是这样做的一个方便的地方

  • 通过将上一步(6)的商除以10,我们得到 表示这是最后一次除法的商(0)。我们也得到 可以直接输出为字符的余数(6),但是 事实证明,避免这样做是最有效的,因此,与以前一样,我们将 将其保存在堆栈上

在这一点上,堆栈保存了我们的5个余数,每个余数都是一个数字 范围为[0,9]的数字。由于堆栈是后进先出(后进先出),因此 我们将首先弹出的值是我们希望显示的第一个数字。我们使用 用5个
POP
分开循环以显示完整的数字。但实际上,, 因为我们希望这个例程也能够处理 少于5位,我们将在数字到达时进行计数,稍后再进行计数 许多流行音乐

    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,"0"         ;Turn into character [0,9] -> ["0","9"]
    mov     ah,02h         ;DOS.DisplayCharacter
    int     21h            ; -> AL
    loop    .b
第二种方法没有第一种方法的缺点:

  • 因为当商变为零时我们就停止了,所以从来没有任何问题 带难看的前导零
  • 分隔器是固定的。这很容易
  • 将此方法应用于显示较大的数字和数据非常简单 这正是下一步要做的

显示DX:AX中保留的无符号32位数字 在一个由2个除法组成的级联上,需要对中的32位值进行除法
DX:AX
10倍。
第1部分除以高股息(扩展为0)
    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,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, Remainder DX=[0,9]
    push    dx             ;(2) 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     .b             ;No, use as next dividend
    pop     dx             ;(2a) First pop (Is digit for sure)
.c: add     dl,"0"         ;Turn into character [0,9] -> ["0","9"]
    mov     ah,02h         ;DOS.DisplayCharacter
    int     21h            ; -> AL
    pop     dx             ;(2b) All remaining pops
    cmp     dx,bx          ;Was it the sentinel?
    jb      .c             ;Not yet
; 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
    ...
; 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
    ...