Here are some things that may help you improve your program
Use consistent formatting
The code as posted has irregular indentation, making it not so easy to read. Assembly language programs are typically very linear and neat. Also, I personally don't use tab characters in my code so that it looks the same everywhere (including printing), but that's a personal preference.
Provide the complete program
The program is missing the definition of sys_exit
(which should have a value of 60). I'd suggest also telling reviewers how you've compiled and linked the program. Here's what I used:
nasm -o rowcol.o -f elf64 rowcol.asmld -o rowcol rowcol.o
Document register use
The comments in your program are generally quite good, but one thing lacking is documentation on how the registers are being used, which is one of the most important aspects to assembly language programming. The x86 architecture is unlike many others in that particular instructions require particular registers. For that reason, it's useful to identify when you'll need to use such instructions and base the register usage around that.
Avoid slow instructions
Although special-purpose instructions such as loop
and repnz scasb
seem appealing, they are, in fact, relatively slow. Instead, it's usually much faster (and not that many more code bytes) to do things with the more generic instructions.
Use address multipliers for efficiency
We can greatly simplify getting a pointer to the environment list into a register:
mov rbp, rsp ; use rbp for stack pointermov rcx, [rbp + 0] ; get argclea rbx, [rbp+8+8*rcx] ; rbx now points to env
Understand environment variables
In Linux, there is a difference between shell variables and environment variables. Environment variables are what your program is searching, but the LINES
and COLUMNS
variables are shell variables that are set by the shell but typically not as environment variables. See this question for details.
Use an IOCTL
The reliable way to get the screen dimensions in Linux is to invoke the TIOCGWINSZ
ioctl
call. In C++ it would might look like this:
#include <sys/ioctl.h>#include <unistd.h>#include <iostream>int main () { struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); std::cout << "lines = "<< w.ws_row << "\ncolumns = "<< w.ws_col << '\n';}
So we just need to put that into assembly language. First, some constants:
sys_ioctl equ 0x10STDOUT_FILENO equ 1TIOCGWINSZ equ 0x5413
Now the winsize
structure:
struc winsize .ws_row: resw 1 .ws_col: resw 1 .ws_xpixel: resw 1 .ws_ypixel: resw 1endstrucsection .bssw resb winsize_size ; allocate enough for the struc
Finally the call:
mov edx, wmov esi, TIOCGWINSZmov edi, STDOUT_FILENOmov eax, sys_ioctlsyscall; do stuff with window size...
If the call was successful (that is, if eax
is 0) then the winsize
structure is filled in with the current dimensions.