I colori sul terminale e le macro
Gli esercizi di questa sezione hanno un duplice scopo:
Il primo problema lo risolviamo con le sequenze di escape. In sostanza inviando una stringa opportuna al terminale è possibile reimpostare gli attributi colore. Questa stringa, in particolare, che chiameremo foba (foreground background) è composta da 9 byte ed ha la seguente struttura:
1Bh | 5Bh | 33h | ---- | 3Bh | 34h | ---- | 6Dh | 00h |
esc | [ | 3 | ; | 4 | m | null | ||
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
I byte nelle posizioni 3 e 6 rappresentano i colori di foreground e di background rispettivamente, e vanno impostati secondo la seguente tabella:
30h | 0 | nero |
31h | 1 | rosso |
32h | 2 | verde |
33h | 3 | ocra |
34h | 4 | blu |
35h | 5 | fucsia |
36h | 6 | celeste |
37h | 7 | bianco |
Ad esempio per impostare i colori rosso/blu (rosso su fondo blu) occorrerà definire una stringa che contenga in posizione 3 il byte 31h ed in posizione 6 il byte 34h, come illustrato di seguito:
foba db 1Bh,5Bh,33h,31h,3Bh,34h,34h,6Dh,00h oppure foba db 1Bh,"[31;44m",0
Tornando al problema del testo colorato sul terminale, una prima soluzione viene proposta nel listato seguente nel quale sono presenti delle macro sviluppate nell'ultimo listato. Per mia convenzione, i nomi delle macro asm iniziano con il carattere @ in modo che sia facile individuarle nel listato del programma. Si noti la presenza della direttiva %include che consente l'aggancio del file con le macro.
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; txtcol_1.asm ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ;compilare con nasm+gcc %include "macrobase.mac" global main extern printf section .data foba db 1Bh,"[37;40m",0 cls db 1Bh,"[2J",1Bh,"[1;1f",0 crlf db 0Dh,0Ah,0 msg20 db "Scritta in verde su fondo nero",0 msg02 db "Scritta in nero su fondo verde",0 msg71 db "Scritta in bianco su fondo rosso",0 section .text main: @outterm cls ; Clear del terminale @fixcol 2,0 ; Imposta Verde/Nero @outterm msg20 ; Emette Messaggio @outterm crlf ; Ritorno a capo @fixcol 0,2 ; Imposta Nero/Verde @outterm msg02 @outterm crlf @fixcol 7,1 ; Imposta Bianco/Rosso @outterm msg71 @outterm crlf @fixcol 7,0 ; Ripristina Bianco/Nero ret ; EOF ::::::::::::::::::::::::::::::::::::::::::::::::::::::
La prima delle macro utilizzate, @fixcol, imposta i colori attivi e necessita di 2 parametri che rappresentano ordinatamente il codice colore di foreground e quello di background. La seconda macro, @outterm, serve ad emettere un messaggio di tipo AsciiZ presente come unico parametro.
Si noti nell'area dati la presenza di un'altra stringa di sequenze di escape, cls la quale serve ad effettuare il clear dello schermo ("[2J") ed il riposizionamento del cursore in alto a sinistra ("[1;1f").
Una normalissima stringa AsciiZ, crlf, serve infine per comandare il ritorno a capo tramite una @outterm.
La seconda soluzione del problema viene proposta con l'uso delle syscall e quindi le due macro precedenti, che facevano uso della printf, sono state modificate in @fixcol2 e @outterm2, in modo da essere indipendenti dalla libc. La compilazione può pertanto essere effettuata con l'uso del linker ld. Il programa segue, per il resto, l'impostazione di quello precedente.
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; txtcol_2.asm ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ;compilare con nasm+ld %define STDOUT 1 %define SYSCALL_EXIT 1 %define SYSCALL_WRITE 4 %include "macrobase.mac" global _start ;necessario per il linker ld section .data foba db 1Bh,"[37;40m",0 cls db 1Bh,"[2J",1Bh,"[1;1f",0 crlf db 0Dh,0Ah,0 msg20 db "Scritta in verde su fondo nero",0 msg02 db "Scritta in nero su fondo verde",0 msg71 db "Scritta in bianco su fondo rosso",0 section .text _start: @outterm2 cls ; Clear del terminale @fixcol2 2,0 ; Imposta Verde/Nero @outterm2 msg20 ; Emette Messaggio @outterm2 crlf ; Ritorno a capo @fixcol2 0,2 ; Imposta Nero/Verde @outterm2 msg02 @outterm2 crlf @fixcol2 7,1 ; Imposta Bianco/Rosso @outterm2 msg71 @outterm2 crlf @fixcol2 7,0 ; Ripristina Bianco/Nero @exit 0 ; uscita normale, code=0 ; EOF ::::::::::::::::::::::::::::::::::::::::::::::::::::::
Ecco una immagine della schermata che si ottiene con i programmi presentati in questa sezione.
Ed, in ultimo, il listato delle macro utilizzate dai programmi precedenti e che viene collegato tramite la direttiva %include "macrobase.mac"
.;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; macrobase.mac ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: %macro @outterm 1 push dword %1 ;Passa il Ptr Stringa call printf add esp,4 ;Riallinea lo Stack %endmacro %macro @fixcol 2 mov byte [foba+3],%1+30h mov byte [foba+6],%2+30h @outterm foba %endmacro %macro @fixcol2 2 mov byte [foba+3],%1+30h mov byte [foba+6],%2+30h @outterm2 foba %endmacro %macro @outterm2 1 mov ecx, %1 ; Puntatore stringa AsciiZ mov esi,ecx ; Utilizza Puntatore esi per usare la lodsb xor edx,edx ; Azzera contatore lunghezza (edx) dec edx ; %%lp: inc edx lodsb or al,al jnz %%lp mov eax, SYSCALL_WRITE ; write function mov ebx, STDOUT ; Arg1: file descriptor int 80h ; syscall kernel to write %endmacro %macro @exit 1 mov EBX, %1 ; exit code=%1 mov EAX, SYSCALL_EXIT ; exit function int 80h ; syscall kernel to take over %endmacro ;EOF :::::::::::::::::::::::::::::::::::::::::::::::::::
Si noti nella @outterm2 la presenza di una etichetta (label) locale %%lp contrassegnata dal prefisso %%. Una tale scelta evita errori, in compilazione, quando la macro viene usata più di una volta.
Nella @outterm2 viene effettuato il calcolo della lunghezza della stringa, passata come parametro in edx, dal momento che la SYSCALL_WRITE vuole in edx proprio questo valore.
Complementi
Con le sequenze di escape è, però, possibile aggiungere una serie di attributi per migliorare ancora di più l'output a colori. Per raggiungere lo scopo guardiamo il parametro colore di foreground come fosse un byte strutturato nella maniera seguente:
B | H | R | U | I | cB | cG | cR |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
I bit di posto 0,1,2 (cR,cG,cB) determinano il colore secondo la classica tripletta RGB. Gli altri bit costituiscono attributi aggiuntivi che vanno opportunamente interpretati.
Ad esempio, per ottenere un colore ocra intenso (giallo) e sottolineato il byte dovrà essere così impostato:
0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
valore che sarà passato come parametro colore di foreground in una qualunque base numerica: 00011011b, 1Bh, 27 comprensibile dall'assemblatore.
La nuova macro @fixcol3 che interpreterà questo byte avrà il compito di comporre una precisa sequenza di escape di lunghezza variabile in funzione degli attributi aggiunti alla tripletta RGB. La macro utilizzerà una nuova stringa dati chiamata fobax, che conterrà al suo interno:
L'attivazione di ciascun attributo comporta l'aggiunta delle seguenti coppie di byte/carattere alla sequenza di escape:
I: db 3Bh,31h o db ";1" o dw 313Bh U: db 3Bh,34h o db ";4" o dw 343Bh R: db 3Bh,37h o db ";7" o dw 373Bh H: db 3Bh,38h o db ";8" o dw 383Bh B: db 3Bh,35h o db ";5" o dw 353Bh
L'intera sequenza dovrà essere chiusa da:
db 6Dh,0 o db "m",0 o dw 006Dh
Ecco la macro @fixcol3 che realizza l'interprete del parametro colore foreground:
%macro @fixcol3 2 mov al,%1 ;;Lettura parametro foreground mov ah,al ;;Impostazione colore foreground and ah,07h add ah,30h mov [foba+3],ah mov byte [foba+6],%2+30h ;;Impostazione colore background mov esi,7 ;;Inizio Interprete attributi test al,08h ;;Test su "I" mov bx,313Bh call %%work test al,10h ;;Test su "U" mov bx,343Bh call %%work test al,20h ;;Test su "R" mov bx,373Bh call %%work test al,40h ;;Test su "H" mov bx,383Bh call %%work test al,80h ;;Test su "B" mov bx,353Bh call %%work jmp %%exit ;.......................................;;Routine accodamento %%work: jz %%wxit ;;Esce se Test=0 mov [foba+esi],bx ;;Accoda bx, se Test=1 add esi,2 %%wxit: ret ;....................................... %%exit: mov bx,006Dh ;;Chiusura Sequenza Escape mov [foba+esi],bx @outterm2 fobax ;;Emissione Sequenza %endmacro
Inserendo la macro @fixcol3 nel file macrobase.mac è possibile compilare il programmino di prova txtcol_3.asm riportato di seguito.
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; txtcol_3.asm ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ;compilare con nasm+ld %define STDOUT 1 %define SYSCALL_EXIT 1 %define SYSCALL_WRITE 4 %include "macrobase.mac" global _start ;necessario per il linker ld section .data fobax db 1Bh,"[0m" foba db 1Bh,"[37;40m",0,0,0,0,0,0,0,0,0,0,0 cls db 1Bh,"[2J",1Bh,"[1;1f",0 crlf db 0Dh,0Ah,0 msg20 db "Scritta in giallo sottolineato su fondo nero",0 section .text _start: @outterm2 cls ; Clear del terminale @fixcol3 00011011b,0 ; Imposta colori e attributi @outterm2 msg20 ; Emette Messaggio @outterm2 crlf ; Ritorno a capo @fixcol3 7,0 ; Ripristina Bianco/Nero exit: mov EBX, 0 ; exit code, normal=0 mov EAX, SYSCALL_EXIT ; exit function int 80h ; syscall kernel to take over ;::::::::::::::::::::::::::::::::::::::
Per governare occorre saper cogliere il semplice e abbracciare l'essenziale: ma per far questo occorre saper ridurre il proprio io e frenare i propri desideri.
Lao Tzu