Assembly 组件中友好编号的检查
我写了一个代码来检查两个数字是否友好。代码适用于少数几对(220和284友好,1184和1210友好,34566和3455不友好) 但对于某些组合来说,它不起作用——66928和66992。应该是友好的,但打印的不是友好的。有什么问题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
.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分钟。在我告诉你怎么做之后,现在就把它当作运动来尝试。