As part of the prologue of all my console applications, I need to determine the extents of the current terminal so if there are less than 132 columns or 43 lines the user can be warned output may not appear as expected. Code has been tested with;
$ AppName /usr/include/*.h
Assemble with source being whatever name you want to give app.
~$ nasm -felf64 source.asm -source.o ~$ ld -osource -osource
which passes 112 arguments to process.
Essentially what I am going for is contiguous flow with the least number of instructions. Time is an important consideration but it is the least important especially considering if my calculations are near correct, this procedure comes in at 4.18 micro seconds.
USE64 global _start section .text ; *----* *----* *----* *----* *----* *----* *----* *----* *----* *----* *----* _start: %define argc [rbp+ 8] %define args [rbp+16] mov rsi, rsp ; Establish pointer to argc. push rbp ; So argc & **args can easily be addressed mov rbp, rsp ; via base pointer. ; This application expects a minimum 132 x 43 terminal. If this sessions metrics ; are less than that, then operator needs to be made aware output to screen ; may not be as expected. ; [A] Establish a pointer to the array of QWORD pointers to environment ; strings. It is determined by &argc + (argc+1) * 8 lodsq ; Determine # of args passed via command-line inc eax ; Bump argument count shl rax, 3 ; Multiply by 8 add rsi, rax ; Add result to &argc ; [B] Intialize the two registers needed for the loop that determines ; matching entries. mov edi, Metrics ; Pntr to the two strings that need to be found. ; RDX Bits 07 - 00 = Count of environment variables. ; 15 - 08 = Columns defined by "COLUMNS=". ; 23 - 16 = Rows " " "LINES=". xor edx, edx mov ecx, edx ; Should be zero, but just to be safe. FindMatch: lodsq ; Get pointer to next environment string. test eax, eax ; NULL pointer indicates end of array. jnz .cont ; Now RBP - 1 = Count of environment strings ; RBP - 2 = Current display columns ; RBP - 3 = rows mov [rbp-4], edx jmp .done .cont: inc dl ; Bump count of environment strings. mov ecx, 6 ; Length of string first string. mov bl, [rax] ; Get first character. ; Determine if this string begins with either 'L' or 'C'. cmp bl, 'L' jz .cmpstr cmp bl, 'C' jnz FindMatch push rdi add edi, ecx ; Bump to point to next string add cl, 2 ; and it is 2 characters longer jmp .cmpstr + 1 ; No need to save RDI again ; Now that the first character matches, determine if the remaining ; do for a count of CL .cmpstr: push rdi push rsi mov rsi, rax ; Move pointer to string into source index. repz cmpsb ; Compare strings for count of CL. jnz .nextone ; Does not match? Carry on. mov rax, rcx ; Both registers are NULL now. .L0: lodsb ; Read ASCII decimal digit. test eax, eax jz .J0 ; Convert ASCII decimal digits to binary. As it is safe to assume we will ; only be expecting characters '0' - '9', this works quite effectively. and al, 15 ; Strip high nibble imul ecx, 10 add ecx, eax jmp .L0 ; Determine which position result will be written based on which ; calculation was done .J0: shl ecx, 16 ; Assume value is # of rows. cmp byte [rdi], 0 jnz $ + 5 shr ecx, 8 ; Move back into columns position. or edx, ecx ; Copy to appropriate position in RDX .nextone: pop rsi pop rdi ; Restore pointer to array of pointers. jmp FindMatch .done: shr edx, 8 sub dx, 0x2b84 ; Equivalent to DH = 43 & DL = 132 test dx, 0x8080 ; Result equal negative in either 8 bit register jz ParseCmdLine ; TODO -> Put some kind of prompting here for user to respond too. ParseCmdLine: ; TODO -> Implement something similar to optarg. Exit: leave ; Kill empty procedure frame xor edi, edi ; Set return code EXIT_SUCCESS mov eax, sys_exit syscall ; Terminate application section .rodata ; ============================================================================= Metrics db 'LINES=' db 'COLUMNS=',0,0 ; So next is page aligned.