x86 过程(32 位)仅适用于我的代码的某些部分

问题描述 投票:0回答:1

欢迎所有真正的解决方案/建议/帮助。预先感谢您。

这是我的基本问题: 为什么“Print.asm”文件底部的“ClrScreen”过程在程序的某些位置不起作用,但在其他位置却起作用?

预期行为: 我希望“Print.asm”中的“ClrScreen”过程在任何被调用的地方都能工作。此过程之所以重要,是因为如果遇到错误,我希望执行“ClrScreen”过程来清除 Linux 终端并输出错误消息。

1.) 线索 1 将指令“call ClrScreen”写入“Battle_Chess.asm”中的第一条指令。您将看到“ClrScreen”在程序中的这个位置按预期工作。

2.) 线索 2 删除我们之前写入“Battle_Chess.asm”开头的“call ClrScreen”指令。现在,在指令“extern Rows”之后写入相同的指令“call ClrScreen”。 “ClrScreen”在程序中的这个位置也按预期工作。

3.) 线索 3 这就是难题发生的地方。 删除我们之前在“extern Rows”指令之后编写的“call ClrScreen”指令。在“Battle_Chess.asm”文件中最后一个“mov DWORD [内存地址],[立即值]”指令之后但在“call GetWinSize”指令出现之前写入指令“call ClrScreen”。最后,要查看该错误,请从“mov DWORD [blackQueen + 4], 0x08000000”指令中删除分号。然后突然“调用 ClrScreen”不再起作用了!您可以继续清除所有分号,但不能清除指令“mov DWORD [blackQueen + 4], 0x08000000”和“mov DWORD [blackKing + 4], 0x10000000”的分号。我不明白这些说明如何影响“呼叫 ClrScreen”。

4.) 线索 4 删除我们之前在“call GetWinSize”指令之前编写的“call ClrScreen”指令。将分号放回到导致“线索 3”中提到的错误的前两条指令上。在“call GetWinSize”指令之后写入“call ClrScreen”指令。同样,“调用 ClrScreen”在某种程度上受到“调用 GetWinSize”的影响。

文件:Battle_Chess.asm

; Program Author: David Mark Serrano
; Program Name: Battle_Chess
; Date Created: July 12, 2023
; Version: 1.0
; Purpose: To render a textual chessboard on the Linux Terminal for a game of chess against an AI opponent

; Built using file "makefile":
;
; Battle_Chess: Board_Render.o Battle_Chess.o Print.o
;   ld -m elf_i386 -o Battle_Chess Battle_Chess.o Board_Render.o Print.o
;
; Print.o: Print.asm
;   nasm -f elf32 -g -o Print.o Print.asm
;
; Board_Render.o: Board_Render.asm
;   nasm -f elf32 -g -o Board_Render.o Board_Render.asm
;
; Battle_Chess.o: Battle_Chess.asm
;   nasm -f elf32 -g -o Battle_Chess.o Battle_Chess.asm

section .data ; Section for initialized data

; File Descriptors
STDIN equ 0
STDOUT equ 1
STDERR equ 2

; Strings used in Battle Chess Engine
Terminal_Length db "Columns: ", 0
Minimum_Columns db "150", 0
Terminal_Height db "Rows: ", 0
Minimum_Rows db "45", 0
Error_Message_0x00000001 db "Error 0x00000001: Required terminal dimensions aren't met. Ensure $COLUMNS = 150 and $LINES = 45.", 0xA, 0 

; Chess Pieces
whitePawns db 8
whiteRooks db 8
whiteKnights db 8
whiteBishops db 8
whiteQueen db 8
whiteKing db 8
blackPawns db 8
blackRooks db 8
blackKnights db 8
blackBishops db 8
blackQueen db 8
blackKing db 8


section .text
    global _start

_start:

    nop ; This instruction is used to aid gdb debugger

    ; External references to global procedures defined in 'Board_Render.asm'
    extern GetWinSize

    ; External references to global procedural buffers defined in 'Board_Render.asm'
    extern WinSizeBuff

    ; External references to global procedures defined in 'Print.asm'
    extern PrintString
    extern PrintRows
    extern PrintColumns
    extern ClrScreen

    ; External references to global procedural buffers defined in 'Print.asm'
    extern Columns
    extern Rows

    ; This group of instructions (Line 53 - Line 76) set the initial positions of all game pieces on the Chessboard
    ; Each 8-byte variable is divided into high 32-bits and low 32-bits
    ; Each location in a variable where a bit is set directly corresponds to the game piece actively on that square
;   mov DWORD [whitePawns], 0x0000FF00 ; The White Pawns are actively on { Files A - H; Rank 2  }
;   mov DWORD [whitePawns + 4], 0x00000000 ; No White Pawns actively on these squares
;   mov DWORD [whiteRooks], 0x00000081 ; The white Rooks are actively on { File A; Rank 1 } and { File H - Rank 1 }
;   mov DWORD [whiteRooks + 4], 0x00000000 ; No White Rooks actively on these squares
;   mov DWORD [whiteKnights], 0x00000042 ; White Knights are actively on { File B; Rank 1 } and { File G; Rank 1 }
;   mov DWORD [whiteKnights + 4], 0x00000000 ; No White Knights actively on these squares
;   mov DWORD [whiteBishops], 0x00000024 ; White Bishops are actively on { File C; Rank 1 } { Rank F; File 1 }
;   mov DWORD [whiteBishops + 4], 0x00000000 ; No White Bishops actively on these squares
;   mov DWORD [whiteQueen], 0x00000008 ; White Queen is actively on { File D; Rank 1 }
;   mov DWORD [whiteQueen + 4], 0x00000000 ; No White Queen on these squares
;   mov DWORD [whiteKing], 0x00000010 ; White King is actively on { File E; Rank 1 }
;   mov DWORD [whiteKing + 4], 0x00000000 ; No White King on these squares
;   mov DWORD [blackPawns], 0x00000000 ; No Black Pawns on these squares
;   mov DWORD [blackPawns + 4], 0x00FF0000 ; Black Pawns are actively on { File A-H; Rank 7 }
;   mov DWORD [blackRooks], 0x00000000 ; No Black Rooks on these squares
;   mov DWORD [blackRooks + 4], 0x81000000 ; Black Rooks are actively on { File A; Rank 8 } { File H; Rank 8 }
;   mov DWORD [blackKnights], 0x00000000 ; No Black Knights on these squares
;   mov DWORD [blackKnights + 4], 0x42000000 ; Black Knights are actively on { File B; Rank 8 } { File G; Rank 8 }
;   mov DWORD [blackBishops], 0x00000000 ; No black Bishops on these squares
;   mov DWORD [blackBishops + 4], 0x24000000 ; Black Bishops are active on { File C; Rank 8 } { File F; Rank 8 }
;   mov DWORD [blackQueen], 0x00000000 ; No Black Queen on these squares
;   mov DWORD [blackQueen + 4], 0x08000000 ; Black Queen is actively on { File D; Rank 8 }
;   mov DWORD [blackKing], 0x00000000 ; No Black King on these squares
;   mov DWORD [blackKing + 4], 0x10000000 ; Black King is actively on { File E; Rank 8 }

    ; This instruction (Line XX) makes a call to procedure 'GWINSZ' in 'Board_Render.asm' file to retrieve dimensions of Linux Terminal
    call GetWinSize

    ; These instructions (Line XX - Line XX) will print the total $LINES of the current Linux Terminal session
    push Terminal_Length ; Push memory address of string 'Terminal_Length' onto stack
    push 0xA ; Push length of string "Terminal_Length' onto stack
    call PrintString ; Call procedure 'PrintString' from 'Print.asm' file
    call PrintColumns ; Call procedure 'PrintRows' from 'Print.asm' file

    ; These instructions (Line XX - Line XX) will ensure minimum Linux Terminal length is met, and if not met, the program will terminate
    xor eax, eax ; Clear 'eax' so that it is ready to store ASCII character in 'al'
    xor ecx, ecx ; Clear 'ecx' so that it is to be used as loop counter for 'CheckColumns' procedure
    xor edx, edx ; Clear 'edx' so that is is ready to store ASCII character in 'dl'
    CheckColumns:
    cmp ecx, 0x3 ; Check if 'ecx' has counted entire string "150"
    je CheckColumns.End
    mov al, BYTE [Columns + ecx] ; Copy contents of 'Columns' buffer in 'Print.asm' file to 'esi'
    mov dl, BYTE [Minimum_Columns + ecx] ; Copy contents of 'Minimum_Columns' in 'Battle_Chess.asm' buffer to 'edi'
    inc ecx ; Increase 'ecx' to update count for indexing into buffers 'Columns' and 'Minimum_Columns'
    cmp al, dl ; Compare ASCII character stored in 'al' with ASCII character stored in 'dl'
    jne Error_0x00000001 ; Jump to label 'Error_0x00000001' if 'al' and 'dl' are not equal
    je CheckColumns ; Repeat loop and jump to label 'Check_Columns' if 'al' and 'dl' are equal
    CheckColumns.End:

    ; These instructions (Line XX - Line XX) will print the total $COLUMNS of the current Linux Terminal session
    push Terminal_Height ; Push memory address of 'Terminal_Height' onto stack
    push 0x7 ; Push length of string 'Terminal_Height' onto stack 
    call PrintString ; Call procedure 'PrintString' from 'Print.asm' file
    call PrintRows ; Call procedure 'PrintColumns' from 'Print.asm' file

    ; These instructions (Line XX - Line XX) will ensure minimum Linux Terminal height is met, and if not met, the program will terminate
    xor eax, eax ; Clear 'eax' so that it is ready to store ASCII character in 'al'
    xor ecx, ecx ; Clear 'ecx' so that it is to be used as loop counter for 'CheckColumns' procedure
    xor edx, edx ; Clear 'edx' so that is is ready to store ASCII character in 'dl'
    CheckRows:
    cmp ecx, 0x2 ; Check if 'ecx' has counted entire string "45"
    je CheckRows.End
    mov al, BYTE [Rows + ecx] ; Copy contents of 'Columns' buffer in 'Print.asm' file to 'esi'
    mov dl, BYTE [Minimum_Rows + ecx] ; Copy contents of 'Minimum_Columns' in 'Battle_Chess.asm' buffer to 'edi'
    inc ecx ; Increase 'ecx' to update count for indexing into buffers 'Columns' and 'Minimum_Columns'
    cmp al, dl ; Compare ASCII character stored in 'al' with ASCII character stored in 'dl'
    jne Error_0x00000001 ; Jump to label 'Error_0x00000001' if 'al' and 'dl' are not equal
    je CheckRows ; Repeat loop and jump to label 'Check_Columns' if 'al' and 'dl' are equal
    CheckRows.End:

    nop ; This instruction is used to aid the GDB debugger

    ; This group of instructions (Line 97 - Line 97) will terminate the program and return code '0' to signal proper execution
    mov eax, 0x1 ; Specify sys_exit syscall
    xor ebx, ebx ; Clear `ebx` register so that it returns exit code 0
    int 0x80 ; Specify Vector Interrupt 128 (Kernel Services / Syscall Dispatcher)

    ; This group of instructions (Line XX - Line XX) will terminate the program and return code '1' to signal error
    Error_0x00000001:
    call ClrScreen ; Clear Linux Terminal so that Error_Message_0x00000001 is printed to 'STDOUT'
    push Error_Message_0x00000001 ; Push memory address of 'Error_Message_0x00000001' onto stack
    push 0x63 ; Push length of 'Error_Message' onto stack (99 characters)
    call PrintString ; Print string 'Error_Message' to STDOUT
    mov eax, 0x1 ; Specify sys_exit syscall 
    mov ebx, 0x1 ; Set 'ebx' register to so that it returns exit code 1
    int 0x80 ; Specify Vector Interrupt 128 (Kernel Services / Syscall Dispatcher)

文件:Board_Render.asm

; Author/Programmer: David Mark Serrano
; Library: Board_Render
; Date Created: 07/21/2023
; Version: 1.0
; Purpose: This is a library with global variables and procedures for rendering the chessboard in the Battle Chess Engine

section .data ; Section for initialized data

; File Descriptor labels for easier referencing
STDIN equ 0 ; fildes 0 is assigned identifier `STDIN`
STDOUT equ 1 ; fildes 1 is assigned identifier `STDOUT`
STDERR equ 2 ; fildes 2 is assigned identifier `STDERR`

; ASCII character definitions for easier referencing
EOL equ 10 ; Linux end-of-line character
SPACECHR equ 32 ; ASCII space character
NULL equ 0 ; ASCII character for null-terminated strings

; Global declarations for 'GWINSZ' procedure and 'winsize_struct' buffer
global GetWinSize
global WinSizeBuff

; 'ioctl' codes
TIOCGWINSZ equ 0x5413

; Buffers needed for procedures
WinSizeBuff db 32

section .text ; x86 assembly instructions go here

GetWinSize:
mov eax, 0x36 ; Specify `ioctl` syscall in 32-bit x86 assembly
mov ebx, STDOUT ; STDOUT file descriptor stored in ebx
mov ecx, TIOCGWINSZ ; 'ioctl' code for retrieving size of Linux Terminal
lea edx, [WinSizeBuff] ; Load address of buffer 'winsize_struct' into register 'edx'
int 0x80 ; Vector Interrupt 128 (Kernel Services Dispatcher/Syscall Dispatcher)
ret ; Return from procedure

文件:Print.asm

; Author/Programmer: David Mark Serrano
; Library: Print
; Date Created: 08/26/2023
; Version: 1.0
; Purpose: To define all procedures related to printing data to text via STDOUT

section .data ; Initialized data goes here

; File descriptors for STDIN/STDOUT/STDERR
STDIN equ 0
STDOUT equ 1
STDERR equ 2

; Escape sequences used in Print.asm
Clr db `\033[2J`, 0  ; Escape sequence to clear the screen followed by null terminator
ClrLen equ $ - Clr ; Calculate the length of the message

; Global declarations for 'ClrScreen' procedure
global ClrScreen


section .text ; x86 assembly instructions go here

; This procedure (Line XX - Line XX) will clear the Linux Terminal screen
ClrScreen:
mov eax, 0x4 ; Syscall number for write
mov ebx, STDOUT ; Specify 'STDOUT' or (File Descriptor 1) as the destination for the output
mov ecx, Clr ; Pointer to 'msg'
mov edx, ClrLen  ; Length of the 'msg'
int 0x80 ; Specify Vector Interrupt 128 (Kernel Services Dispatcher / Syscall Dispatcher 
ret ; Return to main 'Battle_Chess.asm'
debugging assembly x86 nasm ld
1个回答
2
投票

blackQueen db 8
保留1个字节,用值
0x08
初始化。

您正在运行存储超过您在

.data
中实际保留的空间末尾的指令,踩在
Clr:
字符串上,因此当您
write
到标准输出时,您并没有写入 ANSI 转义的字节顺序。

Print.o
是最后一个目标文件,因此它的
.data
紧随其他目标文件中的
.data
之后,这意味着
mov DWORD [blackQueen + 4], imm32
可以踩在它上面。

如果您像我建议的那样使用

strace
来查看写入 stdout 的字节是否仍然是转义序列,和/或在 GDB 中对这些字节设置一个观察点以查看是否有任何内容修改了它们,您就可以看到这一点。

您还会看到,将

Clr
放在其他位置(例如它所属的
section .rodata
)将使您的程序在任何调用时仍会清除屏幕。 (它不会修复你的程序没有在
.data
中为其位板保留足够空间的错误,一旦你对它们进行任何操作,这将是一个令人震惊的问题,因为存储到较早的位板将踩在后面的位板上。)


您希望

dq 0
保留64位(8字节)

或者更好的是,您可以将位板从

whitePawns
blackKing
放入
.bss
中,分别作为
resb 8
resq 1
。 (“reserve”伪指令需要保留多个元素,不像
dd
=“data dword”或“define dword”需要一个值列表)。

您可以将初始条件放入

.rodata
中,以便在重新启动游戏时高效复制,使用
rep movsb
作为简单的 memcpy,或
movaps
16 字节加载/存储,而不是每个 qword 2x mov-immediate。

或者对于单个游戏,在

.data
中使用正确的非零初始值设定项,如
WhitePawns: dq 0x000000000000FF00
,并删除在启动时存储到它们的指令。


相关:

© www.soinside.com 2019 - 2024. All rights reserved.