Assembly 组件中友好编号的检查

Assembly 组件中友好编号的检查,assembly,x86-16,tasm,dosbox,Assembly,X86 16,Tasm,Dosbox,我写了一个代码来检查两个数字是否友好。代码适用于少数几对(220和284友好,1184和1210友好,34566和3455不友好) 但对于某些组合来说,它不起作用——66928和66992。应该是友好的,但打印的不是友好的。有什么问题 .MODEL SMALL .STACK 100H .DATA NUM1 DD ? NUM2 DD ? NUMBER DD ? N DW 1 TEN DD 10 DIVNUM DD 2 DIVSUM DD 0 EnterNumber DB 'ENTER NUMBE

我写了一个代码来检查两个数字是否友好。代码适用于少数几对(220和284友好,1184和1210友好,34566和3455不友好) 但对于某些组合来说,它不起作用——66928和66992。应该是友好的,但打印的不是友好的。有什么问题

.MODEL SMALL
.STACK 100H
.DATA

NUM1 DD ?
NUM2 DD ?
NUMBER DD ?
N DW 1
TEN DD 10
DIVNUM DD 2
DIVSUM DD 0
EnterNumber DB 'ENTER NUMBER: ',13,10,'$'
NOSTR DB 13,10,'NOT AMICABLE NUMBERS',13,10,'$'
YESSTR DB 13,10,'AMICABLE NUMBERS',13,10,'$'

.CODE
.386
getNum PROC NEAR
     MOV AH,9            ; Set print option for int 21h
     MOV DX,OFFSET EnterNumber  ;  Set  DS:DX to point to EnterNumber
     INT 21h             ;  Print DisplayString
     ;FIRST A DIGIT
     MOV AH,1
     INT 21h
     SUB AL,'0'
     MOV AH, 0
     MOV NUMBER,EAX
     MOV N,1
GET_DIGIT:          ;This loop gets all the rest of the digits until 'enter' has been insert and make the digits a nunmber.
     CMP N,9
     JE sof
     MOV AH,1
     INT 21H
     CMP AL,13
     ;AL=='ENTER KEY'
     JE sof
     MOV BX,AX
     SUB BL,'0'
     MOV BH,0
     MOV EAX,number
     MUL TEN
     ADD EAX,EBX
     MOV number,EAX
     INC N
     JMP GET_DIGIT
SOF:
     RET
     getNum ENDP

PNum PROC NEAR
CHECK:
     MOV ECX,DIVNUM
     CMP ECX,NUMBER
     JE FINISH
     MOV EAX,NUMBER
     MOV EDX,0
     DIV DIVNUM
     CMP EDX,0
     JE SUM
     INC DIVNUM 
     JMP CHECK
SUM:
     MOV EBX,DIVNUM
     ADD DIVSUM, EBX    
     INC DIVNUM  
     JMP CHECK
FINISH:
     INC DIVSUM
     RET
     PNum ENDP

MAIN:
     MOV AX,@DATA
     MOV DS,AX
     CALL getNum
     ; ASIGNING NUM1
     MOV EAX,NUMBER
     MOV NUM1,EAX
     MOV NUMBER,0
     CALL getNum
     ;ASIGNING NUM2
     MOV EAX,NUMBER
     MOV NUM2,EAX
         ;CHECKIN NUM1 DIVIDERS.
     MOV EAX,NUM1
     MOV NUMBER,EAX
     CALL PNum
     MOV ECX,DIVSUM
     CMP ECX,NUM2
     JE NEXT
     JMP PRINTNO
NEXT:
     MOV DIVSUM,0
     MOV DIVNUM,2
     MOV EAX,NUM2
     MOV NUMBER,EAX
     CALL PNum
     MOV ECX,DIVSUM
     CMP ECX,NUM1
     JE PRINTYES
     JMP PRINTNO

PRINTNO:
     MOV AH,9            ; Set print option for int 21h
     MOV DX,OFFSET NOSTR  ;  Set  DS:DX to point to NOSTR
     INT 21h             ;  Print DisplayString
     JMP END_CODE
PRINTYES:
     MOV AH,9            ; Set print option for int 21h
     MOV DX,OFFSET YESSTR  ;  Set  DS:DX to point to YESSTR
     INT 21h             ;  Print DisplayString
END_CODE:
     MOV AH,4Ch              ; Set terminate option for int 21h
     INT 21h                 ; Return to DOS (terminate program)
                             ;
     END MAIN  ; Set "main:" as first executable statement


我不能运行tasm/dos,所以我只能在
getNum
中猜测:

     ...
     SUB AL,'0'
     MOV AH, 0
  ; ^^ you clear bits b8-b15
     MOV NUMBER,EAX
  ; ^^ but you store b0-b31 (bits b16-b31 undefined)
在你的源代码中,几乎没有其他地方让我有点皱眉(我的意思是“bug皱眉”,而不是“not my style皱眉”,这是我在源代码的主要部分中使用的,但最终,对于学习汇编的人来说,这种风格看起来是合理的,保持简单,这可能会让你避免很多bug)

我添加了一些对源代码的修改,但我没有验证它们是否有效,所以请搜索我的注释并尝试自己验证更改是否符合预期效果(我也使用小写,因为我更喜欢这种方式,而且您可以很容易地看到我修改的部分代码)

让我知道它是否有效,并询问您是否不理解某些更改

.MODEL SMALL
.STACK 100H
.DATA

NUM1 DD ?
NUM2 DD ?
NUMBER DD ?
DIVSUM DD ?

EnterNumber DB 'ENTER NUMBER: ',13,10,'$'
NOSTR DB 13,10,'NOT AMICABLE NUMBERS',13,10,'$'
YESSTR DB 13,10,'AMICABLE NUMBERS',13,10,'$'

.CODE
.386

getNum PROC NEAR
     ; display user prompt
     MOV AH,9            ; Set print option for int 21h
     MOV DX,OFFSET EnterNumber  ;  Set  DS:DX to point to EnterNumber
     INT 21h             ;  Print DisplayString
     ; get decimal number as separate digit characters
     xor edx,edx         ; number value = 0
     xor cl,cl           ; number of digits = 0
GET_DIGIT:
     ; This loop gets all of the digits until 'enter'
     MOV AH,1
     INT 21h             ; read single char (with echo on screen)
     CMP AL,13           ; AL=='ENTER KEY'
     JE getNum_exit
     ; calculate value of digit and add it to total number
     SUB AL,'0'
     movzx eax,al        ; eax = 32b zero extend al
     ; number (edx) = number*10 + eax
     lea edx,[edx+edx*4] ; number *= 5
     lea edx,[eax+edx*2] ; number = number*2 + eax
     inc cl
     cmp cl,9
     jb GET_DIGIT        ; 9 digits at most
getNum_exit:
     mov [NUMBER],edx
     RET
     getNum ENDP

PNum PROC NEAR
     mov ecx,2           ; divisor (ecx) = 2 (first to try)
     xor ebx,ebx         ; divisor_sum (ebx) = 0
CHECK:
     MOV EAX,NUMBER
     xor edx,edx         ; edx:eax = 64b NUMBER
     cmp ecx,eax         ; finish when divisor >= number
     jae FINISH
     div ecx             ; edx = number mod divisor (eax = quotient)
     test edx,edx        ; AND x,x will set ZF=1 when x==0 (faster cmp x,0)
     jnz not_a_divisor   ; some remainder, not a divisor of number, skip sum
     add ebx,ecx         ; divisor_sum += divisor
not_a_divisor:
     inc ecx             ; next divisor
     JMP CHECK
FINISH:
     inc ebx             ; +1 for "1" divisor
     mov [DIVSUM],ebx    ; return sum in memory global
     RET
     PNum ENDP

MAIN:
     MOV AX,@DATA
     MOV DS,AX
     CALL getNum
     ; ASIGNING NUM1
     MOV EAX,NUMBER
     MOV NUM1,EAX
     CALL getNum

     ; in surrounding code I removed all temporary variables initialization
     ; these belong into the routines themselves, you should set only arguments
     ; outside (ahead of CALL).
     ; My modification to routines make them to init all internal variables
     ; correctly without depending on the external state.

     ;ASIGNING NUM2
     MOV EAX,NUMBER
     MOV NUM2,EAX
         ;CHECKIN NUM1 DIVIDERS.
     MOV EAX,NUM1
     MOV NUMBER,EAX
     CALL PNum
     MOV ECX,DIVSUM
     CMP ECX,NUM2
     JE NEXT
     JMP PRINTNO
NEXT:
     MOV EAX,NUM2
     MOV NUMBER,EAX
     CALL PNum
     MOV ECX,DIVSUM
     CMP ECX,NUM1
     JE PRINTYES
     JMP PRINTNO

PRINTNO:
     MOV AH,9            ; Set print option for int 21h
     MOV DX,OFFSET NOSTR  ;  Set  DS:DX to point to NOSTR
     INT 21h             ;  Print DisplayString
     JMP END_CODE
PRINTYES:
     MOV AH,9            ; Set print option for int 21h
     MOV DX,OFFSET YESSTR  ;  Set  DS:DX to point to YESSTR
     INT 21h             ;  Print DisplayString
END_CODE:
     MOV AH,4Ch              ; Set terminate option for int 21h
     INT 21h                 ; Return to DOS (terminate program)
                             ;
     END MAIN  ; Set "main:" as first executable statement

调试器呢?没有它,你会一直陷入困境。如果没有DOS的话,可以考虑将开发平台切换到WIN/Linux 32/64(一些最新的汇编程序)。在这种情况下,以一步一步的方式尝试有问题的输入只需要很短的时间。我一直在调试,但对于这种大小的数字,需要几个小时。。所以我才请你们帮忙。也许你能看到我错过的一个问题。别开这样的玩笑。如果我对这个问题的看法是正确的,那么在调用
CHECK
之前,
NUM1
/
NUM2
中会有错误的值,一个简单的内存视图会告诉您出了什么问题。关于调试
CHECK
:只需将断点放在
RET
,一步一步只进行3-4次迭代,以验证所有代码路径(除法、求和、跳过),然后运行其余部分直到
RET
并验证结果。无论如何,如果你认为每次都可以发布不正确的代码让别人调试它,那么我只需要爆米花,等待不可避免的结果。所以提高你的调试技能是唯一的出路。我的意思是,也许比我更熟练的人,比如你,可以看到我在代码中犯了一些我不知道的错误。我不是想让人帮我调试。我已经用较小的数字进行了调试,从得到数字到对除数求和,一切都进行得很顺利。但由于某些原因,这对数字只有代码不能正常工作。警告灯应该为您亮起,两个数字都是>=
65536
。你知道,什么是
65536
,对吗?在16位世界里,这是一个相当神奇的数字。同样,在
调用getNum
和存储
NUM1/NUM2
+内存检查之后的简单断点会立即告诉您读取的数字是错误的。可能需要更多的努力来找出原因,但要定位问题,需要5分钟。在我告诉你怎么做之后,现在就把它当作运动来尝试。