欢迎所有真正的解决方案/建议/帮助。预先感谢您。
这是我的基本问题: 为什么“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'
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
,并删除在启动时存储到它们的指令。
相关: