汇编Hangman游戏异常:下划线打印7次并触发段错误求助
我正在编写汇编版Hangman(刽子手)游戏,初期需求为:输入一个字符,与目标单词'SPARTA'的首字符比对,匹配则打印该字符,否则打印下划线。但运行程序时,下划线被打印7次,随后触发段错误。7这个数字十分异常,它既不是2的倍数,也不等于目标单词的字符数。附上我的代码,恳请帮忙排查问题所在。另外,我认为这是我目前写得最好的程序之一。
;obešenec hra SECTION .data zahadneslovo db 'SPARTA' ;info db 'pokusy:' ;pokusy db 5 linefeed db 0xA podtrzitko db '_' SECTION .bss pismenkoin resb 1 stav resb 1 misto resb 1 SECTION .text global _start _start: ;kod: call zeptatsenapismenko call zjisteni call upravenistavu call zobrazeni ;deklerace subrutin: zeptatsenapismenko: mov edx,1 mov ecx,pismenkoin mov ebx,0 mov eax,3 int 80h ret zjisteni: ;subrutina mov eax,pismenkoin mov ecx,0 porovnat:cmp eax,[zahadneslovo+ecx] jz ulozitpismenkoktereserovna zpet: cmp ecx, 5 jz konecc inc ecx jnz porovnat dal: ret ulozitpismenkoktereserovna: ;mov edi,[zahadneslovo+ecx] mov [misto],ecx jmp zpet upravenistavu: mov ebx,6 sub ebx,[misto] mov edx,2 mov ecx,0 ebxkratnasobitdvojkou: mul edx inc ecx cmp ecx,ebx jz hihi jnz ebxkratnasobitdvojkou hihi: ;add al,[misto] mov [stav],al ret zobrazeni: mov eax,0 testy: bt [stav],eax jnc podtrzitkoaznovu jc vytisknipismenkokterejenatomhleporadiveax hemh: cmp eax,6 jz konecc jnz znovu znovu: inc eax jmp testy konecc: ret podtrzitkoaznovu: push eax push ebx push ecx push edx mov edx,1 mov ecx,podtrzitko mov ebx,1 mov eax,4 int 80h pop edx pop ecx pop ebx pop eax jmp hemh vytisknipismenkokterejenatomhleporadiveax: push eax push ebx push ecx push edx mov eax,[misto] mov edx,1 mov ecx,[zahadneslovo+eax] mov ebx,1 mov eax,4 int 80h pop edx pop ecx pop ebx pop eax jmp hemh
核心错误点分析
字符比对逻辑错误
zjisteni子程序中,mov eax,pismenkoin是把输入缓冲区的地址存入eax,而非读取缓冲区里的字符值。正确做法是用mov al, [pismenkoin]读取输入的ASCII字符,后续比对也应该用al而非eax,否则会拿内存地址和字符ASCII码做无效比对,永远无法匹配目标字符。标签重名导致流程混乱
代码中konecc标签在zjisteni和zobrazeni中重复定义,汇编会将其解析为最后出现的实例(即zobrazeni中的konecc)。当zjisteni中循环到ecx=5时,会直接跳转到zobrazeni的ret指令返回,导致upravenistavu和zobrazeni可能被跳过,或者后续逻辑使用未初始化的垃圾值。需给不同子程序的终止标签重命名,比如zjisteni_konec。upravenistavu初始化缺失
进入该子程序时,eax未被初始化就执行mul edx,而eax此时残留的是pismenkoin的地址,导致计算出的stav值完全错误。此外stav仅定义1字节,但计算2的N次方时很容易溢出,破坏其他内存区域。应先将eax初始化为1(因为2^0=1),再执行乘法循环。循环次数错误(打印7次的原因)
zobrazeni中循环终止条件是cmp eax,6,eax从0开始计数,会循环0-6共7次。目标单词SPARTA是6个字符,应改为cmp eax,5,循环0-5共6次。Write系统调用参数错误(段错误的直接原因)
vytisknipismenkokterejenatomhleporadiveax中,mov ecx,[zahadneslovo+eax]把字符的ASCII值存入ecx,但Linux的Write系统调用(eax=4)要求ecx是字符串的内存地址,而非字符值。这会导致系统尝试访问以该ASCII值为地址的非法内存,直接触发段错误。正确写法是mov ecx, zahadneslovo+eax,传入字符的地址。未初始化变量的垃圾值问题
如果输入字符不在目标单词中,ulozitpismenkoktereserovna不会执行,misto会保留未初始化的垃圾值,后续upravenistavu中的计算会基于错误值,导致整个逻辑混乱。需在程序初始化时将misto设为无效值(比如0xFF),并在后续逻辑中判断是否匹配成功。
关键修正示例
修正后的zjisteni子程序:
zjisteni: ;subrutina mov al, [pismenkoin] ; 读取输入字符 mov ecx,0 mov byte [misto], 0xFF ; 初始化misto为无效值 porovnat: cmp al,[zahadneslovo+ecx] jz ulozitpismenkoktereserovna zpet: cmp ecx, 5 jz zjisteni_konec ; 使用专属终止标签 inc ecx jnz porovnat zjisteni_konec: ret ulozitpismenkoktereserovna: mov [misto],ecx jmp zpet
修正后的upravenistavu子程序:
upravenistavu: cmp byte [misto], 0xFF ; 判断是否匹配成功 jz nenalezeno ; 未匹配则设置stav为0(全下划线) mov ebx,6 sub ebx,[misto] mov eax,1 ; 初始化eax为1(2^0) mov edx,2 mov ecx,0 ebxkratnasobitdvojkou: mul edx inc ecx cmp ecx,ebx jz hihi jnz ebxkratnasobitdvojkou hihi: mov [stav],al ret nenalezeno: mov byte [stav],0 ret
修正后的vytisknipismenkokterejenatomhleporadiveax子程序:
vytisknipismenkokterejenatomhleporadiveax: push eax push ebx push ecx push edx mov ecx, zahadneslovo ; 基地址 add ecx, eax ; 加上偏移量,得到当前字符的地址 mov edx,1 mov ebx,1 mov eax,4 int 80h pop edx pop ecx pop ebx pop eax jmp hemh
内容的提问来源于stack exchange,提问作者IlikeAssemblyButIsuck-sorry

