segunda-feira, 5 de setembro de 2011

MSX ainda dando no couro!

É isso aí pessoal ...

Uma gripe me pegou nesta segunda-feira e eu precisei trabalhar em casa...

Para tornar a rotina mais agradável, resolvi usar o Editor de textos no MSX para ir anotando minha rotina de trabalho, muito BOM e GRATIFICANTE!

Eis as provas:



domingo, 4 de setembro de 2011

Dispositivos USB no MSX

Caros,

Segue aqui o primeiro post de uma série que pretendo fazer para demonstrar meu novo projeto de uma placa adaptadora interna para os MSX que converte sinais USB para saída Joystick e Teclado no MSX, convertendo inclusive sinais de Teclado, Mouse, Joystick, Touchpad para serem usados como dispositivos normais no MSX;

O projeto inicialmente está sendo feito em meu Expert DDX 2.0, Fonte Original;

Estou utilizando um HUB na entrada de uma Placa FEZ Domino, que por sua vez comanda uma série de periféricos USB, inclusive Pen Drives, e que possibilita via IOs do USBizi .NET fazer a interação com o Hardware do MSX.

Para começar o trabalho todo, primeiro precisamos conhecer como funciona o Scan do Teclado do MSX.

Repetindo a experiencia que o Tabajara fez e publicou em seu site (http://www.tabalabs.com.br/msx/adpkbd/) posto aqui os resultados, até o ponto que nos interessa no momento, que é entender como funciona o scan de teclado do MSX. Com isso podemos inclusive fazer um hardware utilizando um R8C ou PIC/AVR para interagir com o mesmo (clique na imagem para ampliar);


Diferente do Tabajara que utilizou um HP16500C para debugar o código scan do teclado, eu utilizei um Saleae Logic; simplesmente porquê achei mais pratico para ler apenas 4 canais da PPI do MSX;

Notem que precisamos setar o Port B da PPI com os 8 bits relativos à linha que é informada pela PPI. Dessa forma, conseguiremos simular o teclado;

Para informação, segue as fotos da PPI em teste;



Por enquanto é só. Nos próximos POSTs vamos entender como funciona o Joysitck e como funciona o USBizi, que é o hardware escolhido para esta tarefa;

quarta-feira, 29 de junho de 2011

Upgrade do Expert 1.1 para MSX2 com Kit DDX

Após algumas (poucas) tentativas de botar pra funcionar um KIT de MSX2 no meu Expert 1.1 acabei levando o mesmo no Tabalabs e foi resolvida a pendenga ...

Inicialmente instalei o Kit no Bus Expansion ... mas o micro não iniciava ... depois o Tabajara me alertou que provavelmente eu liguei invertido os fios poois a pinagem do Bus Expansion é diferente da pinagem dos Slots ...

Por fim ... o Taba desmontou tudo e acabou religando no Slot Frontal ... funcionou 100% ...

Agora é voltar para os projetos de MSX que ficaram praticamente 30 dias engavetados ...

Por fim uma foto no final do processo Fudebal... lá no TabaLabs ...

quarta-feira, 22 de junho de 2011

Conserto de Teclado do Gradiente Expert MSX 1.1

Como todos já sabem, eu ando brincando com uns Computadores da linha MSX. Recentemente comprei um Expert 1.1 e seu teclado não funcionava a contento ... por vezes falhando a tecla de espaço (que é geralmente utilizada para Tiros nos jogos) ...
Pois bem ao abrir o mesmo, notei que o elemento condutor (acho que é borracha) do teclado se desfez com o tempo... virando um pó e eventualmente não dava contato ...
Como as chances de encontrar uma membrana dessas hoje em dia é rara e eu diria que é praticamente nula, eu resolvi fazer um gambia**... quer dizer... solução alternativa para colocar meu brinquedo pra funcionar novamente...
Eis as fotos ... e uma pequena legenda após cada uma ...

Para quem não conhece... o teclado do Expert ...
O teclado aberto. Vejam a placa e o único chip ...
É um 74LS145 ... serve para converter a entrada binária para sequencial, no circuito do MSX serve para selecionar qual linha da matriz de teclado será lida no momento.
Notem na membrana, a borracha que aparentemente derreteou ou se esfarelou e espalhou-se no local. parece que alguém passou uma borracha daquelas de apagar lápis por cima.

Uma visão do teclado aberto, por aí dá pra visualizar a placa de cobre, os conectores, o LED piloto e a membrana.
Com um estilete eu cortei a metade de uma célula condutiva da membrana, sempre deixando o suficiente para que o botão que doou o "rim" para o outro botão não pare de funcionar.
Aqui em detalhes o pedaço que foi extraído.
Com fita térmica/isolante, fixei o novo contato, tomando o cuidado de posicionar corretamente o novo contato.
O local de onde foi tirado o pedaço, também foi isolado.
Aproveitando que o teclado estava aberto, substituí o led vermelho por um led azul.
Agora o teclado fechado, e ligado com o LED piloto aceso.
Agora o teclado funcionando ... rodando Zanac no monitor ... além da elegância nata deste ser que vos escreve ... 

PS: Utilizado o corretor ortográfico Tabajara;

sexta-feira, 11 de fevereiro de 2011

Transformando uma Caixa de HD Externo em DiskCase para MSX

Fudebas, nerds e indecisos... este é meu primeiro POST de 2011... e quero fazer algo diferente ... já que resolvi virar criança novamente e botar as mãozinhas em máquinas japonesas (calma gente , computadores, não pensem besteira!) ... eu resolvi colocar no ar um pouco do trabalho para preparar meu MSX 1.1 (Expert Gradiente) ...
Comprei uma interface e um drive de 3.5"para poder fazer meu pequeno MSX conversar com o mundo lá fora ... e deu certo ... vejam o processo de preparação do Case ...

Aqui as partes do case, observem o frame metálico com a fonte, e a frente com a tampa em formato 5.25" ...

Aqui um close no frame metálico... notem o led indicador de alimentação da fonte, um LUXO!


Aqui o drive de 3.5"que vamos colocar no frame metálico...


Aproveitando os postes metálicos do HD que tinha no lugar, encaixei o drive no frame. Como a furação deste tipo de periférico é padrão, facilitou muito a minha vida... Agora só falta a frente e as conexões elétricas...


Detalhe do drive encaixado por cima ...


Detalhe lateral do drive encaixado, notem os postes metálicos (Dourados) que sustentam o drive na posição...


Com a Dremel (tá uma cópia de Dremel), fiz o corte para passar a cabeça do Disk Drive ... esse ficou bao patrão, pra não falar que ficou "meio porco" (nada contra os Palmeirenses ok?) ... sem contar que fazer isso as 23:00 Hs ... numa quinta-feira em um prédio residencial pode render uma bela multa condominial... manzzz me tranquei no quartinho da bagunça e mandei bala... o resultado do corte é esse aí ó!


Mais um detalhe do corte ...


Para a ligação elétrica eu peguei um conector para drive de 3.5" de uma fonte de PC... foi só soldar os fios ligando as cores (duhhhhh) e fazer um acabamento com fita isolante... usei uma auto-fusão da 3M para que no futuro não fique com o adesivo melado (eu odeio isso!) ...


Mais um detalhe de ligação elétrica ...


Agora um detalhe com a tampa da frente encaixada... notem o logotipo do fabricante do case no lado direito... também foi devidamente retirado depois desta foto ... encaixamos o cabo do drive (vou fazer outro POST sobre a preparação do mesmo depois!) ... ligamos o mesmo na interface... eu estou usando uma interface da DDX versão 3.0 (valeu Paulo Maluf!) ...


E está aí o conjunto ligado com a indicação da versão do DiskBasic do cartucho DDX ...


Drive Fechado com o disquete sen introduzido (UI!) no drive ... olhem como ficou o aspecto geral da montagem... eu achei muito positivo! Só o MSX que tá lanhado e precisa de um TAPA... motivo pra outro POST!...


Detalhe do case ligado com o drive e disquete inserido!


Agora exibindo o diretório do disco ...


Carregando o programinha de teste ...


Rodando o programa de teste ...


CTRL-STOP ... Listagem do programa ... Salvando o programa com outro nome e exibindo o diretório do disco ...


Rodando o programa e capturando uma imagem em alta pra fazer um fundo de tela FUDEBA pro meu Windows....


Aqui... formatando um disquete...


Bom pessoal é isso ... grande abraço a todos e daqui há pouco tem mais MSX na parada !!!

Grande abraço do Pacman! e da turmo do 5vcc pra todo mundo! Feliz 2011

sábado, 25 de setembro de 2010

Promoção GM com RFID

No início do ano de 2010 começamos um projeto para a GM usando RFID para identificar os ganhadores de uma promoção de sorteio de carro por meio de uma gravata da GM (gravata = aquele simbolo da GM) que o consumidor recebia via revista e que depois foi entregue na rua, em semáforos pela cidade de São Paulo e outros locais.
Os leitores foram espalhados pelo Brasil afora.

Este é o Spot de TV da Promoção:



E aqui um pouco da produção:

Esta foto mostra o detalhe do carro e o local onde deveríamos aproximar a gravata para acionar o leitor RFID.

Esta foto mostra o painel inteiro.

Só por curiosidade (sei que alguns amigos vão gostar disso, né Sir Jorge!) ... é um ploter/router Zundy ... animal para cortar adesivo/materiais plásticos.

Aqui está a instalação do conjunto leitor por trás do painel.

Aqui uma visão mais ampla do painel.

Gravatas de teste que acompanhavam cada um dos equipamentos.

O pessoal embalando os painéis para seguir viagem até as concessionárias.

Mais uma vez o pessoal embalando tudo!

No final foi uma ação muito legal. Funcionou direitinho, fora a correria de produzir e conferir os equipamentos no tempo que foi necessário, tudo correu bem!

E vamos para o próximo trabalho. Depois vou colocar alguns detalhes do leitor de RFID utilizado para comunicar com as gravatas.

Enjoy!

sexta-feira, 10 de setembro de 2010

Arduino + .NET

É com imenso prazer que venho trazer pra vocês o NetDuíno!
Fantástico ... o sonho dourado de quem trabalha com .NET e também trabalha com microcontroladores...


Pessoalmente sempre achei o Arduino uma solução legal, mas com uso um pouco restrito na prática... sabe aquela coisa de achar legal, mas não usar por querer algo mais personalizado nos seus designs com MCUs.

Pensem bem nas vantagens:

- Ambiente único de desenvolvimento, tanto para MCUs quanto para Aplicações Desktop que podem inclusive comunicar-se com o NetDuíno!
- Não será necessário aprender novos conceitos de programação.
- É possível usar classes e orientação a objeto, o que traz uma certa flexibilidade de reaproveitamento de código!

Para quem quiser mais informações é só dar uma olhada no site http://netduino.com/.

Enjoy! ;-)

terça-feira, 31 de agosto de 2010

I2C BitBang Codigo Genérico

Pessoal,

Depois de muito penar com (PASMEM!) uma 24C02, eu decidi rever meu código de I2C e encontrei um problema que foi solucionado (com o auxilio do nosso amigo Arão, lá da Onipresente PICLIST!), nesta nova versão que apresento do meu código de I2C.
O problema básico é que não estava sendo resetando o BUS I2C a cada transação, o que as vezes ocasionava uma travada no BUS que só voltava após o reset do circuito.
Também notei (à duras penas!) que os Datasheets em sua maioria não falam sobre este Reset no BUS, o que confundiu um pouco. Mas vamos deixar e palavras e vamos ao código.

Primeiro um .C + .H para as 24C02:

- Inicio de 24C02.c ----------------------------------------------------------------------------
#include "sfr_r81b.h"
#include "24C02.h"

// ---------------------- Low Level Functions ---------------

void waitFactor(unsigned int factor) {
unsigned int fc=0;
factor*=waitUnit;
for(fc=0; fc<factor; fc++) asm("NOP");
}
unsigned char getSDAState(void) { _24C02_sda_d = 0; asm("NOP"); return(_24C02_sda); }
unsigned char getSCLState(void) { _24C02_scl_d = 0; asm("NOP"); return(_24C02_scl); }
void setSDAHigh(void) { _24C02_sda_d = 1; _24C02_sda = 1; }
void setSCLHigh(void) { _24C02_scl_d = 1; _24C02_scl = 1; }
void setSDALow(void) { _24C02_sda_d = 1; _24C02_sda = 0; }
void setSCLLow(void) { _24C02_scl_d = 1; _24C02_scl = 0; }
void clockToHigh(void) { setSCLHigh(); waitFactor(1); }
void clockToLow(void) { setSCLLow(); waitFactor(1); }
void clockPulse(void) { clockToHigh(); clockToLow(); }
void sendBit(unsigned char bit) {
if(bit) {
setSDAHigh();
} else {
setSDALow();
}
waitFactor(1);
setSCLHigh(); waitFactor(1);
setSCLLow();  waitFactor(1);
}
unsigned char resetBus (void) {
unsigned char sdaStatus;
clockToHigh();
sdaStatus=getSDAState();
clockToLow();
return(sdaStatus==0?BUSY:ONLINE);
}
void startCondition(void) {
setSDAHigh(); waitFactor(1);
setSCLHigh(); waitFactor(1);
setSDALow(); waitFactor(1);
setSCLLow(); waitFactor(1);
}
void stopCondition(void) {
setSDALow(); waitFactor(1);
setSCLHigh(); waitFactor(1);
setSDAHigh(); waitFactor(1);
setSCLHigh(); waitFactor(1);
}
void writeByte(unsigned char byte) {
unsigned char rotateLeft;
unsigned char tmp;
unsigned char toReturn;
rotateLeft = 0;
while(rotateLeft<8) {
tmp=byte;
tmp=(tmp << rotateLeft) >> 7;
rotateLeft++;
sendBit(tmp);
}
}
unsigned char readByte(void) {
unsigned char byte=0;
unsigned char pos=0;
_24C02_sda_d=0;
while(pos<8) {
clockToHigh();
byte|=getSDAState();
if(pos<7) byte<<=1;
pos++;
clockToLow();
}
return(byte);
}
unsigned char readAck (void) {
unsigned char sdaStatus;
clockToHigh();
sdaStatus=getSDAState();
clockToLow();
return(sdaStatus==0?OK:NOK);
}

// ---------------------- High Level Functions ---------------

void Write_24C02(unsigned char deviceAddress, unsigned char menAddress, unsigned char byte) {
unsigned char myAck=0;
reset_bus:
while(resetBus()==BUSY);
device_address:
startCondition();
writeByte(deviceAddress);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
men_address:
writeByte(menAddress);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
send_byte:
writeByte(byte);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
stopCondition();
}

unsigned char Read_24C02(unsigned char deviceAddress, unsigned char menAddress) {
unsigned char myAck=0;
unsigned char byte=0;
reset_bus:
while(resetBus()==BUSY);
device_address:
startCondition();
writeByte(deviceAddress);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
men_address:
writeByte(menAddress);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
restart_condition:
startCondition();
device_address_read:
writeByte(deviceAddress | 0x01);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
read_byte:
byte = readByte();
sendBit(1);
stopCondition();
return(byte);
}

- Final de 24C02.c ----------------------------------------------------------------------------

- Inicio de 24C02.h ----------------------------------------------------------------------------
#ifndef _24C02_H
#define _24C02_H

#define _24C02_sda_d pd3_4
#define _24C02_sda p3_4
#define _24C02_scl_d pd3_5
#define _24C02_scl p3_5

#define BUSY 0
#define ONLINE 1
#define OK 1
#define NOK 0

#define waitUnit 2

// ---------------------- High Level Functions ---------------

void Write_24C02(unsigned char deviceAddress, unsigned char menAddress, unsigned char byte);
unsigned char Read_24C02(unsigned char deviceAddress, unsigned char menAddress);

#endif
- Final de 24C02.h ----------------------------------------------------------------------------


E agora o mesmo para 24C256:

- Inicio de 24C256.c ----------------------------------------------------------------------------
#include "sfr_r81b.h"
#include "24C256.h"

// ---------------------- Low Level Functions ---------------
void waitFactor(unsigned int factor) {
unsigned int fc=0;
factor*=waitUnit;
for(fc=0; fc<factor; fc++) asm("NOP");
}
unsigned char getSDAState(void) { _24C256_sda_d = 0; asm("NOP"); return(_24C256_sda); }
unsigned char getSCLState(void) { _24C256_scl_d = 0; asm("NOP"); return(_24C256_scl); }
void setSDAHigh(void) { _24C256_sda_d = 1; _24C256_sda = 1; }
void setSCLHigh(void) { _24C256_scl_d = 1; _24C256_scl = 1; }
void setSDALow(void) { _24C256_sda_d = 1; _24C256_sda = 0; }
void setSCLLow(void) { _24C256_scl_d = 1; _24C256_scl = 0; }
void clockToHigh(void) { setSCLHigh(); waitFactor(1); }
void clockToLow(void) { setSCLLow(); waitFactor(1); }
void clockPulse(void) { clockToHigh(); clockToLow(); }
void sendBit(unsigned char bit) {
if(bit) {
setSDAHigh();
} else {
setSDALow();
}
waitFactor(1);
setSCLHigh(); waitFactor(1);
setSCLLow();  waitFactor(1);
}
unsigned char resetBus (void) {
unsigned char sdaStatus;
clockToHigh();
sdaStatus=getSDAState();
clockToLow();
return(sdaStatus==0?BUSY:ONLINE);
}
void startCondition(void) {
setSDAHigh(); waitFactor(1);
setSCLHigh(); waitFactor(1);
setSDALow(); waitFactor(1);
setSCLLow(); waitFactor(1);
}
void stopCondition(void) {
setSDALow(); waitFactor(1);
setSCLHigh(); waitFactor(1);
setSDAHigh(); waitFactor(1);
setSCLHigh(); waitFactor(1);
}
void writeByte(unsigned char byte) {
unsigned char rotateLeft;
unsigned char tmp;
unsigned char toReturn;
rotateLeft = 0;
while(rotateLeft<8) {
tmp=byte;
tmp=(tmp << rotateLeft) >> 7;
rotateLeft++;
sendBit(tmp);
}
}
unsigned char readByte(void) {
unsigned char byte=0;
unsigned char pos=0;
_24C256_sda_d=0;
while(pos<8) {
clockToHigh();
byte|=getSDAState();
if(pos<7) byte<<=1;
pos++;
clockToLow();
}
return(byte);
}
unsigned char readAck (void) {
unsigned char sdaStatus;
clockToHigh();
sdaStatus=getSDAState();
clockToLow();
return(sdaStatus==0?OK:NOK);
}

// ---------------------- High Level Functions ---------------
void Write_24C256(unsigned char deviceAddress, unsigned int menAddress, unsigned char byte) {
unsigned char myAck=0;
unsigned int tmsb,tlsb;
unsigned char msb, lsb;
tlsb = tmsb = menAddress;
tmsb = tmsb >> 8; 
tlsb = (tlsb << 8) >> 8;
msb = (unsigned char) tmsb; 
lsb = (unsigned char) tlsb;
reset_bus:
while(resetBus()==BUSY);
device_address:
startCondition();
writeByte(deviceAddress);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
men_address:
writeByte(msb);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
men_address1:
writeByte(lsb);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
send_byte:
writeByte(byte);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
stopCondition();
}

unsigned char Read_24C256(unsigned char deviceAddress, unsigned int menAddress) {
unsigned char myAck=0;
unsigned char byte=0;
unsigned int tmsb,tlsb;
unsigned char msb, lsb;
tlsb = tmsb = menAddress;
tmsb = tmsb >> 8; 
tlsb = (tlsb << 8) >> 8;
msb = (unsigned char) tmsb; 
lsb = (unsigned char) tlsb;
reset_bus:
while(resetBus()==BUSY);
device_address:
startCondition();
writeByte(deviceAddress);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
men_address:
writeByte(msb);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
men_address1:
writeByte(lsb);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
restart_condition:
startCondition();
device_address_read:
writeByte(deviceAddress | 0x01);
myAck=readAck();
if(myAck==NOK) goto reset_bus;
read_byte:
byte = readByte();
sendBit(1);
stopCondition();
return(byte);
}

- Final de 24C256.c ----------------------------------------------------------------------------

- Inicio de 24C256.h ----------------------------------------------------------------------------
#ifndef _24C256_H
#define _24C256_H

#define _24C256_sda_d pd3_4
#define _24C256_sda p3_4
#define _24C256_scl_d pd3_5
#define _24C256_scl p3_5

#define BUSY 0
#define ONLINE 1
#define OK 1
#define NOK 0

#define waitUnit  2

// ---------------------- High Level Functions ---------------

void Write_24C256(unsigned char deviceAddress, unsigned int menAddress, unsigned char byte);
unsigned char Read_24C256(unsigned char deviceAddress, unsigned int menAddress);

#endif
- Final de 24C256.h ----------------------------------------------------------------------------

Para utilizar os códigos é bem simples.

Basta incluir o arquivo equivalente no header do programa onde vai usar, obviamente colocar o arquivo .C no projeto para ser compilado, e chamar as funções do que se deseja fazer, ler ou gravar.

Exemplo:
#include "24C02.h"

// Protótipos
void main(void);

// Variáveis Globais
unsigned char i=0;
unsigned char carToWrite;
unsigned char car;
unsigned char paraGravar;
unsigned char inicio, final;
unsigned char sdaState;
// Ponto de Entrada
void main(void) {
carToWrite=0;
car=0;
for(;;) {
inicio=0;
final=255;
for(i=inicio;i
Write_24C02(0xA0, i, i);
car=0xFF;
car=Read_24C02(0xA0, i);
if(i!=car) {
asm("nop");
}
}
asm("nop");
}
}

Bom é isso ...

Espero que o código ajude... se precisarem de algo, tipo um projeto pronto e rodando para o R8C/1A por exemplo.. é só me avisar que eu posto um link de download aqui!

Enjoy!


domingo, 1 de agosto de 2010

Apple Lisa

Quando me deparei com este video nao pude deixar de posta-lo.... eh o principio de tudo que conhecemos hoje como informatica, interface homem-maquina ...



Enjoy!

domingo, 13 de junho de 2010

Moon, Beauty Moon

Quero inaugurar aqui um pouco do que venho conhecendo de novas tecnologias, aliás este sempre foi o intuito deste blog... vamos lá... neste final de semana após vários meses, resolvi dar uma olhada no Lua...

Lua é uma linguagem nacional, desenvolvida pelo pessoal da PUC-Rio... me pareceu bem legal, e como eu costumo sempre postar um programinha de exemplo no que mostro pro pessoal, vamos lá... dando um bypass no velho hello world, eu preferi algo um pouco mais efetivo, um programa que imprime a tabuada de um determinado número passado como parâmetro. Veja abaixo:


#!/usr/bin/lua
-- ****************************************************
-- * Tabuada
-- * O argumento de linha e o numero da tabuada que deseja imprimir
-- * Por: Pacman (Alexandre Pereira)
-- * Data: 13/06/2010 - (Um domingo Frio pra Kct!)
-- ****************************************************

numero = arg[1]
if numero == nil then
print("E necessario passar um numero como argumento para imprimir sua tabuada...")
else
print("Tabuada do numero " .. numero)
for multiplicador = 1,10 do
print(multiplicador * numero)
end
end

-- *******************************

Vamos comentar o programa linha por linha,

numero = arg[1]

Com isso pegamos o primeiro parametro da linha de comando;

if numero == nil the

Neste if, verificamos se foi passado o parametro por linha de comando;

Se não foi nós mostramos a seguinte mensagem:

print("E necessario passar um numero como argumento para imprimir sua tabuada...")

caso tenha sido passado o valor como parâmetro executamos as seguintes passos ...

Primeiro imprimimos uma mensagem para mostrar de qual numero a tabuada que está sendo impressa faz parte:

print("Tabuada do numero " .. numero)

em seguida executamos um laço for:

for multiplicador = 1,10 do 

Contando de 1 a 10, e colocando seu valor na variável multiplicador, em seguida imprimimos o valor de numero multiplicado pela variável multiplicador ...

print(multiplicador * numero)

...

Espero que gostem do post e que isso incentive vocês a estudarem mais esta interessante linguagem ...

Na sequencia, vou fazer o mesmo com Python, e posto aqui as impressões ...

Enjoy!