转换十六进制/十进制数(汇编-TASM)

3
我试图简单地按顺序打印数字,即 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,使用循环,首先我将每个数字转换为十六进制并打印,然后将其重置为十进制,递增 1,然后打印下一个数字,直到数字等于 9。当数字等于 9 时,我使用 DAA 简化数字,并在旋转和移位数字后最终将结果存储在字符串中。
输出在 16 之前都很好,但在 16 之后,序列会重复,
期望的输出是:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
当前输出是:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,11,12,13,14,15
为什么会这样呢?
以下是我的代码:
MOV CX,20 ;Number of Iterations


MOV DX,1



L1:
    PUSH DX
    ADD DX,30H  
    MOV AH,02H        ;PRINT Content of DX
    INT 21H
    POP DX
    ADD DX,1
    CMP DX,09d        ;If number is Greater than 9 jump to L2   
    JA L2
LOOP L1


    L2: 
        PUSH DX
        MOV AX,DX
        DAA           ;Convert to the Decimal
        XOR AH,AH         ;SET AH to 0000


        ROR AX,1    
        ROR AX,1    
        ROR AX,1    
        ROR AX,1    

        SHR AH,1
        SHR AH,1
        SHR AH,1
        SHR AH,1

        ADC AX,3030h
        MOV BX,OFFSET Result
        MOV byte ptr[BX],5           ; Length of the String
        MOV byte ptr[BX+4],'$'       ;5th position of string , $=Terminator
        MOV byte ptr[BX+3],AH        ;2nd Number onto 4th position
        MOV byte ptr[BX+2],AL        ;3rd number onto 3rd Position 

        MOV DX,BX
        ADD DX,02     ;1st 2 positions of String are type of string and    
                                  length respectively 
        MOV AH,09H ;to print the string
        INT 21H         

        POP DX      
        ADD DX,1

    LOOP L2

MOV AH,4CH  ;Return control to the DOS
INT 21H

附注:我参考了这张图表来理解数字。

http://www.cheat-sheets.org/saved-copy/ascii.png


1
你应该压缩你的代码,例如使用 ror ax,4 一次而不是四次 ror ax,1。此外,并不是每个人都知道中断调用的作用(我从未在DOS下进行汇编),如果您在这些行中写入简短的注释,那么了解代码的功能将更容易。请尽可能删除多余的空行。空行对于可读性、分隔逻辑代码块非常有用,但是连续多个空行会使代码清单过长。 - IdiotFromOutOfNowhere
1
我正在x64中使用DosBox,所以我认为这就是为什么我不能直接使用ROR、4或SHR、4的原因,我必须分别使用它们。同时,已经添加了注释。 - micheller
1
如果我没记错的话,你正在使用TASM,是吗?可能是汇编器的缺陷,因为我的英特尔手册上说,立即旋转/移位也适用于16位模式。不管怎样,mov word ptr [BX + 2],AX怎么样?;-) - IdiotFromOutOfNowhere
3个回答

4

8086代码只允许在移位和旋转时使用立即数1(或cl)来计数。要启用286代码,请在文件顶部告诉Tasm“ .286”。这只是一个猜测。

我记得以前打印al中的两位数字的方法:

aam
add ax, 3030h
xchg al, ah
int 29h
mov al, ah
int 29h

3

虽然我不确定,并且无法快速测试,但我想试一试。

但是,与其使用两个循环,我建议使用一个循环来处理所有数字。

此外,我感觉问题与 DAA 指令有关,因为它在64位模式下不受支持,而我不太熟悉它。

无论如何,这是我会做的:

      mov  cx,20
      mov   al,1
      mov   bl,10      ; divisor
      mov   bp,offset Result ; no need to load this in the loop!!!

L1:   mov   dx,ax      ; save to register, not to stack
      cmp   ax,09d
      ja    L2         ; number has two digits
      add   al,30h     ; ASCII addend

      ; insert your output code here

      jmp   L3         ; jump over the two digit code
L2:   xor   ah,ah
      div   bl         ; divides AX by ten (no rotate or shift needed)
                       ; quotient in AL, remainder in AH (correct order for little endian)
      add   ax,3030h

      ; insert your output code here (note that the buffer/string address is loaded to BP)

L3:   mov   ax,dx
      inc   ax
      loop  L1

      ; done

如果一位数有前导零,那就更容易了。使用div指令可能比daa加ror加shr指令组合更慢,但你的四次旋转/移位更糟糕:-/(正如我所说,我无法尝试…留给您…如果不起作用,请回来问。)
另一个方法,特别是在这种将数字分开的微不足道的情况下节省div,是将6添加到大于九的数字(例如10d = 0ah --(+6)--> 16d = 10h, daa也是这样做的),然后可以使用之前使用过的旋转/移位组合。更好的方法是添加246,然后添加到AX中,之后您只需使用ror ax,8 (或rol — 在此情况下无关紧要),即10d = 0ah --(+246)--> 256d = 100h,15d = 0fh --(+246)--> 261 = 105h。将它旋转为0001h或0501h,再加上3030h,你就完成了。
更新2级:
使用xchg指令,可以在寄存器之间交换值,这种情况下可以八次逐位旋转,或者-如果您的TASM确实不支持立即旋转,则通过一次交换操作实现。
  xchg al,ah

会执行交换这两个寄存器的内容的工作。

还有一个bswap指令,用于颠倒寄存器内的字节顺序,但显然只适用于32位或更宽的寄存器。

/更新]


1
这对于第20位数字是行不通的, ;20d = 14h --(+6)--> 26d = 1Ah - micheller
是的 :) 但我非常感激你的努力,我这样简化了它, 对于从0A到19th的每个值,我们都要加6d,同样对于接下来的10个值,我们不需要添加6d,而是要添加12d,对于接下来的10个值,我们要添加18d,以此类推 :)在这里: ;10d = 0ah --(+6)--> 16d = 10h ;20d = 14h --(+12)--> 32d = 20h ;30d = 1Eh --(+18)--> 48h = 30h - micheller

0
.model small  
 .stack 100  
 .code  
      mov ax, 0ffffh            ; hex number to find it's bcd  
      mov      bx, 0000  
      mov      dh, 0  
 l9 :     cmp     ax, 10000     ; if ax>10000  
      jb      l2  
      sub      ax, 10000        ; subtract 10000  
      inc      dh               ; add 1 to dh  
      jmp      l9  
 l2 :     cmp      ax, 1000     ; if ax>1000  
      jb      l4  
      sub      ax, 1000  
      add      bx, 1000h        ; add 1000h to result  
      jmp      l2  
 l4 :     cmp      ax, 100      ; if ax>100  
      jb      l6  
      sub      ax, 100  
      add      bx, 100h         ; add 100h to result  
      jmp      l4  
 l6 :     cmp      ax, 10       ; if ax>10  
      jb      l8  
      sub      ax, 10  
      add      bx, 10h          ; add 10h to result  
      jmp      l6  
 l8 :     add      bx, ax       ; add remainder   
                                ; to result  
      mov      ah, 02            
      mov      cx, 0204h        ; Count to display   
                                ; 2 digits  
      go:      rol dh, cl  
      mov      dl, dh  
      and      dl, 0fh  
      add      dl, 30h          ; display 2 msb digits       
      int      21h  
      dec      ch  
      jnz      go  
      mov      ch, 04h          ; Count of digits to be   
                                ; displayed  
      mov      cl, 04h          ; Count to roll by 4 bits  
 l12:     rol      bx, cl       ; roll bl so that msb   
                                ; comes to lsb                  
      mov      dl, bl           ; load dl with data to be   
                                ; displayed  
      and      dl, 0fH          ; get only lsb  
      cmp      dl, 09           ; check if digit is 0-9 or letter A-F      
      jbe      l14  
      add      dl, 07           ; if letter add 37H else only add 30H  
 l14:     add      dl, 30H  
      mov      ah, 02           ; Function 2 under INT 21H      (Display character)  
      int      21H  
      dec      ch               ; Decrement Count  
      jnz      l12  
      mov      ah, 4cH          ;  Terminate Program  
      int      21H  
 end

1
欢迎来到StackOverflow,在回答问题之前,您可能想阅读如何发布一个好的答案。请给出一些背景说明为什么您发布了这段代码以及它的功能是什么,并考虑花些时间对您的答案进行格式化,以便读者能轻松理解。 - Nacho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接