Perl – Operadores – parte I

Este exemplo ilustra o uso de operadores aritméticos e bitwise (bit a bit).

  • Design-time:
#solicita 2 números e realiza operações aritméticas e bit a bit

print "Informe o primeiro número: ";
chomp($n1 = <STDIN>);
print "Informe o segundo número: ";
chomp($n2 = <STDIN>);
print "\n\t\tOPERAÇÕES ARITMÉTICAS\n\n";

print "Soma \t\t $n1 + $n2 = ", $n1 + $n2, "\n";
print "Diferença \t $n1 - $n2 = ", $n1 - $n2, "\n";
print "Produto \t $n1 x $n2 = ", $n1 * $n2, "\n";
print "Potência \t $n1 ^ $n2 = ", $n1 ** $n2, "\n";

if($n2 != 0){
	print "Divisão \t $n1 / $n2 = ", $n1 / $n2, "\n";
	print "Quociente \t $n1 \\ $n2 = ", int($n1 / $n2), "\n";
	print "Resto \t\t $n1 % $n2 = ", $n1 % $n2, "\n";
	print "Raiz \t\t $n1 ^ (1 / $n2) = ", $n1 ** (1 / $n2), "\n";	
}

print "\n\t\tOPERAÇÕES BIT A BIT\n\n";
$n1 += 0;
$n2 += 0;

print "And \t\t $n1 & $n2 = ", $n1 & $n2, "\n";
print "Or \t\t $n1 | $n2 = ", $n1 | $n2, "\n";
print "Xor \t\t $n1 ^ $n2 = ", $n1 ^ $n2, "\n";
print "Shift right \t $n1 >> 1 = ", $n1 >> 1, "\n";
print "\t\t $n2 >> 1 = ", $n2 >> 1, "\n";
print "Shift left \t $n1 << 1 = ", $n1 << 1, "\n";
print "\t\t $n2 << 1 = ", $n2 << 1, "\n";
print "Not \t\t ~$n1 = ", ~$n1, "\n";
print "\t\t ~$n2 = ", ~$n2, "\n\n";
  • Runtime:
Perl rodando em Ubuntu 22.04 (Jammy Jellyfish), captura de tela com Peek
  • Sintaxe:

A primeira novidade aqui pode ser vista nas linhas 4 e 6. Para se obter a entrada do usuário via teclado, usa-se STDIN, que significa standard input e pode ser escrito em caixa baixa ou minúsculas, dentro do operador diamond ou spaceship <> da seguinte forma:

$nome_da_variável = <stdin>;

Em seguida, para se retirar o caráter de nova linha (tecla ENTER), usa-se a função chomp com ou sem os parênteses:

chomp $nome_da_variável;

Nas linhas mencionadas, optamos por compactar as duas instruções em uma apenas:

chomp ($nome_da_variável = <STDIN>);

Observe na linha 7 e nas seguintes contendo a função print o uso do caráter ou código especial, também conhecido por escape sequence, de nova linha, \n, e também daquele de tabulação horizontal, \t.

O operador de multiplicação (linha 11) não é X (xis, vezes), mas sim * (asterisco).

O operador de potenciação (linhas 12 e 18) não é ^ (circunflexo), mas sim ** (asterisco duplo). Veja mais adiante (linha 27) que o circunflexo é o operador bitwise xor (exclusive or, exclusivo ou).

Não há propriamente um operador para se obter o quociente inteiro, mas isto foi feito com a função int aplicada à divisão. Observe também nesta linha 16 a escape sequence para a barra invertida, \\.

A radiciação (linha 18) é obtida ao se elevar o primeiro valor (base) ao expoente que é o inverso do segundo valor. Se este segundo valor for 2, então temos a raiz quadrada.

As linhas 22 e 23 garantem que Perl considere as entradas do usuário como números, somando-as a zero. Perl trata os caracteres alfanuméricos, dígitos e letras, como variáveis escalares, sem muita distinção.

Observe que para não repetir a variável foi usado o operador aritmético de atribuição += por causa da seguinte equivalência entre

operando operador= expressão;

e

operando = operando operador expressão;

Neste caso, o operando é $n1 ou $n2, o operador é +, e a expressão é 0

As linhas 28-31 apresentam os operadores bitwise shift right e shift left, deslocamento à direita e à esquerda. O valor do deslocamento (offset) usado foi 1, mas poderia ser outro, inclusive um dos valores informados – neste exemplo, 5 e 3.

  • Reflexão:

Viver é fazer escolhas. Há escolhas que são conscientes, que dependem da nossa vontade e decisão. Outras são inconscientes, alheias à nossa vontade e decisão. Sabe diferenciar entre as escolhas que você faz de forma ativa e aquelas que são feitas de forma passiva, por você ou para você? Gostar, por exemplo, é você mesmo ou mesma que escolhe? Você escolhe seu rumo ou deixa a vida te levar? 

Assembly – primeiro programa

Assembly é a linguagem de programação de baixo nível que substitui as instruções de linguagem de máquina por instruções mnemônicas. A máquina, neste caso, é o microprocessador do dispositivo a ser programado. O Assembly é específico da arquitetura ou do conjunto de instruções da família de microprocessadores. O aplicativo que faz esta substituição, também chamada de montagem, é o assembler. O assembler é específico do sistema operacional e do microprocessador. Neste post, que deve ser o primeiro de uma série, será feita referência ao Assembly da arquitetura de CPU x86 e ao Netwide Assembler (NASM).

  • Design-time
; nasm -f elf nome_do_programa.asm
; ld -m elf_i386 -s -o nome_do_programa nome_do_programa.o
; ./nome_do_programa
; [label]	opcode	[operand,]	[;comment]

	section	.data
SYS_WRITE	equ	4
STDOUT		equ	1
msg		db	'Oi, mundo!', 10	;string, caráter NewLine
msgLen		equ	$ - msg			;tamanho da string	
SYS_EXIT	equ	1			

	section	.text
	global	_start		;entry point					
_start:	
	mov	eax, SYS_WRITE	;função escrever
	mov	ebx, STDOUT	;argumento 1
	mov	ecx, msg	;argumento 2
	mov	edx, msgLen	;argumento 3
	int	0x80		;call kernel - executa a syscall apontada em eax

	mov	eax, SYS_EXIT	;função sair
;	mov	ebx, 0		;argumento (execução com sucesso)
	int	0x80		;call kernel - executa a syscall apontada em eax
  • Runtime
x86 Assembly rodando no Ubuntu 22.04 (Jammy Jellyfish), captura com Peek
  • Sintaxe

No código-fonte, todo comentário é precedido pelo ponto-e-vírgula.

A linha 1 comenta como se cria o código-objeto (hello3.o) a partir do código-fonte (hello3.asm).

A linha 2 comenta como se cria (e liga) o código executável (hello3) ao código-objeto (hello3.o).

A linha 3 comenta como se roda o código executável cujo nome, no Linux, não tem extensão e segue o comando ./ equivalente à instrução run ou execute.

Pelo comentário da linha 4, vemos que o código é composto basicamente por mnemônicos que são as instruções executáveis (operadores, opcodes), podendo haver rótulos (labels), operandos (argumentos) e comentários.

Este código tem duas seções ou dois segmentos:

.data, onde se definem as constantes com o opcode EQU (linhas 7, 8, 10 e 11) e se declaram as variáveis com o opcode DB (linha 9) se forem inicializadas em bytes.

.text, onde se encontram as demais instruções que, neste exemplo, são os opcodes MOV e INT.

Neste exemplo, a instrução (operador) MOV tem dois operandos: o primeiro é o nome do registro (destino da transferência) e o segundo é o valor de uma constante definida ou de uma variável inicializada (origem da transferência).

Carrega-se o valor 4, referente à função de escrita, no registro EAX (Acumulador). Os argumentos dessa função são carregados pela ordem nos registros EBX (Base), ECX (Contador) e EDX (Dados). O primeiro argumento é a saída padrão do terminal. O segundo argumento é o ponteiro inicializado com o endereço da string (“Oi, mundo!”) a ser escrita. E o terceiro argumento é o comprimento dessa string.

O operador (instrução) INT com o operando hexadecimal 0x80 chama o kernel para executar a função apontada no registro acumulador. Na linha 20, chama a função de escrita (sys_write) , e na linha 24, a função de saída final (sys_exit).

Referências:

NASM

Linux Assembly

Learn Assembly Language

Learn NASM Assembly

  • Reflexão

Nenhum imóvel de luxo foi comprado com dinheiro vivo ou ‘por fora’ durante o planejamento e a escrita desse capítulo, assim como nenhum apartamento funcional foi mantido para ‘comer gente’. Isso é garantido, pelo menos para o autor e certamente a maioria da classe trabalhadora.