我正在学习汇编,我必须编写一个程序(函数)来获取一个数字并返回1
如果它是偶数而0
如果它是&#39 ;不是。
我不得不通过寄存器或标志返回答案,而是通过堆栈(例如我无法将答案放在bx
或ax
中并检查它们的值程序)。我怎么能这样做?
答案 0 :(得分:3)
下一个程序是使用EMU8086 Intel语法(只需复制,粘贴和运行),这就是它的作用:显示一条消息,从键盘捕获一个数字,将数字从字符串转换为数字,检查数字是否为偶数或奇数,堆栈中存储“1”或“0”,并根据“1”或“0”显示消息。在这里,有很多评论可以帮助您理解:
.stack 100h
;------------------------------------------
.data
msj1 db 13,10,'Enter the number: $'
msj2 db 13,10,'The number is even$'
msj3 db 13,10,'The number is odd$'
str db 6 ;MAX NUMBER OF CHARACTERS ALLOWED (5).
db ? ;LENGTH (NUMBER OF CHARACTERS ENTERED BY USER).
db 6 dup (?) ;CHARACTERS ENTERED BY USER.
;------------------------------------------
.code
;INITIALIZE DATA SEGMENT.
mov ax, @data
mov ds, ax
;DISPLAY MESSAGE.
call clear_screen ;DECLARED AT THE END OF THIS CODE.
mov ah, 9
mov dx, offset msj1
int 21h
;CAPTURE NUMBER FROM KEYBOARD AS STRING.
mov ah, 0Ah
mov dx, offset str
int 21h
;CONVERT CAPTURED NUMBER FROM STRING TO NUMERIC.
mov si, offset str ;PARAMETER FOR STRING2NUMBER.
call string2number ;NUMBER RETURNS IN BX.
;CALL PROC TO FIND OUT IF NUMBER IS EVEN OR ODD. THE INSTRUCTION
;"CALL" WILL PUSH IN STACK THE ADDRESS OF THIS INSTRUCTION, THAT'S
;HOW IT KNOWS HOW TO COME BACK HERE TO CONTINUE EXECUTION.
call even_or_odd
;GET RESULT FROM STACK.
pop ax
;DISPLAY RESULT.
cmp al, '1'
je even_number
;IF NO JUMP, AL == '0'.
mov ah, 9
mov dx, offset msj3
int 21h
jmp wait_for_key ;SKIP "EVEN_NUMBER".
even_number:
mov ah, 9
mov dx, offset msj2
int 21h
;WAIT FOR USER TO PRESS ANY KEY.
wait_for_key:
mov ah,7
int 21h
;FINISH THE PROGRAM.
mov ax, 4c00h
int 21h
;------------------------------------------
;THIS PROCEDURE RETURNS '1' IN STACK IF THE NUMBER
;IS EVEN OR '0' IF IT'S ODD.
;ASSUME THE NUMBER COMES IN BX.
proc even_or_odd
;DIVIDE NUMBER BY 2.
mov ax, bx
mov bl, 2
div bl ;AX / BL (NUMBER / 2). RESULT : QUOTIENT=AL, REMAINDER=AH.
;IF REMAINDER IS 0 THEN NUMBER IS EVEN, ELSE IT'S ODD.
cmp ah, 0
je its_even
;IF NO JUMP, IT'S ODD.
mov ax, '0' ;VALUE TO STORE IN STACK.
jmp finish ;SKIP "ITS_EVEN".
its_even:
mov ax, '1' ;VALUE TO STORE IN STACK.
finish:
;STORE VALUE IN STACK. IMPORTANT: WHEN THIS PROCEDURE
;WAS CALLED, THE RETURN ADDRESS WAS PUSHED IN STACK. TO
;RETURN THE VALUE IN STACK IT'S NECESSARY TO RETRIEVE
;THE RETURN ADDRESS FIRST, PUSH THE VALUE ('0' OR '1')
;AND PUSH THE RETURN ADDRESS BACK.
pop bx ;RETRIEVE RETURN ADDRESS FROM THE CALL.
push ax ;VALUE TO RETURN ('0' OR '1').
push bx ;PUT RETURN ADDRESS BACK.
ret ;THIS "RET" POPS THE RETURN ADDRESS. THIS IS HOW
endp ;IT KNOWS HOW TO RETURN WHERE THE PROC WAS CALLED.
;------------------------------------------
;CONVERT STRING TO NUMBER IN BX.
;SI MUST ENTER POINTING TO THE STRING.
proc string2number
;MAKE SI TO POINT TO THE LEAST SIGNIFICANT DIGIT.
inc si ;POINTS TO THE NUMBER OF CHARACTERS ENTERED.
mov cl, [ si ] ;NUMBER OF CHARACTERS ENTERED.
mov ch, 0 ;CLEAR CH, NOW CX==CL.
add si, cx ;NOW SI POINTS TO LEAST SIGNIFICANT DIGIT.
;CONVERT STRING.
mov bx, 0
mov bp, 1 ;MULTIPLE OF 10 TO MULTIPLY EVERY DIGIT.
repeat:
;CONVERT CHARACTER.
mov al, [ si ] ;CHARACTER TO PROCESS.
sub al, 48 ;CONVERT ASCII CHARACTER TO DIGIT.
mov ah, 0 ;CLEAR AH, NOW AX==AL.
mul bp ;AX*BP = DX:AX.
add bx,ax ;ADD RESULT TO BX.
;INCREASE MULTIPLE OF 10 (1, 10, 100...).
mov ax, bp
mov bp, 10
mul bp ;AX*10 = DX:AX.
mov bp, ax ;NEW MULTIPLE OF 10.
;CHECK IF WE HAVE FINISHED.
dec si ;NEXT DIGIT TO PROCESS.
loop repeat ;COUNTER CX-1, IF NOT ZERO, REPEAT.
ret
endp
;------------------------------------------
proc clear_screen
mov ah,0
mov al,3
int 10H
ret
endp
注意变量“str”,用于从键盘捕获数字,使用3-DB格式:第一个DB指定最大长度(加上一个用于结束chr(13)),另一个DB指定长度为用户输入的字符串,以及字符串本身的第三个数据库。
杰斯特是解决问题的另一种方法。甚至还有第三种解决方案:将数字向右移位(指令SHR),丢弃的位存储在进位标志中,然后我们可以通过指令JC或JNC检查进位标志是0还是1。
答案 1 :(得分:2)
当函数返回堆栈上的值时,通常由
实现人们可以直接实现这些想法,或者可以以优化的方式实现这些想法。
以下是执行此操作的典型,简单的代码:
call_site: call get_number ; assumed to eax
push eax ; push argument onto the stack
call is_even_or_odd
pop eax ; get the function result back from the stack
test eax, eax
je even
odd: ...
is_even_or_odd:
push ebp ; save frame pointer
mov eax, 8[ebp] ; get argument (above saved EBP and return address)
and eax, 1 ; now eax == 0 if even, 1 if odd
pop ebp ; pop the push values from the stack
pop edx
leas 4[esp] ; pop the argument
push eax ; push the result
jmp edx ; go to the return address
上述程序以一般方式编码。这个特定的例程可以更紧凑地编码,并具有更好的性能:
is_even_or_odd:
; no need to save frame pointer; just leave EBP alone
pop edx ; get return address
pop eax ; pop the argument
and eax, 1 ; now eax == 0 if even, 1 if odd
push eax ; push the result
push edx ; instead of "jmp edx"
ret
最后的特殊习惯用法是推送返回地址然后执行“ret”,这样硬件就可以准确跟踪其影子堆栈中的返回地址。这意味着当它命中ret指令时,它假定原始返回地址是值(这是它在阴影堆栈中的值)并且可以立即开始在返回点处获取指令。 “jmp edx”习语有效,但会破坏分支地址预测,减慢从子程序返回所需的时间。
另一个变体使用调用堆栈中的空间作为参数, 返回结果。这适用于参数的大小, 等于结果的大小,如下例所示:
is_even_or_odd:
; no need to save frame pointer; just leave EBP alone
mov eax, 4[esp] ; get the argument
and eax, 1 ; now eax == 0 if even, 1 if odd
mov 4[esp], eax ; smash the argument with the result
ret
答案 2 :(得分:0)
这是一件特别愚蠢的事情,当然,这是可能的。 如果您也获得了堆栈上的输入,只需将其替换为结果即可。 假设16位代码,如下所示:
push bp
mov bp, sp
and word [bp+4], 1 ; keep lowest bit
xor byte [bp+4], 1 ; flip it to return 1 for even
pop bp
ret