如何以及在哪里结束这个有限状态机程序的工作?

How and where to end this finite state machine program working?

我根据给我的图表编写了这个程序。我只是想不出在哪里以及如何从 运行 中阻止它。它永远要求输入。在我的程序中的哪里添加退出命令?有什么想法吗?

谢谢。

INCLUDE Irvine32.inc
.data
A DWord ?
B dword ?
prompta  byte "what is your digit a?",0
promptb  byte "what is your digit b?",0
message0 byte "you are in s0 with output ",0
message1 byte "you are in s1 with output ",0
message2 byte "you are in s2 with output ",0
message3 byte "you are in s3 with output ",0
.code

 main PROC


  call s0
   call waitmsg

初始状态为 S0

 myexit proc
   mov eax, white+16*black
   call settextcolor
   call waitmsg
   ret
 myexit endp

此处的退出程序无效

 readdigits proc
 mov edx, offset prompta
 call writestring
 call readint   ; dword into eax
 mov a,eax
 mov edx, offset promptb
 call writestring
 call readint
 mov b,eax

 ret 
 readdigits endp

S0、S1、S2、S3 的程序从这里开始

 s0 proc
 mov edx,offset message0
 call writestring
 mov eax,0               ;Output is 0 in State 0

 call writedec
 call crlf

 call readdigits

.if(a==0)&&(b==0) 
 call s0
.endif
.if(a==1)&&(b==1)
 call s1
.endif
call s2
ret
s0 endp

s1 proc
mov edx,offset message1
call writestring
mov eax,0               ;Output is 1 in State 0
call writedec
call crlf

 call readdigits
.if(a==0)&&(b==0) 
 call s2
.endif
.if(a==1)&&(b==1)
 call s3
.endif
 call s1
 ret
 s1 endp

 s2 proc
 mov edx,offset message2
 call writestring
 mov eax,1                  ;Output is 1 in State 2
 call writedec
 call crlf

 call readdigits
.if(a==0)&&(b==0) 
 call s0
.endif
.if(a==1)&&(b==1)
 call s1
.endif
call s2
ret
s2 endp


s3 proc
mov edx,offset message3
call writestring
mov eax,1                 ;Output is 1 in State 2
call writedec
call crlf

call readdigits
.if(a==0)&&(b==0) 
 call s2
.endif
.if(a==1)&&(b==1)
call s0
.endif
call s1
ret
s3 endp


main endp
end main

通常对于 state-machine,您会将其编写为单个函数,使用 jmp 而不是 call 进入下一个状态。

您的状态函数从不 return,它们总是跳到一个新状态(或跳到 re-run 当前状态,例如 s1 底部的 call s1 .) 最后的 ret 永远不会到达,所以你只是推送了 ever-growing 数量的 return 地址,这些地址实际上阻止你 returning 到 main.

使用 s1: 之类的标签,而不是 s1 proc。或者您仍然可以使用 MASM 语法假装每个函数都是单独的函数,但使用 jmp s2 到 tail-call 下一个状态。

然后当你检测到一个终止条件作为下一个状态时,你可以ret,它将return到main.


这有一个很大的优势,你可以使用像 jne s1 这样的条件分支,而不是跳过 call/jmp。 MASM 的 .if 语法可能不够强大,无法为您做到这一点,但无论如何您都会因使用它而错过大量优化。例如a==0 && b==0 可以用 mov eax, a / or eax, b / jz both_were_zero 检查。另外,只有 2 个 "variables",将它们保存在 call-preserved 寄存器中,例如 ebxesi 之类的,而不是将它们完全保存在内存中。这就是寄存器的用途!你在写 asm

此外,您可以通过对它们进行布局进行优化,这样 s3 就可以 fall-through 变成 s1 而不是以 jmp s1 结尾。或者保持简单并保持 jmp。 (如果我正在做这个优化,我会留下 jmp s1 作为注释作为文档,fall-through 到 s1 作为下一个状态是有意的。)


您将其余代码嵌套在 main proc 中,这很奇怪,但可能不会导致实际问题。但是以 main 开头的第一个代码块表明它从 call waitmsg 落入 myexit proc?这太奇怪了,但如果 myexit 是你想要发生的事情,它应该真的有效。

此外,在文件末尾之前没有 main endp 行,因此您告诉汇编程序其他 proc 声明在 main 中。