Pessoal,
Aproveitando toda a hype do ChatGPT, resolvi fazer um teste também.
Eu já tinha testado algumas vezes e achei bem interessante. Mas tinha sido mais para observar a tão citada naturalidade de linguagem e ver como estava a conversação em inglês e português. Em inglês a coisa flui melhor, confesso.
O ChatGPT, caso você tenha passado os últimos meses em Plutão, é um chatbot com inteligência artificial desenvolvido pela OpenAI, com técnicas de aprendizado supervisionado e por reforço. Apesar de toda a empolgação da imprensa em geral, quando lemos a percepção da imprensa especializada, a coisa muda um pouco de figura, como vocês verão abaixo.
Para esse post, resolvi trazer uma conversa que tive com o ChatGPT sobre programação em MSX, coisa básica mesmo. Percebam que a resposta dele é bonita, mas não tão precisa ou correta.
Iniciei perguntando se ele sabe criar códigos para MSX, especificamente BASIC.
Legal! O código é tipo um "Hello, World!". Ele explica ainda como funciona, fala do comando 'END' para encerrar o programa (o que na prática é desnecessário, porque o MSX-BASIC termina o programa automaticamente quando chega no final) e ainda cita que deve ser apertado o botão 'RUN' ou digitar esse comando para iniciar o programa. Sim, o MSX tinha um botão para iniciar (na verdade era só um atalho de teclado, mas vá lá...).
Depois eu pedi a mesma função em MSX-Assembly, vulga "Linguagem de Máquina". Aqui as coisas começam a apertar para a Inteligência Artifical. Vejam só:
Ele começa colocando o programa em $0100, que é realmente uma posição possível, pois não é acessível pelo BASIC (os programas em BASIC começam em $8000). Entretanto, geralmente começávamos em $C000, o ínicio da terceira página do bloco de memórias do MSX.
Ele cria uma alocação para colocar uma string ("Olá, eu sou o Jayme") e termina essa string com um caracter nulo (zero). Lá no começo e usa o registrador HL (um registrador de 16 bits muito utilizado para guardar posição de memória) para anotar onde está a string (algo como um ponteiro do C). Cria uma subrotina "print" e manda o processamento para lá. No final, acabando a função da subrotina "print", o processamento volta para a função principal e pula (veja o comando "jp", de jump) para a subrotina "end" que por sua vez está... vazia! O programa provavelmente ira travar ou apresentar um erro...
A subrotina "print" é bem interessante. Ele carrega ("ld", de load) o conteúdo do endereço registrado em HL para o registrador "a", incrementa em um o endereço de HL ("inc hl), faz um teste lógico com o conteúdo de "a" e toma um decisão: se o conteúdo de "a" é zero/nulo (lembra que ele colocou um zero no final da string?), chegou ao final da string e aí ele desvia para a subrotina "endprint"; se o conteúdo de "a" não é nulo, ele chama um função da BIOS ($bb18) para imprimir um caractere na tela e depois volta para o início da subrotina "print" para imprimir o próximo caractere da string.
Tem outros modos mais rápidos e que gastam menos bytes para fazer isso, como mostrarei abaixo.
De todo modo, algumas cosiderações são pertinentes e devem ser feitas:
* - "or a, a" - Esse é um teste que ele sugere para saber se o caractere de "a" é zero.
(Linguagem de Máquina, Rossini-Figueredo, 1a edição - 1987 - Ed. Aleph)
Ou seja, ele compara "a" com "a"; se "a" já tiver chegado ao zero e for zero, essa operação da zero e ele salta a subrotina "endprint", caso contrário segue em frente. Só que a sintaxe correta é apenas OR A, uma vez que ele faz a operação de A com o que você pedir (no caso A). Assim, OR A, A escreve-se apenas OR A. O mesmo se fôssemos comparar B, seria OR B (de OR A, B). A lógica está correta, a sintaxe está errada.
* - "$bb18" - Em Assembly, muitas vezes, usamos os números invertidos para representar os endereços. Esse endereço aí $bb18, confesso que nunca vi.
Imaginei então que seria $18bb. Fui procurar nos livros aqui em casa porque realmente não me lembrava qual a rotina da BIOS que imprimia caractere na tela. E... não é essa, até porque essa não existe :(
O correto era usar o ponteiro para indicar a subrotina e não o endereço direto. A função que eu usava era a WRTVRM (Write To VRAM), acessível com um CALL $004D, onde o byte a ser impresso estava no registrador (acumulador) "a" e o endereço da VRAM era o de HL. Então vemos que o programa tem alguns erro... E erros sérios!
Além disso, a subrotina "endprint" contém o comando "ret", de Return. Mas não é retorno para onde estava quando foi acessado, é retorno para o MSX-BASIC. Ou seja, acaba o programa. Esse final deveria ser na subrotina "end", que como disse acima, está vazia.
Confesso que não consultei na hora o valor da função da BIOS mas questionei a subrotina e o comando "ret". Então ele corrigiu:
Depois, tivemos uma longa conversa onde eu expliquei todos esses problema.
A função da BIOS talvez devesse ter sido a WRTVRM ($004D), como eu citei acima, desde o início, mas o ChatGPT ainda tentou usar a função RDSLT que apenas lê o byte do endereço apontado pelo registrador HL.
(Fonte: Livro Vermelho do MSX)
Depois tentou fazer outro código usando a mesma lógica, mas com a função da BIOS correta. Exceto que usou o registrador HL para apontar para o endereço da string enquanto, nessa função, ele aponta para o endereço da VRAM.
Bom, abaixo eu mostro o que eu faria de diferente (e que funciona!).
Esse primeiro caso usa a função WRTVRM, carrega o programa para a posição correta na memória e faz a sequência correta dos passos:
WRTVRM: EQU $4D ;ponteiro para a função WRTVRM
ORG $C000 ;inicia o programa
LD HL,0 ;endereço da VRAM - posição 0x0 da tela
LD BC, MESSAGE ;endereço do início da mensagem
INICIO: LD A, (BC) ;carrega a primeira letra em A
CALL WRTVRM ;chama a função
INC HL ;próxima posição na tela
INC BC ;próximo caracter
OR A ;se A for nulo,
JR Z, END ;vai para o fim
JR INICIO ;se A não for nulo, reinicia
END: RET
MESSAGE:DEFM "Olá, eu sou o Jayme"
DEFB 0
Outra opção seria trocar a condicional: ao invés de "OR A", fazer um "CP 0":
WRTVRM: EQU $4D ;ponteiro para a função WRTVRM
ORG $C000 ;inicia o programa
LD HL,0 ;endereço da VRAM - posição 0x0 da tela
LD BC, MESSAGE ;endereço do início da mensagem
INICIO: LD A, (BC) ;carrega a primeira letra em A
CALL WRTVRM ;chama a função
INC HL ;próxima posição na tela
INC BC ;próximo caracter
CP 0 ;testa para saber se A é nulo
JR NZ, INICIO ;se A não for nulo, reinicia
RET ;fim
MESSAGE:DEFM "Olá, eu sou o Jayme"
DEFB 0
Essa última opção economiza alguns bytes. Muito importante se a gente usa um computador lá da década de 1980 com escassos 64KB...
Outra opção seria usar a função LDRIKVM, que copia um bloco inteiro da RAM para a VRAM (ou seja, copia a string inteira). Aqui precisamos de alguns outros registradores.
(Fonte: Livro Vermelho do MSX)
Veja que usaremos outros registradores agora. Como iremos copiar um bloco da RAM, precisamos saber o tamanho da string.
O | l | á | , | e | u | s | o | u | o | J | a | y | m | e | 0 | ||||
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
Aí está, nossa string tem 19 caracteres (20 se fossemos contar o caracter nulo).
LDIRVM: EQU $5C ;ponteiro para a função WRTVRM
ORG $C000 ;inicia o programa
LD DE, 0 ;endereço da VRAM - posição 0x0 da tela
LD HL, MESSAGE ;endereço do início da mensagem
LD BC, 19 ;tamanho da string
CALL LDIRVM ;chama a função
RET ;fim
MESSAGE:DEFM "Olá, eu sou o Jayme"
Vejam como essa fica bem mais enxuta. Tem um ponto aqui que definimos o tamanho da string no código. Poderíamos ter criado uma subrotina que contaria o tamanho da string até o nulo (daria 20) e retiraríamos o nulo (20 - 1 = 19). Outra opção.
E uma última opção seria a função CHPUT ($00A2). Essa função imprime o byte presente no acumulador em modo texto (seja Screen 0, 40x24 ou Screen 1, 32x24). Nesse caso, o processo é similar ao que ele estava fazendo.
(Fonte: Livro Vermelho do MSX)
Aqui seria mais ou menos igual as anteriores, imprimindo um caracter por vez, só que obrigatoriamente em modo texto (Screen 0 ou 1).
CHPUT: EQU $A2 ;ponteiro para a função CHPUT
ORG $C000 ;inicia o programa
LD HL, MESSAGE ;endereço do ínicio da mensagem
INICIO: LD A, (HL) ;carrega a primeira letra em A
CALL CHPUT ;chama a função
INC HL ;próximo caracter
CP 0 ;testa para saber se A é nulo
JR NZ, INICIO ;se A não for nulo, reinicia
RET ;fim
MESSAGE:DEFM "Olá, eu sou o Jayme"
DEFB 0
Quando joguei este código no ChatGPT, ele escreveu isso:
Ele falou exatamente o que o programa faz e o que eu havia pedido lá no início...
Concluindo: a ferramenta é muito poderosa, inquestionável. Mas ainda está longe de ser autosuficiente. A ferramenta parece genial, de outro mundo, desde que você não domine o assunto a ser discutido. Neste caso, você consegue encontrar vários erros.
Cabe lembrar que o ChatGPT está funcionando desde novembro de 2022, ou seja, menos de 3 meses do dia em que escrevo esse post. Imagine como estará em um ou em dez anos...
Em tempo: não me lembrava como era gostoso programar em Assembly!
Caso queiram fazer alguma brincadeira, testem os códigos no vários emuladores online:
Por enquanto é isso!
Nenhum comentário:
Postar um comentário